diff options
-rw-r--r-- | lib/public_key/doc/src/public_key.xml | 28 | ||||
-rw-r--r-- | lib/public_key/src/public_key.erl | 38 | ||||
-rw-r--r-- | lib/public_key/test/public_key_SUITE.erl | 19 | ||||
-rw-r--r-- | lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem | 24 | ||||
-rw-r--r-- | lib/public_key/test/public_key_SUITE_data/verify_hostname.conf | 3 |
5 files changed, 86 insertions, 26 deletions
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml index 7284da0499..5d57109140 100644 --- a/lib/public_key/doc/src/public_key.xml +++ b/lib/public_key/doc/src/public_key.xml @@ -986,10 +986,38 @@ fun(#'DistributionPoint'{}, #'CertificateList'{}, <p>The <c>ip</c> Reference ID takes an <seealso marker="inet:inet#type-ip_address">inet:ip_address()</seealso> or an ip address in string format (E.g "10.0.1.1" or "1234::5678:9012") as second element. </p> + <p>See <seealso marker="#pkix_verify_hostname_match_fun-1">pkix_verify_hostname_match_fun/1</seealso> for a + function that return a fun suitable for this option. + </p> </desc> </func> <func> + <name>pkix_verify_hostname_match_fun(Alg) -> fun(RefId | FQDN::string(), PresentedID) -> boolean() | default</name> + <fsummary>Returns a fun that is intendended as argument to the match_fun option in pkix_verify_hostname/3. + </fsummary> + <type> + <v>Alg = https</v> + <d>The algorithm for wich the fun should implement the special matching rules</d> + <v>RefId</v> + <d>See <seealso marker="#pkix_verify_hostname-3">pkix_verify_hostname/3</seealso>.</d> + <v>FQDN</v> + <d>See <seealso marker="#pkix_verify_hostname-3">pkix_verify_hostname/3</seealso>.</d> + <v>PresentedID</v> + <d>See <seealso marker="#pkix_verify_hostname-3">pkix_verify_hostname/3</seealso>.</d> + </type> + <desc> + <p>The return value of calling this function is intended to be used in the <c>match_fun</c> option in + <seealso marker="#pkix_verify_hostname-3">pkix_verify_hostname/3</seealso>. + </p> + <p>The returned fun augments the verify hostname matching according to the specific rules for + the protocol in the argument. + </p> + </desc> + </func> + + + <func> <name>sign(Msg, DigestType, Key) -> binary()</name> <name>sign(Msg, DigestType, Key, Options) -> binary()</name> <fsummary>Creates a digital signature.</fsummary> diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 1c4acc9e1a..f2b57fd330 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -49,6 +49,7 @@ pkix_normalize_name/1, pkix_path_validation/3, pkix_verify_hostname/2, pkix_verify_hostname/3, + pkix_verify_hostname_match_fun/1, ssh_decode/2, ssh_encode/2, ssh_hostkey_fingerprint/1, ssh_hostkey_fingerprint/2, ssh_curvename2oid/1, oid2ssh_curvename/1, @@ -883,12 +884,23 @@ pkix_crls_validate(OtpCert, DPAndCRLs0, Options) -> Options, pubkey_crl:init_revokation_state()). %-------------------------------------------------------------------- --spec pkix_verify_hostname(Cert :: #'OTPCertificate'{} | binary(), - ReferenceIDs :: [{uri_id | dns_id | ip | srv_id | oid(), string()}]) -> boolean(). +-spec pkix_verify_hostname(#'OTPCertificate'{} | binary(), + referenceIDs() + ) -> boolean(). --spec pkix_verify_hostname(Cert :: #'OTPCertificate'{} | binary(), - ReferenceIDs :: [{uri_id | dns_id | ip | srv_id | oid(), string()}], - Options :: proplists:proplist()) -> boolean(). +-spec pkix_verify_hostname(#'OTPCertificate'{} | binary(), + referenceIDs(), + proplists:proplist()) -> boolean(). + +-type referenceIDs() :: [referenceID()] . +-type referenceID() :: {uri_id | dns_id | ip | srv_id | oid(), string()} . + +-spec pkix_verify_hostname_match_fun(high_level_alg()) -> match_fun() . + +-type high_level_alg() :: https . +-type match_fun() :: fun((ReferenceID::referenceID() | string(), + PresentedID::{atom()|oid(),string()}) -> match_fun_result() ) . +-type match_fun_result() :: boolean() | default . %% Description: Validates a hostname to RFC 6125 %%-------------------------------------------------------------------- @@ -953,6 +965,11 @@ pkix_verify_hostname(Cert = #'OTPCertificate'{tbsCertificate = TbsCert}, Referen end end. +pkix_verify_hostname_match_fun(https) -> + fun({dns_id,FQDN=[_|_]}, {dNSName,Name=[_|_]}) -> verify_hostname_match_wildcard(FQDN, Name); + (_, _) -> default + end. + %%-------------------------------------------------------------------- -spec ssh_decode(binary(), public_key | ssh_file()) -> [{public_key(), Attributes::list()}] ; (binary(), ssh2_pubkey) -> public_key() @@ -1516,9 +1533,7 @@ verify_hostname_match_default(Ref, Pres) -> verify_hostname_match_default0(FQDN=[_|_], {cn,FQDN}) -> not lists:member($*, FQDN); verify_hostname_match_default0(FQDN=[_|_], {cn,Name=[_|_]}) -> - [F1|Fs] = string:tokens(FQDN, "."), - [N1|Ns] = string:tokens(Name, "."), - match_wild(F1,N1) andalso Fs==Ns; + verify_hostname_match_wildcard(FQDN, Name); verify_hostname_match_default0({dns_id,R}, {dNSName,P}) -> R==P; verify_hostname_match_default0({uri_id,R}, {uniformResourceIdentifier,P}) -> @@ -1553,6 +1568,13 @@ verify_hostname_match_default0({srv_id,R}, {?srvName_OID,P}) -> verify_hostname_match_default0(_, _) -> false. + +verify_hostname_match_wildcard(FQDN, Name) -> + [F1|Fs] = string:tokens(FQDN, "."), + [N1|Ns] = string:tokens(Name, "."), + match_wild(F1,N1) andalso Fs==Ns. + + ok({ok,X}) -> X. l16_to_tup(L) -> list_to_tuple(l16_to_tup(L, [])). diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index 572748edc9..fcc9bdc080 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -310,7 +310,7 @@ init_ec_pem_encode_generated(Config) -> ec_pem_encode_generated() -> [{doc, "PEM-encode generated EC key"}]. -ec_pem_encode_generated(Config) -> +ec_pem_encode_generated(_Config) -> Key1 = public_key:generate_key({namedCurve, 'secp384r1'}), public_key:pem_entry_encode('ECPrivateKey', Key1), @@ -965,7 +965,7 @@ pkix_verify_hostname_cn(Config) -> %% openssl req -x509 -nodes -newkey rsa:1024 -keyout /dev/null -extensions SAN -config public_key_SUITE_data/verify_hostname.conf 2>/dev/null > public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem %% %% Subject: C=SE, CN=example.com -%% Subject Alternative Name: DNS:kb.example.org, URI:http://www.example.org, URI:https://wws.example.org +%% Subject Alternative Name: DNS:kb.example.org, DNS:*.example.org, URI:http://www.example.org, URI:https://wws.example.org pkix_verify_hostname_subjAltName(Config) -> DataDir = proplists:get_value(data_dir, Config), @@ -984,7 +984,16 @@ pkix_verify_hostname_subjAltName(Config) -> {dns_id,"wws.example.org"}]), %% Check that a dns_id matches a DNS subjAltName: - true = public_key:pkix_verify_hostname(Cert, [{dns_id,"kb.example.org"}]). + true = public_key:pkix_verify_hostname(Cert, [{dns_id,"kb.example.org"}]), + + %% Check that a dns_id does not match a DNS subjAltName wiht wildcard + false = public_key:pkix_verify_hostname(Cert, [{dns_id,"other.example.org"}]), + + %% Check that a dns_id does nmatches a DNS subjAltName wiht wildcard with matchfun + true = public_key:pkix_verify_hostname(Cert, [{dns_id,"other.example.org"}], + [{match_fun, public_key:pkix_verify_hostname_match_fun(https)} + ] + ). %%-------------------------------------------------------------------- %% Uses the pem-file for pkix_verify_hostname_cn @@ -1351,7 +1360,7 @@ do_gen_ec_param(File) -> ct:fail({key_gen_fail, File}) end. -init_per_testcase_gen_ec_param(TC, Curve, Config) -> +init_per_testcase_gen_ec_param(_TC, Curve, Config) -> case crypto:ec_curves() of [] -> {skip, missing_ec_support}; @@ -1367,7 +1376,7 @@ init_per_testcase_gen_ec_param(TC, Curve, Config) -> end. -crypto_supported_curve(Curve, Curves) -> +crypto_supported_curve(Curve, _Curves) -> try crypto:generate_key(ecdh, Curve) of {error,_} -> false; % Just in case crypto is changed in the future... _-> true diff --git a/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem b/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem index 83e1ad37b3..7ab9ed7b96 100644 --- a/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem +++ b/lib/public_key/test/public_key_SUITE_data/pkix_verify_hostname_subjAltName.pem @@ -1,14 +1,14 @@ -----BEGIN CERTIFICATE----- -MIICEjCCAXugAwIBAgIJANwliLph5EiAMA0GCSqGSIb3DQEBCwUAMCMxCzAJBgNV -BAYTAlNFMRQwEgYDVQQDEwtleGFtcGxlLmNvbTAeFw0xNjEyMjAxNTEyMjRaFw0x -NzAxMTkxNTEyMjRaMCMxCzAJBgNVBAYTAlNFMRQwEgYDVQQDEwtleGFtcGxlLmNv -bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAydstIN157w8QxkVaOl3wm81j -fgZ8gqO3BXkECPF6bw5ewLlmePL6Qs4RypsaRe7cKJ9rHFlwhpdcYkxWSWEt2N7Z -Ry3N4SjuU04ohWbYgy3ijTt7bJg7jOV1Dh56BnI4hwhQj0oNFizNZOeRRfEzdMnS -+uk03t/Qre2NS7KbwnUCAwEAAaNOMEwwSgYDVR0RBEMwQYIOa2IuZXhhbXBsZS5v -cmeGFmh0dHA6Ly93d3cuZXhhbXBsZS5vcmeGF2h0dHBzOi8vd3dzLmV4YW1wbGUu -b3JnMA0GCSqGSIb3DQEBCwUAA4GBAKqFqW5gCso422bXriCBJoygokOTTOw1Rzpq -K8Mm0B8W9rrW9OTkoLEcjekllZcUCZFin2HovHC5HlHZz+mQvBI1M6sN2HVQbSzS -EgL66U9gwJVnn9/U1hXhJ0LO28aGbyE29DxnewNR741dWN3oFxCdlNaO6eMWaEsO -gduJ5sDl +MIICITCCAYqgAwIBAgIJAP31suf/Fi4oMA0GCSqGSIb3DQEBCwUAMCMxCzAJBgNV +BAYTAlNFMRQwEgYDVQQDEwtleGFtcGxlLmNvbTAeFw0xODA1MTcxMDIzNDBaFw0x +ODA2MTYxMDIzNDBaMCMxCzAJBgNVBAYTAlNFMRQwEgYDVQQDEwtleGFtcGxlLmNv +bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsUVMXSM4Q6vYp7H4Svsfv4QQ +dmUD3IdTbtumlyAqLZuc6Z0HU9IOE0wpF97+5AE3moHluwN/MtSX/fb9oxCjh3L6 +iDla770uUoIgiWkA9lyzuYXt7zGsqc0EmGMJRAHp4jOxI26U/C8wdXoyZsGD8GPr +hYAI2Me4CkdDqCoRuUUCAwEAAaNdMFswWQYDVR0RBFIwUIIOa2IuZXhhbXBsZS5v +cmeCDSouZXhhbXBsZS5vcmeGFmh0dHA6Ly93d3cuZXhhbXBsZS5vcmeGF2h0dHBz +Oi8vd3dzLmV4YW1wbGUub3JnMA0GCSqGSIb3DQEBCwUAA4GBAKs8vWMqpXiuFhcq +6W1dMrVB4tuDjt1Ctr3g2USXBLgm8NxsZzslFyDnrvtZY0hbjcAkGKMMhy8lFD5t ++GjBbyp7MKII6vJaVvc+wbrsbNdvioB1puGwbgVhgD3Kb79do9h6JrNncjMvBN7j +VK6BUB8TUofFmztMjoPlxFOs/7qK -----END CERTIFICATE----- diff --git a/lib/public_key/test/public_key_SUITE_data/verify_hostname.conf b/lib/public_key/test/public_key_SUITE_data/verify_hostname.conf index a28864dc78..6b4e4f284e 100644 --- a/lib/public_key/test/public_key_SUITE_data/verify_hostname.conf +++ b/lib/public_key/test/public_key_SUITE_data/verify_hostname.conf @@ -10,7 +10,8 @@ CN=example.com subjectAltName = @alt_names [alt_names] -DNS = kb.example.org +DNS.1 = kb.example.org +DNS.2 = *.example.org URI.1 = http://www.example.org URI.2 = https://wws.example.org |