diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 2 | ||||
-rw-r--r-- | lib/auto-verify.c | 147 | ||||
-rw-r--r-- | lib/gnutls_alert.c | 1 | ||||
-rw-r--r-- | lib/gnutls_cert.c | 2 | ||||
-rw-r--r-- | lib/gnutls_errors.c | 2 | ||||
-rw-r--r-- | lib/gnutls_handshake.c | 2 | ||||
-rw-r--r-- | lib/gnutls_int.h | 8 | ||||
-rw-r--r-- | lib/gnutls_priority.c | 5 | ||||
-rw-r--r-- | lib/gnutls_x509.c | 3 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 60 | ||||
-rw-r--r-- | lib/libgnutls.map | 3 |
11 files changed, 209 insertions, 26 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 955fa10bb2..6e95ce6397 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -82,7 +82,7 @@ COBJECTS = gnutls_range.c gnutls_record.c \ random.c crypto-api.c gnutls_privkey.c gnutls_pcert.c \ gnutls_pubkey.c locks.c gnutls_dtls.c system_override.c \ crypto-backend.c verify-tofu.c pin.c tpm.c fips.c \ - safe-memset.c inet_pton.c + safe-memset.c inet_pton.c auto-verify.c if ENABLE_SELF_CHECKS COBJECTS += crypto-selftests.c crypto-selftests-pk.c diff --git a/lib/auto-verify.c b/lib/auto-verify.c new file mode 100644 index 0000000000..1de1442c2b --- /dev/null +++ b/lib/auto-verify.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2015 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "gnutls_errors.h" +#include <auth/cert.h> +#include <gnutls/gnutls.h> + +/* This file implements the client certificate auto verification functionality. + */ + +/* The actual verification callback. */ +static int auto_verify_cb(gnutls_session_t session) +{ + unsigned int status; + int ret; + + if (session->internals.vc_elements == 0) { + ret = gnutls_certificate_verify_peers2(session, &status); + } else { + ret = gnutls_certificate_verify_peers(session, session->internals.vc_data, + session->internals.vc_elements, &status); + } + if (ret < 0) { + return gnutls_assert_val(GNUTLS_E_CERTIFICATE_ERROR); + } + + session->internals.vc_status = status; + + if (status != 0) /* Certificate is not trusted */ + return gnutls_assert_val(GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR); + + /* notify gnutls to continue handshake normally */ + return 0; +} + +/** + * gnutls_session_set_verify_cert: + * @session: is a gnutls session + * @hostname: is the expected name of the peer; may be %NULL + * @flags: flags for certificate verification -- #gnutls_certificate_verify_flags + * + * This function instructs GnuTLS to verify the peer's certificate + * using the provided hostname. If the verification fails the handshake + * will also fail with %GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR. In that + * case the verification result can be obtained using gnutls_session_get_verify_cert_status(). + * + * The @hostname pointer provided must remain valid for the lifetime + * of the session. More precisely it should be available during any subsequent + * handshakes. If no hostname is provided, no hostname verification + * will be performed. For a more advanced verification function check + * gnutls_session_set_verify_cert2(). + * + * The gnutls_session_set_verify_cert() function is intended to be used by TLS + * clients to verify the server's certificate. + * + * Since: 3.5.0 + **/ +void gnutls_session_set_verify_cert(gnutls_session_t session, + const char *hostname, unsigned flags) +{ + if (hostname) { + session->internals.vc_sdata.type = GNUTLS_DT_DNS_HOSTNAME; + session->internals.vc_sdata.data = (void*)hostname; + session->internals.vc_sdata.size = 0; + session->internals.vc_elements = 1; + session->internals.vc_data = &session->internals.vc_sdata; + } else { + session->internals.vc_elements = 0; + } + + if (flags) + session->internals.additional_verify_flags |= flags; + + gnutls_session_set_verify_function(session, auto_verify_cb); +} + +/** + * gnutls_session_set_verify_cert2: + * @session: is a gnutls session + * @data: an array of typed data + * @elements: the number of data elements + * @flags: flags for certificate verification -- #gnutls_certificate_verify_flags + * + * This function instructs GnuTLS to verify the peer's certificate + * using the provided typed data information. If the verification fails the handshake + * will also fail with %GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR. In that + * case the verification result can be obtained using gnutls_session_get_verify_cert_status(). + * + * The acceptable typed data are the same as in gnutls_certificate_verify_peers(), + * and once set must remain valid for the lifetime of the session. More precisely + * they should be available during any subsequent handshakes. + * + * Since: 3.5.0 + **/ +void gnutls_session_set_verify_cert2(gnutls_session_t session, + gnutls_typed_vdata_st * data, + unsigned elements, + unsigned flags) +{ + session->internals.vc_data = data; + session->internals.vc_elements = elements; + + if (flags) + session->internals.additional_verify_flags |= flags; + + gnutls_session_set_verify_function(session, auto_verify_cb); +} + +/** + * gnutls_session_get_verify_cert_status: + * @session: is a gnutls session + * + * This function returns the status of the verification when initiated + * via auto-verification, i.e., by gnutls_session_set_verify_cert2() or + * gnutls_session_set_verify_cert(). If no certificate verification + * was occurred then the return value would be set to ((unsigned int)-1). + * + * The certificate verification status is the same as in gnutls_certificate_verify_peers(). + * + * Returns: the certificate verification status. + * + * Since: 3.5.0 + **/ +unsigned int gnutls_session_get_verify_cert_status(gnutls_session_t session) +{ + return session->internals.vc_status; +} diff --git a/lib/gnutls_alert.c b/lib/gnutls_alert.c index 66270dc18a..1d183930ed 100644 --- a/lib/gnutls_alert.c +++ b/lib/gnutls_alert.c @@ -228,6 +228,7 @@ int gnutls_error_to_alert(int err, int *level) case GNUTLS_E_ASN1_SYNTAX_ERROR: case GNUTLS_E_ASN1_DER_OVERFLOW: case GNUTLS_E_CERTIFICATE_ERROR: + case GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR: ret = GNUTLS_A_BAD_CERTIFICATE; _level = GNUTLS_AL_FATAL; break; diff --git a/lib/gnutls_cert.c b/lib/gnutls_cert.c index 7064aadbf6..bd99c7fc61 100644 --- a/lib/gnutls_cert.c +++ b/lib/gnutls_cert.c @@ -600,7 +600,7 @@ _gnutls_openpgp_crt_verify_peers(gnutls_session_t session, return GNUTLS_E_NO_CERTIFICATE_FOUND; } - verify_flags = cred->verify_flags | session->internals.priorities.additional_verify_flags; + verify_flags = cred->verify_flags | session->internals.additional_verify_flags; /* generate a list of gnutls_certs based on the auth info * raw certs. diff --git a/lib/gnutls_errors.c b/lib/gnutls_errors.c index 50f2a0ff2c..91a0fa7925 100644 --- a/lib/gnutls_errors.c +++ b/lib/gnutls_errors.c @@ -153,6 +153,8 @@ static const gnutls_error_entry error_entries[] = { GNUTLS_E_RECORD_LIMIT_REACHED), ERROR_ENTRY(N_("Error in the certificate."), GNUTLS_E_CERTIFICATE_ERROR), + ERROR_ENTRY(N_("Error in the certificate verification."), + GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR), ERROR_ENTRY(N_("Could not authenticate peer."), GNUTLS_E_AUTH_ERROR), ERROR_ENTRY(N_ diff --git a/lib/gnutls_handshake.c b/lib/gnutls_handshake.c index d8cc93ce15..d28f543345 100644 --- a/lib/gnutls_handshake.c +++ b/lib/gnutls_handshake.c @@ -2530,6 +2530,8 @@ int gnutls_handshake(gnutls_session_t session) session->internals.handshake_in_progress = 1; gettime(&session->internals.dtls.handshake_start_time); + session->internals.vc_status = -1; + if (session->internals.handshake_timeout_ms && session->internals.handshake_endtime == 0) session->internals.handshake_endtime = session->internals.dtls.handshake_start_time.tv_sec + diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 3db0e1f699..83b3b5b035 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2000-2012 Free Software Foundation, Inc. + * Copyright (C) 2000-2015 Free Software Foundation, Inc. + * Copyright (C) 2015 Red Hat, Inc. * * Author: Nikos Mavrogiannopoulos * @@ -1002,6 +1003,11 @@ typedef struct { /* a verify callback to override the verify callback from the credentials * structure */ gnutls_certificate_verify_function *verify_callback; + gnutls_typed_vdata_st *vc_data; + gnutls_typed_vdata_st vc_sdata; + unsigned vc_elements; + unsigned vc_status; + unsigned int additional_verify_flags; /* may be set by priorities or the vc functions */ /* If you add anything here, check _gnutls_handshake_internal_state_clear(). */ diff --git a/lib/gnutls_priority.c b/lib/gnutls_priority.c index f60674c174..72c6d1e3a3 100644 --- a/lib/gnutls_priority.c +++ b/lib/gnutls_priority.c @@ -1,5 +1,6 @@ /* - * Copyright (C) 2004-2012 Free Software Foundation, Inc. + * Copyright (C) 2004-2015 Free Software Foundation, Inc. + * Copyright (C) 2015 Red Hat, Inc. * * Author: Nikos Mavrogiannopoulos * @@ -637,6 +638,8 @@ gnutls_priority_set(gnutls_session_t session, gnutls_priority_t priority) session->internals.priorities.compression.algorithms == 0) return gnutls_assert_val(GNUTLS_E_NO_PRIORITIES_WERE_SET); + session->internals.additional_verify_flags |= priority->additional_verify_flags; + return 0; } diff --git a/lib/gnutls_x509.c b/lib/gnutls_x509.c index 22dea94fdf..648a0b9e5a 100644 --- a/lib/gnutls_x509.c +++ b/lib/gnutls_x509.c @@ -224,8 +224,7 @@ _gnutls_x509_cert_verify_peers(gnutls_session_t session, } verify_flags = - cred->verify_flags | session->internals.priorities. - additional_verify_flags; + cred->verify_flags | session->internals.additional_verify_flags; /* generate a list of gnutls_certs based on the auth info * raw certs. */ diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index c249f7b75f..ffcf04302b 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -1168,6 +1168,45 @@ char *gnutls_session_get_desc(gnutls_session_t session); typedef int gnutls_certificate_verify_function(gnutls_session_t); void gnutls_session_set_verify_function(gnutls_session_t session, gnutls_certificate_verify_function * func); +/** + * gnutls_vdata_types_t: + * @GNUTLS_DT_UNKNOWN: Unknown data type. + * @GNUTLS_DT_DNS_HOSTNAME: The data contain a null-terminated DNS hostname; the hostname will be + * matched using the RFC6125 rules. + * @GNUTLS_DT_RFC822NAME: The data contain a null-terminated email address; the email will be + * matched against the RFC822Name field of the certificate, or the EMAIL DN component if the + * former isn't available. Prior to matching the email address will be converted to ACE + * (ASCII-compatible-encoding). + * @GNUTLS_DT_KEY_PURPOSE_OID: The data contain a null-terminated key purpose OID. It will be matched + * against the certificate's Extended Key Usage extension. + * + * Enumeration of different typed-data options. They are used as input to certificate + * verification functions to provide information about the name and purpose of the + * certificate. Only a single option of a type can be provided to the relevant functions. + */ +typedef enum { + GNUTLS_DT_UNKNOWN = 0, + GNUTLS_DT_DNS_HOSTNAME = 1, + GNUTLS_DT_KEY_PURPOSE_OID = 2, + GNUTLS_DT_RFC822NAME = 3 +} gnutls_vdata_types_t; + +typedef struct { + gnutls_vdata_types_t type; + unsigned char *data; + unsigned int size; +} gnutls_typed_vdata_st; + +void gnutls_session_set_verify_cert(gnutls_session_t session, + const char *hostname, unsigned flags); + +void +gnutls_session_set_verify_cert2(gnutls_session_t session, + gnutls_typed_vdata_st * data, + unsigned elements, unsigned flags); + +unsigned int gnutls_session_get_verify_cert_status(gnutls_session_t); + int gnutls_session_set_premaster(gnutls_session_t session, unsigned int entity, gnutls_protocol_t version, @@ -1941,26 +1980,6 @@ int gnutls_certificate_verify_peers3(gnutls_session_t session, const char *hostname, unsigned int *status); -/** - * gnutls_vdata_types_t: - * @GNUTLS_DT_UNKNOWN: Unknown data type. - * @GNUTLS_DT_DNS_HOSTNAME: The data contain a null-terminated DNS hostname. - * @GNUTLS_DT_KEY_PURPOSE_OID: The data contain a null-terminated key purpose OID. - * - * Enumeration of different key exchange algorithms. - */ -typedef enum { - GNUTLS_DT_UNKNOWN = 0, - GNUTLS_DT_DNS_HOSTNAME = 1, - GNUTLS_DT_KEY_PURPOSE_OID = 2 -} gnutls_vdata_types_t; - -typedef struct { - gnutls_vdata_types_t type; - unsigned char *data; - unsigned int size; -} gnutls_typed_vdata_st; - int gnutls_certificate_verify_peers(gnutls_session_t session, gnutls_typed_vdata_st * data, @@ -2346,6 +2365,7 @@ int gnutls_fips140_mode_enabled(void); #define GNUTLS_E_AUTH_ERROR -343 #define GNUTLS_E_NO_APPLICATION_PROTOCOL -344 #define GNUTLS_E_SOCKETS_INIT_ERROR -345 +#define GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR -348 #define GNUTLS_E_SELF_TEST_ERROR -400 #define GNUTLS_E_NO_SELF_TEST -401 diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 8430932682..801b048b81 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1026,6 +1026,9 @@ GNUTLS_3_1_0 { gnutls_x509_othername_to_virtual; _gnutls_global_init_skip; gnutls_session_set_verify_function; + gnutls_session_set_verify_cert; + gnutls_session_set_verify_cert2; + gnutls_session_get_verify_cert_status; } GNUTLS_3_0_0; GNUTLS_FIPS140 { |