diff options
Diffstat (limited to 'lib/ssl/src/ssl_handshake.erl')
-rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 183 |
1 files changed, 84 insertions, 99 deletions
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 4e2fdd1e8f..7d6c21438e 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -81,9 +81,10 @@ ]). -export([get_cert_params/1, + select_own_cert/1, server_name/3, validation_fun_and_state/4, - handle_path_validation_error/7]). + path_validation_alert/1]). %%==================================================================== %% Create handshake messages @@ -125,30 +126,33 @@ server_hello_done() -> #server_hello_done{}. %%-------------------------------------------------------------------- --spec certificate(der_cert(), db_handle(), certdb_ref(), client | server) -> #certificate{} | #alert{}. +-spec certificate([der_cert()] | undefined, db_handle(), certdb_ref(), client | server) -> #certificate{} | #alert{}. %% %% Description: Creates a certificate message. %%-------------------------------------------------------------------- -certificate(OwnCert, CertDbHandle, CertDbRef, client) -> +certificate(undefined, _, _, client) -> + %% If no suitable certificate is available, the client + %% SHOULD send a certificate message containing no + %% certificates. (chapter 7.4.6. RFC 4346) + #certificate{asn1_certificates = []}; +certificate([OwnCert], CertDbHandle, CertDbRef, client) -> Chain = case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of {ok, _, CertChain} -> CertChain; {error, _} -> - %% If no suitable certificate is available, the client - %% SHOULD send a certificate message containing no - %% certificates. (chapter 7.4.6. RFC 4346) - [] - end, + certificate(undefined, CertDbHandle, CertDbRef, client) + end, #certificate{asn1_certificates = Chain}; - -certificate(OwnCert, CertDbHandle, CertDbRef, server) -> +certificate([OwnCert], CertDbHandle, CertDbRef, server) -> case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of {ok, _, Chain} -> #certificate{asn1_certificates = Chain}; {error, Error} -> ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {server_has_no_suitable_certificates, Error}) - end. + end; +certificate([_, _ |_] = Chain, _, _, _) -> + #certificate{asn1_certificates = Chain}. %%-------------------------------------------------------------------- -spec client_certificate_verify(undefined | der_cert(), binary(), @@ -162,7 +166,7 @@ client_certificate_verify(undefined, _, _, _, _, _) -> ignore; client_certificate_verify(_, _, _, _, undefined, _) -> ignore; -client_certificate_verify(OwnCert, MasterSecret, Version, +client_certificate_verify([OwnCert|_], MasterSecret, Version, {HashAlgo, SignAlgo}, PrivateKey, {Handshake, _}) -> case public_key:pkix_is_fixed_dh_cert(OwnCert) of @@ -344,46 +348,21 @@ next_protocol(SelectedProtocol) -> %%-------------------------------------------------------------------- certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef, #{server_name_indication := ServerNameIndication, - partial_chain := PartialChain, - verify_fun := VerifyFun, - customize_hostname_check := CustomizeHostnameCheck, - crl_check := CrlCheck, - log_level := Level, - signature_algs := SignAlgos, - depth := Depth} = Opts, CRLDbHandle, Role, Host, Version, - #{cert_ext := CertExt, - ocsp_responder_certs := OcspResponderCerts, - ocsp_state := OcspState}) -> + partial_chain := PartialChain} = SSlOptions, + CRLDbHandle, Role, Host, Version, CertExt) -> ServerName = server_name(ServerNameIndication, Host, Role), - [PeerCert | ChainCerts ] = ASN1Certs, + [PeerCert | _ChainCerts ] = ASN1Certs, try - {TrustedCert, CertPath} = - ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef, - PartialChain), - ValidationFunAndState = validation_fun_and_state(VerifyFun, #{role => Role, - certdb => CertDbHandle, - certdb_ref => CertDbRef, - server_name => ServerName, - customize_hostname_check => - CustomizeHostnameCheck, - signature_algs => SignAlgos, - signature_algs_cert => undefined, - version => Version, - crl_check => CrlCheck, - crl_db => CRLDbHandle, - cert_ext => CertExt, - issuer => TrustedCert, - ocsp_responder_certs => OcspResponderCerts, - ocsp_state => OcspState}, - CertPath, Level), - Options = [{max_path_length, Depth}, - {verify_fun, ValidationFunAndState}], - case public_key:pkix_path_validation(TrustedCert, CertPath, Options) of + PathsAndAnchors = + ssl_certificate:trusted_cert_and_paths(ASN1Certs, CertDbHandle, CertDbRef, + PartialChain), + + case path_validate(PathsAndAnchors, ServerName, Role, CertDbHandle, CertDbRef, CRLDbHandle, + Version, SSlOptions, CertExt) of {ok, {PublicKeyInfo, _}} -> {PeerCert, PublicKeyInfo}; {error, Reason} -> - handle_path_validation_error(Reason, PeerCert, ChainCerts, Opts, Options, - CertDbHandle, CertDbRef) + path_validation_alert(Reason) end catch error:{_,{error, {asn1, Asn1Reason}}} -> @@ -706,6 +685,11 @@ encode_extensions([#signature_algorithms_cert{ Len = ListLen + 2, encode_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_CERT_EXT), ?UINT16(Len), ?UINT16(ListLen), SignSchemeList/binary, Acc/binary>>); +encode_extensions([#sni{hostname = ""} | Rest], Acc) -> + HostnameBin = <<>>, + encode_extensions(Rest, <<?UINT16(?SNI_EXT), ?UINT16(0), + HostnameBin/binary, + Acc/binary>>); encode_extensions([#sni{hostname = Hostname} | Rest], Acc) -> HostLen = length(Hostname), HostnameBin = list_to_binary(Hostname), @@ -1640,6 +1624,11 @@ get_cert_params(Cert) -> end, {SignAlgo, Param, PublicKeyAlgo, RSAKeySize}. +select_own_cert([OwnCert| _]) -> + OwnCert; +select_own_cert(undefined) -> + undefined. + get_signature_scheme(undefined) -> undefined; get_signature_scheme(#signature_algorithms_cert{ @@ -1876,58 +1865,6 @@ maybe_check_hostname(OtpCert, valid_peer, SslState) -> maybe_check_hostname(_, valid, _) -> valid. -handle_path_validation_error({bad_cert, unknown_ca} = Reason, PeerCert, Chain, - Opts, Options, CertDbHandle, CertsDbRef) -> - handle_incomplete_chain(PeerCert, Chain, Opts, Options, CertDbHandle, CertsDbRef, Reason); -handle_path_validation_error({bad_cert, invalid_issuer} = Reason, PeerCert, Chain0, - Opts, Options, CertDbHandle, CertsDbRef) -> - handle_unordered_chain(PeerCert, Chain0, Opts, Options, CertDbHandle, CertsDbRef, Reason); -handle_path_validation_error(Reason, _, _, _, _,_, _) -> - path_validation_alert(Reason). - -handle_incomplete_chain(PeerCert, Chain0, - #{partial_chain := PartialChain} = Opts, Options, CertDbHandle, CertsDbRef, Reason) -> - case ssl_certificate:certificate_chain(PeerCert, CertDbHandle, CertsDbRef) of - {ok, _, [PeerCert | _] = Chain} when Chain =/= Chain0 -> %% Chain candidate found - case ssl_certificate:trusted_cert_and_path(Chain, - CertDbHandle, CertsDbRef, - PartialChain) of - {unknown_ca, []} -> - path_validation_alert(Reason); - {Trusted, Path} -> - case public_key:pkix_path_validation(Trusted, Path, Options) of - {ok, {PublicKeyInfo,_}} -> - {PeerCert, PublicKeyInfo}; - {error, PathError} -> - handle_unordered_chain(PeerCert, Chain0, Opts, Options, - CertDbHandle, CertsDbRef, PathError) - end - end; - _ -> - handle_unordered_chain(PeerCert, Chain0, Opts, Options, CertDbHandle, CertsDbRef, Reason) - end. - -handle_unordered_chain(PeerCert, Chain0, - #{partial_chain := PartialChain}, Options, CertDbHandle, CertsDbRef, Reason) -> - {ok, ExtractedCerts} = ssl_pkix_db:extract_trusted_certs({der, Chain0}), - case ssl_certificate:certificate_chain(PeerCert, CertDbHandle, ExtractedCerts, Chain0) of - {ok, _, Chain} when Chain =/= Chain0 -> %% Chain appaears to be unordered - case ssl_certificate:trusted_cert_and_path(Chain, - CertDbHandle, CertsDbRef, - PartialChain) of - {unknown_ca, []} -> - path_validation_alert(Reason); - {Trusted, Path} -> - case public_key:pkix_path_validation(Trusted, Path, Options) of - {ok, {PublicKeyInfo,_}} -> - {PeerCert, PublicKeyInfo}; - {error, PathError} -> - path_validation_alert(PathError) - end - end; - _ -> - path_validation_alert(Reason) - end. path_validation_alert({bad_cert, cert_expired}) -> ?ALERT_REC(?FATAL, ?CERTIFICATE_EXPIRED); @@ -3594,3 +3531,51 @@ empty_extensions(_, server_hello) -> handle_log(Level, {LogLevel, ReportMap, Meta}) -> ssl_logger:log(Level, LogLevel, ReportMap, Meta). + + +path_validate([{TrustedCert, Path}], ServerName, Role, CertDbHandle, CertDbRef, CRLDbHandle, + Version, SslOptions, CertExt) -> + path_validation(TrustedCert, Path, ServerName, Role, CertDbHandle, CertDbRef, + CRLDbHandle, Version, SslOptions, CertExt); +path_validate([{TrustedCert, Path} | Rest], ServerName, Role, CertDbHandle, CertDbRef, CRLDbHandle, + Version, SslOptions, CertExt) -> + case path_validation(TrustedCert, Path, ServerName, + Role, CertDbHandle, CRLDbHandle, CertDbRef, + Version, SslOptions, CertExt) of + {ok, _} = Result -> + Result; + {error, _} -> + path_validate(Rest, ServerName, Role, CertDbHandle, CertDbRef, CRLDbHandle, + Version, SslOptions, CertExt) + end. + +path_validation(TrustedCert, Path, ServerName, Role, CertDbHandle, CertDbRef, CRLDbHandle, Version, + #{verify_fun := VerifyFun, + customize_hostname_check := CustomizeHostnameCheck, + crl_check := CrlCheck, + log_level := Level, + signature_algs := SignAlgos, + depth := Depth}, + #{cert_ext := CertExt, + ocsp_responder_certs := OcspResponderCerts, + ocsp_state := OcspState}) -> + ValidationFunAndState = + validation_fun_and_state(VerifyFun, #{role => Role, + certdb => CertDbHandle, + certdb_ref => CertDbRef, + server_name => ServerName, + customize_hostname_check => + CustomizeHostnameCheck, + signature_algs => SignAlgos, + signature_algs_cert => undefined, + version => Version, + crl_check => CrlCheck, + crl_db => CRLDbHandle, + cert_ext => CertExt, + issuer => TrustedCert, + ocsp_responder_certs => OcspResponderCerts, + ocsp_state => OcspState}, + Path, Level), + Options = [{max_path_length, Depth}, + {verify_fun, ValidationFunAndState}], + public_key:pkix_path_validation(TrustedCert, Path, Options). |