X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Ftls-openssl.c;h=a7dad0805104b3a824ba16c8c25d398df4378fd5;hb=77bb000fa965b786ddb1085dd5af6c80c7d425b0;hp=951e46c253c75c48f4829667dfde820639200c02;hpb=d6453af2c864d0bd57785f5f808bc366d6f18970;p=exim.git diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 951e46c25..a7dad0805 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.2 2004/11/25 15:29:37 ph10 Exp $ */ +/* $Cambridge: exim/src/src/tls-openssl.c,v 1.23 2010/06/05 09:10:10 pdp Exp $ */ /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2004 */ +/* 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; } } @@ -182,9 +190,6 @@ else tls_peerdn = txt; } - -debug_printf("+++verify_callback_called=%d\n", verify_callback_called); - if (!verify_callback_called) tls_certificate_verified = TRUE; verify_callback_called = TRUE; @@ -227,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; @@ -246,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 @@ -293,18 +299,27 @@ Returns: OK/DEFER/FAIL */ static int -tls_init(host_item *host, uschar *dhparam, uschar *certificate, uschar *privatekey, - address_item *addr) +tls_init(host_item *host, uschar *dhparam, uschar *certificate, + uschar *privatekey, address_item *addr) { +long init_options; +BOOL okay; + SSL_load_error_strings(); /* basic set up */ OpenSSL_add_ssl_algorithms(); +#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256) +/* SHA256 is becoming ever more 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 @@ -317,7 +332,7 @@ afterwards. */ if (!RAND_status()) { randstuff r; - r.t = time(NULL); + gettimeofday(&r.tv, NULL); r.p = getpid(); RAND_seed((uschar *)(&r), sizeof(r)); @@ -325,49 +340,41 @@ 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 level. */ -if (!(SSL_CTX_set_info_callback(ctx, (void (*)())info_callback))) - return tls_error(US"SSL_CTX_set_info_callback", host); +SSL_CTX_set_info_callback(ctx, (void (*)())info_callback); -/* The following patch was supplied by Robert Roselius */ +/* Apply administrator-supplied work-arounds. +Historically we applied just one requested option, +SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS, but when bug 994 requested a second, we +moved to an administrator-controlled list of options to specify and +grandfathered in the first one as the default value for "openssl_options". -#if OPENSSL_VERSION_NUMBER > 0x00906040L -/* Enable client-bug workaround. - Versions of OpenSSL as of 0.9.6d include a "CBC countermeasure" feature, - which causes problems with some clients (such as the Certicom SSL Plus - library used by Eudora). This option, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS, - disables the coutermeasure allowing Eudora to connect. - Some poppers and MTAs use SSL_OP_ALL, which enables all such bug - workarounds. */ -/* XXX (Silently?) ignore failure here? XXX*/ +No OpenSSL version number checks: the options we accept depend upon the +availability of the option value macros from OpenSSL. */ -if (!(SSL_CTX_set_options(ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS))) - return tls_error(US"SSL_CTX_set_option", host); -#endif +okay = tls_openssl_options_parse(openssl_options, &init_options); +if (!okay) + return tls_error("openssl_options parsing failed", host, NULL); + +if (init_options) + { + DEBUG(D_tls) debug_printf("setting SSL CTX options: %#lx\n", init_options); + if (!(SSL_CTX_set_options(ctx, init_options))) + return tls_error(string_sprintf( + "SSL_CTX_set_option(%#lx)", init_options), host, NULL); + } +else + DEBUG(D_tls) debug_printf("no SSL CTX options to set\n"); /* Initialize with DH parameters if supplied */ -if (!init_dh(dhparam)) return DEFER; +if (!init_dh(dhparam, host)) return DEFER; /* Set up certificate and key */ @@ -382,19 +389,23 @@ 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 && !expand_check(privatekey, US"tls_privatekey", &expanded)) return DEFER; - if (expanded != NULL) + /* If expansion was forced to fail, key_expanded will be NULL. If the result + of the expansion is an empty string, ignore it also, and assume the private + key is in the same file as the certificate. */ + + if (expanded != NULL && *expanded != 0) { 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 +502,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 +525,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) { @@ -526,34 +537,51 @@ if (expcerts != NULL) #if OPENSSL_VERSION_NUMBER > 0x00907000L + /* This bit of code is now the version supplied by Lars Mainka. (I have + * merely reformatted it into the Exim code style.) + + * "From here I changed the code to add support for multiple crl's + * in pem format in one file or to support hashed directory entries in + * pem format instead of a file. This method now uses the library function + * X509_STORE_load_locations to add the CRL location to the SSL context. + * OpenSSL will then handle the verify against CA certs and CRLs by + * itself in the verify callback." */ + if (!expand_check(crl, US"tls_crl", &expcrl)) return DEFER; if (expcrl != NULL && *expcrl != 0) { - BIO *crl_bio; - X509_CRL *crl_x509; - X509_STORE *cvstore; - - cvstore = SSL_CTX_get_cert_store(ctx); /* cert validation store */ - - crl_bio = BIO_new(BIO_s_file_internal()); - if (crl_bio != NULL) + struct stat statbufcrl; + if (Ustat(expcrl, &statbufcrl) < 0) { - if (BIO_read_filename(crl_bio, expcrl)) + log_write(0, LOG_MAIN|LOG_PANIC, + "failed to stat %s for certificates revocation lists", expcrl); + return DEFER; + } + else + { + /* is it a file or directory? */ + uschar *file, *dir; + X509_STORE *cvstore = SSL_CTX_get_cert_store(ctx); + if ((statbufcrl.st_mode & S_IFMT) == S_IFDIR) { - crl_x509 = PEM_read_bio_X509_CRL(crl_bio, NULL, NULL, NULL); - BIO_free(crl_bio); - X509_STORE_add_crl(cvstore, crl_x509); - X509_CRL_free(crl_x509); - X509_STORE_set_flags(cvstore, - X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); + file = NULL; + dir = expcrl; + DEBUG(D_tls) debug_printf("SSL CRL value is a directory %s\n", dir); } else { - BIO_free(crl_bio); - return tls_error(US"BIO_read_filename", host); + file = expcrl; + dir = 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, NULL); + + /* setting the flags to check against the complete crl chain */ + + X509_STORE_set_flags(cvstore, + X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); } - else return tls_error(US"BIO_new", host); } #endif /* OPENSSL_VERSION_NUMBER > 0x00907000L */ @@ -580,6 +608,11 @@ a TLS session. Arguments: require_ciphers allowed ciphers + ------------------------------------------------------ + require_mac list of allowed MACs ) Not used + require_kx list of allowed key_exchange methods ) for + require_proto list of allowed protocols ) OpenSSL + ------------------------------------------------------ Returns: OK on success DEFER for errors before the start of the negotiation @@ -588,7 +621,8 @@ Returns: OK on success */ int -tls_server_start(uschar *require_ciphers) +tls_server_start(uschar *require_ciphers, uschar *require_mac, + uschar *require_kx, uschar *require_proto) { int rc; uschar *expciphers; @@ -597,9 +631,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; } @@ -623,7 +655,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 @@ -647,7 +679,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 @@ -666,7 +698,8 @@ if (!tls_on_connect) /* Now negotiate the TLS session. We put our own timer on it, since it seems that the OpenSSL library doesn't. */ -SSL_set_fd(ssl, fileno(smtp_out)); +SSL_set_wfd(ssl, fileno(smtp_out)); +SSL_set_rfd(ssl, fileno(smtp_in)); SSL_set_accept_state(ssl); DEBUG(D_tls) debug_printf("Calling SSL_accept\n"); @@ -678,11 +711,10 @@ 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); + if (ERR_get_error() == 0) + log_write(0, LOG_MAIN, + " => client disconnected cleanly (rejected our certificate?)\n"); return FAIL; } @@ -709,6 +741,7 @@ receive_getc = tls_getc; receive_ungetc = tls_ungetc; receive_feof = tls_feof; receive_ferror = tls_ferror; +receive_smtp_buffered = tls_smtp_buffered; tls_active = fileno(smtp_out); return OK; @@ -727,12 +760,19 @@ return OK; Argument: fd the fd of the connection host connected host (for messages) + addr the first address dhparam DH parameter file certificate certificate file privatekey private key file verify_certs file for certificate verify crl file containing CRL require_ciphers list of allowed ciphers + ------------------------------------------------------ + require_mac list of allowed MACs ) Not used + require_kx list of allowed key_exchange methods ) for + require_proto list of allowed protocols ) OpenSSL + ------------------------------------------------------ + timeout startup timeout Returns: OK on success FAIL otherwise - note that tls_error() will not give DEFER @@ -742,7 +782,8 @@ Returns: OK on success int tls_client_start(int fd, host_item *host, address_item *addr, uschar *dhparam, uschar *certificate, uschar *privatekey, uschar *verify_certs, uschar *crl, - uschar *require_ciphers, int timeout) + uschar *require_ciphers, uschar *require_mac, uschar *require_kx, + uschar *require_proto, int timeout) { static uschar txt[256]; uschar *expciphers; @@ -768,13 +809,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); @@ -788,15 +829,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"); @@ -854,6 +887,7 @@ if (ssl_xfer_buffer_lwm >= ssl_xfer_buffer_hwm) receive_ungetc = smtp_ungetc; receive_feof = smtp_feof; receive_ferror = smtp_ferror; + receive_smtp_buffered = smtp_buffered; SSL_free(ssl); ssl = NULL; @@ -872,7 +906,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; } @@ -1008,4 +1044,280 @@ 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; +} + + + + +/************************************************* +* OpenSSL option parse * +*************************************************/ + +/* Parse one option for tls_openssl_options_parse below + +Arguments: + name one option name + value place to store a value for it +Returns success or failure in parsing +*/ + +struct exim_openssl_option { + uschar *name; + long value; +}; +/* We could use a macro to expand, but we need the ifdef and not all the +options document which version they were introduced in. Policylet: include +all options unless explicitly for DTLS, let the administrator choose which +to apply. + +This list is current as of: + ==> 0.9.8n <== */ +static struct exim_openssl_option exim_openssl_options[] = { +/* KEEP SORTED ALPHABETICALLY! */ +#ifdef SSL_OP_ALL + { "all", SSL_OP_ALL }, +#endif +#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION + { "allow_unsafe_legacy_renegotiation", SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION }, +#endif +#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE + { "cipher_server_preference", SSL_OP_CIPHER_SERVER_PREFERENCE }, +#endif +#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS + { "dont_insert_empty_fragments", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS }, +#endif +#ifdef SSL_OP_EPHEMERAL_RSA + { "ephemeral_rsa", SSL_OP_EPHEMERAL_RSA }, +#endif +#ifdef SSL_OP_LEGACY_SERVER_CONNECT + { "legacy_server_connect", SSL_OP_LEGACY_SERVER_CONNECT }, +#endif +#ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER + { "microsoft_big_sslv3_buffer", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER }, +#endif +#ifdef SSL_OP_MICROSOFT_SESS_ID_BUG + { "microsoft_sess_id_bug", SSL_OP_MICROSOFT_SESS_ID_BUG }, +#endif +#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING + { "msie_sslv2_rsa_padding", SSL_OP_MSIE_SSLV2_RSA_PADDING }, +#endif +#ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG + { "netscape_challenge_bug", SSL_OP_NETSCAPE_CHALLENGE_BUG }, +#endif +#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG + { "netscape_reuse_cipher_change_bug", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG }, +#endif +#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION + { "no_session_resumption_on_renegotiation", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION }, +#endif +#ifdef SSL_OP_SINGLE_DH_USE + { "single_dh_use", SSL_OP_SINGLE_DH_USE }, +#endif +#ifdef SSL_OP_SINGLE_ECDH_USE + { "single_ecdh_use", SSL_OP_SINGLE_ECDH_USE }, +#endif +#ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG + { "ssleay_080_client_dh_bug", SSL_OP_SSLEAY_080_CLIENT_DH_BUG }, +#endif +#ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG + { "sslref2_reuse_cert_type_bug", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG }, +#endif +#ifdef SSL_OP_TLS_BLOCK_PADDING_BUG + { "tls_block_padding_bug", SSL_OP_TLS_BLOCK_PADDING_BUG }, +#endif +#ifdef SSL_OP_TLS_D5_BUG + { "tls_d5_bug", SSL_OP_TLS_D5_BUG }, +#endif +#ifdef SSL_OP_TLS_ROLLBACK_BUG + { "tls_rollback_bug", SSL_OP_TLS_ROLLBACK_BUG }, +#endif +}; +static int exim_openssl_options_size = + sizeof(exim_openssl_options)/sizeof(struct exim_openssl_option); + +static BOOL +tls_openssl_one_option_parse(uschar *name, long *value) +{ +int first = 0; +int last = exim_openssl_options_size; +while (last > first) + { + int middle = (first + last)/2; + int c = Ustrcmp(name, exim_openssl_options[middle].name); + if (c == 0) + { + *value = exim_openssl_options[middle].value; + return TRUE; + } + else if (c > 0) + first = middle + 1; + else + last = middle; + } +return FALSE; +} + + + + +/************************************************* +* OpenSSL option parsing logic * +*************************************************/ + +/* OpenSSL has a number of compatibility options which an administrator might +reasonably wish to set. Interpret a list similarly to decode_bits(), so that +we look like log_selector. + +Arguments: + option_spec the administrator-supplied string of options + results ptr to long storage for the options bitmap +Returns success or failure +*/ + +BOOL +tls_openssl_options_parse(uschar *option_spec, long *results) +{ +long result, item; +uschar *s, *end; +uschar keep_c; +BOOL adding, item_parsed; + +/* We grandfather in as default the one option which we used to set always. */ +#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS +result = SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; +#else +result = 0L; +#endif + +if (option_spec == NULL) + { + *results = result; + return TRUE; + } + +for (s=option_spec; *s != '\0'; /**/) + { + while (isspace(*s)) ++s; + if (*s == '\0') + break; + if (*s != '+' && *s != '-') + { + DEBUG(D_tls) debug_printf("malformed openssl option setting: " + "+ or - expected but found \"%s\"", s); + return FALSE; + } + adding = *s++ == '+'; + for (end = s; (*end != '\0') && !isspace(*end); ++end) /**/ ; + keep_c = *end; + *end = '\0'; + item_parsed = tls_openssl_one_option_parse(s, &item); + if (!item_parsed) + { + DEBUG(D_tls) debug_printf("openssl option setting unrecognised: \"%s\"", s); + return FALSE; + } + DEBUG(D_tls) debug_printf("openssl option, %s from %lx: %lx (%s)\n", + adding ? "adding" : "removing", result, item, s); + if (adding) + result |= item; + else + result &= ~item; + *end = keep_c; + s = end; + } + +*results = result; +return TRUE; +} + /* End of tls-openssl.c */