X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/53947857fdb3c00bb673f6d2ac326dc4ccf01c6e..75fe387d4b7dd458b79fc22d593095cd84ca8ea4:/src/src/tls-gnu.c diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index f0e391f97..1953be1e4 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -76,8 +76,10 @@ typedef struct exim_gnutls_state { int fd_out; BOOL peer_cert_verified; BOOL trigger_sni_changes; + BOOL have_set_peerdn; const struct host_item *host; uschar *peerdn; + uschar *ciphersuite; uschar *received_sni; const uschar *tls_certificate; @@ -98,17 +100,14 @@ typedef struct exim_gnutls_state { int xfer_buffer_hwm; int xfer_eof; int xfer_error; - - uschar cipherbuf[256]; } exim_gnutls_state_st; static const exim_gnutls_state_st exim_gnutls_state_init = { - NULL, NULL, NULL, VERIFY_NONE, -1, -1, FALSE, FALSE, - NULL, NULL, NULL, + NULL, NULL, NULL, VERIFY_NONE, -1, -1, FALSE, FALSE, FALSE, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, - "" }; /* Not only do we have our own APIs which don't pass around state, assuming @@ -140,12 +139,6 @@ static const char * const exim_default_gnutls_priority = "NORMAL"; static BOOL exim_gnutls_base_init_done = FALSE; -/* ------------------------------------------------------------------------ */ -/* Callback declarations */ - -static void exim_gnutls_logger_cb(int level, const char *message); -static int exim_sni_handling_cb(gnutls_session_t session); - /* ------------------------------------------------------------------------ */ /* macros */ @@ -154,24 +147,47 @@ static int exim_sni_handling_cb(gnutls_session_t session); /* Set this to control gnutls_global_set_log_level(); values 0 to 9 will setup the library logging; a value less than 0 disables the calls to set up logging callbacks. */ +#ifndef EXIM_GNUTLS_LIBRARY_LOG_LEVEL #define EXIM_GNUTLS_LIBRARY_LOG_LEVEL -1 +#endif +#ifndef EXIM_CLIENT_DH_MIN_BITS #define EXIM_CLIENT_DH_MIN_BITS 1024 +#endif + +/* With GnuTLS 2.12.x+ we have gnutls_sec_param_to_pk_bits() with which we +can ask for a bit-strength. Without that, we stick to the constant we had +before, for now. */ +#ifndef EXIM_SERVER_DH_BITS_PRE2_12 +#define EXIM_SERVER_DH_BITS_PRE2_12 1024 +#endif #define exim_gnutls_err_check(Label) do { \ if (rc != GNUTLS_E_SUCCESS) { return tls_error((Label), gnutls_strerror(rc), host); } } while (0) -#define exim_gnutls_err_debugreturn0(Label) do { \ - if (rc != GNUTLS_E_SUCCESS) { \ - DEBUG(D_tls) debug_printf("TLS failure: %s: %s", (Label), gnutls_strerror(rc)); \ - return 0; } } while (0) - #define expand_check_tlsvar(Varname) expand_check(state->Varname, US #Varname, &state->exp_##Varname) #if GNUTLS_VERSION_NUMBER >= 0x020c00 #define HAVE_GNUTLS_SESSION_CHANNEL_BINDING +#define HAVE_GNUTLS_SEC_PARAM_CONSTANTS +#define HAVE_GNUTLS_RND +#endif + + + + +/* ------------------------------------------------------------------------ */ +/* Callback declarations */ + +#if EXIM_GNUTLS_LIBRARY_LOG_LEVEL >= 0 +static void exim_gnutls_logger_cb(int level, const char *message); #endif +static int exim_sni_handling_cb(gnutls_session_t session); + + + + /* ------------------------------------------------------------------------ */ /* Static functions */ @@ -280,11 +296,7 @@ Argument: static void extract_exim_vars_from_tls_state(exim_gnutls_state_st *state) { -gnutls_protocol_t protocol; gnutls_cipher_algorithm_t cipher; -gnutls_kx_algorithm_t kx; -gnutls_mac_algorithm_t mac; -uschar *p; #ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING int old_pool; int rc; @@ -299,26 +311,7 @@ cipher = gnutls_cipher_get(state->session); /* returns size in "bytes" */ tls_bits = gnutls_cipher_get_key_size(cipher) * 8; -if (!*state->cipherbuf) - { - protocol = gnutls_protocol_get_version(state->session); - mac = gnutls_mac_get(state->session); - kx = gnutls_kx_get(state->session); - - string_format(state->cipherbuf, sizeof(state->cipherbuf), - "%s:%s:%u", - gnutls_protocol_get_name(protocol), - gnutls_cipher_suite_get_name(kx, cipher, mac), - tls_bits); - - /* I don't see a way that spaces could occur, in the current GnuTLS - code base, but it was a concern in the old code and perhaps older GnuTLS - releases did return "TLS 1.0"; play it safe, just in case. */ - for (p = state->cipherbuf; *p != '\0'; ++p) - if (isspace(*p)) - *p = '-'; - } -tls_cipher = state->cipherbuf; +tls_cipher = state->ciphersuite; DEBUG(D_tls) debug_printf("cipher: %s\n", tls_cipher); @@ -380,21 +373,30 @@ gnutls_datum m; uschar filename[PATH_MAX]; size_t sz; host_item *host = NULL; /* dummy for macros */ -const char * const dh_param_fn_ext = "normal"; /* change as dh_bits changes */ DEBUG(D_tls) debug_printf("Initialising GnuTLS server params.\n"); rc = gnutls_dh_params_init(&dh_server_params); exim_gnutls_err_check(US"gnutls_dh_params_init"); -/* If you change this, also change dh_param_fn_ext so that we can use a +#ifdef HAVE_GNUTLS_SEC_PARAM_CONSTANTS +/* If you change this constant, also change dh_param_fn_ext so that we can use a different filename and ensure we have sufficient bits. */ dh_bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_NORMAL); if (!dh_bits) return tls_error(US"gnutls_sec_param_to_pk_bits() failed", NULL, NULL); +DEBUG(D_tls) + debug_printf("GnuTLS tells us that for D-H PK, NORMAL is %d bits.\n", + dh_bits); +#else +dh_bits = EXIM_SERVER_DH_BITS_PRE2_12; +DEBUG(D_tls) + debug_printf("GnuTLS lacks gnutls_sec_param_to_pk_bits(), using %d bits.\n", + dh_bits); +#endif if (!string_format(filename, sizeof(filename), - "%s/gnutls-params-%s", spool_directory, dh_param_fn_ext)) + "%s/gnutls-params-%d", spool_directory, dh_bits)) return tls_error(US"overlong filename", NULL, NULL); /* Open the cache file for reading and if successful, read it and set up the @@ -562,6 +564,7 @@ Returns: OK/DEFER/FAIL static int tls_expand_session_files(exim_gnutls_state_st *state) { +struct stat statbuf; int rc; const host_item *host = state->host; /* macro should be reconsidered? */ uschar *saved_tls_certificate = NULL; @@ -583,6 +586,7 @@ if (!state->host) } else { + /* useful for debugging */ saved_tls_certificate = state->exp_tls_certificate; saved_tls_privatekey = state->exp_tls_privatekey; saved_tls_verify_certificates = state->exp_tls_verify_certificates; @@ -590,6 +594,9 @@ if (!state->host) } } +rc = gnutls_certificate_allocate_credentials(&state->x509_cred); +exim_gnutls_err_check(US"gnutls_certificate_allocate_credentials"); + /* remember: expand_check_tlsvar() is expand_check() but fiddling with state members, assuming consistent naming; and expand_check() returns false if expansion failed, unless expansion was forced to fail. */ @@ -625,7 +632,6 @@ if (state->tls_privatekey == NULL || *state->tls_privatekey == '\0') if (state->exp_tls_certificate && *state->exp_tls_certificate) { - BOOL setit = TRUE; DEBUG(D_tls) debug_printf("certificate file = %s\nkey file = %s\n", state->exp_tls_certificate, state->exp_tls_privatekey); @@ -634,25 +640,22 @@ if (state->exp_tls_certificate && *state->exp_tls_certificate) if ((Ustrcmp(state->exp_tls_certificate, saved_tls_certificate) == 0) && (Ustrcmp(state->exp_tls_privatekey, saved_tls_privatekey) == 0)) { - DEBUG(D_tls) debug_printf("cert and key unchanged with SNI.\n"); - setit = FALSE; + DEBUG(D_tls) debug_printf("TLS SNI: cert and key unchanged\n"); } else { - DEBUG(D_tls) debug_printf("SNI changed cert/key pair.\n"); + DEBUG(D_tls) debug_printf("TLS SNI: have a changed cert/key pair.\n"); } } - if (setit) - { - rc = gnutls_certificate_set_x509_key_file(state->x509_cred, - CS state->exp_tls_certificate, CS state->exp_tls_privatekey, - GNUTLS_X509_FMT_PEM); - exim_gnutls_err_check( - string_sprintf("cert/key setup: cert=%s key=%s", - state->exp_tls_certificate, state->exp_tls_privatekey)); - } - } + rc = gnutls_certificate_set_x509_key_file(state->x509_cred, + CS state->exp_tls_certificate, CS state->exp_tls_privatekey, + GNUTLS_X509_FMT_PEM); + exim_gnutls_err_check( + string_sprintf("cert/key setup: cert=%s key=%s", + state->exp_tls_certificate, state->exp_tls_privatekey)); + DEBUG(D_tls) debug_printf("TLS: cert/key registered\n"); + } /* tls_certificate */ /* Set the trusted CAs file if one is provided, and then add the CRL if one is provided. Experiment shows that, if the certificate file is empty, an unhelpful @@ -662,89 +665,130 @@ behaviour. */ if (state->tls_verify_certificates && *state->tls_verify_certificates) { - struct stat statbuf; - BOOL setit_vc = TRUE, setit_crl = TRUE; - if (!expand_check_tlsvar(tls_verify_certificates)) return DEFER; if (state->tls_crl && *state->tls_crl) if (!expand_check_tlsvar(tls_crl)) return DEFER; - if (state->received_sni) + if (!(state->exp_tls_verify_certificates && + *state->exp_tls_verify_certificates)) { - if (Ustrcmp(state->exp_tls_verify_certificates, saved_tls_verify_certificates) == 0) - setit_vc = FALSE; - if (Ustrcmp(state->exp_tls_crl, saved_tls_crl) == 0) - setit_crl = FALSE; + DEBUG(D_tls) + debug_printf("TLS: tls_verify_certificates expanded empty, ignoring\n"); + /* With no tls_verify_certificates, we ignore tls_crl too */ + return OK; } + } +else + { + DEBUG(D_tls) + debug_printf("TLS: tls_verify_certificates not set or empty, ignoring\n"); + return OK; + } - /* nb: early exit; change if add more expansions to this function */ - if (!(setit_vc || setit_crl)) - return OK; +if (Ustat(state->exp_tls_verify_certificates, &statbuf) < 0) + { + log_write(0, LOG_MAIN|LOG_PANIC, "could not stat %s " + "(tls_verify_certificates): %s", state->exp_tls_verify_certificates, + strerror(errno)); + return DEFER; + } - if (Ustat(state->exp_tls_verify_certificates, &statbuf) < 0) - { - log_write(0, LOG_MAIN|LOG_PANIC, "could not stat %s " - "(tls_verify_certificates): %s", state->exp_tls_verify_certificates, - strerror(errno)); - return DEFER; - } +if (!S_ISREG(statbuf.st_mode)) + { + DEBUG(D_tls) + debug_printf("verify certificates path is not a file: \"%s\"\n%s\n", + state->exp_tls_verify_certificates, + S_ISDIR(statbuf.st_mode) + ? " it's a directory, that's OpenSSL, this is GnuTLS" + : " (not a directory either)"); + log_write(0, LOG_MAIN|LOG_PANIC, + "tls_verify_certificates \"%s\" is not a file", + state->exp_tls_verify_certificates); + return DEFER; + } - if (!S_ISREG(statbuf.st_mode)) - { - DEBUG(D_tls) - debug_printf("verify certificates path is not a file: \"%s\"\n%s\n", - state->exp_tls_verify_certificates, - S_ISDIR(statbuf.st_mode) - ? " it's a directory, that's OpenSSL, this is GnuTLS" - : " (not a directory either)"); - log_write(0, LOG_MAIN|LOG_PANIC, - "tls_verify_certificates \"%s\" is not a file", - state->exp_tls_verify_certificates); - return DEFER; - } +DEBUG(D_tls) debug_printf("verify certificates = %s size=" OFF_T_FMT "\n", + state->exp_tls_verify_certificates, statbuf.st_size); - DEBUG(D_tls) debug_printf("verify certificates = %s size=" OFF_T_FMT "\n", - state->exp_tls_verify_certificates, statbuf.st_size); +if (statbuf.st_size == 0) + { + DEBUG(D_tls) + debug_printf("cert file empty, no certs, no verification, ignoring any CRL\n"); + return OK; + } - /* If the CA cert file is empty, there's no point in loading the CRL file, - as we aren't verifying, so checking for revocation is pointless. */ +cert_count = gnutls_certificate_set_x509_trust_file(state->x509_cred, + CS state->exp_tls_verify_certificates, GNUTLS_X509_FMT_PEM); +if (cert_count < 0) + { + rc = cert_count; + exim_gnutls_err_check(US"gnutls_certificate_set_x509_trust_file"); + } +DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", cert_count); - if (statbuf.st_size > 0) +if (state->tls_crl && *state->tls_crl) + { + if (state->exp_tls_crl && *state->exp_tls_crl) { - if (setit_vc) - { - cert_count = gnutls_certificate_set_x509_trust_file(state->x509_cred, - CS state->exp_tls_verify_certificates, GNUTLS_X509_FMT_PEM); - if (cert_count < 0) - { - rc = cert_count; - exim_gnutls_err_check(US"gnutls_certificate_set_x509_trust_file"); - } - DEBUG(D_tls) debug_printf("Added %d certificate authorities.\n", cert_count); - } - - if (setit_crl && state->tls_crl && *state->tls_crl) - { - if (state->exp_tls_crl && *state->exp_tls_crl) - { - DEBUG(D_tls) debug_printf("loading CRL file = %s\n", state->exp_tls_crl); - rc = gnutls_certificate_set_x509_crl_file(state->x509_cred, - CS state->exp_tls_crl, GNUTLS_X509_FMT_PEM); - exim_gnutls_err_check(US"gnutls_certificate_set_x509_crl_file"); - } - } - } /* statbuf.st_size */ - } /* tls_verify_certificates */ + DEBUG(D_tls) debug_printf("loading CRL file = %s\n", state->exp_tls_crl); + rc = gnutls_certificate_set_x509_crl_file(state->x509_cred, + CS state->exp_tls_crl, GNUTLS_X509_FMT_PEM); + exim_gnutls_err_check(US"gnutls_certificate_set_x509_crl_file"); + } + } return OK; -/* also above, during verify_certificates/crl, during SNI, if unchanged */ } +/************************************************* +* Set X.509 state variables * +*************************************************/ + +/* In GnuTLS, the registered cert/key are not replaced by a later +set of a cert/key, so for SNI support we need a whole new x509_cred +structure. Which means various other non-re-expanded pieces of state +need to be re-set in the new struct, so the setting logic is pulled +out to this. + +Arguments: + state exim_gnutls_state_st * + +Returns: OK/DEFER/FAIL +*/ + +static int +tls_set_remaining_x509(exim_gnutls_state_st *state) +{ +int rc; +const host_item *host = state->host; /* macro should be reconsidered? */ + +/* Create D-H parameters, or read them from the cache file. This function does +its own SMTP error messaging. This only happens for the server, TLS D-H ignores +client-side params. */ + +if (!state->host) + { + if (!dh_server_params) + { + rc = init_server_dh(); + if (rc != OK) return rc; + } + gnutls_certificate_set_dh_params(state->x509_cred, dh_server_params); + } + +/* Link the credentials to the session. */ + +rc = gnutls_credentials_set(state->session, GNUTLS_CRD_CERTIFICATE, state->x509_cred); +exim_gnutls_err_check(US"gnutls_credentials_set"); + +return OK; +} + /************************************************* * Initialize for GnuTLS * *************************************************/ @@ -825,9 +869,6 @@ state->tls_sni = sni; state->tls_verify_certificates = cas; state->tls_crl = crl; -rc = gnutls_certificate_allocate_credentials(&state->x509_cred); -exim_gnutls_err_check(US"gnutls_certificate_allocate_credentials"); - /* This handles the variables that might get re-expanded after TLS SNI; that's tls_certificate, tls_privatekey, tls_verify_certificates, tls_crl */ @@ -836,21 +877,11 @@ DEBUG(D_tls) rc = tls_expand_session_files(state); if (rc != OK) return rc; -/* Create D-H parameters, or read them from the cache file. This function does -its own SMTP error messaging. This only happens for the server, TLS D-H ignores -client-side params. */ +/* These are all other parts of the x509_cred handling, since SNI in GnuTLS +requires a new structure afterwards. */ -if (!host) - { - rc = init_server_dh(); - if (rc != OK) return rc; - gnutls_certificate_set_dh_params(state->x509_cred, dh_server_params); - } - -/* Link the credentials to the session. */ - -rc = gnutls_credentials_set(state->session, GNUTLS_CRD_CERTIFICATE, state->x509_cred); -exim_gnutls_err_check(US"gnutls_credentials_set"); +rc = tls_set_remaining_x509(state); +if (rc != OK) return rc; /* set SNI in client, only */ if (host) @@ -896,6 +927,9 @@ if (state->tls_require_ciphers && *state->tls_require_ciphers) } if (want_default_priorities) { + DEBUG(D_tls) + debug_printf("GnuTLS using default session cipher/priority \"%s\"\n", + exim_default_gnutls_priority); rc = gnutls_priority_init(&state->priority_cache, exim_default_gnutls_priority, &errpos); p = US exim_default_gnutls_priority; @@ -936,7 +970,22 @@ return OK; *************************************************/ /* Called from both server and client code. -Only this is allowed to set state->peerdn and we use that to detect double-calls. +Only this is allowed to set state->peerdn and state->have_set_peerdn +and we use that to detect double-calls. + +NOTE: the state blocks last while the TLS connection is up, which is fine +for logging in the server side, but for the client side, we log after teardown +in src/deliver.c. While the session is up, we can twist about states and +repoint tls_* globals, but those variables used for logging or other variable +expansion that happens _after_ delivery need to have a longer life-time. + +So for those, we get the data from POOL_PERM; the re-invoke guard keeps us from +doing this more than once per generation of a state context. We set them in +the state context, and repoint tls_* to them. After the state goes away, the +tls_* copies of the pointers remain valid and client delivery logging is happy. + +tls_certificate_verified is a BOOL, so the tls_peerdn and tls_cipher issues +don't apply. Arguments: state exim_gnutls_state_st * @@ -947,24 +996,54 @@ Returns: OK/DEFER/FAIL static int peer_status(exim_gnutls_state_st *state) { +uschar cipherbuf[256]; const gnutls_datum *cert_list; -int rc; +int old_pool, rc; unsigned int cert_list_size = 0; +gnutls_protocol_t protocol; +gnutls_cipher_algorithm_t cipher; +gnutls_kx_algorithm_t kx; +gnutls_mac_algorithm_t mac; gnutls_certificate_type_t ct; gnutls_x509_crt_t crt; -uschar *dn_buf; +uschar *p, *dn_buf; size_t sz; -if (state->peerdn) +if (state->have_set_peerdn) return OK; +state->have_set_peerdn = TRUE; -state->peerdn = US"unknown"; +state->peerdn = NULL; +/* tls_cipher */ +cipher = gnutls_cipher_get(state->session); +protocol = gnutls_protocol_get_version(state->session); +mac = gnutls_mac_get(state->session); +kx = gnutls_kx_get(state->session); + +string_format(cipherbuf, sizeof(cipherbuf), + "%s:%s:%d", + gnutls_protocol_get_name(protocol), + gnutls_cipher_suite_get_name(kx, cipher, mac), + (int) gnutls_cipher_get_key_size(cipher) * 8); + +/* I don't see a way that spaces could occur, in the current GnuTLS +code base, but it was a concern in the old code and perhaps older GnuTLS +releases did return "TLS 1.0"; play it safe, just in case. */ +for (p = cipherbuf; *p != '\0'; ++p) + if (isspace(*p)) + *p = '-'; +old_pool = store_pool; +store_pool = POOL_PERM; +state->ciphersuite = string_copy(cipherbuf); +store_pool = old_pool; +tls_cipher = state->ciphersuite; + +/* tls_peerdn */ cert_list = gnutls_certificate_get_peers(state->session, &cert_list_size); if (cert_list == NULL || cert_list_size == 0) { - state->peerdn = US"unknown (no certificate)"; DEBUG(D_tls) debug_printf("TLS: no certificate from peer (%p & %d)\n", cert_list, cert_list_size); if (state->verify_requirement == VERIFY_REQUIRED) @@ -977,7 +1056,6 @@ ct = gnutls_certificate_type_get(state->session); if (ct != GNUTLS_CRT_X509) { const char *ctn = gnutls_certificate_type_get_name(ct); - state->peerdn = string_sprintf("unknown (type %s)", ctn); DEBUG(D_tls) debug_printf("TLS: peer cert not X.509 but instead \"%s\"\n", ctn); if (state->verify_requirement == VERIFY_REQUIRED) @@ -1064,7 +1142,7 @@ if ((rc < 0) || (verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED)) != 0) DEBUG(D_tls) debug_printf("TLS certificate verification failed (%s): peerdn=%s\n", - *error, state->peerdn); + *error, state->peerdn ? state->peerdn : US""); if (state->verify_requirement == VERIFY_REQUIRED) { @@ -1077,7 +1155,8 @@ if ((rc < 0) || (verify & (GNUTLS_CERT_INVALID|GNUTLS_CERT_REVOKED)) != 0) else { state->peer_cert_verified = TRUE; - DEBUG(D_tls) debug_printf("TLS certificate verified: peerdn=%s\n", state->peerdn); + DEBUG(D_tls) debug_printf("TLS certificate verified: peerdn=%s\n", + state->peerdn ? state->peerdn : US""); } tls_peerdn = state->peerdn; @@ -1095,11 +1174,13 @@ return TRUE; * gnutls_global_set_log_function() * gnutls_global_set_log_level() 0..9 */ +#if EXIM_GNUTLS_LIBRARY_LOG_LEVEL >= 0 static void exim_gnutls_logger_cb(int level, const char *message) { DEBUG(D_tls) debug_printf("GnuTLS<%d>: %s\n", level, message); } +#endif /* Called after client hello, should handle SNI work. @@ -1127,7 +1208,18 @@ unsigned int sni_type; int rc, old_pool; rc = gnutls_server_name_get(session, sni_name, &data_len, &sni_type, 0); -exim_gnutls_err_debugreturn0("gnutls_server_name_get()"); +if (rc != GNUTLS_E_SUCCESS) + { + DEBUG(D_tls) { + if (rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) + debug_printf("TLS: no SNI presented in handshake.\n"); + else + debug_printf("TLS failure: gnutls_server_name_get(): %s [%d]\n", + gnutls_strerror(rc), rc); + }; + return 0; + } + if (sni_type != GNUTLS_NAME_DNS) { DEBUG(D_tls) debug_printf("TLS: ignoring SNI of unhandled type %u\n", sni_type); @@ -1157,8 +1249,10 @@ if (rc != OK) return GNUTLS_E_APPLICATION_ERROR_MIN; } -rc = gnutls_credentials_set(state->session, GNUTLS_CRD_CERTIFICATE, state->x509_cred); -return (rc == GNUTLS_E_SUCCESS) ? 0 : rc; +rc = tls_set_remaining_x509(state); +if (rc != OK) return GNUTLS_E_APPLICATION_ERROR_MIN; + +return 0; } @@ -1406,6 +1500,10 @@ do } while ((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)); alarm(0); +if (rc != GNUTLS_E_SUCCESS) + return tls_error(US"gnutls_handshake", + sigalrm_seen ? "timed out" : gnutls_strerror(rc), state->host); + DEBUG(D_tls) debug_printf("gnutls_handshake was successful\n"); /* Verify late */ @@ -1419,7 +1517,7 @@ if (state->verify_requirement != VERIFY_NONE && rc = peer_status(state); if (rc != OK) return rc; -/* Sets various Exim expansion variables; always safe within server */ +/* Sets various Exim expansion variables; may need to adjust for ACL callouts */ extract_exim_vars_from_tls_state(state); @@ -1667,6 +1765,7 @@ Arguments: Returns a random number in range [0, max-1] */ +#ifdef HAVE_GNUTLS_RND int vaguely_random_number(int max) { @@ -1704,6 +1803,13 @@ for (p = smallbuf; needed_len; --needed_len, ++p) * smooth distribution and cares enough then they should submit a patch then. */ return r % max; } +#else /* HAVE_GNUTLS_RND */ +int +vaguely_random_number(int max) +{ + return vaguely_random_number_fallback(max); +} +#endif /* HAVE_GNUTLS_RND */