X-Git-Url: https://git.hcoop.net/bpt/emacs.git/blobdiff_plain/8121e4813da7e5898eb216a5de3c17f4875cac61..refs/heads/wip:/src/gnutls.c diff --git a/src/gnutls.c b/src/gnutls.c index db0a6dac01..ef9367c2af 100644 --- a/src/gnutls.c +++ b/src/gnutls.c @@ -1,5 +1,5 @@ /* GnuTLS glue for GNU Emacs. - Copyright (C) 2010-2013 Free Software Foundation, Inc. + Copyright (C) 2010-2014 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -21,6 +21,7 @@ along with GNU Emacs. If not, see . */ #include "lisp.h" #include "process.h" +#include "coding.h" #ifdef HAVE_GNUTLS #include @@ -49,13 +50,16 @@ static Lisp_Object QCgnutls_bootprop_loglevel; static Lisp_Object QCgnutls_bootprop_hostname; static Lisp_Object QCgnutls_bootprop_min_prime_bits; static Lisp_Object QCgnutls_bootprop_verify_flags; -static Lisp_Object QCgnutls_bootprop_verify_hostname_error; +static Lisp_Object QCgnutls_bootprop_verify_error; /* Callback keys for `gnutls-boot'. Unused currently. */ static Lisp_Object QCgnutls_bootprop_callbacks_verify; static void gnutls_log_function (int, const char *); -static void gnutls_log_function2 (int, const char*, const char*); +static void gnutls_log_function2 (int, const char *, const char *); +#ifdef HAVE_GNUTLS3 +static void gnutls_audit_log_function (gnutls_session_t, const char *); +#endif #ifdef WINDOWSNT @@ -108,6 +112,9 @@ DEF_GNUTLS_FN (void, gnutls_dh_set_prime_bits, DEF_GNUTLS_FN (int, gnutls_error_is_fatal, (int)); DEF_GNUTLS_FN (int, gnutls_global_init, (void)); DEF_GNUTLS_FN (void, gnutls_global_set_log_function, (gnutls_log_func)); +#ifdef HAVE_GNUTLS3 +DEF_GNUTLS_FN (void, gnutls_global_set_audit_log_function, (gnutls_audit_log_func)); +#endif DEF_GNUTLS_FN (void, gnutls_global_set_log_level, (int)); DEF_GNUTLS_FN (void, gnutls_global_set_mem_functions, (gnutls_alloc_function, gnutls_alloc_function, @@ -173,6 +180,9 @@ init_gnutls_functions (void) LOAD_GNUTLS_FN (library, gnutls_error_is_fatal); LOAD_GNUTLS_FN (library, gnutls_global_init); LOAD_GNUTLS_FN (library, gnutls_global_set_log_function); +#ifdef HAVE_GNUTLS3 + LOAD_GNUTLS_FN (library, gnutls_global_set_audit_log_function); +#endif LOAD_GNUTLS_FN (library, gnutls_global_set_log_level); LOAD_GNUTLS_FN (library, gnutls_global_set_mem_functions); LOAD_GNUTLS_FN (library, gnutls_handshake); @@ -230,6 +240,9 @@ init_gnutls_functions (void) #define fn_gnutls_error_is_fatal gnutls_error_is_fatal #define fn_gnutls_global_init gnutls_global_init #define fn_gnutls_global_set_log_function gnutls_global_set_log_function +#ifdef HAVE_GNUTLS3 +#define fn_gnutls_global_set_audit_log_function gnutls_global_set_audit_log_function +#endif #define fn_gnutls_global_set_log_level gnutls_global_set_log_level #define fn_gnutls_global_set_mem_functions gnutls_global_set_mem_functions #define fn_gnutls_handshake gnutls_handshake @@ -239,7 +252,9 @@ init_gnutls_functions (void) #define fn_gnutls_record_recv gnutls_record_recv #define fn_gnutls_record_send gnutls_record_send #define fn_gnutls_strerror gnutls_strerror +#ifdef WINDOWSNT #define fn_gnutls_transport_set_errno gnutls_transport_set_errno +#endif #define fn_gnutls_transport_set_ptr2 gnutls_transport_set_ptr2 #define fn_gnutls_x509_crt_check_hostname gnutls_x509_crt_check_hostname #define fn_gnutls_x509_crt_deinit gnutls_x509_crt_deinit @@ -249,23 +264,35 @@ init_gnutls_functions (void) #endif /* !WINDOWSNT */ +#ifdef HAVE_GNUTLS3 +/* Function to log a simple audit message. */ +static void +gnutls_audit_log_function (gnutls_session_t session, const char *string) +{ + if (global_gnutls_log_level >= 1) + { + message ("gnutls.c: [audit] %s", string); + } +} +#endif + /* Function to log a simple message. */ static void -gnutls_log_function (int level, const char* string) +gnutls_log_function (int level, const char *string) { message ("gnutls.c: [%d] %s", level, string); } /* Function to log a message and a string. */ static void -gnutls_log_function2 (int level, const char* string, const char* extra) +gnutls_log_function2 (int level, const char *string, const char *extra) { message ("gnutls.c: [%d] %s %s", level, string, extra); } /* Function to log a message and an integer. */ static void -gnutls_log_function2i (int level, const char* string, int extra) +gnutls_log_function2i (int level, const char *string, int extra) { message ("gnutls.c: [%d] %s %d", level, string, extra); } @@ -338,17 +365,19 @@ emacs_gnutls_handshake (struct Lisp_Process *proc) return ret; } -int +ptrdiff_t emacs_gnutls_record_check_pending (gnutls_session_t state) { return fn_gnutls_record_check_pending (state); } +#ifdef WINDOWSNT void emacs_gnutls_transport_set_errno (gnutls_session_t state, int err) { fn_gnutls_transport_set_errno (state, err); } +#endif ptrdiff_t emacs_gnutls_write (struct Lisp_Process *proc, const char *buf, ptrdiff_t nbyte) @@ -464,8 +493,20 @@ emacs_gnutls_handle_error (gnutls_session_t session, int err) else { ret = 1; - GNUTLS_LOG2 (1, max_log_level, "non-fatal error:", str); - /* TODO: EAGAIN AKA Qgnutls_e_again should be level 2. */ + + switch (err) + { + case GNUTLS_E_AGAIN: + GNUTLS_LOG2 (3, + max_log_level, + "retry:", + str); + default: + GNUTLS_LOG2 (1, + max_log_level, + "non-fatal error:", + str); + } } if (err == GNUTLS_E_WARNING_ALERT_RECEIVED @@ -713,8 +754,12 @@ certificates for `gnutls-x509pki'. :verify-flags is a bitset as per GnuTLS' gnutls_certificate_set_verify_flags. -:verify-hostname-error, if non-nil, makes a hostname mismatch an -error. Otherwise it will be just a warning. +:verify-hostname-error is ignored. Pass :hostname in :verify-error +instead. + +:verify-error is a list of symbols to express verification checks or +`t' to do all checks. Currently it can contain `:trustfiles' and +`:hostname' to verify the certificate or the hostname respectively. :min-prime-bits is the minimum accepted number of bits the client will accept in Diffie-Hellman key exchange. @@ -741,6 +786,7 @@ one trustfile (usually a CA bundle). */) { int ret = GNUTLS_E_SUCCESS; int max_log_level = 0; + bool verify_error_all = 0; gnutls_session_t state; gnutls_certificate_credentials_t x509_cred = NULL; @@ -748,7 +794,7 @@ one trustfile (usually a CA bundle). */) Lisp_Object global_init; char const *priority_string_ptr = "NORMAL"; /* default priority string. */ unsigned int peer_verification; - char* c_hostname; + char *c_hostname; /* Placeholders for the property list elements. */ Lisp_Object priority_string; @@ -758,8 +804,7 @@ one trustfile (usually a CA bundle). */) /* Lisp_Object callbacks; */ Lisp_Object loglevel; Lisp_Object hostname; - /* Lisp_Object verify_error; */ - Lisp_Object verify_hostname_error; + Lisp_Object verify_error; Lisp_Object prime_bits; CHECK_PROCESS (proc); @@ -767,16 +812,10 @@ one trustfile (usually a CA bundle). */) CHECK_LIST (proplist); if (NILP (Fgnutls_available_p ())) - { - error ("GnuTLS not available"); - return gnutls_make_error (GNUTLS_EMACS_ERROR_NOT_LOADED); - } + error ("GnuTLS not available"); if (!EQ (type, Qgnutls_x509pki) && !EQ (type, Qgnutls_anon)) - { - error ("Invalid GnuTLS credential type"); - return gnutls_make_error (GNUTLS_EMACS_ERROR_INVALID_TYPE); - } + error ("Invalid GnuTLS credential type"); hostname = Fplist_get (proplist, QCgnutls_bootprop_hostname); priority_string = Fplist_get (proplist, QCgnutls_bootprop_priority); @@ -784,19 +823,30 @@ one trustfile (usually a CA bundle). */) keylist = Fplist_get (proplist, QCgnutls_bootprop_keylist); crlfiles = Fplist_get (proplist, QCgnutls_bootprop_crlfiles); loglevel = Fplist_get (proplist, QCgnutls_bootprop_loglevel); - verify_hostname_error = Fplist_get (proplist, QCgnutls_bootprop_verify_hostname_error); + verify_error = Fplist_get (proplist, QCgnutls_bootprop_verify_error); prime_bits = Fplist_get (proplist, QCgnutls_bootprop_min_prime_bits); + if (EQ (verify_error, Qt)) + { + verify_error_all = 1; + } + else if (NILP (Flistp (verify_error))) + { + error ("gnutls-boot: invalid :verify_error parameter (not a list)"); + } + if (!STRINGP (hostname)) - error ("gnutls-boot: invalid :hostname parameter"); + error ("gnutls-boot: invalid :hostname parameter (not a string)"); c_hostname = SSDATA (hostname); state = XPROCESS (proc)->gnutls_state; - XPROCESS (proc)->gnutls_p = 1; if (TYPE_RANGED_INTEGERP (int, loglevel)) { fn_gnutls_global_set_log_function (gnutls_log_function); +#ifdef HAVE_GNUTLS3 + fn_gnutls_global_set_audit_log_function (gnutls_audit_log_function); +#endif fn_gnutls_global_set_log_level (XINT (loglevel)); max_log_level = XINT (loglevel); XPROCESS (proc)->gnutls_log_level = max_log_level; @@ -812,7 +862,6 @@ one trustfile (usually a CA bundle). */) emacs_gnutls_deinit (proc); /* Mark PROC as a GnuTLS process. */ - XPROCESS (proc)->gnutls_p = 1; XPROCESS (proc)->gnutls_state = NULL; XPROCESS (proc)->gnutls_x509_cred = NULL; XPROCESS (proc)->gnutls_anon_cred = NULL; @@ -864,6 +913,13 @@ one trustfile (usually a CA bundle). */) { GNUTLS_LOG2 (1, max_log_level, "setting the trustfile: ", SSDATA (trustfile)); + trustfile = ENCODE_FILE (trustfile); +#ifdef WINDOWSNT + /* Since GnuTLS doesn't support UTF-8 or UTF-16 encoded + file names on Windows, we need to re-encode the file + name using the current ANSI codepage. */ + trustfile = ansi_encode_filename (trustfile); +#endif ret = fn_gnutls_certificate_set_x509_trust_file (x509_cred, SSDATA (trustfile), @@ -886,6 +942,10 @@ one trustfile (usually a CA bundle). */) { GNUTLS_LOG2 (1, max_log_level, "setting the CRL file: ", SSDATA (crlfile)); + crlfile = ENCODE_FILE (crlfile); +#ifdef WINDOWSNT + crlfile = ansi_encode_filename (crlfile); +#endif ret = fn_gnutls_certificate_set_x509_crl_file (x509_cred, SSDATA (crlfile), file_format); @@ -909,6 +969,12 @@ one trustfile (usually a CA bundle). */) SSDATA (keyfile)); GNUTLS_LOG2 (1, max_log_level, "setting the client cert file: ", SSDATA (certfile)); + keyfile = ENCODE_FILE (keyfile); + certfile = ENCODE_FILE (certfile); +#ifdef WINDOWSNT + keyfile = ansi_encode_filename (keyfile); + certfile = ansi_encode_filename (certfile); +#endif ret = fn_gnutls_certificate_set_x509_key_file (x509_cred, SSDATA (certfile), SSDATA (keyfile), file_format); @@ -1012,14 +1078,17 @@ one trustfile (usually a CA bundle). */) if (peer_verification != 0) { - if (NILP (verify_hostname_error)) - GNUTLS_LOG2 (1, max_log_level, "certificate validation failed:", - c_hostname); - else - { + if (verify_error_all + || !NILP (Fmember (QCgnutls_bootprop_trustfiles, verify_error))) + { emacs_gnutls_deinit (proc); error ("Certificate validation failed %s, verification code %d", c_hostname, peer_verification); + } + else + { + GNUTLS_LOG2 (1, max_log_level, "certificate validation failed:", + c_hostname); } } @@ -1059,19 +1128,25 @@ one trustfile (usually a CA bundle). */) if (!fn_gnutls_x509_crt_check_hostname (gnutls_verify_cert, c_hostname)) { - if (NILP (verify_hostname_error)) - GNUTLS_LOG2 (1, max_log_level, "x509 certificate does not match:", - c_hostname); - else - { + if (verify_error_all + || !NILP (Fmember (QCgnutls_bootprop_hostname, verify_error))) + { fn_gnutls_x509_crt_deinit (gnutls_verify_cert); emacs_gnutls_deinit (proc); error ("The x509 certificate does not match \"%s\"", c_hostname); + } + else + { + GNUTLS_LOG2 (1, max_log_level, "x509 certificate does not match:", + c_hostname); } } fn_gnutls_x509_crt_deinit (gnutls_verify_cert); } + /* Set this flag only if the whole initialization succeeded. */ + XPROCESS (proc)->gnutls_p = 1; + return gnutls_make_error (ret); } @@ -1107,6 +1182,8 @@ This function may also return `gnutls-e-again', or void syms_of_gnutls (void) { +#include "gnutls.x" + gnutls_global_initialized = 0; DEFSYM (Qgnutls_dll, "gnutls"); @@ -1123,7 +1200,7 @@ syms_of_gnutls (void) DEFSYM (QCgnutls_bootprop_min_prime_bits, ":min-prime-bits"); DEFSYM (QCgnutls_bootprop_loglevel, ":loglevel"); DEFSYM (QCgnutls_bootprop_verify_flags, ":verify-flags"); - DEFSYM (QCgnutls_bootprop_verify_hostname_error, ":verify-hostname-error"); + DEFSYM (QCgnutls_bootprop_verify_error, ":verify-error"); DEFSYM (Qgnutls_e_interrupted, "gnutls-e-interrupted"); Fput (Qgnutls_e_interrupted, Qgnutls_code, @@ -1141,15 +1218,6 @@ syms_of_gnutls (void) Fput (Qgnutls_e_not_ready_for_handshake, Qgnutls_code, make_number (GNUTLS_E_APPLICATION_ERROR_MIN)); - defsubr (&Sgnutls_get_initstage); - defsubr (&Sgnutls_errorp); - defsubr (&Sgnutls_error_fatalp); - defsubr (&Sgnutls_error_string); - defsubr (&Sgnutls_boot); - defsubr (&Sgnutls_deinit); - defsubr (&Sgnutls_bye); - defsubr (&Sgnutls_available_p); - DEFVAR_INT ("gnutls-log-level", global_gnutls_log_level, doc: /* Logging level used by the GnuTLS functions. Set this larger than 0 to get debug output in the *Messages* buffer.