diff options
Diffstat (limited to 'src/gnutls.c')
-rw-r--r-- | src/gnutls.c | 827 |
1 files changed, 613 insertions, 214 deletions
diff --git a/src/gnutls.c b/src/gnutls.c index d7a1399f106..a0de0238c47 100644 --- a/src/gnutls.c +++ b/src/gnutls.c @@ -1,5 +1,5 @@ /* GnuTLS glue for GNU Emacs. - Copyright (C) 2010-2017 Free Software Foundation, Inc. + Copyright (C) 2010-2022 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -25,29 +25,62 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include "gnutls.h" #include "coding.h" #include "buffer.h" +#include "pdumper.h" -#if 0x030014 <= GNUTLS_VERSION_NUMBER -# define HAVE_GNUTLS_X509_SYSTEM_TRUST -#endif +#ifdef HAVE_GNUTLS + +# if GNUTLS_VERSION_NUMBER >= 0x030014 +# define HAVE_GNUTLS_X509_SYSTEM_TRUST +# endif + +# if GNUTLS_VERSION_NUMBER >= 0x030200 +# define HAVE_GNUTLS_CIPHER_GET_IV_SIZE +# endif + +# if GNUTLS_VERSION_NUMBER >= 0x030202 +# define HAVE_GNUTLS_CIPHER_GET_TAG_SIZE +# define HAVE_GNUTLS_DIGEST_LIST /* also gnutls_digest_get_name */ +# endif + +# if GNUTLS_VERSION_NUMBER >= 0x030205 +# define HAVE_GNUTLS_EXT__DUMBFW +# endif + +# if GNUTLS_VERSION_NUMBER >= 0x030400 +# define HAVE_GNUTLS_ETM_STATUS +# endif + +# if GNUTLS_VERSION_NUMBER < 0x030600 +# define HAVE_GNUTLS_COMPRESSION_GET +# endif + +/* gnutls_mac_get_nonce_size was added in GnuTLS 3.2.0, but was + exported only since 3.3.0. */ +# if GNUTLS_VERSION_NUMBER >= 0x030300 +# define HAVE_GNUTLS_MAC_GET_NONCE_SIZE +# endif + +# if GNUTLS_VERSION_NUMBER >= 0x030501 +# define HAVE_GNUTLS_EXT_GET_NAME +# endif /* Although AEAD support started in GnuTLS 3.4.0 and works in 3.5.14, it was broken through at least GnuTLS 3.4.10; see: - https://lists.gnu.org/archive/html/emacs-devel/2017-07/msg00992.html + https://lists.gnu.org/r/emacs-devel/2017-07/msg00992.html The relevant fix seems to have been made in GnuTLS 3.5.1; see: https://gitlab.com/gnutls/gnutls/commit/568935848dd6b82b9315d8b6c529d00e2605e03d So, require 3.5.1. */ -#if 0x030501 <= GNUTLS_VERSION_NUMBER -# define HAVE_GNUTLS_AEAD -#endif - -#ifdef HAVE_GNUTLS +# if GNUTLS_VERSION_NUMBER >= 0x030501 +# define HAVE_GNUTLS_AEAD +# endif # ifdef WINDOWSNT # include <windows.h> +# include "w32common.h" # include "w32.h" # endif -static bool emacs_gnutls_handle_error (gnutls_session_t, int); +static int emacs_gnutls_handle_error (gnutls_session_t, int); static bool gnutls_global_initialized; @@ -134,6 +167,8 @@ DEF_DLL_FN (int, gnutls_x509_crt_check_hostname, DEF_DLL_FN (int, gnutls_x509_crt_check_issuer, (gnutls_x509_crt_t, gnutls_x509_crt_t)); DEF_DLL_FN (void, gnutls_x509_crt_deinit, (gnutls_x509_crt_t)); +DEF_DLL_FN (int, gnutls_x509_crt_export, + (gnutls_x509_crt_t, gnutls_x509_crt_fmt_t, void *, size_t *)); DEF_DLL_FN (int, gnutls_x509_crt_import, (gnutls_x509_crt_t, const gnutls_datum_t *, gnutls_x509_crt_fmt_t)); @@ -155,6 +190,9 @@ DEF_DLL_FN (int, gnutls_x509_crt_get_dn, (gnutls_x509_crt_t, char *, size_t *)); DEF_DLL_FN (int, gnutls_x509_crt_get_pk_algorithm, (gnutls_x509_crt_t, unsigned int *)); +DEF_DLL_FN (int, gnutls_x509_crt_print, + (gnutls_x509_crt_t, gnutls_certificate_print_formats_t, + gnutls_datum_t *)); DEF_DLL_FN (const char *, gnutls_pk_algorithm_get_name, (gnutls_pk_algorithm_t)); DEF_DLL_FN (int, gnutls_pk_bits_to_sec_param, @@ -183,19 +221,33 @@ DEF_DLL_FN (const char *, gnutls_cipher_get_name, (gnutls_cipher_algorithm_t)); DEF_DLL_FN (gnutls_mac_algorithm_t, gnutls_mac_get, (gnutls_session_t)); DEF_DLL_FN (const char *, gnutls_mac_get_name, (gnutls_mac_algorithm_t)); +# ifdef HAVE_GNUTLS_COMPRESSION_GET +DEF_DLL_FN (gnutls_compression_method_t, gnutls_compression_get, + (gnutls_session_t)); +DEF_DLL_FN (const char *, gnutls_compression_get_name, + (gnutls_compression_method_t)); +# endif +DEF_DLL_FN (unsigned, gnutls_safe_renegotiation_status, (gnutls_session_t)); # ifdef HAVE_GNUTLS3 -DEF_DLL_FN (int, gnutls_rnd, (gnutls_rnd_level_t, void *, size_t)); DEF_DLL_FN (const gnutls_mac_algorithm_t *, gnutls_mac_list, (void)); +# ifdef HAVE_GNUTLS_MAC_GET_NONCE_SIZE DEF_DLL_FN (size_t, gnutls_mac_get_nonce_size, (gnutls_mac_algorithm_t)); +# endif DEF_DLL_FN (size_t, gnutls_mac_get_key_size, (gnutls_mac_algorithm_t)); +# ifdef HAVE_GNUTLS_DIGEST_LIST DEF_DLL_FN (const gnutls_digest_algorithm_t *, gnutls_digest_list, (void)); DEF_DLL_FN (const char *, gnutls_digest_get_name, (gnutls_digest_algorithm_t)); +# endif DEF_DLL_FN (gnutls_cipher_algorithm_t *, gnutls_cipher_list, (void)); +# ifdef HAVE_GNUTLS_CIPHER_GET_IV_SIZE DEF_DLL_FN (int, gnutls_cipher_get_iv_size, (gnutls_cipher_algorithm_t)); +# endif DEF_DLL_FN (size_t, gnutls_cipher_get_key_size, (gnutls_cipher_algorithm_t)); DEF_DLL_FN (int, gnutls_cipher_get_block_size, (gnutls_cipher_algorithm_t)); +# ifdef HAVE_GNUTLS_CIPHER_GET_TAG_SIZE DEF_DLL_FN (int, gnutls_cipher_get_tag_size, (gnutls_cipher_algorithm_t)); +# endif DEF_DLL_FN (int, gnutls_cipher_init, (gnutls_cipher_hd_t *, gnutls_cipher_algorithm_t, const gnutls_datum_t *, const gnutls_datum_t *)); @@ -217,6 +269,9 @@ DEF_DLL_FN (int, gnutls_aead_cipher_decrypt, (gnutls_aead_cipher_hd_t, const void *, size_t, const void *, size_t, size_t, const void *, size_t, void *, size_t *)); # endif +# ifdef HAVE_GNUTLS_ETM_STATUS +DEF_DLL_FN (unsigned, gnutls_session_etm_status, (gnutls_session_t)); +# endif DEF_DLL_FN (int, gnutls_hmac_init, (gnutls_hmac_hd_t *, gnutls_mac_algorithm_t, const void *, size_t)); DEF_DLL_FN (int, gnutls_hmac_get_len, (gnutls_mac_algorithm_t)); @@ -229,8 +284,12 @@ DEF_DLL_FN (int, gnutls_hash_get_len, (gnutls_digest_algorithm_t)); DEF_DLL_FN (int, gnutls_hash, (gnutls_hash_hd_t, const void *, size_t)); DEF_DLL_FN (void, gnutls_hash_deinit, (gnutls_hash_hd_t, void *)); DEF_DLL_FN (void, gnutls_hash_output, (gnutls_hash_hd_t, void *)); +# ifdef HAVE_GNUTLS_EXT_GET_NAME +DEF_DLL_FN (const char *, gnutls_ext_get_name, (unsigned int)); +# endif # endif /* HAVE_GNUTLS3 */ +static gnutls_free_function *gnutls_free_func; static bool init_gnutls_functions (void) @@ -286,6 +345,7 @@ init_gnutls_functions (void) LOAD_DLL_FN (library, gnutls_x509_crt_check_hostname); LOAD_DLL_FN (library, gnutls_x509_crt_check_issuer); LOAD_DLL_FN (library, gnutls_x509_crt_deinit); + LOAD_DLL_FN (library, gnutls_x509_crt_export); LOAD_DLL_FN (library, gnutls_x509_crt_import); LOAD_DLL_FN (library, gnutls_x509_crt_init); LOAD_DLL_FN (library, gnutls_x509_crt_get_fingerprint); @@ -296,6 +356,7 @@ init_gnutls_functions (void) LOAD_DLL_FN (library, gnutls_x509_crt_get_expiration_time); LOAD_DLL_FN (library, gnutls_x509_crt_get_dn); LOAD_DLL_FN (library, gnutls_x509_crt_get_pk_algorithm); + LOAD_DLL_FN (library, gnutls_x509_crt_print); LOAD_DLL_FN (library, gnutls_pk_algorithm_get_name); LOAD_DLL_FN (library, gnutls_pk_bits_to_sec_param); LOAD_DLL_FN (library, gnutls_x509_crt_get_issuer_unique_id); @@ -313,18 +374,30 @@ init_gnutls_functions (void) LOAD_DLL_FN (library, gnutls_cipher_get_name); LOAD_DLL_FN (library, gnutls_mac_get); LOAD_DLL_FN (library, gnutls_mac_get_name); +# ifdef HAVE_GNUTLS_COMPRESSION_GET + LOAD_DLL_FN (library, gnutls_compression_get); + LOAD_DLL_FN (library, gnutls_compression_get_name); +# endif + LOAD_DLL_FN (library, gnutls_safe_renegotiation_status); # ifdef HAVE_GNUTLS3 - LOAD_DLL_FN (library, gnutls_rnd); LOAD_DLL_FN (library, gnutls_mac_list); +# ifdef HAVE_GNUTLS_MAC_GET_NONCE_SIZE LOAD_DLL_FN (library, gnutls_mac_get_nonce_size); +# endif LOAD_DLL_FN (library, gnutls_mac_get_key_size); +# ifdef HAVE_GNUTLS_DIGEST_LIST LOAD_DLL_FN (library, gnutls_digest_list); LOAD_DLL_FN (library, gnutls_digest_get_name); +# endif LOAD_DLL_FN (library, gnutls_cipher_list); +# ifdef HAVE_GNUTLS_CIPHER_GET_IV_SIZE LOAD_DLL_FN (library, gnutls_cipher_get_iv_size); +# endif LOAD_DLL_FN (library, gnutls_cipher_get_key_size); LOAD_DLL_FN (library, gnutls_cipher_get_block_size); +# ifdef HAVE_GNUTLS_CIPHER_GET_TAG_SIZE LOAD_DLL_FN (library, gnutls_cipher_get_tag_size); +# endif LOAD_DLL_FN (library, gnutls_cipher_init); LOAD_DLL_FN (library, gnutls_cipher_set_iv); LOAD_DLL_FN (library, gnutls_cipher_encrypt2); @@ -336,6 +409,9 @@ init_gnutls_functions (void) LOAD_DLL_FN (library, gnutls_aead_cipher_encrypt); LOAD_DLL_FN (library, gnutls_aead_cipher_decrypt); # endif +# ifdef HAVE_GNUTLS_ETM_STATUS + LOAD_DLL_FN (library, gnutls_session_etm_status); +# endif LOAD_DLL_FN (library, gnutls_hmac_init); LOAD_DLL_FN (library, gnutls_hmac_get_len); LOAD_DLL_FN (library, gnutls_hmac); @@ -346,10 +422,19 @@ init_gnutls_functions (void) LOAD_DLL_FN (library, gnutls_hash); LOAD_DLL_FN (library, gnutls_hash_deinit); LOAD_DLL_FN (library, gnutls_hash_output); +# ifdef HAVE_GNUTLS_EXT_GET_NAME + LOAD_DLL_FN (library, gnutls_ext_get_name); +# endif # endif /* HAVE_GNUTLS3 */ - max_log_level = global_gnutls_log_level; + /* gnutls_free is a variable inside GnuTLS, whose value is the + "free" function. So it needs special handling. */ + gnutls_free_func = (gnutls_free_function *) GetProcAddress (library, + "gnutls_free"); + if (!gnutls_free_func) + return false; + max_log_level = clip_to_bounds (INT_MIN, global_gnutls_log_level, INT_MAX); { Lisp_Object name = CAR_SAFE (Fget (Qgnutls, QCloaded_from)); GNUTLS_LOG2 (1, max_log_level, "GnuTLS library loaded:", @@ -391,6 +476,11 @@ init_gnutls_functions (void) # define gnutls_kx_get_name fn_gnutls_kx_get_name # define gnutls_mac_get fn_gnutls_mac_get # define gnutls_mac_get_name fn_gnutls_mac_get_name +# ifdef HAVE_GNUTLS_COMPRESSION_GET +# define gnutls_compression_get fn_gnutls_compression_get +# define gnutls_compression_get_name fn_gnutls_compression_get_name +# endif +# define gnutls_safe_renegotiation_status fn_gnutls_safe_renegotiation_status # define gnutls_pk_algorithm_get_name fn_gnutls_pk_algorithm_get_name # define gnutls_pk_bits_to_sec_param fn_gnutls_pk_bits_to_sec_param # define gnutls_priority_set_direct fn_gnutls_priority_set_direct @@ -410,6 +500,7 @@ init_gnutls_functions (void) # define gnutls_x509_crt_check_hostname fn_gnutls_x509_crt_check_hostname # define gnutls_x509_crt_check_issuer fn_gnutls_x509_crt_check_issuer # define gnutls_x509_crt_deinit fn_gnutls_x509_crt_deinit +# define gnutls_x509_crt_export fn_gnutls_x509_crt_export # define gnutls_x509_crt_get_activation_time fn_gnutls_x509_crt_get_activation_time # define gnutls_x509_crt_get_dn fn_gnutls_x509_crt_get_dn # define gnutls_x509_crt_get_expiration_time fn_gnutls_x509_crt_get_expiration_time @@ -418,6 +509,7 @@ init_gnutls_functions (void) # define gnutls_x509_crt_get_issuer_unique_id fn_gnutls_x509_crt_get_issuer_unique_id # define gnutls_x509_crt_get_key_id fn_gnutls_x509_crt_get_key_id # define gnutls_x509_crt_get_pk_algorithm fn_gnutls_x509_crt_get_pk_algorithm +# define gnutls_x509_crt_print fn_gnutls_x509_crt_print # define gnutls_x509_crt_get_serial fn_gnutls_x509_crt_get_serial # define gnutls_x509_crt_get_signature_algorithm fn_gnutls_x509_crt_get_signature_algorithm # define gnutls_x509_crt_get_subject_unique_id fn_gnutls_x509_crt_get_subject_unique_id @@ -425,27 +517,37 @@ init_gnutls_functions (void) # define gnutls_x509_crt_import fn_gnutls_x509_crt_import # define gnutls_x509_crt_init fn_gnutls_x509_crt_init # ifdef HAVE_GNUTLS3 -# define gnutls_rnd fn_gnutls_rnd # define gnutls_mac_list fn_gnutls_mac_list -# define gnutls_mac_get_nonce_size fn_gnutls_mac_get_nonce_size +# ifdef HAVE_GNUTLS_MAC_GET_NONCE_SIZE +# define gnutls_mac_get_nonce_size fn_gnutls_mac_get_nonce_size +# endif # define gnutls_mac_get_key_size fn_gnutls_mac_get_key_size -# define gnutls_digest_list fn_gnutls_digest_list -# define gnutls_digest_get_name fn_gnutls_digest_get_name +# ifdef HAVE_GNUTLS_DIGEST_LIST +# define gnutls_digest_list fn_gnutls_digest_list +# define gnutls_digest_get_name fn_gnutls_digest_get_name +# endif # define gnutls_cipher_list fn_gnutls_cipher_list -# define gnutls_cipher_get_iv_size fn_gnutls_cipher_get_iv_size +# ifdef HAVE_GNUTLS_CIPHER_GET_IV_SIZE +# define gnutls_cipher_get_iv_size fn_gnutls_cipher_get_iv_size +# endif # define gnutls_cipher_get_key_size fn_gnutls_cipher_get_key_size # define gnutls_cipher_get_block_size fn_gnutls_cipher_get_block_size -# define gnutls_cipher_get_tag_size fn_gnutls_cipher_get_tag_size +# ifdef HAVE_GNUTLS_CIPHER_GET_TAG_SIZE +# define gnutls_cipher_get_tag_size fn_gnutls_cipher_get_tag_size +# endif # define gnutls_cipher_init fn_gnutls_cipher_init # define gnutls_cipher_set_iv fn_gnutls_cipher_set_iv # define gnutls_cipher_encrypt2 fn_gnutls_cipher_encrypt2 # define gnutls_cipher_decrypt2 fn_gnutls_cipher_decrypt2 # define gnutls_cipher_deinit fn_gnutls_cipher_deinit # ifdef HAVE_GNUTLS_AEAD -# define gnutls_aead_cipher_encrypt fn_gnutls_aead_cipher_encrypt -# define gnutls_aead_cipher_decrypt fn_gnutls_aead_cipher_decrypt -# define gnutls_aead_cipher_init fn_gnutls_aead_cipher_init -# define gnutls_aead_cipher_deinit fn_gnutls_aead_cipher_deinit +# define gnutls_aead_cipher_encrypt fn_gnutls_aead_cipher_encrypt +# define gnutls_aead_cipher_decrypt fn_gnutls_aead_cipher_decrypt +# define gnutls_aead_cipher_init fn_gnutls_aead_cipher_init +# define gnutls_aead_cipher_deinit fn_gnutls_aead_cipher_deinit +# endif +# ifdef HAVE_GNUTLS_ETM_STATUS +# define gnutls_session_etm_status fn_gnutls_session_etm_status # endif # define gnutls_hmac_init fn_gnutls_hmac_init # define gnutls_hmac_get_len fn_gnutls_hmac_get_len @@ -457,15 +559,16 @@ init_gnutls_functions (void) # define gnutls_hash fn_gnutls_hash # define gnutls_hash_deinit fn_gnutls_hash_deinit # define gnutls_hash_output fn_gnutls_hash_output +# ifdef HAVE_GNUTLS_EXT_GET_NAME +# define gnutls_ext_get_name fn_gnutls_ext_get_name +# endif # endif /* HAVE_GNUTLS3 */ -/* This wrapper is called from fns.c, which doesn't know about the - LOAD_DLL_FN stuff above. */ -int -w32_gnutls_rnd (gnutls_rnd_level_t level, void *data, size_t len) -{ - return gnutls_rnd (level, data, len); -} +/* gnutls_free_func is a data pointer to a variable which holds an + address of a function. We use #undef because MinGW64 defines + gnutls_free as a macro as well in the GnuTLS headers. */ +# undef gnutls_free +# define gnutls_free (*gnutls_free_func) # endif /* WINDOWSNT */ @@ -513,6 +616,9 @@ gnutls_try_handshake (struct Lisp_Process *proc) gnutls_session_t state = proc->gnutls_state; int ret; bool non_blocking = proc->is_non_blocking_client; + /* Sleep for ten milliseconds when busy-looping in + gnutls_handshake. */ + struct timespec delay = { 0, 1000 * 1000 * 10 }; if (proc->gnutls_complete_negotiation_p) non_blocking = false; @@ -520,15 +626,15 @@ gnutls_try_handshake (struct Lisp_Process *proc) if (non_blocking) proc->gnutls_p = true; - do + while ((ret = gnutls_handshake (state)) < 0) { - ret = gnutls_handshake (state); - emacs_gnutls_handle_error (state, ret); + if (emacs_gnutls_handle_error (state, ret) == 0) /* fatal */ + break; maybe_quit (); + if (non_blocking && ret != GNUTLS_E_INTERRUPTED) + break; + nanosleep (&delay, NULL); } - while (ret < 0 - && gnutls_error_is_fatal (ret) == 0 - && ! non_blocking); proc->gnutls_initstage = GNUTLS_STAGE_HANDSHAKE_TRIED; @@ -623,8 +729,6 @@ emacs_gnutls_transport_set_errno (gnutls_session_t state, int err) ptrdiff_t emacs_gnutls_write (struct Lisp_Process *proc, const char *buf, ptrdiff_t nbyte) { - ssize_t rtnval = 0; - ptrdiff_t bytes_written; gnutls_session_t state = proc->gnutls_state; if (proc->gnutls_initstage != GNUTLS_STAGE_READY) @@ -633,25 +737,19 @@ emacs_gnutls_write (struct Lisp_Process *proc, const char *buf, ptrdiff_t nbyte) return 0; } - bytes_written = 0; + ptrdiff_t bytes_written = 0; while (nbyte > 0) { - rtnval = gnutls_record_send (state, buf, nbyte); + ssize_t rtnval; + do + rtnval = gnutls_record_send (state, buf, nbyte); + while (rtnval == GNUTLS_E_INTERRUPTED); if (rtnval < 0) { - if (rtnval == GNUTLS_E_INTERRUPTED) - continue; - else - { - /* If we get GNUTLS_E_AGAIN, then set errno - appropriately so that send_process retries the - correct way instead of erroring out. */ - if (rtnval == GNUTLS_E_AGAIN) - errno = EAGAIN; - break; - } + emacs_gnutls_handle_error (state, rtnval); + break; } buf += rtnval; @@ -659,14 +757,12 @@ emacs_gnutls_write (struct Lisp_Process *proc, const char *buf, ptrdiff_t nbyte) bytes_written += rtnval; } - emacs_gnutls_handle_error (state, rtnval); return (bytes_written); } ptrdiff_t emacs_gnutls_read (struct Lisp_Process *proc, char *buf, ptrdiff_t nbyte) { - ssize_t rtnval; gnutls_session_t state = proc->gnutls_state; if (proc->gnutls_initstage != GNUTLS_STAGE_READY) @@ -675,19 +771,18 @@ emacs_gnutls_read (struct Lisp_Process *proc, char *buf, ptrdiff_t nbyte) return -1; } - rtnval = gnutls_record_recv (state, buf, nbyte); + ssize_t rtnval; + do + rtnval = gnutls_record_recv (state, buf, nbyte); + while (rtnval == GNUTLS_E_INTERRUPTED); + if (rtnval >= 0) return rtnval; else if (rtnval == GNUTLS_E_UNEXPECTED_PACKET_LENGTH) /* The peer closed the connection. */ return 0; - else if (emacs_gnutls_handle_error (state, rtnval)) - /* non-fatal error */ - return -1; - else { - /* a fatal error occurred */ - return 0; - } + else + return emacs_gnutls_handle_error (state, rtnval); } static char const * @@ -698,25 +793,25 @@ emacs_gnutls_strerror (int err) } /* Report a GnuTLS error to the user. - Return true if the error code was successfully handled. */ -static bool + SESSION is the GnuTLS session, ERR is the (negative) GnuTLS error code. + Return 0 if the error was fatal, -1 (setting errno) otherwise so + that the caller can notice the error and attempt a repair. */ +static int emacs_gnutls_handle_error (gnutls_session_t session, int err) { - int max_log_level = 0; - - bool ret; + int ret; /* TODO: use a Lisp_Object generated by gnutls_make_error? */ - if (err >= 0) - return 1; check_memory_full (err); - max_log_level = global_gnutls_log_level; + int max_log_level + = clip_to_bounds (INT_MIN, global_gnutls_log_level, INT_MAX); /* TODO: use gnutls-error-fatalp and gnutls-error-string. */ char const *str = emacs_gnutls_strerror (err); + int errnum = EINVAL; if (gnutls_error_is_fatal (err)) { @@ -730,11 +825,11 @@ emacs_gnutls_handle_error (gnutls_session_t session, int err) # endif GNUTLS_LOG2 (level, max_log_level, "fatal error:", str); - ret = false; + ret = 0; } else { - ret = true; + ret = -1; switch (err) { @@ -750,6 +845,26 @@ emacs_gnutls_handle_error (gnutls_session_t session, int err) "non-fatal error:", str); } + + switch (err) + { + case GNUTLS_E_AGAIN: + errnum = EAGAIN; + break; + +# ifdef EMSGSIZE + case GNUTLS_E_LARGE_PACKET: + case GNUTLS_E_PUSH_ERROR: + errnum = EMSGSIZE; + break; +# endif + +# if defined HAVE_GNUTLS3 && defined ECONNRESET + case GNUTLS_E_PREMATURE_TERMINATION: + errnum = ECONNRESET; + break; +# endif + } } if (err == GNUTLS_E_WARNING_ALERT_RECEIVED @@ -763,11 +878,13 @@ emacs_gnutls_handle_error (gnutls_session_t session, int err) GNUTLS_LOG2 (level, max_log_level, "Received alert: ", str); } + + errno = errnum; return ret; } /* convert an integer error to a Lisp_Object; it will be either a - known symbol like `gnutls_e_interrupted' and `gnutls_e_again' or + known symbol like 'gnutls_e_interrupted' and 'gnutls_e_again' or simply the integer value of the error. GNUTLS_E_SUCCESS is mapped to Qt. */ static Lisp_Object @@ -786,7 +903,20 @@ gnutls_make_error (int err) } check_memory_full (err); - return make_number (err); + return make_fixnum (err); +} + +static void +gnutls_deinit_certificates (struct Lisp_Process *p) +{ + if (! p->gnutls_certificates) + return; + + for (int i = 0; i < p->gnutls_certificates_length; i++) + gnutls_x509_crt_deinit (p->gnutls_certificates[i]); + + xfree (p->gnutls_certificates); + p->gnutls_certificates = NULL; } Lisp_Object @@ -823,6 +953,9 @@ emacs_gnutls_deinit (Lisp_Object proc) GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_INIT - 1; } + if (XPROCESS (proc)->gnutls_certificates) + gnutls_deinit_certificates (XPROCESS (proc)); + XPROCESS (proc)->gnutls_p = false; return Qt; } @@ -847,7 +980,7 @@ See also `gnutls-boot'. */) { CHECK_PROCESS (proc); - return make_number (GNUTLS_INITSTAGE (proc)); + return make_fixnum (GNUTLS_INITSTAGE (proc)); } DEFUN ("gnutls-errorp", Fgnutls_errorp, Sgnutls_errorp, 1, 1, 0, @@ -887,10 +1020,10 @@ Usage: (gnutls-error-fatalp ERROR) */) } } - if (! TYPE_RANGED_INTEGERP (int, err)) + if (! TYPE_RANGED_FIXNUMP (int, err)) error ("Not an error symbol or code"); - if (0 == gnutls_error_is_fatal (XINT (err))) + if (0 == gnutls_error_is_fatal (XFIXNUM (err))) return Qnil; return Qt; @@ -919,10 +1052,10 @@ usage: (gnutls-error-string ERROR) */) } } - if (! TYPE_RANGED_INTEGERP (int, err)) + if (! TYPE_RANGED_FIXNUMP (int, err)) return build_string ("Not an error symbol or code"); - return build_string (emacs_gnutls_strerror (XINT (err))); + return build_string (emacs_gnutls_strerror (XFIXNUM (err))); } DEFUN ("gnutls-deinit", Fgnutls_deinit, Sgnutls_deinit, 1, 1, 0, @@ -954,7 +1087,35 @@ gnutls_hex_string (unsigned char *buf, ptrdiff_t buf_size, const char *prefix) } static Lisp_Object -gnutls_certificate_details (gnutls_x509_crt_t cert) +emacs_gnutls_certificate_export_pem (gnutls_x509_crt_t cert) +{ + size_t size = 0; + int err = gnutls_x509_crt_export (cert, GNUTLS_X509_FMT_PEM, NULL, &size); + check_memory_full (err); + + if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) + { + USE_SAFE_ALLOCA; + char *buf = SAFE_ALLOCA (size); + err = gnutls_x509_crt_export (cert, GNUTLS_X509_FMT_PEM, buf, &size); + check_memory_full (err); + + if (err < GNUTLS_E_SUCCESS) + error ("GnuTLS certificate export error: %s", + emacs_gnutls_strerror (err)); + + Lisp_Object result = build_string (buf); + SAFE_FREE (); + return result; + } + else if (err < GNUTLS_E_SUCCESS) + error ("GnuTLS certificate export error: %s", emacs_gnutls_strerror (err)); + + return Qnil; +} + +static Lisp_Object +emacs_gnutls_certificate_details (gnutls_x509_crt_t cert) { Lisp_Object res = Qnil; int err; @@ -966,7 +1127,7 @@ gnutls_certificate_details (gnutls_x509_crt_t cert) check_memory_full (version); if (version >= GNUTLS_E_SUCCESS) res = nconc2 (res, list2 (intern (":version"), - make_number (version))); + make_fixnum (version))); } /* Serial. */ @@ -1122,6 +1283,10 @@ gnutls_certificate_details (gnutls_x509_crt_t cert) xfree (buf); } + /* PEM */ + res = nconc2 (res, list2 (intern (":pem"), + emacs_gnutls_certificate_export_pem(cert))); + return res; } @@ -1159,14 +1324,45 @@ DEFUN ("gnutls-peer-status-warning-describe", Fgnutls_peer_status_warning_descri if (EQ (status_symbol, intern (":no-host-match"))) return build_string ("certificate host does not match hostname"); + if (EQ (status_symbol, intern (":signature-failure"))) + return build_string ("certificate signature could not be verified"); + + if (EQ (status_symbol, intern (":revocation-data-superseded"))) + return build_string ("certificate revocation data are old and have been " + "superseded"); + + if (EQ (status_symbol, intern (":revocation-data-issued-in-future"))) + return build_string ("certificate revocation data have a future issue date"); + + if (EQ (status_symbol, intern (":signer-constraints-failure"))) + return build_string ("certificate signer constraints were violated"); + + if (EQ (status_symbol, intern (":purpose-mismatch"))) + return build_string ("certificate does not match the intended purpose"); + + if (EQ (status_symbol, intern (":missing-ocsp-status"))) + return build_string ("certificate requires the server to send a OCSP " + "certificate status, but no status was received"); + + if (EQ (status_symbol, intern (":invalid-ocsp-status"))) + return build_string ("the received OCSP certificate status is invalid"); + return Qnil; } DEFUN ("gnutls-peer-status", Fgnutls_peer_status, Sgnutls_peer_status, 1, 1, 0, doc: /* Describe a GnuTLS PROC peer certificate and any warnings about it. + The return value is a property list with top-level keys :warnings and -:certificate. The :warnings entry is a list of symbols you can describe with -`gnutls-peer-status-warning-describe'. */) +:certificates. + +The :warnings entry is a list of symbols you can get a description of +with `gnutls-peer-status-warning-describe', and :certificates is the +certificate chain for the connection, with the host certificate +first, and intermediary certificates (if any) following it. + +In addition, for backwards compatibility, the host certificate is also +returned as the :certificate entry. */) (Lisp_Object proc) { Lisp_Object warnings = Qnil, result = Qnil; @@ -1202,15 +1398,44 @@ The return value is a property list with top-level keys :warnings and if (verification & GNUTLS_CERT_EXPIRED) warnings = Fcons (intern (":expired"), warnings); +# if GNUTLS_VERSION_NUMBER >= 0x030100 + if (verification & GNUTLS_CERT_SIGNATURE_FAILURE) + warnings = Fcons (intern (":signature-failure"), warnings); + +# if GNUTLS_VERSION_NUMBER >= 0x030114 + if (verification & GNUTLS_CERT_REVOCATION_DATA_SUPERSEDED) + warnings = Fcons (intern (":revocation-data-superseded"), warnings); + + if (verification & GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE) + warnings = Fcons (intern (":revocation-data-issued-in-future"), warnings); + + if (verification & GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE) + warnings = Fcons (intern (":signer-constraints-failure"), warnings); + +# if GNUTLS_VERSION_NUMBER >= 0x030400 + if (verification & GNUTLS_CERT_PURPOSE_MISMATCH) + warnings = Fcons (intern (":purpose-mismatch"), warnings); + +# if GNUTLS_VERSION_NUMBER >= 0x030501 + if (verification & GNUTLS_CERT_MISSING_OCSP_STATUS) + warnings = Fcons (intern (":missing-ocsp-status"), warnings); + + if (verification & GNUTLS_CERT_INVALID_OCSP_STATUS) + warnings = Fcons (intern (":invalid-ocsp-status"), warnings); +# endif +# endif +# endif +# endif + if (XPROCESS (proc)->gnutls_extra_peer_verification & CERTIFICATE_NOT_MATCHING) warnings = Fcons (intern (":no-host-match"), warnings); /* This could get called in the INIT stage, when the certificate is not yet set. */ - if (XPROCESS (proc)->gnutls_certificate != NULL && - gnutls_x509_crt_check_issuer(XPROCESS (proc)->gnutls_certificate, - XPROCESS (proc)->gnutls_certificate)) + if (XPROCESS (proc)->gnutls_certificates != NULL && + gnutls_x509_crt_check_issuer(XPROCESS (proc)->gnutls_certificates[0], + XPROCESS (proc)->gnutls_certificates[0])) warnings = Fcons (intern (":self-signed"), warnings); if (!NILP (warnings)) @@ -1218,10 +1443,21 @@ The return value is a property list with top-level keys :warnings and /* This could get called in the INIT stage, when the certificate is not yet set. */ - if (XPROCESS (proc)->gnutls_certificate != NULL) - result = nconc2 (result, list2 - (intern (":certificate"), - gnutls_certificate_details (XPROCESS (proc)->gnutls_certificate))); + if (XPROCESS (proc)->gnutls_certificates != NULL) + { + Lisp_Object certs = Qnil; + + /* Return all the certificates in a list. */ + for (int i = 0; i < XPROCESS (proc)->gnutls_certificates_length; i++) + certs = nconc2 (certs, list1 (emacs_gnutls_certificate_details + (XPROCESS (proc)->gnutls_certificates[i]))); + + result = nconc2 (result, list2 (intern (":certificates"), certs)); + + /* Return the host certificate in its own element for + compatibility reasons. */ + result = nconc2 (result, list2 (intern (":certificate"), Fcar (certs))); + } state = XPROCESS (proc)->gnutls_state; @@ -1231,7 +1467,7 @@ The return value is a property list with top-level keys :warnings and check_memory_full (bits); if (bits > 0) result = nconc2 (result, list2 (intern (":diffie-hellman-prime-bits"), - make_number (bits))); + make_fixnum (bits))); } /* Key exchange. */ @@ -1241,10 +1477,10 @@ The return value is a property list with top-level keys :warnings and (gnutls_kx_get (state))))); /* Protocol name. */ + gnutls_protocol_t proto = gnutls_protocol_get_version (state); result = nconc2 (result, list2 (intern (":protocol"), - build_string (gnutls_protocol_get_name - (gnutls_protocol_get_version (state))))); + build_string (gnutls_protocol_get_name (proto)))); /* Cipher name. */ result = nconc2 @@ -1258,14 +1494,34 @@ The return value is a property list with top-level keys :warnings and build_string (gnutls_mac_get_name (gnutls_mac_get (state))))); + /* Compression name. */ +# ifdef HAVE_GNUTLS_COMPRESSION_GET + result = nconc2 + (result, list2 (intern (":compression"), + build_string (gnutls_compression_get_name + (gnutls_compression_get (state))))); +# endif + + /* Encrypt-then-MAC. */ +# ifdef HAVE_GNUTLS_ETM_STATUS + result = nconc2 + (result, list2 (intern (":encrypt-then-mac"), + gnutls_session_etm_status (state) ? Qt : Qnil)); +# endif + + /* Renegotiation Indication */ + if (proto <= GNUTLS_TLS1_2) + result = nconc2 + (result, list2 (intern (":safe-renegotiation"), + gnutls_safe_renegotiation_status (state) ? Qt : Qnil)); return result; } /* Initialize global GnuTLS state to defaults. - Call `gnutls-global-deinit' when GnuTLS usage is no longer needed. + Call 'gnutls-global-deinit' when GnuTLS usage is no longer needed. Return zero on success. */ -Lisp_Object +static Lisp_Object emacs_gnutls_global_init (void) { int ret = GNUTLS_E_SUCCESS; @@ -1294,7 +1550,7 @@ gnutls_ip_address_p (char *string) # if 0 /* Deinitialize global GnuTLS state. - See also `gnutls-global-init'. */ + See also 'gnutls-global-init'. */ static Lisp_Object emacs_gnutls_global_deinit (void) { @@ -1319,6 +1575,52 @@ boot_error (struct Lisp_Process *p, const char *m, ...) va_end (ap); } +DEFUN ("gnutls-format-certificate", Fgnutls_format_certificate, + Sgnutls_format_certificate, 1, 1, 0, + doc: /* Format a X.509 certificate to a string. + +Given a PEM-encoded X.509 certificate CERT, returns a human-readable +string representation. */) + (Lisp_Object cert) +{ + CHECK_STRING (cert); + + int err; + gnutls_x509_crt_t crt; + + err = gnutls_x509_crt_init (&crt); + check_memory_full (err); + if (err < GNUTLS_E_SUCCESS) + error ("gnutls-format-certificate error: %s", emacs_gnutls_strerror (err)); + + gnutls_datum_t crt_data = { SDATA (cert), strlen (SSDATA (cert)) }; + err = gnutls_x509_crt_import (crt, &crt_data, GNUTLS_X509_FMT_PEM); + check_memory_full (err); + if (err < GNUTLS_E_SUCCESS) + { + gnutls_x509_crt_deinit (crt); + error ("gnutls-format-certificate error: %s", + emacs_gnutls_strerror (err)); + } + + gnutls_datum_t out; + err = gnutls_x509_crt_print (crt, GNUTLS_CRT_PRINT_FULL, &out); + check_memory_full (err); + if (err < GNUTLS_E_SUCCESS) + { + gnutls_x509_crt_deinit (crt); + error ("gnutls-format-certificate error: %s", + emacs_gnutls_strerror (err)); + } + + Lisp_Object result = make_string_from_bytes ((char *) out.data, out.size, + out.size); + gnutls_free (out.data); + gnutls_x509_crt_deinit (crt); + + return result; +} + Lisp_Object gnutls_verify_boot (Lisp_Object proc, Lisp_Object proplist) { @@ -1333,10 +1635,10 @@ gnutls_verify_boot (Lisp_Object proc, Lisp_Object proplist) char *c_hostname; if (NILP (proplist)) - proplist = Fcdr (Fplist_get (p->childp, QCtls_parameters)); + proplist = Fcdr (plist_get (p->childp, QCtls_parameters)); - verify_error = Fplist_get (proplist, QCverify_error); - hostname = Fplist_get (proplist, QChostname); + verify_error = plist_get (proplist, QCverify_error); + hostname = plist_get (proplist, QChostname); if (EQ (verify_error, Qt)) verify_error_all = true; @@ -1364,9 +1666,9 @@ gnutls_verify_boot (Lisp_Object proc, Lisp_Object proplist) if (ret < GNUTLS_E_SUCCESS) return gnutls_make_error (ret); - XPROCESS (proc)->gnutls_peer_verification = peer_verification; + p->gnutls_peer_verification = peer_verification; - warnings = Fplist_get (Fgnutls_peer_status (proc), intern (":warnings")); + warnings = plist_get (Fgnutls_peer_status (proc), intern (":warnings")); if (!NILP (warnings)) { for (Lisp_Object tail = warnings; CONSP (tail); tail = XCDR (tail)) @@ -1401,49 +1703,60 @@ gnutls_verify_boot (Lisp_Object proc, Lisp_Object proplist) can be easily extended to work with openpgp keys as well. */ if (gnutls_certificate_type_get (state) == GNUTLS_CRT_X509) { - gnutls_x509_crt_t gnutls_verify_cert; - const gnutls_datum_t *gnutls_verify_cert_list; - unsigned int gnutls_verify_cert_list_size; + const gnutls_datum_t *cert_list; + unsigned int cert_list_length; + int failed_import = 0; - ret = gnutls_x509_crt_init (&gnutls_verify_cert); - if (ret < GNUTLS_E_SUCCESS) - return gnutls_make_error (ret); - - gnutls_verify_cert_list - = gnutls_certificate_get_peers (state, &gnutls_verify_cert_list_size); + cert_list = gnutls_certificate_get_peers (state, &cert_list_length); - if (gnutls_verify_cert_list == NULL) + if (cert_list == NULL) { - gnutls_x509_crt_deinit (gnutls_verify_cert); emacs_gnutls_deinit (proc); boot_error (p, "No x509 certificate was found\n"); return Qnil; } - /* Check only the first certificate in the given chain. */ - ret = gnutls_x509_crt_import (gnutls_verify_cert, - &gnutls_verify_cert_list[0], - GNUTLS_X509_FMT_DER); + /* Check only the first certificate in the given chain, but + store them all. */ + p->gnutls_certificates = + xmalloc (cert_list_length * sizeof (gnutls_x509_crt_t)); + p->gnutls_certificates_length = cert_list_length; - if (ret < GNUTLS_E_SUCCESS) + for (int i = cert_list_length - 1; i >= 0; i--) { - gnutls_x509_crt_deinit (gnutls_verify_cert); - return gnutls_make_error (ret); + gnutls_x509_crt_t cert; + + gnutls_x509_crt_init (&cert); + + if (ret < GNUTLS_E_SUCCESS) + failed_import = ret; + else + { + ret = gnutls_x509_crt_import (cert, &cert_list[i], + GNUTLS_X509_FMT_DER); + + if (ret < GNUTLS_E_SUCCESS) + failed_import = ret; + } + + p->gnutls_certificates[i] = cert; } - XPROCESS (proc)->gnutls_certificate = gnutls_verify_cert; + if (failed_import != 0) + { + gnutls_deinit_certificates (p); + return gnutls_make_error (failed_import); + } - int err = gnutls_x509_crt_check_hostname (gnutls_verify_cert, + int err = gnutls_x509_crt_check_hostname (p->gnutls_certificates[0], c_hostname); check_memory_full (err); if (!err) { - XPROCESS (proc)->gnutls_extra_peer_verification - |= CERTIFICATE_NOT_MATCHING; + p->gnutls_extra_peer_verification |= CERTIFICATE_NOT_MATCHING; if (verify_error_all || !NILP (Fmember (QChostname, verify_error))) { - gnutls_x509_crt_deinit (gnutls_verify_cert); emacs_gnutls_deinit (proc); boot_error (p, "The x509 certificate does not match \"%s\"", c_hostname); @@ -1456,7 +1769,7 @@ gnutls_verify_boot (Lisp_Object proc, Lisp_Object proplist) } /* Set this flag only if the whole initialization succeeded. */ - XPROCESS (proc)->gnutls_p = true; + p->gnutls_p = true; return gnutls_make_error (ret); } @@ -1557,13 +1870,13 @@ one trustfile (usually a CA bundle). */) return Qnil; } - hostname = Fplist_get (proplist, QChostname); - priority_string = Fplist_get (proplist, QCpriority); - trustfiles = Fplist_get (proplist, QCtrustfiles); - keylist = Fplist_get (proplist, QCkeylist); - crlfiles = Fplist_get (proplist, QCcrlfiles); - loglevel = Fplist_get (proplist, QCloglevel); - prime_bits = Fplist_get (proplist, QCmin_prime_bits); + hostname = plist_get (proplist, QChostname); + priority_string = plist_get (proplist, QCpriority); + trustfiles = plist_get (proplist, QCtrustfiles); + keylist = plist_get (proplist, QCkeylist); + crlfiles = plist_get (proplist, QCcrlfiles); + loglevel = plist_get (proplist, QCloglevel); + prime_bits = plist_get (proplist, QCmin_prime_bits); if (!STRINGP (hostname)) { @@ -1574,14 +1887,17 @@ one trustfile (usually a CA bundle). */) state = XPROCESS (proc)->gnutls_state; - if (TYPE_RANGED_INTEGERP (int, loglevel)) + if (INTEGERP (loglevel)) { gnutls_global_set_log_function (gnutls_log_function); # ifdef HAVE_GNUTLS3 gnutls_global_set_audit_log_function (gnutls_audit_log_function); # endif - gnutls_global_set_log_level (XINT (loglevel)); - max_log_level = XINT (loglevel); + int level = (FIXNUMP (loglevel) + ? clip_to_bounds (INT_MIN, XFIXNUM (loglevel), INT_MAX) + : NILP (Fnatnump (loglevel)) ? INT_MIN : INT_MAX); + gnutls_global_set_log_level (level); + max_log_level = level; XPROCESS (proc)->gnutls_log_level = max_log_level; } @@ -1613,10 +1929,10 @@ one trustfile (usually a CA bundle). */) check_memory_full (gnutls_certificate_allocate_credentials (&x509_cred)); XPROCESS (proc)->gnutls_x509_cred = x509_cred; - verify_flags = Fplist_get (proplist, QCverify_flags); - if (TYPE_RANGED_INTEGERP (unsigned int, verify_flags)) + verify_flags = plist_get (proplist, QCverify_flags); + if (TYPE_RANGED_FIXNUMP (unsigned int, verify_flags)) { - gnutls_verify_flags = XFASTINT (verify_flags); + gnutls_verify_flags = XFIXNAT (verify_flags); GNUTLS_LOG (2, max_log_level, "setting verification flags"); } else if (NILP (verify_flags)) @@ -1775,8 +2091,8 @@ one trustfile (usually a CA bundle). */) GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_PRIORITY; - if (INTEGERP (prime_bits)) - gnutls_dh_set_prime_bits (state, XUINT (prime_bits)); + if (FIXNUMP (prime_bits)) + gnutls_dh_set_prime_bits (state, XUFIXNUM (prime_bits)); ret = EQ (type, Qgnutls_x509pki) ? gnutls_credentials_set (state, GNUTLS_CRD_CERTIFICATE, x509_cred) @@ -1793,7 +2109,7 @@ one trustfile (usually a CA bundle). */) } XPROCESS (proc)->gnutls_complete_negotiation_p = - !NILP (Fplist_get (proplist, QCcomplete_negotiation)); + !NILP (plist_get (proplist, QCcomplete_negotiation)); GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_CRED_SET; ret = emacs_gnutls_handshake (XPROCESS (proc)); if (ret < GNUTLS_E_SUCCESS) @@ -1825,7 +2141,8 @@ This function may also return `gnutls-e-again', or state = XPROCESS (proc)->gnutls_state; - gnutls_x509_crt_deinit (XPROCESS (proc)->gnutls_certificate); + if (XPROCESS (proc)->gnutls_certificates) + gnutls_deinit_certificates (XPROCESS (proc)); ret = gnutls_bye (state, NILP (cont) ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR); @@ -1836,6 +2153,24 @@ This function may also return `gnutls-e-again', or #ifdef HAVE_GNUTLS3 +# ifndef HAVE_GNUTLS_CIPHER_GET_IV_SIZE + /* Block size is equivalent. */ +# define gnutls_cipher_get_iv_size(cipher) gnutls_cipher_get_block_size (cipher) +# endif + +# ifndef HAVE_GNUTLS_CIPHER_GET_TAG_SIZE + /* Tag size is irrelevant. */ +# define gnutls_cipher_get_tag_size(cipher) 0 +# endif + +# ifndef HAVE_GNUTLS_DIGEST_LIST + /* The mac algorithms are equivalent. */ +# define gnutls_digest_list() \ + ((gnutls_digest_algorithm_t const *) gnutls_mac_list ()) +# define gnutls_digest_get_name(id) \ + gnutls_mac_get_name ((gnutls_mac_algorithm_t) (id)) +# endif + DEFUN ("gnutls-ciphers", Fgnutls_ciphers, Sgnutls_ciphers, 0, 0, 0, doc: /* Return alist of GnuTLS symmetric cipher descriptions as plists. The alist key is the cipher name. */) @@ -1859,20 +2194,20 @@ The alist key is the cipher name. */) ptrdiff_t cipher_tag_size = gnutls_cipher_get_tag_size (gca); Lisp_Object cp - = listn (CONSTYPE_HEAP, 15, cipher_symbol, - QCcipher_id, make_number (gca), + = list (cipher_symbol, + QCcipher_id, make_fixnum (gca), QCtype, Qgnutls_type_cipher, QCcipher_aead_capable, cipher_tag_size == 0 ? Qnil : Qt, - QCcipher_tagsize, make_number (cipher_tag_size), + QCcipher_tagsize, make_fixnum (cipher_tag_size), QCcipher_blocksize, - make_number (gnutls_cipher_get_block_size (gca)), + make_fixnum (gnutls_cipher_get_block_size (gca)), QCcipher_keysize, - make_number (gnutls_cipher_get_key_size (gca)), + make_fixnum (gnutls_cipher_get_key_size (gca)), QCcipher_ivsize, - make_number (gnutls_cipher_get_iv_size (gca))); + make_fixnum (gnutls_cipher_get_iv_size (gca))); ciphers = Fcons (cp, ciphers); } @@ -1959,11 +2294,13 @@ gnutls_symmetric_aead (bool encrypting, gnutls_cipher_algorithm_t gca, SAFE_FREE (); return list2 (output, actual_iv); # else - printmax_t print_gca = gca; - error ("GnuTLS AEAD cipher %"pMd" is invalid or not found", print_gca); + intmax_t print_gca = gca; + error ("GnuTLS AEAD cipher %"PRIdMAX" is invalid or not found", print_gca); # endif } +static Lisp_Object cipher_cache; + static Lisp_Object gnutls_symmetric (bool encrypting, Lisp_Object cipher, Lisp_Object key, Lisp_Object iv, @@ -1994,22 +2331,32 @@ gnutls_symmetric (bool encrypting, Lisp_Object cipher, cipher = intern (SSDATA (cipher)); if (SYMBOLP (cipher)) - info = XCDR (Fassq (cipher, Fgnutls_ciphers ())); - else if (TYPE_RANGED_INTEGERP (gnutls_cipher_algorithm_t, cipher)) - gca = XINT (cipher); + { + if (NILP (cipher_cache)) + cipher_cache = Fgnutls_ciphers (); + info = Fassq (cipher, cipher_cache); + if (!CONSP (info)) + xsignal2 (Qerror, + build_string ("GnuTLS cipher is invalid or not found"), + cipher); + info = XCDR (info); + } + else if (TYPE_RANGED_FIXNUMP (gnutls_cipher_algorithm_t, cipher)) + gca = XFIXNUM (cipher); else info = cipher; if (!NILP (info) && CONSP (info)) { - Lisp_Object v = Fplist_get (info, QCcipher_id); - if (TYPE_RANGED_INTEGERP (gnutls_cipher_algorithm_t, v)) - gca = XINT (v); + Lisp_Object v = plist_get (info, QCcipher_id); + if (TYPE_RANGED_FIXNUMP (gnutls_cipher_algorithm_t, v)) + gca = XFIXNUM (v); } ptrdiff_t key_size = gnutls_cipher_get_key_size (gca); if (key_size == 0) - error ("GnuTLS cipher is invalid or not found"); + xsignal2 (Qerror, + build_string ("GnuTLS cipher is invalid or not found"), cipher); ptrdiff_t kstart_byte, kend_byte; const char *kdata = extract_data_from_object (key, &kstart_byte, &kend_byte); @@ -2117,11 +2464,10 @@ The KEY can be specified as a buffer or string or in other ways (see Info node `(elisp)Format of GnuTLS Cryptography Inputs'). The KEY will be wiped after use if it's a string. -The IV and INPUT and the optional AEAD_AUTH can be specified as a -buffer or string or in other ways (see Info node `(elisp)Format of -GnuTLS Cryptography Inputs'). +The IV and INPUT and the optional AEAD_AUTH can also be specified as a +buffer or string or in other ways. -The alist of symmetric ciphers can be obtained with `gnutls-ciphers`. +The alist of symmetric ciphers can be obtained with `gnutls-ciphers'. The CIPHER may be a string or symbol matching a key in that alist, or a plist with the :cipher-id numeric property, or the number itself. @@ -2144,11 +2490,10 @@ The KEY can be specified as a buffer or string or in other ways (see Info node `(elisp)Format of GnuTLS Cryptography Inputs'). The KEY will be wiped after use if it's a string. -The IV and INPUT and the optional AEAD_AUTH can be specified as a -buffer or string or in other ways (see Info node `(elisp)Format of -GnuTLS Cryptography Inputs'). +The IV and INPUT and the optional AEAD_AUTH can also be specified as a +buffer or string or in other ways. -The alist of symmetric ciphers can be obtained with `gnutls-ciphers`. +The alist of symmetric ciphers can be obtained with `gnutls-ciphers'. The CIPHER may be a string or symbol matching a key in that alist, or a plist with the `:cipher-id' numeric property, or the number itself. @@ -2178,18 +2523,22 @@ name. */) /* A symbol representing the GnuTLS MAC algorithm. */ Lisp_Object gma_symbol = intern (gnutls_mac_get_name (gma)); - Lisp_Object mp = listn (CONSTYPE_HEAP, 11, gma_symbol, - QCmac_algorithm_id, make_number (gma), + size_t nonce_size = 0; +# ifdef HAVE_GNUTLS_MAC_GET_NONCE_SIZE + nonce_size = gnutls_mac_get_nonce_size (gma); +# endif + Lisp_Object mp = list (gma_symbol, + QCmac_algorithm_id, make_fixnum (gma), QCtype, Qgnutls_type_mac_algorithm, QCmac_algorithm_length, - make_number (gnutls_hmac_get_len (gma)), + make_fixnum (gnutls_hmac_get_len (gma)), QCmac_algorithm_keysize, - make_number (gnutls_mac_get_key_size (gma)), + make_fixnum (gnutls_mac_get_key_size (gma)), QCmac_algorithm_noncesize, - make_number (gnutls_mac_get_nonce_size (gma))); + make_fixnum (nonce_size)); mac_algorithms = Fcons (mp, mac_algorithms); } @@ -2213,12 +2562,12 @@ method name. */) /* A symbol representing the GnuTLS digest algorithm. */ Lisp_Object gda_symbol = intern (gnutls_digest_get_name (gda)); - Lisp_Object mp = listn (CONSTYPE_HEAP, 7, gda_symbol, - QCdigest_algorithm_id, make_number (gda), + Lisp_Object mp = list (gda_symbol, + QCdigest_algorithm_id, make_fixnum (gda), QCtype, Qgnutls_type_digest_algorithm, QCdigest_algorithm_length, - make_number (gnutls_hash_get_len (gda))); + make_fixnum (gnutls_hash_get_len (gda))); digest_algorithms = Fcons (mp, digest_algorithms); } @@ -2235,10 +2584,10 @@ The KEY can be specified as a buffer or string or in other ways (see Info node `(elisp)Format of GnuTLS Cryptography Inputs'). The KEY will be wiped after use if it's a string. -The INPUT can be specified as a buffer or string or in other -ways (see Info node `(elisp)Format of GnuTLS Cryptography Inputs'). +The INPUT can also be specified as a buffer or string or in other +ways. -The alist of MAC algorithms can be obtained with `gnutls-macs`. The +The alist of MAC algorithms can be obtained with `gnutls-macs'. The HASH-METHOD may be a string or symbol matching a key in that alist, or a plist with the `:mac-algorithm-id' numeric property, or the number itself. */) @@ -2261,22 +2610,31 @@ itself. */) hash_method = intern (SSDATA (hash_method)); if (SYMBOLP (hash_method)) - info = XCDR (Fassq (hash_method, Fgnutls_macs ())); - else if (TYPE_RANGED_INTEGERP (gnutls_mac_algorithm_t, hash_method)) - gma = XINT (hash_method); + { + info = Fassq (hash_method, Fgnutls_macs ()); + if (!CONSP (info)) + xsignal2 (Qerror, + build_string ("GnuTLS MAC-method is invalid or not found"), + hash_method); + info = XCDR (info); + } + else if (TYPE_RANGED_FIXNUMP (gnutls_mac_algorithm_t, hash_method)) + gma = XFIXNUM (hash_method); else info = hash_method; if (!NILP (info) && CONSP (info)) { - Lisp_Object v = Fplist_get (info, QCmac_algorithm_id); - if (TYPE_RANGED_INTEGERP (gnutls_mac_algorithm_t, v)) - gma = XINT (v); + Lisp_Object v = plist_get (info, QCmac_algorithm_id); + if (TYPE_RANGED_FIXNUMP (gnutls_mac_algorithm_t, v)) + gma = XFIXNUM (v); } ptrdiff_t digest_length = gnutls_hmac_get_len (gma); if (digest_length == 0) - error ("GnuTLS MAC-method is invalid or not found"); + xsignal2 (Qerror, + build_string ("GnuTLS MAC-method is invalid or not found"), + hash_method); ptrdiff_t kstart_byte, kend_byte; const char *kdata = extract_data_from_object (key, &kstart_byte, &kend_byte); @@ -2324,7 +2682,7 @@ Return nil on error. The INPUT can be specified as a buffer or string or in other ways (see Info node `(elisp)Format of GnuTLS Cryptography Inputs'). -The alist of digest algorithms can be obtained with `gnutls-digests`. +The alist of digest algorithms can be obtained with `gnutls-digests'. The DIGEST-METHOD may be a string or symbol matching a key in that alist, or a plist with the `:digest-algorithm-id' numeric property, or the number itself. */) @@ -2342,22 +2700,31 @@ the number itself. */) digest_method = intern (SSDATA (digest_method)); if (SYMBOLP (digest_method)) - info = XCDR (Fassq (digest_method, Fgnutls_digests ())); - else if (TYPE_RANGED_INTEGERP (gnutls_digest_algorithm_t, digest_method)) - gda = XINT (digest_method); + { + info = Fassq (digest_method, Fgnutls_digests ()); + if (!CONSP (info)) + xsignal2 (Qerror, + build_string ("GnuTLS digest-method is invalid or not found"), + digest_method); + info = XCDR (info); + } + else if (TYPE_RANGED_FIXNUMP (gnutls_digest_algorithm_t, digest_method)) + gda = XFIXNUM (digest_method); else info = digest_method; if (!NILP (info) && CONSP (info)) { - Lisp_Object v = Fplist_get (info, QCdigest_algorithm_id); - if (TYPE_RANGED_INTEGERP (gnutls_digest_algorithm_t, v)) - gda = XINT (v); + Lisp_Object v = plist_get (info, QCdigest_algorithm_id); + if (TYPE_RANGED_FIXNUMP (gnutls_digest_algorithm_t, v)) + gda = XFIXNUM (v); } ptrdiff_t digest_length = gnutls_hash_get_len (gda); if (digest_length == 0) - error ("GnuTLS digest-method is invalid or not found"); + xsignal2 (Qerror, + build_string ("GnuTLS digest-method is invalid or not found"), + digest_method); gnutls_hash_hd_t hash; int ret = gnutls_hash_init (&hash, gda); @@ -2399,13 +2766,30 @@ GnuTLS 3 or higher : the list will contain `gnutls3'. GnuTLS MACs : the list will contain `macs'. GnuTLS digests : the list will contain `digests'. GnuTLS symmetric ciphers: the list will contain `ciphers'. -GnuTLS AEAD ciphers : the list will contain `AEAD-ciphers'. */) +GnuTLS AEAD ciphers : the list will contain `AEAD-ciphers'. +%DUMBFW : the list will contain `ClientHello\\ Padding'. +Any GnuTLS extension with ID up to 100 + : the list will contain its name. */) (void) { Lisp_Object capabilities = Qnil; #ifdef HAVE_GNUTLS +# ifdef WINDOWSNT + Lisp_Object found = Fassq (Qgnutls, Vlibrary_cache); + if (CONSP (found)) + return XCDR (found); + + /* Load the GnuTLS DLL and find exported functions. The external + library cache is updated after the capabilities have been + determined. */ + if (!init_gnutls_functions ()) + return Qnil; +# endif /* WINDOWSNT */ + + capabilities = Fcons (intern("gnutls"), capabilities); + # ifdef HAVE_GNUTLS3 capabilities = Fcons (intern("gnutls3"), capabilities); capabilities = Fcons (intern("digests"), capabilities); @@ -2416,19 +2800,25 @@ GnuTLS AEAD ciphers : the list will contain `AEAD-ciphers'. */) # endif capabilities = Fcons (intern("macs"), capabilities); -# endif /* HAVE_GNUTLS3 */ -# ifdef WINDOWSNT - Lisp_Object found = Fassq (Qgnutls, Vlibrary_cache); - if (CONSP (found)) - return XCDR (found); - else +# ifdef HAVE_GNUTLS_EXT_GET_NAME + for (unsigned int ext=0; ext < 100; ext++) { - Lisp_Object status; - status = init_gnutls_functions () ? capabilities : Qnil; - Vlibrary_cache = Fcons (Fcons (Qgnutls, status), Vlibrary_cache); - return status; + const char* name = gnutls_ext_get_name(ext); + if (name != NULL) + { + capabilities = Fcons (intern(name), capabilities); + } } +# endif +# endif /* HAVE_GNUTLS3 */ + +# ifdef HAVE_GNUTLS_EXT__DUMBFW + capabilities = Fcons (intern("ClientHello Padding"), capabilities); +# endif + +# ifdef WINDOWSNT + Vlibrary_cache = Fcons (Fcons (Qgnutls, capabilities), Vlibrary_cache); # endif /* WINDOWSNT */ #endif /* HAVE_GNUTLS */ @@ -2438,18 +2828,24 @@ GnuTLS AEAD ciphers : the list will contain `AEAD-ciphers'. */) void syms_of_gnutls (void) { - DEFSYM (Qlibgnutls_version, "libgnutls-version"); - Fset (Qlibgnutls_version, + DEFVAR_LISP ("libgnutls-version", Vlibgnutls_version, + doc: /* The version of libgnutls that Emacs was compiled with. +The version number is encoded as an integer with the major version in +the ten thousands place, minor version in the hundreds, and patch +level in the ones. For builds without libgnutls, the value is -1. */); + Vlibgnutls_version = make_fixnum #ifdef HAVE_GNUTLS - make_number (GNUTLS_VERSION_MAJOR * 10000 - + GNUTLS_VERSION_MINOR * 100 - + GNUTLS_VERSION_PATCH) + (GNUTLS_VERSION_MAJOR * 10000 + + GNUTLS_VERSION_MINOR * 100 + + GNUTLS_VERSION_PATCH) #else - make_number (-1) + (-1) #endif - ); + ; + #ifdef HAVE_GNUTLS gnutls_global_initialized = 0; + PDUMPER_IGNORE (gnutls_global_initialized); DEFSYM (Qgnutls_code, "gnutls-code"); DEFSYM (Qgnutls_anon, "gnutls-anon"); @@ -2472,7 +2868,6 @@ syms_of_gnutls (void) DEFSYM (QCcipher_blocksize, ":cipher-blocksize"); DEFSYM (QCcipher_keysize, ":cipher-keysize"); DEFSYM (QCcipher_tagsize, ":cipher-tagsize"); - DEFSYM (QCcipher_keysize, ":cipher-keysize"); DEFSYM (QCcipher_ivsize, ":cipher-ivsize"); DEFSYM (QCmac_algorithm_id, ":mac-algorithm-id"); @@ -2490,19 +2885,19 @@ syms_of_gnutls (void) DEFSYM (Qgnutls_e_interrupted, "gnutls-e-interrupted"); Fput (Qgnutls_e_interrupted, Qgnutls_code, - make_number (GNUTLS_E_INTERRUPTED)); + make_fixnum (GNUTLS_E_INTERRUPTED)); DEFSYM (Qgnutls_e_again, "gnutls-e-again"); Fput (Qgnutls_e_again, Qgnutls_code, - make_number (GNUTLS_E_AGAIN)); + make_fixnum (GNUTLS_E_AGAIN)); DEFSYM (Qgnutls_e_invalid_session, "gnutls-e-invalid-session"); Fput (Qgnutls_e_invalid_session, Qgnutls_code, - make_number (GNUTLS_E_INVALID_SESSION)); + make_fixnum (GNUTLS_E_INVALID_SESSION)); DEFSYM (Qgnutls_e_not_ready_for_handshake, "gnutls-e-not-ready-for-handshake"); Fput (Qgnutls_e_not_ready_for_handshake, Qgnutls_code, - make_number (GNUTLS_E_APPLICATION_ERROR_MIN)); + make_fixnum (GNUTLS_E_APPLICATION_ERROR_MIN)); defsubr (&Sgnutls_get_initstage); defsubr (&Sgnutls_asynchronous_parameters); @@ -2514,6 +2909,7 @@ syms_of_gnutls (void) defsubr (&Sgnutls_bye); defsubr (&Sgnutls_peer_status); defsubr (&Sgnutls_peer_status_warning_describe); + defsubr (&Sgnutls_format_certificate); #ifdef HAVE_GNUTLS3 defsubr (&Sgnutls_ciphers); @@ -2523,6 +2919,9 @@ syms_of_gnutls (void) defsubr (&Sgnutls_hash_digest); defsubr (&Sgnutls_symmetric_encrypt); defsubr (&Sgnutls_symmetric_decrypt); + + cipher_cache = Qnil; + staticpro (&cipher_cache); #endif DEFVAR_INT ("gnutls-log-level", global_gnutls_log_level, |