zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

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