X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/58eb016e585187a87ade7602b2aecb2208605320..0a49a7a4f1090b6f1ce1d0f9d969804c9226b53e:/src/src/tls-openssl.c diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index a9b67dcdd..a9251c7f4 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1,10 +1,10 @@ -/* $Cambridge: exim/src/src/tls-openssl.c,v 1.12 2007/04/13 15:13:47 ph10 Exp $ */ +/* $Cambridge: exim/src/src/tls-openssl.c,v 1.22 2009/11/16 19:50:37 nm4 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2007 */ +/* Copyright (c) University of Cambridge 1995 - 2009 */ /* See the file NOTICE for conditions of use and distribution. */ /* This module provides the TLS (aka SSL) support for Exim using the OpenSSL @@ -26,8 +26,8 @@ functions from the OpenSSL library. */ /* Structure for collecting random data for seeding. */ typedef struct randstuff { - time_t t; - pid_t p; + struct timeval tv; + pid_t p; } randstuff; /* Local static variables */ @@ -62,25 +62,33 @@ Argument: prefix text to include in the logged error host NULL if setting up a server; the connected host if setting up a client + msg error message or NULL if we should ask OpenSSL Returns: OK/DEFER/FAIL */ static int -tls_error(uschar *prefix, host_item *host) +tls_error(uschar *prefix, host_item *host, uschar *msg) { -ERR_error_string(ERR_get_error(), ssl_errstring); +if (msg == NULL) + { + ERR_error_string(ERR_get_error(), ssl_errstring); + msg = (uschar *)ssl_errstring; + } + if (host == NULL) { - log_write(0, LOG_MAIN, "TLS error on connection from %s (%s): %s", - (sender_fullhost != NULL)? sender_fullhost : US"local process", - prefix, ssl_errstring); + uschar *conn_info = smtp_get_connection_info(); + if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) + conn_info += 5; + log_write(0, LOG_MAIN, "TLS error on %s (%s): %s", + conn_info, prefix, msg); return DEFER; } else { log_write(0, LOG_MAIN, "TLS error on connection to %s [%s] (%s): %s", - host->name, host->address, prefix, ssl_errstring); + host->name, host->address, prefix, msg); return FAIL; } } @@ -224,12 +232,13 @@ DEBUG(D_tls) debug_printf("SSL info: %s\n", SSL_state_string_long(s)); Arguments: dhparam DH parameter file + host connected host, if client; NULL if server Returns: TRUE if OK (nothing to set up, or setup worked) */ static BOOL -init_dh(uschar *dhparam) +init_dh(uschar *dhparam, host_item *host) { BOOL yield = TRUE; BIO *bio; @@ -243,16 +252,16 @@ if (dhexpanded == NULL) return TRUE; if ((bio = BIO_new_file(CS dhexpanded, "r")) == NULL) { - log_write(0, LOG_MAIN, "DH: could not read %s: %s", dhexpanded, - strerror(errno)); + tls_error(string_sprintf("could not read dhparams file %s", dhexpanded), + host, (uschar *)strerror(errno)); yield = FALSE; } else { if ((dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL)) == NULL) { - log_write(0, LOG_MAIN, "DH: could not load params from %s", - dhexpanded); + tls_error(string_sprintf("could not read dhparams file %s", dhexpanded), + host, NULL); yield = FALSE; } else @@ -296,12 +305,18 @@ tls_init(host_item *host, uschar *dhparam, uschar *certificate, SSL_load_error_strings(); /* basic set up */ OpenSSL_add_ssl_algorithms(); +#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) +/* SHA256 is becoming ever moar popular. This makes sure it gets added to the +list of available digests. */ +EVP_add_digest(EVP_sha256()); +#endif + /* Create a context */ ctx = SSL_CTX_new((host == NULL)? SSLv23_server_method() : SSLv23_client_method()); -if (ctx == NULL) return tls_error(US"SSL_CTX_new", host); +if (ctx == NULL) return tls_error(US"SSL_CTX_new", host, NULL); /* It turns out that we need to seed the random number generator this early in order to get the full complement of ciphers to work. It took me roughly a day @@ -314,7 +329,7 @@ afterwards. */ if (!RAND_status()) { randstuff r; - r.t = time(NULL); + gettimeofday(&r.tv, NULL); r.p = getpid(); RAND_seed((uschar *)(&r), sizeof(r)); @@ -322,22 +337,8 @@ if (!RAND_status()) if (addr != NULL) RAND_seed((uschar *)addr, sizeof(addr)); if (!RAND_status()) - { - if (host == NULL) - { - log_write(0, LOG_MAIN, "TLS error on connection from %s: " - "unable to seed random number generator", - (sender_fullhost != NULL)? sender_fullhost : US"local process"); - return DEFER; - } - else - { - log_write(0, LOG_MAIN, "TLS error on connection to %s [%s]: " - "unable to seed random number generator", - host->name, host->address); - return FAIL; - } - } + return tls_error(US"RAND_status", host, + US"unable to seed random number generator"); } /* Set up the information callback, which outputs if debugging is at a suitable @@ -358,12 +359,12 @@ SSL_CTX_set_info_callback(ctx, (void (*)())info_callback); /* XXX (Silently?) ignore failure here? XXX*/ if (!(SSL_CTX_set_options(ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS))) - return tls_error(US"SSL_CTX_set_option", host); + return tls_error(US"SSL_CTX_set_option", host, NULL); #endif /* Initialize with DH parameters if supplied */ -if (!init_dh(dhparam)) return DEFER; +if (!init_dh(dhparam, host)) return DEFER; /* Set up certificate and key */ @@ -378,7 +379,7 @@ if (certificate != NULL) DEBUG(D_tls) debug_printf("tls_certificate file %s\n", expanded); if (!SSL_CTX_use_certificate_chain_file(ctx, CS expanded)) return tls_error(string_sprintf( - "SSL_CTX_use_certificate_chain_file file=%s", expanded), host); + "SSL_CTX_use_certificate_chain_file file=%s", expanded), host, NULL); } if (privatekey != NULL && @@ -394,7 +395,7 @@ if (certificate != NULL) DEBUG(D_tls) debug_printf("tls_privatekey file %s\n", expanded); if (!SSL_CTX_use_PrivateKey_file(ctx, CS expanded, SSL_FILETYPE_PEM)) return tls_error(string_sprintf( - "SSL_CTX_use_PrivateKey_file file=%s", expanded), host); + "SSL_CTX_use_PrivateKey_file file=%s", expanded), host, NULL); } } @@ -491,7 +492,7 @@ if (expcerts != NULL) { struct stat statbuf; if (!SSL_CTX_set_default_verify_paths(ctx)) - return tls_error(US"SSL_CTX_set_default_verify_paths", host); + return tls_error(US"SSL_CTX_set_default_verify_paths", host, NULL); if (Ustat(expcerts, &statbuf) < 0) { @@ -514,7 +515,7 @@ if (expcerts != NULL) if ((file == NULL || statbuf.st_size > 0) && !SSL_CTX_load_verify_locations(ctx, CS file, CS dir)) - return tls_error(US"SSL_CTX_load_verify_locations", host); + return tls_error(US"SSL_CTX_load_verify_locations", host, NULL); if (file != NULL) { @@ -564,7 +565,7 @@ if (expcerts != NULL) DEBUG(D_tls) debug_printf("SSL CRL value is a file %s\n", file); } if (X509_STORE_load_locations(cvstore, CS file, CS dir) == 0) - return tls_error(US"X509_STORE_load_locations", host); + return tls_error(US"X509_STORE_load_locations", host, NULL); /* setting the flags to check against the complete crl chain */ @@ -620,9 +621,7 @@ uschar *expciphers; if (tls_active >= 0) { - log_write(0, LOG_MAIN, "STARTTLS received in already encrypted " - "connection from %s", - (sender_fullhost != NULL)? sender_fullhost : US"local process"); + tls_error(US"STARTTLS received after TLS started", NULL, US""); smtp_printf("554 Already in TLS\r\n"); return FAIL; } @@ -646,7 +645,7 @@ if (expciphers != NULL) while (*s != 0) { if (*s == '_') *s = '-'; s++; } DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers); if (!SSL_CTX_set_cipher_list(ctx, CS expciphers)) - return tls_error(US"SSL_CTX_set_cipher_list", NULL); + return tls_error(US"SSL_CTX_set_cipher_list", NULL, NULL); } /* If this is a host for which certificate verification is mandatory or @@ -670,7 +669,7 @@ else if (verify_check_host(&tls_try_verify_hosts) == OK) /* Prepare for new connection */ -if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", NULL); +if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", NULL, NULL); SSL_clear(ssl); /* Set context and tell client to go ahead, except in the case of TLS startup @@ -702,11 +701,7 @@ alarm(0); if (rc <= 0) { - if (sigalrm_seen) Ustrcpy(ssl_errstring, "timed out"); - else ERR_error_string(ERR_get_error(), ssl_errstring); - log_write(0, LOG_MAIN, "TLS error on connection from %s (SSL_accept): %s", - (sender_fullhost != NULL)? sender_fullhost : US"local process", - ssl_errstring); + tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL); return FAIL; } @@ -801,13 +796,13 @@ if (expciphers != NULL) while (*s != 0) { if (*s == '_') *s = '-'; s++; } DEBUG(D_tls) debug_printf("required ciphers: %s\n", expciphers); if (!SSL_CTX_set_cipher_list(ctx, CS expciphers)) - return tls_error(US"SSL_CTX_set_cipher_list", host); + return tls_error(US"SSL_CTX_set_cipher_list", host, NULL); } rc = setup_certs(verify_certs, crl, host, FALSE); if (rc != OK) return rc; -if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", host); +if ((ssl = SSL_new(ctx)) == NULL) return tls_error(US"SSL_new", host, NULL); SSL_set_session_id_context(ssl, sid_ctx, Ustrlen(sid_ctx)); SSL_set_fd(ssl, fd); SSL_set_connect_state(ssl); @@ -821,15 +816,7 @@ rc = SSL_connect(ssl); alarm(0); if (rc <= 0) - { - if (sigalrm_seen) - { - log_write(0, LOG_MAIN, "TLS error on connection to %s [%s]: " - "SSL_connect timed out", host->name, host->address); - return FAIL; - } - else return tls_error(US"SSL_connect", host); - } + return tls_error(US"SSL_connect", host, sigalrm_seen ? US"timed out" : NULL); DEBUG(D_tls) debug_printf("SSL_connect succeeded\n"); @@ -906,7 +893,9 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) ssl_xfer_error = 1; return EOF; } - +#ifndef DISABLE_DKIM + dkim_exim_verify_feed(ssl_xfer_buffer, inbytes); +#endif ssl_xfer_buffer_hwm = inbytes; ssl_xfer_buffer_lwm = 0; } @@ -1042,4 +1031,96 @@ ssl = NULL; tls_active = -1; } + + + +/************************************************* +* Report the library versions. * +*************************************************/ + +/* There have historically been some issues with binary compatibility in +OpenSSL libraries; if Exim (like many other applications) is built against +one version of OpenSSL but the run-time linker picks up another version, +it can result in serious failures, including crashing with a SIGSEGV. So +report the version found by the compiler and the run-time version. + +Arguments: a FILE* to print the results to +Returns: nothing +*/ + +void +tls_version_report(FILE *f) +{ +fprintf(f, "OpenSSL compile-time version: %s\n", OPENSSL_VERSION_TEXT); +fprintf(f, "OpenSSL runtime version: %s\n", SSLeay_version(SSLEAY_VERSION)); +} + + + + +/************************************************* +* Pseudo-random number generation * +*************************************************/ + +/* Pseudo-random number generation. The result is not expected to be +cryptographically strong but not so weak that someone will shoot themselves +in the foot using it as a nonce in input in some email header scheme or +whatever weirdness they'll twist this into. The result should handle fork() +and avoid repeating sequences. OpenSSL handles that for us. + +Arguments: + max range maximum +Returns a random number in range [0, max-1] +*/ + +int +pseudo_random_number(int max) +{ +unsigned int r; +int i, needed_len; +uschar *p; +uschar smallbuf[sizeof(r)]; + +if (max <= 1) + return 0; + +/* OpenSSL auto-seeds from /dev/random, etc, but this a double-check. */ +if (!RAND_status()) + { + randstuff r; + gettimeofday(&r.tv, NULL); + r.p = getpid(); + + RAND_seed((uschar *)(&r), sizeof(r)); + } +/* We're after pseudo-random, not random; if we still don't have enough data +in the internal PRNG then our options are limited. We could sleep and hope +for entropy to come along (prayer technique) but if the system is so depleted +in the first place then something is likely to just keep taking it. Instead, +we'll just take whatever little bit of pseudo-random we can still manage to +get. */ + +needed_len = sizeof(r); +/* Don't take 8 times more entropy than needed if int is 8 octets and we were +asked for a number less than 10. */ +for (r = max, i = 0; r; ++i) + r >>= 1; +i = (i + 7) / 8; +if (i < needed_len) + needed_len = i; + +/* We do not care if crypto-strong */ +(void) RAND_pseudo_bytes(smallbuf, needed_len); +r = 0; +for (p = smallbuf; needed_len; --needed_len, ++p) + { + r *= 256; + r += *p; + } + +/* We don't particularly care about weighted results; if someone wants +smooth distribution and cares enough then they should submit a patch then. */ +return r % max; +} + /* End of tls-openssl.c */