ssl.ex (27915B)
1 defmodule Mint.Core.Transport.SSL do 2 @moduledoc false 3 4 require Logger 5 require Record 6 7 @behaviour Mint.Core.Transport 8 9 # From RFC7540 appendix A 10 @blocked_ciphers MapSet.new([ 11 {:null, :null, :null}, 12 {:rsa, :null, :md5}, 13 {:rsa, :null, :sha}, 14 {:rsa_export, :rc4_40, :md5}, 15 {:rsa, :rc4_128, :md5}, 16 {:rsa, :rc4_128, :sha}, 17 {:rsa_export, :rc2_cbc_40, :md5}, 18 {:rsa, :idea_cbc, :sha}, 19 {:rsa_export, :des40_cbc, :sha}, 20 {:rsa, :des_cbc, :sha}, 21 {:rsa, :"3des_ede_cbc", :sha}, 22 {:dh_dss_export, :des40_cbc, :sha}, 23 {:dh_dss, :des_cbc, :sha}, 24 {:dh_dss, :"3des_ede_cbc", :sha}, 25 {:dh_rsa_export, :des40_cbc, :sha}, 26 {:dh_rsa, :des_cbc, :sha}, 27 {:dh_rsa, :"3des_ede_cbc", :sha}, 28 {:dhe_dss_export, :des40_cbc, :sha}, 29 {:dhe_dss, :des_cbc, :sha}, 30 {:dhe_dss, :"3des_ede_cbc", :sha}, 31 {:dhe_rsa_export, :des40_cbc, :sha}, 32 {:dhe_rsa, :des_cbc, :sha}, 33 {:dhe_rsa, :"3des_ede_cbc", :sha}, 34 {:dh_anon_export, :rc4_40, :md5}, 35 {:dh_anon, :rc4_128, :md5}, 36 {:dh_anon_export, :des40_cbc, :sha}, 37 {:dh_anon, :des_cbc, :sha}, 38 {:dh_anon, :"3des_ede_cbc", :sha}, 39 {:krb5, :des_cbc, :sha}, 40 {:krb5, :"3des_ede_cbc", :sha}, 41 {:krb5, :rc4_128, :sha}, 42 {:krb5, :idea_cbc, :sha}, 43 {:krb5, :des_cbc, :md5}, 44 {:krb5, :"3des_ede_cbc", :md5}, 45 {:krb5, :rc4_128, :md5}, 46 {:krb5, :idea_cbc, :md5}, 47 {:krb5_export, :des_cbc_40, :sha}, 48 {:krb5_export, :rc2_cbc_40, :sha}, 49 {:krb5_export, :rc4_40, :sha}, 50 {:krb5_export, :des_cbc_40, :md5}, 51 {:krb5_export, :rc2_cbc_40, :md5}, 52 {:krb5_export, :rc4_40, :md5}, 53 {:psk, :null, :sha}, 54 {:dhe_psk, :null, :sha}, 55 {:rsa_psk, :null, :sha}, 56 {:rsa, :aes_128_cbc, :sha}, 57 {:dh_dss, :aes_128_cbc, :sha}, 58 {:dh_rsa, :aes_128_cbc, :sha}, 59 {:dhe_dss, :aes_128_cbc, :sha}, 60 {:dhe_rsa, :aes_128_cbc, :sha}, 61 {:dh_anon, :aes_128_cbc, :sha}, 62 {:rsa, :aes_256_cbc, :sha}, 63 {:dh_dss, :aes_256_cbc, :sha}, 64 {:dh_rsa, :aes_256_cbc, :sha}, 65 {:dhe_dss, :aes_256_cbc, :sha}, 66 {:dhe_rsa, :aes_256_cbc, :sha}, 67 {:dh_anon, :aes_256_cbc, :sha}, 68 {:rsa, :null, :sha256}, 69 {:rsa, :aes_128_cbc, :sha256}, 70 {:rsa, :aes_256_cbc, :sha256}, 71 {:dh_dss, :aes_128_cbc, :sha256}, 72 {:dh_rsa, :aes_128_cbc, :sha256}, 73 {:dhe_dss, :aes_128_cbc, :sha256}, 74 {:rsa, :camellia_128_cbc, :sha}, 75 {:dh_dss, :camellia_128_cbc, :sha}, 76 {:dh_rsa, :camellia_128_cbc, :sha}, 77 {:dhe_dss, :camellia_128_cbc, :sha}, 78 {:dhe_rsa, :camellia_128_cbc, :sha}, 79 {:dh_anon, :camellia_128_cbc, :sha}, 80 {:dhe_rsa, :aes_128_cbc, :sha256}, 81 {:dh_dss, :aes_256_cbc, :sha256}, 82 {:dh_rsa, :aes_256_cbc, :sha256}, 83 {:dhe_dss, :aes_256_cbc, :sha256}, 84 {:dhe_rsa, :aes_256_cbc, :sha256}, 85 {:dh_anon, :aes_128_cbc, :sha256}, 86 {:dh_anon, :aes_256_cbc, :sha256}, 87 {:rsa, :camellia_256_cbc, :sha}, 88 {:dh_dss, :camellia_256_cbc, :sha}, 89 {:dh_rsa, :camellia_256_cbc, :sha}, 90 {:dhe_dss, :camellia_256_cbc, :sha}, 91 {:dhe_rsa, :camellia_256_cbc, :sha}, 92 {:dh_anon, :camellia_256_cbc, :sha}, 93 {:psk, :rc4_128, :sha}, 94 {:psk, :"3des_ede_cbc", :sha}, 95 {:psk, :aes_128_cbc, :sha}, 96 {:psk, :aes_256_cbc, :sha}, 97 {:dhe_psk, :rc4_128, :sha}, 98 {:dhe_psk, :"3des_ede_cbc", :sha}, 99 {:dhe_psk, :aes_128_cbc, :sha}, 100 {:dhe_psk, :aes_256_cbc, :sha}, 101 {:rsa_psk, :rc4_128, :sha}, 102 {:rsa_psk, :"3des_ede_cbc", :sha}, 103 {:rsa_psk, :aes_128_cbc, :sha}, 104 {:rsa_psk, :aes_256_cbc, :sha}, 105 {:rsa, :seed_cbc, :sha}, 106 {:dh_dss, :seed_cbc, :sha}, 107 {:dh_rsa, :seed_cbc, :sha}, 108 {:dhe_dss, :seed_cbc, :sha}, 109 {:dhe_rsa, :seed_cbc, :sha}, 110 {:dh_anon, :seed_cbc, :sha}, 111 {:rsa, :aes_128_gcm, :sha256}, 112 {:rsa, :aes_256_gcm, :sha384}, 113 {:dh_rsa, :aes_128_gcm, :sha256}, 114 {:dh_rsa, :aes_256_gcm, :sha384}, 115 {:dh_dss, :aes_128_gcm, :sha256}, 116 {:dh_dss, :aes_256_gcm, :sha384}, 117 {:dh_anon, :aes_128_gcm, :sha256}, 118 {:dh_anon, :aes_256_gcm, :sha384}, 119 {:psk, :aes_128_gcm, :sha256}, 120 {:psk, :aes_256_gcm, :sha384}, 121 {:rsa_psk, :aes_128_gcm, :sha256}, 122 {:rsa_psk, :aes_256_gcm, :sha384}, 123 {:psk, :aes_128_cbc, :sha256}, 124 {:psk, :aes_256_cbc, :sha384}, 125 {:psk, :null, :sha256}, 126 {:psk, :null, :sha384}, 127 {:dhe_psk, :aes_128_cbc, :sha256}, 128 {:dhe_psk, :aes_256_cbc, :sha384}, 129 {:dhe_psk, :null, :sha256}, 130 {:dhe_psk, :null, :sha384}, 131 {:rsa_psk, :aes_128_cbc, :sha256}, 132 {:rsa_psk, :aes_256_cbc, :sha384}, 133 {:rsa_psk, :null, :sha256}, 134 {:rsa_psk, :null, :sha384}, 135 {:rsa, :camellia_128_cbc, :sha256}, 136 {:dh_dss, :camellia_128_cbc, :sha256}, 137 {:dh_rsa, :camellia_128_cbc, :sha256}, 138 {:dhe_dss, :camellia_128_cbc, :sha256}, 139 {:dhe_rsa, :camellia_128_cbc, :sha256}, 140 {:dh_anon, :camellia_128_cbc, :sha256}, 141 {:rsa, :camellia_256_cbc, :sha256}, 142 {:dh_dss, :camellia_256_cbc, :sha256}, 143 {:dh_rsa, :camellia_256_cbc, :sha256}, 144 {:dhe_dss, :camellia_256_cbc, :sha256}, 145 {:dhe_rsa, :camellia_256_cbc, :sha256}, 146 {:dh_anon, :camellia_256_cbc, :sha256}, 147 {:ecdh_ecdsa, :null, :sha}, 148 {:ecdh_ecdsa, :rc4_128, :sha}, 149 {:ecdh_ecdsa, :"3des_ede_cbc", :sha}, 150 {:ecdh_ecdsa, :aes_128_cbc, :sha}, 151 {:ecdh_ecdsa, :aes_256_cbc, :sha}, 152 {:ecdhe_ecdsa, :null, :sha}, 153 {:ecdhe_ecdsa, :rc4_128, :sha}, 154 {:ecdhe_ecdsa, :"3des_ede_cbc", :sha}, 155 {:ecdhe_ecdsa, :aes_128_cbc, :sha}, 156 {:ecdhe_ecdsa, :aes_256_cbc, :sha}, 157 {:ecdh_rsa, :null, :sha}, 158 {:ecdh_rsa, :rc4_128, :sha}, 159 {:ecdh_rsa, :"3des_ede_cbc", :sha}, 160 {:ecdh_rsa, :aes_128_cbc, :sha}, 161 {:ecdh_rsa, :aes_256_cbc, :sha}, 162 {:ecdhe_rsa, :null, :sha}, 163 {:ecdhe_rsa, :rc4_128, :sha}, 164 {:ecdhe_rsa, :"3des_ede_cbc", :sha}, 165 {:ecdhe_rsa, :aes_128_cbc, :sha}, 166 {:ecdhe_rsa, :aes_256_cbc, :sha}, 167 {:ecdh_anon, :null, :sha}, 168 {:ecdh_anon, :rc4_128, :sha}, 169 {:ecdh_anon, :"3des_ede_cbc", :sha}, 170 {:ecdh_anon, :aes_128_cbc, :sha}, 171 {:ecdh_anon, :aes_256_cbc, :sha}, 172 {:srp_sha, :"3des_ede_cbc", :sha}, 173 {:srp_sha_rsa, :"3des_ede_cbc", :sha}, 174 {:srp_sha_dss, :"3des_ede_cbc", :sha}, 175 {:srp_sha, :aes_128_cbc, :sha}, 176 {:srp_sha_rsa, :aes_128_cbc, :sha}, 177 {:srp_sha_dss, :aes_128_cbc, :sha}, 178 {:srp_sha, :aes_256_cbc, :sha}, 179 {:srp_sha_rsa, :aes_256_cbc, :sha}, 180 {:srp_sha_dss, :aes_256_cbc, :sha}, 181 {:ecdhe_ecdsa, :aes_128_cbc, :sha256}, 182 {:ecdhe_ecdsa, :aes_256_cbc, :sha384}, 183 {:ecdh_ecdsa, :aes_128_cbc, :sha256}, 184 {:ecdh_ecdsa, :aes_256_cbc, :sha384}, 185 {:ecdhe_rsa, :aes_128_cbc, :sha256}, 186 {:ecdhe_rsa, :aes_256_cbc, :sha384}, 187 {:ecdh_rsa, :aes_128_cbc, :sha256}, 188 {:ecdh_rsa, :aes_256_cbc, :sha384}, 189 {:ecdh_ecdsa, :aes_128_gcm, :sha256}, 190 {:ecdh_ecdsa, :aes_256_gcm, :sha384}, 191 {:ecdh_rsa, :aes_128_gcm, :sha256}, 192 {:ecdh_rsa, :aes_256_gcm, :sha384}, 193 {:ecdhe_psk, :rc4_128, :sha}, 194 {:ecdhe_psk, :"3des_ede_cbc", :sha}, 195 {:ecdhe_psk, :aes_128_cbc, :sha}, 196 {:ecdhe_psk, :aes_256_cbc, :sha}, 197 {:ecdhe_psk, :aes_128_cbc, :sha256}, 198 {:ecdhe_psk, :aes_256_cbc, :sha384}, 199 {:ecdhe_psk, :null, :sha}, 200 {:ecdhe_psk, :null, :sha256}, 201 {:ecdhe_psk, :null, :sha384}, 202 {:rsa, :aria_128_cbc, :sha256}, 203 {:rsa, :aria_256_cbc, :sha384}, 204 {:dh_dss, :aria_128_cbc, :sha256}, 205 {:dh_dss, :aria_256_cbc, :sha384}, 206 {:dh_rsa, :aria_128_cbc, :sha256}, 207 {:dh_rsa, :aria_256_cbc, :sha384}, 208 {:dhe_dss, :aria_128_cbc, :sha256}, 209 {:dhe_dss, :aria_256_cbc, :sha384}, 210 {:dhe_rsa, :aria_128_cbc, :sha256}, 211 {:dhe_rsa, :aria_256_cbc, :sha384}, 212 {:dh_anon, :aria_128_cbc, :sha256}, 213 {:dh_anon, :aria_256_cbc, :sha384}, 214 {:ecdhe_ecdsa, :aria_128_cbc, :sha256}, 215 {:ecdhe_ecdsa, :aria_256_cbc, :sha384}, 216 {:ecdh_ecdsa, :aria_128_cbc, :sha256}, 217 {:ecdh_ecdsa, :aria_256_cbc, :sha384}, 218 {:ecdhe_rsa, :aria_128_cbc, :sha256}, 219 {:ecdhe_rsa, :aria_256_cbc, :sha384}, 220 {:ecdh_rsa, :aria_128_cbc, :sha256}, 221 {:ecdh_rsa, :aria_256_cbc, :sha384}, 222 {:rsa, :aria_128_gcm, :sha256}, 223 {:rsa, :aria_256_gcm, :sha384}, 224 {:dh_rsa, :aria_128_gcm, :sha256}, 225 {:dh_rsa, :aria_256_gcm, :sha384}, 226 {:dh_dss, :aria_128_gcm, :sha256}, 227 {:dh_dss, :aria_256_gcm, :sha384}, 228 {:dh_anon, :aria_128_gcm, :sha256}, 229 {:dh_anon, :aria_256_gcm, :sha384}, 230 {:ecdh_ecdsa, :aria_128_gcm, :sha256}, 231 {:ecdh_ecdsa, :aria_256_gcm, :sha384}, 232 {:ecdh_rsa, :aria_128_gcm, :sha256}, 233 {:ecdh_rsa, :aria_256_gcm, :sha384}, 234 {:psk, :aria_128_cbc, :sha256}, 235 {:psk, :aria_256_cbc, :sha384}, 236 {:dhe_psk, :aria_128_cbc, :sha256}, 237 {:dhe_psk, :aria_256_cbc, :sha384}, 238 {:rsa_psk, :aria_128_cbc, :sha256}, 239 {:rsa_psk, :aria_256_cbc, :sha384}, 240 {:psk, :aria_128_gcm, :sha256}, 241 {:psk, :aria_256_gcm, :sha384}, 242 {:rsa_psk, :aria_128_gcm, :sha256}, 243 {:rsa_psk, :aria_256_gcm, :sha384}, 244 {:ecdhe_psk, :aria_128_cbc, :sha256}, 245 {:ecdhe_psk, :aria_256_cbc, :sha384}, 246 {:ecdhe_ecdsa, :camellia_128_cbc, :sha256}, 247 {:ecdhe_ecdsa, :camellia_256_cbc, :sha384}, 248 {:ecdh_ecdsa, :camellia_128_cbc, :sha256}, 249 {:ecdh_ecdsa, :camellia_256_cbc, :sha384}, 250 {:ecdhe_rsa, :camellia_128_cbc, :sha256}, 251 {:ecdhe_rsa, :camellia_256_cbc, :sha384}, 252 {:ecdh_rsa, :camellia_128_cbc, :sha256}, 253 {:ecdh_rsa, :camellia_256_cbc, :sha384}, 254 {:rsa, :camellia_128_gcm, :sha256}, 255 {:rsa, :camellia_256_gcm, :sha384}, 256 {:dh_rsa, :camellia_128_gcm, :sha256}, 257 {:dh_rsa, :camellia_256_gcm, :sha384}, 258 {:dh_dss, :camellia_128_gcm, :sha256}, 259 {:dh_dss, :camellia_256_gcm, :sha384}, 260 {:dh_anon, :camellia_128_gcm, :sha256}, 261 {:dh_anon, :camellia_256_gcm, :sha384}, 262 {:ecdh_ecdsa, :camellia_128_gcm, :sha256}, 263 {:ecdh_ecdsa, :camellia_256_gcm, :sha384}, 264 {:ecdh_rsa, :camellia_128_gcm, :sha256}, 265 {:ecdh_rsa, :camellia_256_gcm, :sha384}, 266 {:psk, :camellia_128_gcm, :sha256}, 267 {:psk, :camellia_256_gcm, :sha384}, 268 {:rsa_psk, :camellia_128_gcm, :sha256}, 269 {:rsa_psk, :camellia_256_gcm, :sha384}, 270 {:psk, :camellia_128_cbc, :sha256}, 271 {:psk, :camellia_256_cbc, :sha384}, 272 {:dhe_psk, :camellia_128_cbc, :sha256}, 273 {:dhe_psk, :camellia_256_cbc, :sha384}, 274 {:rsa_psk, :camellia_128_cbc, :sha256}, 275 {:rsa_psk, :camellia_256_cbc, :sha384}, 276 {:ecdhe_psk, :camellia_128_cbc, :sha256}, 277 {:ecdhe_psk, :camellia_256_cbc, :sha384}, 278 {:rsa, :aes_128, :ccm}, 279 {:rsa, :aes_256, :ccm}, 280 {:rsa, :aes_128, :ccm_8}, 281 {:rsa, :aes_256, :ccm_8}, 282 {:psk, :aes_128, :ccm}, 283 {:psk, :aes_256, :ccm}, 284 {:psk, :aes_128, :ccm_8}, 285 {:psk, :aes_256, :ccm_8} 286 ]) 287 288 @transport_opts [ 289 packet: :raw, 290 mode: :binary, 291 active: false 292 ] 293 294 @default_versions [:"tlsv1.3", :"tlsv1.2"] 295 @default_timeout 30_000 296 297 Record.defrecordp( 298 :certificate, 299 :Certificate, 300 Record.extract(:Certificate, from_lib: "public_key/include/OTP-PUB-KEY.hrl") 301 ) 302 303 Record.defrecordp( 304 :tbs_certificate, 305 :OTPTBSCertificate, 306 Record.extract(:OTPTBSCertificate, from_lib: "public_key/include/OTP-PUB-KEY.hrl") 307 ) 308 309 # TODO: Document how to enable revocation checking: 310 # crl_check: true 311 # crl_cache: {:ssl_crl_cache, {:internal, [http: 30_000]}} 312 313 @impl true 314 def connect(address, port, opts) do 315 hostname = Mint.Core.Util.hostname(opts, address) 316 opts = Keyword.delete(opts, :hostname) 317 318 connect(address, hostname, port, opts) 319 end 320 321 defp connect(address, hostname, port, opts) when is_binary(address), 322 do: connect(String.to_charlist(address), hostname, port, opts) 323 324 defp connect(address, hostname, port, opts) do 325 timeout = Keyword.get(opts, :timeout, @default_timeout) 326 inet6? = Keyword.get(opts, :inet6, false) 327 328 opts = ssl_opts(String.to_charlist(hostname), opts) 329 330 if inet6? do 331 # Try inet6 first, then fall back to the defaults provided by 332 # ssl/gen_tcp if connection fails. 333 case :ssl.connect(address, port, [:inet6 | opts], timeout) do 334 {:ok, sslsocket} -> 335 {:ok, sslsocket} 336 337 _error -> 338 wrap_err(:ssl.connect(address, port, opts, timeout)) 339 end 340 else 341 # Use the defaults provided by ssl/gen_tcp. 342 wrap_err(:ssl.connect(address, port, opts, timeout)) 343 end 344 end 345 346 @impl true 347 def upgrade(socket, :http, hostname, _port, opts) do 348 hostname = String.to_charlist(hostname) 349 timeout = Keyword.get(opts, :timeout, @default_timeout) 350 351 # Seems like this is not set in :ssl.connect/2 correctly, so set it explicitly 352 Mint.Core.Transport.TCP.setopts(socket, active: false) 353 354 wrap_err(:ssl.connect(socket, ssl_opts(hostname, opts), timeout)) 355 end 356 357 def upgrade(_socket, :https, _hostname, _port, _opts) do 358 raise "nested SSL sessions are not supported" 359 end 360 361 @impl true 362 def negotiated_protocol(socket) do 363 wrap_err(:ssl.negotiated_protocol(socket)) 364 end 365 366 @impl true 367 def send(socket, payload) do 368 wrap_err(:ssl.send(socket, payload)) 369 end 370 371 @impl true 372 def close(socket) do 373 wrap_err(:ssl.close(socket)) 374 end 375 376 @impl true 377 def recv(socket, bytes, timeout) do 378 wrap_err(:ssl.recv(socket, bytes, timeout)) 379 end 380 381 @impl true 382 def controlling_process(socket, pid) do 383 # We do this dance because it's what gen_tcp does in Erlang. However, ssl 384 # doesn't do this so we need to do it ourselves. Implementation roughly 385 # taken from this: 386 # https://github.com/erlang/otp/blob/fc1f0444e32b039194189af97fb3d5358a2b91e3/lib/kernel/src/inet.erl#L1696-L1754 387 with {:ok, active: active} <- getopts(socket, [:active]), 388 :ok <- setopts(socket, active: false), 389 :ok <- forward_messages_to_new_controlling_process(socket, pid), 390 :ok <- wrap_err(:ssl.controlling_process(socket, pid)) do 391 if(active == :once, do: setopts(socket, active: :once), else: :ok) 392 end 393 end 394 395 defp forward_messages_to_new_controlling_process(socket, pid) do 396 receive do 397 {:ssl, ^socket, _data} = message -> 398 Kernel.send(pid, message) 399 forward_messages_to_new_controlling_process(socket, pid) 400 401 {:ssl_error, ^socket, error} -> 402 {:error, error} 403 404 {:ssl_closed, ^socket} -> 405 {:error, :closed} 406 after 407 0 -> 408 :ok 409 end 410 end 411 412 @impl true 413 def setopts(socket, opts) do 414 wrap_err(:ssl.setopts(socket, opts)) 415 end 416 417 @impl true 418 def getopts(socket, opts) do 419 wrap_err(:ssl.getopts(socket, opts)) 420 end 421 422 @impl true 423 def wrap_error(reason) do 424 %Mint.TransportError{reason: reason} 425 end 426 427 defp ssl_opts(hostname, opts) do 428 default_ssl_opts(hostname) 429 |> Keyword.merge(opts) 430 |> Keyword.merge(@transport_opts) 431 |> Keyword.drop([:timeout, :inet6]) 432 |> add_verify_opts(hostname) 433 |> remove_incompatible_ssl_opts() 434 |> add_ciphers_opt() 435 end 436 437 defp add_verify_opts(opts, hostname) do 438 verify = Keyword.get(opts, :verify) 439 440 if verify == :verify_peer do 441 opts 442 |> add_cacerts() 443 |> add_partial_chain_fun() 444 |> customize_hostname_check(hostname) 445 else 446 opts 447 end 448 end 449 450 defp remove_incompatible_ssl_opts(opts) do 451 # These are the TLS versions that are compatible with :reuse_sessions and :secure_renegotiate 452 # If none of the compatible TLS versions are present in the transport options, then 453 # :reuse_sessions and :secure_renegotiate will be removed from the transport options. 454 compatible_versions = [:tlsv1, :"tlsv1.1", :"tlsv1.2"] 455 versions_opt = Keyword.get(opts, :versions, []) 456 457 if Enum.any?(compatible_versions, &(&1 in versions_opt)) do 458 opts 459 else 460 opts 461 |> Keyword.delete(:reuse_sessions) 462 |> Keyword.delete(:secure_renegotiate) 463 end 464 end 465 466 defp customize_hostname_check(opts, host_or_ip) do 467 if ssl_version() >= [9, 0] do 468 # From OTP 20.0 use built-in support for custom hostname checks 469 add_customize_hostname_check(opts) 470 else 471 # Before OTP 20.0 use mint_shims for hostname check, from a custom 472 # verify_fun 473 add_verify_fun(opts, host_or_ip) 474 end 475 end 476 477 defp add_customize_hostname_check(opts) do 478 Keyword.put_new(opts, :customize_hostname_check, match_fun: &match_fun/2) 479 end 480 481 defp add_verify_fun(opts, host_or_ip) do 482 Keyword.put_new_lazy(opts, :verify_fun, fn -> 483 reference_ids = [dns_id: host_or_ip, ip: host_or_ip] 484 {&verify_fun/3, reference_ids} 485 end) 486 end 487 488 def verify_fun(_, {:bad_cert, _} = reason, _), do: {:fail, reason} 489 def verify_fun(_, {:extension, _}, state), do: {:unknown, state} 490 def verify_fun(_, :valid, state), do: {:valid, state} 491 492 def verify_fun(cert, :valid_peer, state) do 493 if :mint_shims.pkix_verify_hostname(cert, state, match_fun: &match_fun/2) do 494 {:valid, state} 495 else 496 {:fail, {:bad_cert, :hostname_check_failed}} 497 end 498 end 499 500 # Wildcard domain handling for DNS ID entries in the subjectAltName X.509 501 # extension. Note that this is a subset of the wildcard patterns implemented 502 # by OTP when matching against the subject CN attribute, but this is the only 503 # wildcard usage defined by the CA/Browser Forum's Baseline Requirements, and 504 # therefore the only pattern used in commercially issued certificates. 505 defp match_fun({:dns_id, reference}, {:dNSName, [?*, ?. | presented]}) do 506 case domain_without_host(reference) do 507 '' -> 508 :default 509 510 domain -> 511 # TODO: replace with `:string.casefold/1` eventually 512 :string.to_lower(domain) == :string.to_lower(presented) 513 end 514 end 515 516 defp match_fun(_reference, _presented), do: :default 517 518 defp domain_without_host([]), do: [] 519 defp domain_without_host([?. | domain]), do: domain 520 defp domain_without_host([_ | more]), do: domain_without_host(more) 521 522 defp add_ciphers_opt(opts) do 523 Keyword.put_new_lazy(opts, :ciphers, fn -> 524 versions = opts[:versions] 525 get_ciphers_for_versions(versions) 526 end) 527 end 528 529 defp default_ssl_opts(hostname) do 530 # TODO: Add revocation check 531 532 # Note: the :ciphers option is added once the :versions option 533 # has been merged with the user-specified value 534 [ 535 server_name_indication: hostname, 536 versions: ssl_versions(), 537 verify: :verify_peer, 538 depth: 4, 539 secure_renegotiate: true, 540 reuse_sessions: true 541 ] 542 end 543 544 @doc false 545 def ssl_versions() do 546 available_versions = :ssl.versions()[:available] 547 versions = Enum.filter(@default_versions, &(&1 in available_versions)) 548 549 # Remove buggy TLS 1.3 versions 550 if ssl_version() < [10, 0] do 551 versions -- [:"tlsv1.3"] 552 else 553 versions 554 end 555 end 556 557 defp add_cacerts(opts) do 558 if Keyword.has_key?(opts, :cacertfile) or Keyword.has_key?(opts, :cacerts) do 559 opts 560 else 561 raise_on_missing_castore!() 562 Keyword.put(opts, :cacertfile, CAStore.file_path()) 563 end 564 end 565 566 defp add_partial_chain_fun(opts) do 567 if Keyword.has_key?(opts, :partial_chain) do 568 opts 569 else 570 case Keyword.fetch(opts, :cacerts) do 571 {:ok, cacerts} -> 572 cacerts = decode_cacerts(cacerts) 573 fun = &partial_chain(cacerts, &1) 574 Keyword.put(opts, :partial_chain, fun) 575 576 :error -> 577 path = Keyword.fetch!(opts, :cacertfile) 578 cacerts = get_cacertfile(path) 579 fun = &partial_chain(cacerts, &1) 580 Keyword.put(opts, :partial_chain, fun) 581 end 582 end 583 end 584 585 defp get_cacertfile(path) do 586 if Application.get_env(:mint, :persistent_term) do 587 case :persistent_term.get({:mint, {:cacertfile, path}}, :error) do 588 {:ok, cacerts} -> 589 cacerts 590 591 :error -> 592 cacerts = decode_cacertfile(path) 593 :persistent_term.put({:mint, {:cacertfile, path}}, {:ok, cacerts}) 594 cacerts 595 end 596 else 597 decode_cacertfile(path) 598 end 599 end 600 601 defp decode_cacertfile(path) do 602 path 603 |> File.read!() 604 |> :public_key.pem_decode() 605 |> Enum.filter(&match?({:Certificate, _, :not_encrypted}, &1)) 606 |> Enum.map(&:public_key.pem_entry_decode/1) 607 end 608 609 defp decode_cacerts(certs) do 610 Enum.map(certs, &:public_key.pkix_decode_cert(&1, :plain)) 611 end 612 613 def partial_chain(cacerts, certs) do 614 # TODO: Shim this with OTP 21.1 implementation? 615 616 certs = 617 certs 618 |> Enum.map(&{&1, :public_key.pkix_decode_cert(&1, :plain)}) 619 |> Enum.drop_while(&cert_expired?/1) 620 621 trusted = 622 Enum.find_value(certs, fn {der, cert} -> 623 trusted? = 624 Enum.find(cacerts, fn cacert -> 625 extract_public_key_info(cacert) == extract_public_key_info(cert) 626 end) 627 628 if trusted?, do: der 629 end) 630 631 if trusted do 632 {:trusted_ca, trusted} 633 else 634 :unknown_ca 635 end 636 end 637 638 defp cert_expired?({_der, cert}) do 639 now = DateTime.utc_now() 640 {not_before, not_after} = extract_validity(cert) 641 642 DateTime.compare(now, not_before) == :lt or 643 DateTime.compare(now, not_after) == :gt 644 end 645 646 defp extract_validity(cert) do 647 {:Validity, not_before, not_after} = 648 cert 649 |> certificate(:tbsCertificate) 650 |> tbs_certificate(:validity) 651 652 {to_datetime!(not_before), to_datetime!(not_after)} 653 end 654 655 defp extract_public_key_info(cert) do 656 cert 657 |> certificate(:tbsCertificate) 658 |> tbs_certificate(:subjectPublicKeyInfo) 659 end 660 661 defp to_datetime!({:utcTime, time}) do 662 "20#{time}" 663 |> to_datetime!() 664 end 665 666 defp to_datetime!({:generalTime, time}) do 667 time 668 |> to_string() 669 |> to_datetime!() 670 end 671 672 defp to_datetime!( 673 <<year::binary-size(4), month::binary-size(2), day::binary-size(2), hour::binary-size(2), 674 minute::binary-size(2), second::binary-size(2), "Z"::binary>> 675 ) do 676 {:ok, datetime, _} = 677 DateTime.from_iso8601("#{year}-#{month}-#{day}T#{hour}:#{minute}:#{second}Z") 678 679 datetime 680 end 681 682 defp blocked_cipher?(%{cipher: cipher, key_exchange: kex, prf: prf}), 683 do: blocked_cipher?({kex, cipher, prf}) 684 685 defp blocked_cipher?({kex, cipher, _mac, prf}), do: blocked_cipher?({kex, cipher, prf}) 686 defp blocked_cipher?({_kex, _cipher, _prf} = suite), do: suite in @blocked_ciphers 687 688 defp raise_on_missing_castore! do 689 Code.ensure_loaded?(CAStore) || 690 raise """ 691 default CA trust store not available; please add `:castore` to your project's \ 692 dependencies or specify the trust store using the :cacertfile/:cacerts option \ 693 within :transport_options. From OTP 25, you can also use: 694 695 * :public_key.cacerts_get/0 to get certificates that you loaded from files or 696 * from the OS with :public_key.cacerts_load/0,1 697 698 See: https://www.erlang.org/blog/my-otp-25-highlights/#ca-certificates-can-be-fetched-from-the-os-standard-place 699 """ 700 end 701 702 defp wrap_err({:error, reason}), do: {:error, wrap_error(reason)} 703 defp wrap_err(other), do: other 704 705 @doc false 706 def ssl_version() do 707 Application.spec(:ssl, :vsn) 708 |> List.to_string() 709 |> String.split(".") 710 |> Enum.map(&String.to_integer/1) 711 end 712 713 @doc false 714 def get_ciphers_for_versions(versions) do 715 if ssl_version() >= [8, 2, 4] do 716 # :ssl.filter_cipher_suites/2 is available in ssl v8.2.4+ 717 versions 718 |> Enum.flat_map(&:ssl.filter_cipher_suites(:ssl.cipher_suites(:all, &1), [])) 719 |> Enum.uniq() 720 else 721 :ssl.cipher_suites(:all) 722 end 723 |> Enum.reject(&blocked_cipher?/1) 724 end 725 end