diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 8c3805c2199a..33b404089a0f 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -160,7 +160,7 @@ -type oid() :: tuple(). -type cert_id() :: {SerialNr::integer(), issuer_name()} . -type issuer_name() :: {rdnSequence,[[#'AttributeTypeAndValue'{}]]} . --type bad_cert_reason() :: cert_expired | invalid_issuer | invalid_signature | name_not_permitted | missing_basic_constraint | invalid_key_usage | {revoked, crl_reason()} | atom(). +-type bad_cert_reason() :: cert_expired | invalid_issuer | invalid_signature | name_not_permitted | missing_basic_constraint | invalid_key_usage | duplicate_cert_in_path | {revoked, crl_reason()} | atom(). -type combined_cert() :: #cert{}. -type cert() :: der_cert() | otp_cert(). @@ -1161,7 +1161,12 @@ pkix_path_validation(#'OTPCertificate'{} = TrustedCert, CertChain, Options) MaxPathDefault, [{verify_fun, {VerifyFun, UserState1}} | proplists:delete(verify_fun, Options)]), - path_validation(CertChain, ValidationState) + case exists_duplicate_cert(CertChain) of + true -> + {error, {bad_cert, duplicate_cert_in_path}}; + false -> + path_validation(CertChain, ValidationState) + end catch throw:{bad_cert, _} = Result -> {error, Result} @@ -1553,6 +1558,20 @@ do_pem_entry_decode({Asn1Type,_, _} = PemEntry, Password) -> Der = pubkey_pem:decipher(PemEntry, Password), der_decode(Asn1Type, Der). +%% The only way a path with duplicates could be somehow wrongly +%% passed is if the certs are located together and also are +%% self-signed. This is what we need to possible protect against. We +%% only check for togetherness here as it helps with the case not +%% otherwise caught. It can result in a different error message for +%% cases already failing before but that is not important, the +%% important thing is that it will be rejected. +exists_duplicate_cert([]) -> + false; +exists_duplicate_cert([Cert, Cert | _]) -> + true; +exists_duplicate_cert([_ | Rest]) -> + exists_duplicate_cert(Rest). + path_validation([], #path_validation_state{working_public_key_algorithm = Algorithm, working_public_key = diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index 1a779e03bdee..c5fef5b8852e 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -836,8 +836,12 @@ pkix_path_validation(Config) when is_list(Config) -> {error, {bad_cert,invalid_issuer}} = public_key:pkix_path_validation(Trusted, [Cert2], []), - + {ok, _} = public_key:pkix_path_validation(Trusted, [Cert1, Cert2], []), + + {error, {bad_cert, duplicate_cert_in_path}} = + public_key:pkix_path_validation(Trusted, [Cert1, Cert1, Cert2], []), + {error, issuer_not_found} = public_key:pkix_issuer_id(Cert2, other), CertK3 = {Cert3,_} = erl_make_certs:make_cert([{issuer, CertK1},