summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS1
-rw-r--r--doc/cha-cert-auth.texi9
-rw-r--r--doc/cha-cert-auth2.texi2
-rw-r--r--doc/examples/ex-client-x509.c4
-rw-r--r--doc/examples/verify.c4
-rw-r--r--lib/gnutls_cert.c44
-rw-r--r--lib/gnutls_x509.c31
-rw-r--r--lib/gnutls_x509.h1
-rw-r--r--lib/includes/gnutls/gnutls.h.in5
-rw-r--r--lib/libgnutls.map1
-rw-r--r--tests/mini-x509.c67
11 files changed, 158 insertions, 11 deletions
diff --git a/NEWS b/NEWS
index 606f79c729..78ee7e914a 100644
--- a/NEWS
+++ b/NEWS
@@ -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);