diff options
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | doc/cha-cert-auth.texi | 9 | ||||
-rw-r--r-- | doc/cha-cert-auth2.texi | 2 | ||||
-rw-r--r-- | doc/examples/ex-client-x509.c | 4 | ||||
-rw-r--r-- | doc/examples/verify.c | 4 | ||||
-rw-r--r-- | lib/gnutls_cert.c | 44 | ||||
-rw-r--r-- | lib/gnutls_x509.c | 31 | ||||
-rw-r--r-- | lib/gnutls_x509.h | 1 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 5 | ||||
-rw-r--r-- | lib/libgnutls.map | 1 | ||||
-rw-r--r-- | tests/mini-x509.c | 67 |
11 files changed, 158 insertions, 11 deletions
@@ -103,6 +103,7 @@ That option enables (when running on FIPS140-enabled system): ** API and ABI modifications: GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS: Added +gnutls_certificate_verify_peers4: Added gnutls_privkey_generate: Added gnutls_pkcs11_crt_is_known: Added gnutls_fips140_mode_enabled: Added diff --git a/doc/cha-cert-auth.texi b/doc/cha-cert-auth.texi index fcd089cb88..dfd4f8c9ec 100644 --- a/doc/cha-cert-auth.texi +++ b/doc/cha-cert-auth.texi @@ -349,10 +349,13 @@ When operating in the context of a TLS session, the trusted certificate authority list may also be set using: @showfuncC{gnutls_certificate_set_x509_trust_file,gnutls_certificate_set_x509_crl_file,gnutls_certificate_set_x509_system_trust} -Then it is not required to setup a trusted list as above. -The function @funcref{gnutls_certificate_verify_peers3} -may then be used to verify the peer's certificate chain and identity. The flags +In that case it is not required to setup a trusted list as above, and +the function @funcref{gnutls_certificate_verify_peers3} +may be used to verify the peer's certificate chain and identity. The flags are set similarly to the verification functions in the previous section. +Note that in certain cases it is required to check the marked purpose of +the end certificate (e.g. @code{GNUTLS_KP_TLS_WWW_SERVER}); in these case +@funcref{gnutls_certificate_verify_peers4} should be used instead. There is also the possibility to pass some input to the verification functions in the form of flags. For @funcref{gnutls_x509_trust_list_verify_crt} the diff --git a/doc/cha-cert-auth2.texi b/doc/cha-cert-auth2.texi index 0cd328240e..a6482a8a4c 100644 --- a/doc/cha-cert-auth2.texi +++ b/doc/cha-cert-auth2.texi @@ -154,7 +154,7 @@ in a CRL and/or perform an OCSP check for the certificate. Note that in the context of a TLS session the server may provide an OCSP response that will used during the TLS certificate verification -(see @funcref{gnutls_certificate_verify_peers3}). +(see @funcref{gnutls_certificate_verify_peers2}). You may obtain this response using @funcref{gnutls_ocsp_status_request_get}. Before performing the OCSP query, the application will need to figure diff --git a/doc/examples/ex-client-x509.c b/doc/examples/ex-client-x509.c index df96444f9f..dc6b2985e0 100644 --- a/doc/examples/ex-client-x509.c +++ b/doc/examples/ex-client-x509.c @@ -158,7 +158,9 @@ static int _verify_certificate_callback(gnutls_session_t session) /* This verification function uses the trusted CAs in the credentials * structure. So you must have installed one or more CA certificates. */ - ret = gnutls_certificate_verify_peers3(session, hostname, &status); + ret = gnutls_certificate_verify_peers4(session, hostname, + GNUTLS_KP_TLS_WWW_SERVER, + &status); if (ret < 0) { printf("Error\n"); return GNUTLS_E_CERTIFICATE_ERROR; diff --git a/doc/examples/verify.c b/doc/examples/verify.c index 4d0d059ea6..86d35808fc 100644 --- a/doc/examples/verify.c +++ b/doc/examples/verify.c @@ -23,7 +23,9 @@ int verify_certificate_callback(gnutls_session_t session) /* This verification function uses the trusted CAs in the credentials * structure. So you must have installed one or more CA certificates. */ - ret = gnutls_certificate_verify_peers3(session, hostname, &status); + ret = gnutls_certificate_verify_peers4(session, hostname, + GNUTLS_KP_TLS_WWW_SERVER, + &status); if (ret < 0) { printf("Error\n"); return GNUTLS_E_CERTIFICATE_ERROR; diff --git a/lib/gnutls_cert.c b/lib/gnutls_cert.c index da40426711..557ee6b71a 100644 --- a/lib/gnutls_cert.c +++ b/lib/gnutls_cert.c @@ -656,7 +656,7 @@ int gnutls_certificate_verify_peers2(gnutls_session_t session, unsigned int *status) { - return gnutls_certificate_verify_peers3(session, NULL, status); + return gnutls_certificate_verify_peers4(session, NULL, NULL, status); } /** @@ -679,6 +679,9 @@ gnutls_certificate_verify_peers2(gnutls_session_t session, * be accurate for ascii names; non-ascii names are compared byte-by-byte. * If names do not match the %GNUTLS_CERT_UNEXPECTED_OWNER status flag will be set. * + * In order to verify the purpose of the end-certificate (by checking the extended + * key usage), use gnutls_certificate_verify_peers4(). + * * Returns: a negative error code on error and %GNUTLS_E_SUCCESS (0) on success. * * Since: 3.1.4 @@ -688,6 +691,44 @@ gnutls_certificate_verify_peers3(gnutls_session_t session, const char *hostname, unsigned int *status) { + return gnutls_certificate_verify_peers4(session, hostname, NULL, status); +} + +/** + * gnutls_certificate_verify_peers4: + * @session: is a gnutls session + * @hostname: is the expected name of the peer; may be %NULL + * @purpose: is the expected key purpose OID, see the %GNUTLS_KP definitions + * @status: is the output of the verification + * + * This function will verify the peer's certificate and store the + * status in the @status variable as a bitwise or'd gnutls_certificate_status_t + * values or zero if the certificate is trusted. Note that value in @status + * is set only when the return value of this function is success (i.e, failure + * to trust a certificate does not imply a negative return value). + * The default verification flags used by this function can be overriden + * using gnutls_certificate_set_verify_flags(). See the documentation + * of gnutls_certificate_verify_peers2() for details in the verification process. + * + * If the @hostname provided is non-NULL then this function will compare + * the hostname in the certificate against the given. The comparison will + * be accurate for ascii names; non-ascii names are compared byte-by-byte. + * If names do not match the %GNUTLS_CERT_UNEXPECTED_OWNER status flag will be set. + * + * If @purpose is non-NULL and the end-certificate contains the extended key usage + * PKIX extension, it will be required to be have the provided key purpose (e.g., %GNUTLS_KP_TLS_WWW_SERVER), + * or verification will fail with %GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE status. + * + * Returns: a negative error code on error and %GNUTLS_E_SUCCESS (0) on success. + * + * Since: 3.3.0 + **/ +int +gnutls_certificate_verify_peers4(gnutls_session_t session, + const char *hostname, + const char *purpose, + unsigned int *status) +{ cert_auth_info_t info; CHECK_AUTH(GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST); @@ -703,6 +744,7 @@ gnutls_certificate_verify_peers3(gnutls_session_t session, switch (gnutls_certificate_type_get(session)) { case GNUTLS_CRT_X509: return _gnutls_x509_cert_verify_peers(session, hostname, + purpose, status); #ifdef ENABLE_OPENPGP case GNUTLS_CRT_OPENPGP: diff --git a/lib/gnutls_x509.c b/lib/gnutls_x509.c index 96ae1dbb33..9ef0c04f5e 100644 --- a/lib/gnutls_x509.c +++ b/lib/gnutls_x509.c @@ -182,7 +182,9 @@ check_ocsp_response(gnutls_session_t session, gnutls_x509_crt_t cert, -*/ int _gnutls_x509_cert_verify_peers(gnutls_session_t session, - const char *hostname, unsigned int *status) + const char *hostname, + const char *purpose, + unsigned int *status) { cert_auth_info_t info; gnutls_certificate_credentials_t cred; @@ -306,6 +308,33 @@ _gnutls_x509_cert_verify_peers(gnutls_session_t session, *status |= GNUTLS_CERT_UNEXPECTED_OWNER|GNUTLS_CERT_INVALID; } + if (purpose) { + char oid[MAX_OID_SIZE]; + size_t oid_size; + + for (i=0;;i++) { + oid_size = sizeof(oid); + ret = gnutls_x509_crt_get_key_purpose_oid(peer_certificate_list[0], + i, oid, &oid_size, NULL); + if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + if (i==0) + break; + else { /* no acceptable purpose was found */ + gnutls_assert(); + *status |= GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE|GNUTLS_CERT_INVALID; + break; + } + } else if (ret < 0) { + gnutls_assert(); + *status |= GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE|GNUTLS_CERT_INVALID; + break; + } + + if (strcmp(oid, purpose) == 0 || strcmp(oid, GNUTLS_KP_ANY) == 0) + break; + } + } + CLEAR_CERTS; *status |= ocsp_status; diff --git a/lib/gnutls_x509.h b/lib/gnutls_x509.h index ce56385f49..d919b5f27f 100644 --- a/lib/gnutls_x509.h +++ b/lib/gnutls_x509.h @@ -25,6 +25,7 @@ int _gnutls_x509_cert_verify_peers(gnutls_session_t session, const char *hostname, + const char *purpose, unsigned int *status); #define PEM_CERT_SEP2 "-----BEGIN X509 CERTIFICATE" diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 151a31dad5..71244da554 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -1911,6 +1911,11 @@ int gnutls_certificate_verify_peers2(gnutls_session_t session, int gnutls_certificate_verify_peers3(gnutls_session_t session, const char *hostname, unsigned int *status); +int +gnutls_certificate_verify_peers4(gnutls_session_t session, + const char *hostname, + const char *purpose, + unsigned int *status); int gnutls_certificate_verification_status_print(unsigned int status, gnutls_certificate_type_t diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 8834c093a7..38b76caec6 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1010,6 +1010,7 @@ GNUTLS_3_1_0 { gnutls_x509_ext_export_key_purposes; gnutls_x509_crt_check_hostname2; gnutls_openpgp_crt_check_hostname2; + gnutls_certificate_verify_peers4; } GNUTLS_3_0_0; GNUTLS_PRIVATE { diff --git a/tests/mini-x509.c b/tests/mini-x509.c index 477bf41d3b..3bb3908b4b 100644 --- a/tests/mini-x509.c +++ b/tests/mini-x509.c @@ -39,6 +39,30 @@ static void tls_log_func(int level, const char *str) fprintf(stderr, "%s|<%d>| %s", side, level, str); } +static unsigned char ca_cert_pem[] = +"-----BEGIN CERTIFICATE-----\n" +"MIIC4DCCAcigAwIBAgIBADANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDEwRDQS0w\n" +"MCIYDzIwMTQwNDA0MTk1OTA1WhgPOTk5OTEyMzEyMzU5NTlaMA8xDTALBgNVBAMT\n" +"BENBLTAwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQD46JAPKrTsNTHl\n" +"zD06eIYBF/8Z+TR0wukp9Cdh8Sw77dODLjy/QrVKiDgDZZdyUc8Agsdr86i95O0p\n" +"w19Np3a0wja0VC9uwppZrpuHsrWukwxIBXoViyBc20Y6Ce8j0scCbR10SP565qXC\n" +"i8vr86S4xmQMRZMtwohP/GWQzt45jqkHPYHjdKzwo2b2XI7joDq0dvbr3MSONkGs\n" +"z7A/1Bl3iH5keDTWjqpJRWqXE79IhGOhELy+gG4VLJDGHWCr2mq24b9Kirp+TTxl\n" +"lUwJRbchqUqerlFdt1NgDoGaJyd73Sh0qcZzmEiOI2hGvBtG86tdQ6veC9dl05et\n" +"pM+6RMABAgMBAAGjQzBBMA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcE\n" +"ADAdBgNVHQ4EFgQUGD0RYr2H7kfjQUcBMxSTCDQnhu0wDQYJKoZIhvcNAQELBQAD\n" +"ggEBALnHMubZ6WJ/XOFyDuo0imwg2onrPas3MuKT4+y0aHY943BgAOEc3jKitRjc\n" +"qhb0IUD+NS7itRwNtCgI3v5Ym5nnQoVk+aOD/D724TjJ9XaPQJzOnuGaZX99VN2F\n" +"sgwAtDXedlDQ+I6KLzLd6VW+UyWTG4qiRjOGDnG2kM1wAEOM27TzHV/YWleGjhtA\n" +"bRHxkioOni5goNlTzazxF4v9VD2uinWrIFyZmF6vQuMm6rKFgq6higAU8uesFo7+\n" +"3qpeRjNrPC4fNJUBvv+PC0WnP0PLnD/rY/ZcTYjLb/vJp1fiMJ5fU7jJklBhX2TE\n" +"tstcP7FUV5HA/s9BxgAh0Z2wyyY=\n" +"-----END CERTIFICATE-----\n"; + +const gnutls_datum_t ca_cert = { ca_cert_pem, + sizeof(ca_cert_pem) +}; + static unsigned char server_cert_pem[] = "-----BEGIN CERTIFICATE-----\n" "MIIDIzCCAgugAwIBAgIMUz8PCR2sdRK56V6OMA0GCSqGSIb3DQEBCwUAMA8xDTAL\n" @@ -119,6 +143,7 @@ const gnutls_datum_t server_key = { server_key_pem, void doit(void) { int exit_code = EXIT_SUCCESS; + int ret; /* Server stuff. */ gnutls_certificate_credentials_t serverx509cred; gnutls_session_t server; @@ -154,10 +179,23 @@ void doit(void) gnutls_transport_set_ptr(server, server); /* Init client */ - gnutls_certificate_allocate_credentials(&clientx509cred); - gnutls_init(&client, GNUTLS_CLIENT); - gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, + ret = gnutls_certificate_allocate_credentials(&clientx509cred); + if (ret < 0) + exit(1); + + ret = gnutls_certificate_set_x509_trust_mem(clientx509cred, &ca_cert, GNUTLS_X509_FMT_PEM); + if (ret < 0) + exit(1); + + ret = gnutls_init(&client, GNUTLS_CLIENT); + if (ret < 0) + exit(1); + + ret = gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, clientx509cred); + if (ret < 0) + exit(1); + gnutls_priority_set_direct(client, "NORMAL", NULL); gnutls_transport_set_push_function(client, client_push); gnutls_transport_set_pull_function(client, client_pull); @@ -168,12 +206,35 @@ void doit(void) /* check the number of certificates received */ { unsigned cert_list_size = 0; + unsigned status; gnutls_certificate_get_peers(client, &cert_list_size); if (cert_list_size < 2) { fprintf(stderr, "received a certificate list of %d!\n", cert_list_size); exit(1); } + + ret = gnutls_certificate_verify_peers4(client, "localhost1", GNUTLS_KP_TLS_WWW_SERVER, &status); + if (ret < 0) { + fprintf(stderr, "could not verify certificate: %s\n", gnutls_strerror(ret)); + exit(1); + } + + if (status == 0) { + fprintf(stderr, "should not have accepted!\n"); + exit(1); + } + + ret = gnutls_certificate_verify_peers4(client, "localhost", GNUTLS_KP_TLS_WWW_SERVER, &status); + if (ret < 0) { + fprintf(stderr, "could not verify certificate: %s\n", gnutls_strerror(ret)); + exit(1); + } + + if (status != 0) { + fprintf(stderr, "could not verify certificate: %.4x\n", status); + exit(1); + } } gnutls_bye(client, GNUTLS_SHUT_RDWR); |