From d2e2d0736c4a7c8f3052cffbe0fa1a8b81239a6a Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Sat, 31 Jul 2004 07:59:41 +0000 Subject: Added some default limits in the verification of certificate chains, to avoid denial of service attacks. Also added gnutls_certificate_set_verify_limits() to override them. --- NEWS | 3 ++ lib/auth_cert.h | 2 ++ lib/gnutls.h.in.in | 3 +- lib/gnutls_cert.c | 86 ++++++++++++++++++++++++++++++++++++++----------- lib/gnutls_errors.c | 1 + lib/gnutls_errors_int.h | 1 + lib/gnutls_int.h | 5 +++ lib/gnutls_ui.c | 19 ++++++++++- lib/gnutls_ui.h | 1 + lib/gnutls_x509.c | 36 +++++++++++++++++++++ 10 files changed, 136 insertions(+), 21 deletions(-) diff --git a/NEWS b/NEWS index 22a70f60a3..d99109d90c 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,9 @@ Version 1.0.17 latest (yet unreleased) draft. Unfortunately this breaks compatibility with previous versions. - Changed the makefiles to be more portable. +- Added some default limits in the verification of certificate + chains, to avoid denial of service attacks. Also added + gnutls_certificate_set_verify_limits() to override them. Version 1.0.16 (10/07/2004) - Do not free the SRP (prime and generator) parameters obtained from the diff --git a/lib/auth_cert.h b/lib/auth_cert.h index a27dbffc92..2d4b6cc1eb 100644 --- a/lib/auth_cert.h +++ b/lib/auth_cert.h @@ -81,6 +81,8 @@ typedef struct { unsigned int verify_flags; /* flags to be used at * certificate verification. */ + unsigned int verify_depth; + unsigned int verify_bits; /* holds a sequence of the * RDNs of the CAs above. diff --git a/lib/gnutls.h.in.in b/lib/gnutls.h.in.in index 114f348bda..41cccb244d 100644 --- a/lib/gnutls.h.in.in +++ b/lib/gnutls.h.in.in @@ -363,7 +363,8 @@ void gnutls_certificate_free_crls(gnutls_certificate_credentials sc); void gnutls_certificate_set_dh_params(gnutls_certificate_credentials res, gnutls_dh_params); void gnutls_certificate_set_rsa_export_params(gnutls_certificate_credentials res, gnutls_rsa_params rsa_params); void gnutls_certificate_set_verify_flags(gnutls_certificate_credentials res, unsigned int flags); - +void gnutls_certificate_set_verify_limits(gnutls_certificate_credentials res, unsigned int max_bits, + unsigned int max_depth); int gnutls_certificate_set_x509_trust_file( gnutls_certificate_credentials res, const char* CAFILE, gnutls_x509_crt_fmt); diff --git a/lib/gnutls_cert.c b/lib/gnutls_cert.c index 5c1ca58869..81875677ad 100644 --- a/lib/gnutls_cert.c +++ b/lib/gnutls_cert.c @@ -227,6 +227,9 @@ int gnutls_certificate_allocate_credentials(gnutls_certificate_credentials * res if (*res == NULL) return GNUTLS_E_MEMORY_ERROR; + + (*res)->verify_bits = DEFAULT_VERIFY_BITS; + (*res)->verify_depth = DEFAULT_VERIFY_DEPTH; return 0; } @@ -482,6 +485,11 @@ int _gnutls_openpgp_cert_verify_peers(gnutls_session session) return GNUTLS_E_NO_CERTIFICATE_FOUND; } + if (info->ncerts > cred->verify_depth) { + gnutls_assert(); + return GNUTLS_E_CONSTRAINT_ERROR; + } + /* generate a list of gnutls_certs based on the auth info * raw certs. */ @@ -508,6 +516,59 @@ int _gnutls_openpgp_cert_verify_peers(gnutls_session session) return verify; } +/** + * gnutls_certificate_verify_peers2 - This function returns the peer's certificate verification status + * @session: is a gnutls session + * @status: is the output of the verification + * + * This function will try to verify the peer's certificate and return its status (trusted, invalid etc.). + * The value of @status should be one or more of the gnutls_certificate_status_t + * enumerated elements bitwise or'd. + * However you must also check the peer's name in order to check if the verified certificate belongs to the + * actual peer. + * + * Returns a negative error code on error and zero on success. + * + * This is the same as gnutls_x509_verify_certificate(). + * + **/ +int gnutls_certificate_verify_peers2(gnutls_session session, unsigned int *status) +{ + CERTIFICATE_AUTH_INFO info; + int ret; + + CHECK_AUTH(GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST); + + info = _gnutls_get_auth_info(session); + if (info == NULL) { + return GNUTLS_E_NO_CERTIFICATE_FOUND; + } + + if (info->raw_certificate_list == NULL || info->ncerts == 0) + return GNUTLS_E_NO_CERTIFICATE_FOUND; + + switch (gnutls_certificate_type_get(session)) { + case GNUTLS_CRT_X509: { + ret = _gnutls_x509_cert_verify_peers(session); + *status = ret; + + if (ret < 0) + return ret; + return 0; + } + case GNUTLS_CRT_OPENPGP: { + ret = _gnutls_openpgp_cert_verify_peers(session); + *status = ret; + + if (ret < 0) + return ret; + return 0; + } + default: + return GNUTLS_E_INVALID_REQUEST; + } +} + /** * gnutls_certificate_verify_peers - This function returns the peer's certificate verification status * @session: is a gnutls session @@ -517,33 +578,20 @@ int _gnutls_openpgp_cert_verify_peers(gnutls_session session) * actual peer. * * The return value should be one or more of the gnutls_certificate_status - * enumerated elements bitwise or'd. + * enumerated elements bitwise or'd, or a negative error code on error. * * This is the same as gnutls_x509_verify_certificate(). * **/ int gnutls_certificate_verify_peers(gnutls_session session) { - CERTIFICATE_AUTH_INFO info; - - CHECK_AUTH(GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST); +unsigned int status; +int ret; - info = _gnutls_get_auth_info(session); - if (info == NULL) { - return GNUTLS_E_NO_CERTIFICATE_FOUND; - } + ret = gnutls_certificate_verify_peers2( session, &status); + if (ret < 0) return ret; - if (info->raw_certificate_list == NULL || info->ncerts == 0) - return GNUTLS_E_NO_CERTIFICATE_FOUND; - - switch( gnutls_certificate_type_get( session)) { - case GNUTLS_CRT_X509: - return _gnutls_x509_cert_verify_peers( session); - case GNUTLS_CRT_OPENPGP: - return _gnutls_openpgp_cert_verify_peers( session); - default: - return GNUTLS_E_INVALID_REQUEST; - } + return status; } /** diff --git a/lib/gnutls_errors.c b/lib/gnutls_errors.c index c68239e28c..734947204d 100644 --- a/lib/gnutls_errors.c +++ b/lib/gnutls_errors.c @@ -148,6 +148,7 @@ static gnutls_error_entry error_algorithms[] = { ERROR_ENTRY("The PKCS structure's bag type is unknown.", GNUTLS_E_UNKNOWN_PKCS_BAG_TYPE, 1), ERROR_ENTRY("The given password contains invalid characters.", GNUTLS_E_INVALID_PASSWORD, 1), ERROR_ENTRY("The Message Authentication Code verification failed.", GNUTLS_E_MAC_VERIFY_FAILED, 1), + ERROR_ENTRY("Some constraint limits were reached.", GNUTLS_E_CONSTRAINT_ERROR, 1), {NULL, NULL, 0, 0} }; diff --git a/lib/gnutls_errors_int.h b/lib/gnutls_errors_int.h index 3f64c626d7..10f7e27f75 100644 --- a/lib/gnutls_errors_int.h +++ b/lib/gnutls_errors_int.h @@ -115,6 +115,7 @@ #define GNUTLS_E_UNKNOWN_PKCS_BAG_TYPE -98 #define GNUTLS_E_INVALID_PASSWORD -99 #define GNUTLS_E_MAC_VERIFY_FAILED -100 /* for PKCS #12 MAC */ +#define GNUTLS_E_CONSTRAINT_ERROR -101 #define GNUTLS_E_BASE64_ENCODING_ERROR -201 #define GNUTLS_E_INCOMPATIBLE_GCRYPT_LIBRARY -202 diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index f14d7e5ced..65e072d4af 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -92,6 +92,11 @@ typedef void * gnutls_transport_ptr; #define HANDSHAKE_HEADER_SIZE 4 +/* defaults for verification functions + */ +#define DEFAULT_VERIFY_DEPTH 5 +#define DEFAULT_VERIFY_BITS 8200 + #include #include diff --git a/lib/gnutls_ui.c b/lib/gnutls_ui.c index b19a4b8499..8a5708a1de 100644 --- a/lib/gnutls_ui.c +++ b/lib/gnutls_ui.c @@ -373,7 +373,6 @@ void gnutls_anon_set_params_function(gnutls_anon_server_credentials res, res->params_func = func; } - /** * gnutls_certificate_set_verify_flags - This function will set the flags to be used at certificate verification * @res: is a gnutls_certificate_credentials structure @@ -388,6 +387,24 @@ void gnutls_certificate_set_verify_flags(gnutls_certificate_credentials res, uns res->verify_flags = flags; } +/** + * gnutls_certificate_set_verify_limits - This function will set the upper limits to be used at certificate verification + * @res: is a gnutls_certificate_credentials structure + * @max_bits: is the number of bits of an acceptable certificate (default 8200) + * @max_depth: is maximum depth of the verification of a certificate chain (default 5) + * + * This function will set some upper limits for the default verification function + * (gnutls_certificate_verify_peers()) to avoid denial of service attacks. + * + **/ +void gnutls_certificate_set_verify_limits(gnutls_certificate_credentials res, unsigned int max_bits, + unsigned int max_depth) +{ + res->verify_depth = max_depth; + res->verify_bits = max_bits; +} + + /** * gnutls_certificate_set_rsa_export_params - This function will set the RSA parameters for a server to use * @res: is a gnutls_certificate_credentials structure diff --git a/lib/gnutls_ui.h b/lib/gnutls_ui.h index f30f5afae0..0d6f04224b 100644 --- a/lib/gnutls_ui.h +++ b/lib/gnutls_ui.h @@ -105,6 +105,7 @@ time_t gnutls_certificate_expiration_time_peers(gnutls_session session); int gnutls_certificate_client_get_request_status(gnutls_session); int gnutls_certificate_verify_peers(gnutls_session); +int gnutls_certificate_verify_peers2(gnutls_session, unsigned int* status); int gnutls_pem_base64_encode(const char *header, const gnutls_datum * data, char *result, size_t * result_size); diff --git a/lib/gnutls_x509.c b/lib/gnutls_x509.c index 5d7d5484a3..437c945928 100644 --- a/lib/gnutls_x509.c +++ b/lib/gnutls_x509.c @@ -53,6 +53,29 @@ * some x509 certificate parsing functions. */ +/* Check if the number of bits of the key in the certificate + * is unacceptable. + */ +inline +static int check_bits( gnutls_x509_crt crt, unsigned int max_bits) +{ +int ret; +unsigned int bits; + + ret = gnutls_x509_crt_get_pk_algorithm( crt, &bits); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + if ( bits > max_bits) { + gnutls_assert(); + return GNUTLS_E_CONSTRAINT_ERROR; + } + + return 0; +} + #define CLEAR_CERTS for(x=0;xraw_certificate_list == NULL || info->ncerts == 0) return GNUTLS_E_NO_CERTIFICATE_FOUND; + + if (info->ncerts > cred->verify_depth) { + gnutls_assert(); + return GNUTLS_E_CONSTRAINT_ERROR; + } + /* generate a list of gnutls_certs based on the auth info * raw certs. @@ -122,6 +151,13 @@ int _gnutls_x509_cert_verify_peers(gnutls_session session) CLEAR_CERTS; return ret; } + + ret = check_bits( peer_certificate_list[i], cred->verify_bits); + if (ret < 0) { + gnutls_assert(); + CLEAR_CERTS; + return ret; + } } /* Verify certificate -- cgit v1.2.1