X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Ftls-openssl.c;h=7e3cc3f78e053355603716b8f2e57973b2793930;hb=8cb1a4f75c3d1acaed66495ccbd7820694ee20b0;hp=057a0e006cdb3ac1cc8a0172d03b7ad93109e0e7;hpb=5b2fd993eadb7b476e5ef14028c7db09fda6c3ae;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 057a0e006..7e3cc3f78 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -46,6 +46,7 @@ functions from the OpenSSL library. */ #endif #if OPENSSL_VERSION_NUMBER >= 0x10100000L # define EXIM_HAVE_OCSP_RESP_COUNT +# define OPENSSL_AUTO_SHA256 #else # define EXIM_HAVE_EPHEM_RSA_KEX # define EXIM_HAVE_RAND_PSEUDO @@ -74,6 +75,7 @@ change this guard and punt the issue for a while longer. */ # define EXIM_HAVE_OPENSSL_CIPHER_GET_ID # define EXIM_HAVE_SESSION_TICKET # define EXIM_HAVE_OPESSL_TRACE +# define EXIM_HAVE_OPESSL_GET0_SERIAL # else # define EXIM_NEED_OPENSSL_INIT # endif @@ -1714,6 +1716,7 @@ tls_in.ocsp = OCSP_NOT_RESP; if (!olist) return SSL_TLSEXT_ERR_NOACK; +#ifdef EXIM_HAVE_OPESSL_GET0_SERIAL { const X509 * cert_sent = SSL_get_certificate(s); const ASN1_INTEGER * cert_serial = X509_get0_serialNumber(cert_sent); @@ -1761,6 +1764,13 @@ if (!olist) return SSL_TLSEXT_ERR_NOACK; } } +#else +if (olist->next) + { + DEBUG(D_tls) debug_printf("OpenSSL version too early to support multi-leaf OCSP\n"); + return SSL_TLSEXT_ERR_NOACK; + } +#endif /*XXX could we do the i2d earlier, rather than during the callback? */ response_der = NULL; @@ -1936,6 +1946,23 @@ return i; * Initialize for TLS * *************************************************/ +static void +tls_openssl_init(void) +{ +#ifdef EXIM_NEED_OPENSSL_INIT +SSL_load_error_strings(); /* basic set up */ +OpenSSL_add_ssl_algorithms(); +#endif + +#if defined(EXIM_HAVE_SHA256) && !defined(OPENSSL_AUTO_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 +} + + + /* Called from both server and client code, to do preliminary initialization of the library. We allocate and return a context structure. @@ -1991,16 +2018,7 @@ cbinfo->host = host; cbinfo->event_action = NULL; #endif -#ifdef EXIM_NEED_OPENSSL_INIT -SSL_load_error_strings(); /* basic set up */ -OpenSSL_add_ssl_algorithms(); -#endif - -#ifdef EXIM_HAVE_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 +tls_openssl_init(); /* Create a context. The OpenSSL docs in 1.0.1b have not been updated to clarify TLS variant @@ -2194,14 +2212,13 @@ Returns: pointer to allocated string in perm-pool */ static uschar * -construct_cipher_name(SSL * ssl, int * bits) +construct_cipher_name(SSL * ssl, const uschar * ver, int * bits) { int pool = store_pool; /* With OpenSSL 1.0.0a, 'c' needs to be const but the documentation doesn't yet reflect that. It should be a safe change anyway, even 0.9.8 versions have the accessor functions use const in the prototype. */ -const uschar * ver = CUS SSL_get_version(ssl); const SSL_CIPHER * c = (const SSL_CIPHER *) SSL_get_current_cipher(ssl); uschar * s; @@ -2232,6 +2249,21 @@ return cipher_stdname(id >> 8, id & 0xff); } +static const uschar * +tlsver_name(SSL * ssl) +{ +uschar * s, * p; +int pool = store_pool; + +store_pool = POOL_PERM; +s = string_copy(US SSL_get_version(ssl)); +store_pool = pool; +if ((p = Ustrchr(s, 'v'))) /* TLSv1.2 -> TLS1.2 */ + for (;; p++) if (!(*p = p[1])) break; +return CUS s; +} + + static void peer_cert(SSL * ssl, tls_support * tlsp, uschar * peerdn, unsigned siz) { @@ -2621,8 +2653,41 @@ ALARM_CLR(0); if (rc <= 0) { - (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr); - return FAIL; + int error = SSL_get_error(server_ssl, rc); + switch(error) + { + case SSL_ERROR_NONE: + break; + + case SSL_ERROR_ZERO_RETURN: + DEBUG(D_tls) debug_printf("Got SSL_ERROR_ZERO_RETURN\n"); + (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr); + + if (SSL_get_shutdown(server_ssl) == SSL_RECEIVED_SHUTDOWN) + SSL_shutdown(server_ssl); + + tls_close(NULL, TLS_NO_SHUTDOWN); + return FAIL; + + /* Handle genuine errors */ + case SSL_ERROR_SSL: + (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr); + return FAIL; + + default: + DEBUG(D_tls) debug_printf("Got SSL error %d\n", error); + if (error == SSL_ERROR_SYSCALL) + { + if (!errno) + { + *errstr = US"SSL_accept: TCP connection closed by peer"; + return FAIL; + } + DEBUG(D_tls) debug_printf(" - syscall %s\n", strerror(errno)); + } + (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr); + return FAIL; + } } DEBUG(D_tls) debug_printf("SSL_accept was successful\n"); @@ -2637,12 +2702,13 @@ if (SSL_session_reused(server_ssl)) } #endif -/* TLS has been set up. Adjust the input functions to read via TLS, -and initialize things. */ +/* TLS has been set up. Record data for the connection, +adjust the input functions to read via TLS, and initialize things. */ peer_cert(server_ssl, &tls_in, peerdn, sizeof(peerdn)); -tls_in.cipher = construct_cipher_name(server_ssl, &tls_in.bits); +tls_in.ver = tlsver_name(server_ssl); +tls_in.cipher = construct_cipher_name(server_ssl, tls_in.ver, &tls_in.bits); tls_in.cipher_stdname = cipher_stdname_ssl(server_ssl); DEBUG(D_tls) @@ -2675,6 +2741,20 @@ DEBUG(D_tls) tls_in.ourcert = crt ? X509_dup(crt) : NULL; } +/* Channel-binding info for authenticators +See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/ */ + { + uschar c, * s; + size_t len = SSL_get_peer_finished(server_ssl, &c, 0); + int old_pool = store_pool; + + SSL_get_peer_finished(server_ssl, s = store_get((int)len, FALSE), len); + store_pool = POOL_PERM; + tls_in.channelbinding = b64encode_taint(CUS s, (int)len, FALSE); + store_pool = old_pool; + DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n"); + } + /* Only used by the server-side tls (tls_in), including tls_getc. Client-side (tls_out) reads (seem to?) go via smtp_read_response()/ip_recv(). @@ -3227,7 +3307,8 @@ tls_client_resume_posthandshake(exim_client_ctx, tlsp); peer_cert(exim_client_ctx->ssl, tlsp, peerdn, sizeof(peerdn)); -tlsp->cipher = construct_cipher_name(exim_client_ctx->ssl, &tlsp->bits); +tlsp->ver = tlsver_name(exim_client_ctx->ssl); +tlsp->cipher = construct_cipher_name(exim_client_ctx->ssl, tlsp->ver, &tlsp->bits); tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl); /* Record the certificate we presented */ @@ -3236,6 +3317,20 @@ tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl); tlsp->ourcert = crt ? X509_dup(crt) : NULL; } +/*XXX will this work with continued-TLS? */ +/* Channel-binding info for authenticators */ + { + uschar c, * s; + size_t len = SSL_get_finished(exim_client_ctx->ssl, &c, 0); + int old_pool = store_pool; + + SSL_get_finished(exim_client_ctx->ssl, s = store_get((int)len, TRUE), len); + store_pool = POOL_PERM; + tlsp->channelbinding = b64encode_taint(CUS s, (int)len, TRUE); + store_pool = old_pool; + DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n"); + } + tlsp->active.sock = cctx->sock; tlsp->active.tls_ctx = exim_client_ctx; cctx->tls_ctx = exim_client_ctx; @@ -3436,11 +3531,12 @@ Arguments: Returns: the number of bytes after a successful write, -1 after a failed write -Used by both server-side and client-side TLS. +Used by both server-side and client-side TLS. Calling with len zero and more unset +will flush buffered writes; buff can be null for this case. */ int -tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more) +tls_write(void * ct_ctx, const uschar * buff, size_t len, BOOL more) { size_t olen = len; int outbytes, error; @@ -3466,14 +3562,16 @@ a store reset there, so use POOL_PERM. */ if ((more || corked)) { -#ifdef SUPPORT_PIPE_CONNECT + if (!len) buff = US &error; /* dummy just so that string_catn is ok */ + +#ifndef DISABLE_PIPE_CONNECT int save_pool = store_pool; store_pool = POOL_PERM; #endif corked = string_catn(corked, buff, len); -#ifdef SUPPORT_PIPE_CONNECT +#ifndef DISABLE_PIPE_CONNECT store_pool = save_pool; #endif @@ -3495,16 +3593,16 @@ for (int left = len; left > 0;) DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error); switch (error) { + case SSL_ERROR_NONE: /* the usual case */ + left -= outbytes; + buff += outbytes; + break; + case SSL_ERROR_SSL: ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring)); log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring); return -1; - case SSL_ERROR_NONE: - left -= outbytes; - buff += outbytes; - break; - case SSL_ERROR_ZERO_RETURN: log_write(0, LOG_MAIN, "SSL channel closed on write"); return -1; @@ -3619,18 +3717,7 @@ tls_validate_require_cipher(void) SSL_CTX *ctx; uschar *s, *expciphers, *err; -/* this duplicates from tls_init(), we need a better "init just global -state, for no specific purpose" singleton function of our own */ - -#ifdef EXIM_NEED_OPENSSL_INIT -SSL_load_error_strings(); -OpenSSL_add_ssl_algorithms(); -#endif -#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 +tls_openssl_init(); if (!(tls_require_ciphers && *tls_require_ciphers)) return NULL;