Uploaded image for project: 'Erlang/OTP'
  1. Erlang/OTP
  2. ERL-1232

TLS Hostname Validation Not Performed when User verify_fun is Supplied

    XMLWordPrintable

    Details

    • Type: Bug
    • Status: Resolved
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: OTP 23
    • Component/s: ssl
    • Labels:
      None

      Description

      When using ssl:connect with a server_name_indication option, the verify option set to verify_peer, and a custom verification fun (the one from the docs is sufficient), there is no code path that performs the hostname check (called from ssl_certificate:validate/3).

      When no verify_fun is supplied, after all the extensions have been checked, and there is still a valid or valid_peer result, the ssl_certificate:validate/3 function is called, thus correctly performing a hostname check.

      The hostname check was added in commit e9b0dbb4a95dbc8e328f08d6df6654dcbe13db09 but not added to the ssl_handshake:validation_fun_and_state/10 clause when a user verify function was supplied.

      This issue can reproduced by performing an `ssl:connect` to wrong.host.badssl.com with that set as the server_name_indication field. Use a simple verify fun as follows, and set verify to verify_peer:

      VerifyFun = fun
          (_Cert, {bad_cert, hostname_check_failed), _UserState) ->
              % If the fix has worked correctly, you should hit this code...
              {fail, hostname_check_failed};
          (_Cert, {bad_cert, Reason}, _UserState) ->
              {fail, Reason};
          (_Cert, {extension, Extension}, UserState) ->
              {unknown, UserState};
          (_Cert, Status, UserState) ->
              {valid, UserState}
      end
      

      We've applied the following patch to get round the issue:

      --- a/ssl_handshake.erl 2020-04-22 10:24:16.841533365 +0100
      +++ b/ssl_handshake.erl 2020-04-22 10:26:47.875577948 +0100
      @@ -1641,34 +1641,45 @@
       %%-------------Handle handshake messages --------------------------------
       validation_fun_and_state({Fun, UserState0}, Role,  CertDbHandle, CertDbRef,
                                ServerNameIndication, CustomizeHostCheck, CRLCheck,
                                CRLDbHandle, CertPath, LogLevel) ->
           {fun(OtpCert, {extension, _} = Extension, {SslState, UserState}) ->
                   case ssl_certificate:validate(OtpCert,
                                                 Extension,
                                                 SslState) of
                       {valid, NewSslState} ->
                           {valid, {NewSslState, UserState}};
                       {fail, Reason} ->
                           apply_user_fun(Fun, OtpCert, Reason, UserState,
                                          SslState, CertPath, LogLevel);
                       {unknown, _} ->
                           apply_user_fun(Fun, OtpCert,
                                          Extension, UserState, SslState, CertPath, LogLevel)
                   end;
      +       (OtpCert, VerifyResult, {SslState, UserState}) when (VerifyResult == valid);
      +                                               (VerifyResult == valid_peer) ->
      +            case ssl_certificate:validate(OtpCert,
      +                                          Extension,
      +                                          SslState) of
      +                {valid, NewSslState} ->
      +                    {valid, {NewSslState, UserState}};
      +                {fail, Reason} ->
      +                    apply_user_fun(Fun, OtpCert, Reason, UserState,
      +                                   SslState, CertPath, LogLevel)
      +            end;
              (OtpCert, VerifyResult, {SslState, UserState}) ->
                   apply_user_fun(Fun, OtpCert, VerifyResult, UserState,
                                  SslState, CertPath, LogLevel)
            end, {{Role, CertDbHandle, CertDbRef, {ServerNameIndication, CustomizeHostCheck}, CRLCheck, CRLDbHandle}, UserState0}};
       validation_fun_and_state(undefined, Role, CertDbHandle, CertDbRef,
                                ServerNameIndication, CustomizeHostCheck, CRLCheck,
                                CRLDbHandle, CertPath, LogLevel) ->
           {fun(OtpCert, {extension, _} = Extension, SslState) ->
                   ssl_certificate:validate(OtpCert,
                                            Extension,
                                            SslState);
              (OtpCert, VerifyResult, SslState) when (VerifyResult == valid) or
                                                      (VerifyResult == valid_peer) ->
                   case crl_check(OtpCert, CRLCheck, CertDbHandle, CertDbRef,
                                   CRLDbHandle, VerifyResult, CertPath, LogLevel) of
                       valid ->
                            ssl_certificate:validate(OtpCert,
      

      We noticed this in 20.1 but it is still an issue in master (where the above diff is from).

      Thanks

        Attachments

          Activity

            People

            Assignee:
            otp_team_ps Team PS
            Reporter:
            acaruth Andrew Caruth
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved: