From f49ba0a241c56dd5ad75441eafff6288e5cb44d1 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Thu, 25 Dec 2014 16:01:00 -0800 Subject: [PATCH 2/2] Port memory-full checking to GnuTLS 3.3 Instead of using gnutls_global_set_mem_functions, check every call to a GnuTLS function that might return an indication of memory exhaustion. Suggested by Dmitry Antipov in: http://lists.gnu.org/archive/html/emacs-devel/2014-12/msg02056.html * gnutls.c (check_memory_full): New function. (emacs_gnutls_handshake, emacs_gnutls_handle_error) (gnutls_make_error, gnutls_certificate_details) (Fgnutls_peer_status, Fgnutls_boot): Use it. (emacs_gnutls_global_init): Avoid gnutls_global_set_mem_functions. --- src/ChangeLog | 11 +++++++++++ src/gnutls.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/ChangeLog b/src/ChangeLog index 6b35ed1..dfae08e 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,5 +1,16 @@ 2014-12-25 Paul Eggert + Port memory-full checking to GnuTLS 3.3 + Instead of using gnutls_global_set_mem_functions, check every call + to a GnuTLS function that might return an indication of memory + exhaustion. Suggested by Dmitry Antipov in: + http://lists.gnu.org/archive/html/emacs-devel/2014-12/msg02056.html + * gnutls.c (check_memory_full): New function. + (emacs_gnutls_handshake, emacs_gnutls_handle_error) + (gnutls_make_error, gnutls_certificate_details) + (Fgnutls_peer_status, Fgnutls_boot): Use it. + (emacs_gnutls_global_init): Avoid gnutls_global_set_mem_functions. + Wrap gnutls.c functions more simply * gnutls.c: If WINDOWSNT, use '#define gnutls_FOO fn_gnutls_FOO' to wrap gnutls functions, rather than the inverse when not WINDOWSNT. diff --git a/src/gnutls.c b/src/gnutls.c index 2a19591..2876156 100644 --- a/src/gnutls.c +++ b/src/gnutls.c @@ -385,6 +385,17 @@ init_gnutls_functions (void) #endif +/* Report memory exhaustion if ERR is an out-of-memory indication. */ +static void +check_memory_full (int err) +{ + /* When GnuTLS exhausts memory, it doesn't say how much memory it + asked for, so tell the Emacs allocator that GnuTLS asked for no + bytes. This isn't accurate, but it's good enough. */ + if (err == GNUTLS_E_MEMORY_ERROR) + memory_full (0); +} + #ifdef HAVE_GNUTLS3 /* Log a simple audit message. */ static void @@ -481,7 +492,7 @@ emacs_gnutls_handshake (struct Lisp_Process *proc) } else { - gnutls_alert_send_appropriate (state, ret); + check_memory_full (gnutls_alert_send_appropriate (state, ret)); } return ret; } @@ -598,6 +609,8 @@ emacs_gnutls_handle_error (gnutls_session_t session, int err) if (err >= 0) return 1; + check_memory_full (err); + max_log_level = global_gnutls_log_level; /* TODO: use gnutls-error-fatalp and gnutls-error-string. */ @@ -663,6 +676,7 @@ gnutls_make_error (int err) return Qgnutls_e_invalid_session; } + check_memory_full (err); return make_number (err); } @@ -823,6 +837,7 @@ gnutls_certificate_details (gnutls_x509_crt_t cert) /* Version. */ { int version = gnutls_x509_crt_get_version (cert); + check_memory_full (version); if (version >= GNUTLS_E_SUCCESS) res = nconc2 (res, list2 (intern (":version"), make_number (version))); @@ -831,10 +846,12 @@ gnutls_certificate_details (gnutls_x509_crt_t cert) /* Serial. */ buf_size = 0; err = gnutls_x509_crt_get_serial (cert, NULL, &buf_size); + check_memory_full (err); if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) { void *serial = xmalloc (buf_size); err = gnutls_x509_crt_get_serial (cert, serial, &buf_size); + check_memory_full (err); if (err >= GNUTLS_E_SUCCESS) res = nconc2 (res, list2 (intern (":serial-number"), gnutls_hex_string (serial, buf_size, ""))); @@ -844,10 +861,12 @@ gnutls_certificate_details (gnutls_x509_crt_t cert) /* Issuer. */ buf_size = 0; err = gnutls_x509_crt_get_issuer_dn (cert, NULL, &buf_size); + check_memory_full (err); if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) { char *dn = xmalloc (buf_size); err = gnutls_x509_crt_get_issuer_dn (cert, dn, &buf_size); + check_memory_full (err); if (err >= GNUTLS_E_SUCCESS) res = nconc2 (res, list2 (intern (":issuer"), make_string (dn, buf_size))); @@ -873,10 +892,12 @@ gnutls_certificate_details (gnutls_x509_crt_t cert) /* Subject. */ buf_size = 0; err = gnutls_x509_crt_get_dn (cert, NULL, &buf_size); + check_memory_full (err); if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) { char *dn = xmalloc (buf_size); err = gnutls_x509_crt_get_dn (cert, dn, &buf_size); + check_memory_full (err); if (err >= GNUTLS_E_SUCCESS) res = nconc2 (res, list2 (intern (":subject"), make_string (dn, buf_size))); @@ -890,6 +911,7 @@ gnutls_certificate_details (gnutls_x509_crt_t cert) unsigned int bits; err = gnutls_x509_crt_get_pk_algorithm (cert, &bits); + check_memory_full (err); if (err >= GNUTLS_E_SUCCESS) { const char *name = gnutls_pk_algorithm_get_name (err); @@ -907,10 +929,12 @@ gnutls_certificate_details (gnutls_x509_crt_t cert) /* Unique IDs. */ buf_size = 0; err = gnutls_x509_crt_get_issuer_unique_id (cert, NULL, &buf_size); + check_memory_full (err); if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) { char *buf = xmalloc (buf_size); err = gnutls_x509_crt_get_issuer_unique_id (cert, buf, &buf_size); + check_memory_full (err); if (err >= GNUTLS_E_SUCCESS) res = nconc2 (res, list2 (intern (":issuer-unique-id"), make_string (buf, buf_size))); @@ -919,10 +943,12 @@ gnutls_certificate_details (gnutls_x509_crt_t cert) buf_size = 0; err = gnutls_x509_crt_get_subject_unique_id (cert, NULL, &buf_size); + check_memory_full (err); if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) { char *buf = xmalloc (buf_size); err = gnutls_x509_crt_get_subject_unique_id (cert, buf, &buf_size); + check_memory_full (err); if (err >= GNUTLS_E_SUCCESS) res = nconc2 (res, list2 (intern (":subject-unique-id"), make_string (buf, buf_size))); @@ -932,6 +958,7 @@ gnutls_certificate_details (gnutls_x509_crt_t cert) /* Signature. */ err = gnutls_x509_crt_get_signature_algorithm (cert); + check_memory_full (err); if (err >= GNUTLS_E_SUCCESS) { const char *name = gnutls_sign_get_name (err); @@ -943,10 +970,12 @@ gnutls_certificate_details (gnutls_x509_crt_t cert) /* Public key ID. */ buf_size = 0; err = gnutls_x509_crt_get_key_id (cert, 0, NULL, &buf_size); + check_memory_full (err); if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) { void *buf = xmalloc (buf_size); err = gnutls_x509_crt_get_key_id (cert, 0, buf, &buf_size); + check_memory_full (err); if (err >= GNUTLS_E_SUCCESS) res = nconc2 (res, list2 (intern (":public-key-id"), gnutls_hex_string (buf, buf_size, "sha1:"))); @@ -957,11 +986,13 @@ gnutls_certificate_details (gnutls_x509_crt_t cert) buf_size = 0; err = gnutls_x509_crt_get_fingerprint (cert, GNUTLS_DIG_SHA1, NULL, &buf_size); + check_memory_full (err); if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) { void *buf = xmalloc (buf_size); err = gnutls_x509_crt_get_fingerprint (cert, GNUTLS_DIG_SHA1, buf, &buf_size); + check_memory_full (err); if (err >= GNUTLS_E_SUCCESS) res = nconc2 (res, list2 (intern (":certificate-id"), gnutls_hex_string (buf, buf_size, "sha1:"))); @@ -1063,6 +1094,7 @@ The return value is a property list with top-level keys :warnings and /* Diffie-Hellman prime bits. */ { int bits = gnutls_dh_get_prime_bits (state); + check_memory_full (bits); if (bits > 0) result = nconc2 (result, list2 (intern (":diffie-hellman-prime-bits"), make_number (bits))); @@ -1105,11 +1137,8 @@ emacs_gnutls_global_init (void) int ret = GNUTLS_E_SUCCESS; if (!gnutls_global_initialized) - { - gnutls_global_set_mem_functions (xmalloc, xmalloc, NULL, - xrealloc, xfree); - ret = gnutls_global_init (); - } + ret = gnutls_global_init (); + gnutls_global_initialized = 1; return gnutls_make_error (ret); @@ -1292,7 +1321,7 @@ one trustfile (usually a CA bundle). */) unsigned int gnutls_verify_flags = GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT; GNUTLS_LOG (2, max_log_level, "allocating x509 credentials"); - gnutls_certificate_allocate_credentials (&x509_cred); + check_memory_full (gnutls_certificate_allocate_credentials (&x509_cred)); XPROCESS (proc)->gnutls_x509_cred = x509_cred; verify_flags = Fplist_get (proplist, QCgnutls_bootprop_verify_flags); @@ -1311,7 +1340,7 @@ one trustfile (usually a CA bundle). */) else /* Qgnutls_anon: */ { GNUTLS_LOG (2, max_log_level, "allocating anon credentials"); - gnutls_anon_allocate_client_credentials (&anon_cred); + check_memory_full (gnutls_anon_allocate_client_credentials (&anon_cred)); XPROCESS (proc)->gnutls_anon_cred = anon_cred; } @@ -1327,8 +1356,11 @@ one trustfile (usually a CA bundle). */) (GNUTLS_VERSION_MINOR > 0 || GNUTLS_VERSION_PATCH >= 20) > 3 ret = gnutls_certificate_set_x509_system_trust (x509_cred); if (ret < GNUTLS_E_SUCCESS) - GNUTLS_LOG2i (4, max_log_level, - "setting system trust failed with code ", ret); + { + check_memory_full (ret); + GNUTLS_LOG2i (4, max_log_level, + "setting system trust failed with code ", ret); + } #endif for (tail = trustfiles; CONSP (tail); tail = XCDR (tail)) @@ -1546,7 +1578,10 @@ one trustfile (usually a CA bundle). */) XPROCESS (proc)->gnutls_certificate = gnutls_verify_cert; - if (!gnutls_x509_crt_check_hostname (gnutls_verify_cert, c_hostname)) + int err = gnutls_x509_crt_check_hostname (gnutls_verify_cert, + c_hostname); + check_memory_full (err); + if (!err) { XPROCESS (proc)->gnutls_extra_peer_verification |= CERTIFICATE_NOT_MATCHING; -- 1.9.3