summaryrefslogtreecommitdiff
path: root/lib/ssl/src/ssl_handshake.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src/ssl_handshake.erl')
-rw-r--r--lib/ssl/src/ssl_handshake.erl183
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).