OpenSSL: Debug output TLS 1.3 keying
[exim.git] / src / src / tls-openssl.c
index cda31f140626efed4c6def4f9b141569c62259fe..692022063a8f7889c82ab1f0440964f2575890a4 100644 (file)
@@ -54,23 +54,24 @@ functions from the OpenSSL library. */
 # define EXIM_HAVE_SHA256
 #endif
 
-/*
- * X509_check_host provides sane certificate hostname checking, but was added
- * to OpenSSL late, after other projects forked off the code-base.  So in
- * addition to guarding against the base version number, beware that LibreSSL
- * does not (at this time) support this function.
- *
- * If LibreSSL gains a different API, perhaps via libtls, then we'll probably
- * opt to disentangle and ask a LibreSSL user to provide glue for a third
- * crypto provider for libtls instead of continuing to tie the OpenSSL glue
- * into even twistier knots.  If LibreSSL gains the same API, we can just
- * change this guard and punt the issue for a while longer.
- */
+/* X509_check_host provides sane certificate hostname checking, but was added
+to OpenSSL late, after other projects forked off the code-base.  So in
+addition to guarding against the base version number, beware that LibreSSL
+does not (at this time) support this function.
+
+If LibreSSL gains a different API, perhaps via libtls, then we'll probably
+opt to disentangle and ask a LibreSSL user to provide glue for a third
+crypto provider for libtls instead of continuing to tie the OpenSSL glue
+into even twistier knots.  If LibreSSL gains the same API, we can just
+change this guard and punt the issue for a while longer. */
+
 #ifndef LIBRESSL_VERSION_NUMBER
 # if OPENSSL_VERSION_NUMBER >= 0x010100000L
 #  define EXIM_HAVE_OPENSSL_CHECKHOST
 #  define EXIM_HAVE_OPENSSL_DH_BITS
 #  define EXIM_HAVE_OPENSSL_TLS_METHOD
+# else
+#  define EXIM_NEED_OPENSSL_INIT
 # endif
 # if OPENSSL_VERSION_NUMBER >= 0x010000000L \
     && (OPENSSL_VERSION_NUMBER & 0x0000ff000L) >= 0x000002000L
@@ -90,6 +91,12 @@ functions from the OpenSSL library. */
 # endif
 #endif
 
+#ifndef LIBRESSL_VERSION_NUMBER
+# if OPENSSL_VERSION_NUMBER >= 0x010101000L
+#  define OPENSSL_HAVE_KEYLOG_CB
+# endif
+#endif
+
 #if !defined(EXIM_HAVE_OPENSSL_TLSEXT) && !defined(DISABLE_OCSP)
 # warning "OpenSSL library version too old; define DISABLE_OCSP in Makefile"
 # define DISABLE_OCSP
@@ -218,10 +225,9 @@ static int exim_openssl_options_size = nelem(exim_openssl_options);
 void
 options_tls(void)
 {
-struct exim_openssl_option * o;
 uschar buf[64];
 
-for (o = exim_openssl_options;
+for (struct exim_openssl_option * o = exim_openssl_options;
      o < exim_openssl_options + nelem(exim_openssl_options); o++)
   {
   /* Trailing X is workaround for problem with _OPT_OPENSSL_NO_TLSV1
@@ -368,7 +374,9 @@ if (!msg)
   msg = US ssl_errstring;
   }
 
-if (errstr) *errstr = string_sprintf("(%s): %s", prefix, msg);
+msg = string_sprintf("(%s): %s", prefix, msg);
+DEBUG(D_tls) debug_printf("TLS error '%s'\n", msg);
+if (errstr) *errstr = msg;
 return host ? FAIL : DEFER;
 }
 
@@ -424,10 +432,9 @@ void
 x509_store_dump_cert_s_names(X509_STORE * store)
 {
 STACK_OF(X509_OBJECT) * roots= store->objs;
-int i;
 static uschar name[256];
 
-for(i= 0; i<sk_X509_OBJECT_num(roots); i++)
+for (int i= 0; i < sk_X509_OBJECT_num(roots); i++)
   {
   X509_OBJECT * tmp_obj= sk_X509_OBJECT_value(roots, i);
   if(tmp_obj->type == X509_LU_X509)
@@ -749,17 +756,17 @@ DEBUG(D_tls)
   const uschar * str;
 
   if (where & SSL_ST_CONNECT)
-     str = "SSL_connect";
+     str = US"SSL_connect";
   else if (where & SSL_ST_ACCEPT)
-     str = "SSL_accept";
+     str = US"SSL_accept";
   else
-     str = "SSL info (undefined)";
+     str = US"SSL info (undefined)";
 
   if (where & SSL_CB_LOOP)
      debug_printf("%s: %s\n", str, SSL_state_string_long(s));
   else if (where & SSL_CB_ALERT)
     debug_printf("SSL3 alert %s:%s:%s\n",
-         str = where & SSL_CB_READ ? "read" : "write",
+         str = where & SSL_CB_READ ? US"read" : US"write",
          SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
   else if (where & SSL_CB_EXIT)
      if (ret == 0)
@@ -773,6 +780,12 @@ DEBUG(D_tls)
   }
 }
 
+static void
+keylog_callback(const SSL *ssl, const char *line)
+{
+DEBUG(D_tls) debug_printf("%.200s\n", line);
+}
+
 
 
 /*************************************************
@@ -1137,8 +1150,7 @@ bad:
   if (f.running_in_test_harness)
     {
     extern char ** environ;
-    uschar ** p;
-    if (environ) for (p = USS environ; *p; p++)
+    if (environ) for (uschar ** p = USS environ; *p; p++)
       if (Ustrncmp(*p, "EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK", 42) == 0)
        {
        DEBUG(D_tls) debug_printf("Supplying known bad OCSP response\n");
@@ -1172,7 +1184,7 @@ if (!(x509 = X509_new()))
   goto err;
 
 where = US"generating pkey";
-if (!(rsa = rsa_callback(NULL, 0, 1024)))
+if (!(rsa = rsa_callback(NULL, 0, 2048)))
   goto err;
 
 where = US"assigning pkey";
@@ -1300,8 +1312,8 @@ else
       if ((err = tls_add_certfile(sctx, cbinfo, expanded, errstr)))
        return err;
 
-  if (cbinfo->privatekey != NULL &&
-      !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded, errstr))
+  if (  cbinfo->privatekey
+     && !expand_check(cbinfo->privatekey, US"tls_privatekey", &expanded, errstr))
     return DEFER;
 
   /* If expansion was forced to fail, key_expanded will be NULL. If the result
@@ -1404,7 +1416,7 @@ if (!(server_sni = SSL_CTX_new(SSLv23_server_method())))
   {
   ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
   DEBUG(D_tls) debug_printf("SSL_CTX_new() failed: %s\n", ssl_errstring);
-  return SSL_TLSEXT_ERR_NOACK;
+  goto bad;
   }
 
 /* Not sure how many of these are actually needed, since SSL object
@@ -1420,11 +1432,11 @@ SSL_CTX_set_tlsext_servername_arg(server_sni, cbinfo);
 if (  !init_dh(server_sni, cbinfo->dhparam, NULL, &dummy_errstr)
    || !init_ecdh(server_sni, NULL, &dummy_errstr)
    )
-  return SSL_TLSEXT_ERR_NOACK;
+  goto bad;
 
 if (  cbinfo->server_cipher_list
    && !SSL_CTX_set_cipher_list(server_sni, CS cbinfo->server_cipher_list))
-  return SSL_TLSEXT_ERR_NOACK;
+  goto bad;
 
 #ifndef DISABLE_OCSP
 if (cbinfo->u_ocsp.server.file)
@@ -1436,17 +1448,18 @@ if (cbinfo->u_ocsp.server.file)
 
 if ((rc = setup_certs(server_sni, tls_verify_certificates, tls_crl, NULL, FALSE,
                      verify_callback_server, &dummy_errstr)) != OK)
-  return SSL_TLSEXT_ERR_NOACK;
+  goto bad;
 
 /* do this after setup_certs, because this can require the certs for verifying
 OCSP information. */
 if ((rc = tls_expand_session_files(server_sni, cbinfo, &dummy_errstr)) != OK)
-  return SSL_TLSEXT_ERR_NOACK;
+  goto bad;
 
 DEBUG(D_tls) debug_printf("Switching SSL context.\n");
 SSL_set_SSL_CTX(s, server_sni);
-
 return SSL_TLSEXT_ERR_OK;
+
+bad: return SSL_TLSEXT_ERR_ALERT_FATAL;
 }
 #endif /* EXIM_HAVE_OPENSSL_TLSEXT */
 
@@ -1714,8 +1727,10 @@ 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
@@ -1765,6 +1780,9 @@ if (!RAND_status())
 level. */
 
 DEBUG(D_tls) SSL_CTX_set_info_callback(ctx, (void (*)())info_callback);
+#ifdef OPENSSL_HAVE_KEYLOG_CB
+DEBUG(D_tls) SSL_CTX_set_keylog_callback(ctx, (void (*)())keylog_callback);
+#endif
 
 /* Automatically re-try reads/writes after renegotiation. */
 (void) SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
@@ -2045,14 +2063,13 @@ if (expcerts && *expcerts)
       /* Load the list of CAs for which we will accept certs, for sending
       to the client.  This is only for the one-file tls_verify_certificates
       variant.
-      If a list isn't loaded into the server, but
-      some verify locations are set, the server end appears to make
-      a wildcard request for client certs.
+      If a list isn't loaded into the server, but some verify locations are set,
+      the server end appears to make a wildcard request for client certs.
       Meanwhile, the client library as default behaviour *ignores* the list
       we send over the wire - see man SSL_CTX_set_client_cert_cb.
       Because of this, and that the dir variant is likely only used for
-      the public-CA bundle (not for a private CA), not worth fixing.
-      */
+      the public-CA bundle (not for a private CA), not worth fixing.  */
+
       if (file)
        {
        STACK_OF(X509_NAME) * names = SSL_load_client_CA_file(CS file);
@@ -2120,7 +2137,7 @@ if (expcerts && *expcerts)
   /* If verification is optional, don't fail if no certificate */
 
   SSL_CTX_set_verify(sctx,
-    SSL_VERIFY_PEER | (optional? 0 : SSL_VERIFY_FAIL_IF_NO_PEER_CERT),
+    SSL_VERIFY_PEER | (optional ? 0 : SSL_VERIFY_FAIL_IF_NO_PEER_CERT),
     cert_vfy_cb);
   }
 
@@ -2263,9 +2280,9 @@ SSL_set_accept_state(server_ssl);
 DEBUG(D_tls) debug_printf("Calling SSL_accept\n");
 
 sigalrm_seen = FALSE;
-if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
+if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
 rc = SSL_accept(server_ssl);
-alarm(0);
+ALARM_CLR(0);
 
 if (rc <= 0)
   {
@@ -2366,7 +2383,6 @@ return OK;
 static int
 dane_tlsa_load(SSL * ssl, host_item * host, dns_answer * dnsa, uschar ** errstr)
 {
-dns_record * rr;
 dns_scan dnss;
 const char * hostnames[2] = { CS host->name, NULL };
 int found = 0;
@@ -2374,8 +2390,7 @@ int found = 0;
 if (DANESSL_init(ssl, NULL, hostnames) != 1)
   return tls_error(US"hostnames load", host, NULL, errstr);
 
-for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS);
-     rr;
+for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
      rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)
     ) if (rr->type == T_TLSA && rr->size > 3)
   {
@@ -2643,9 +2658,9 @@ client_static_cbinfo->event_action = tb ? tb->event_action : NULL;
 
 DEBUG(D_tls) debug_printf("Calling SSL_connect\n");
 sigalrm_seen = FALSE;
-alarm(ob->command_timeout);
+ALARM(ob->command_timeout);
 rc = SSL_connect(exim_client_ctx->ssl);
-alarm(0);
+ALARM_CLR(0);
 
 #ifdef SUPPORT_DANE
 if (tlsa_dnsa)
@@ -2689,11 +2704,11 @@ int inbytes;
 DEBUG(D_tls) debug_printf("Calling SSL_read(%p, %p, %u)\n", server_ssl,
   ssl_xfer_buffer, ssl_xfer_buffer_size);
 
-if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
+if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
 inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer,
                  MIN(ssl_xfer_buffer_size, lim));
 error = SSL_get_error(server_ssl, inbytes);
-if (smtp_receive_timeout > 0) alarm(0);
+if (smtp_receive_timeout > 0) ALARM_CLR(0);
 
 if (had_command_timeout)               /* set by signal handler */
   smtp_command_timeout_exit();         /* does not return */
@@ -2898,7 +2913,8 @@ Used by both server-side and client-side TLS.
 int
 tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more)
 {
-int outbytes, error, left;
+size_t olen = len;
+int outbytes, error;
 SSL * ssl = ct_ctx ? ((exim_openssl_client_tls_ctx *)ct_ctx)->ssl : server_ssl;
 static gstring * corked = NULL;
 
@@ -2909,10 +2925,23 @@ DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__,
 "more" is notified.  This hack is only ok if small amounts are involved AND only
 one stream does it, in one context (i.e. no store reset).  Currently it is used
 for the responses to the received SMTP MAIL , RCPT, DATA sequence, only. */
+/* + if PIPE_COMMAND, banner & ehlo-resp for smmtp-on-connect. Suspect there's
+a store reset there, so use POOL_PERM. */
+/* + if CHUNKING, cmds EHLO,MAIL,RCPT(s),BDAT */
 
-if (!ct_ctx && (more || corked))
+if ((more || corked))
   {
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  int save_pool = store_pool;
+  store_pool = POOL_PERM;
+#endif
+
   corked = string_catn(corked, buff, len);
+
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  store_pool = save_pool;
+#endif
+
   if (more)
     return len;
   buff = CUS corked->s;
@@ -2920,7 +2949,7 @@ if (!ct_ctx && (more || corked))
   corked = NULL;
   }
 
-for (left = len; left > 0;)
+for (int left = len; left > 0;)
   {
   DEBUG(D_tls) debug_printf("SSL_write(%p, %p, %d)\n", ssl, buff, left);
   outbytes = SSL_write(ssl, CS buff, left);
@@ -2953,7 +2982,7 @@ for (left = len; left > 0;)
       return -1;
     }
   }
-return len;
+return olen;
 }
 
 
@@ -2995,9 +3024,9 @@ if (shutdown)
   if (  (rc = SSL_shutdown(*sslp)) == 0        /* send "close notify" alert */
      && shutdown > 1)
     {
-    alarm(2);
+    ALARM(2);
     rc = SSL_shutdown(*sslp);          /* wait for response */
-    alarm(0);
+    ALARM_CLR(0);
     }
 
   if (rc < 0) DEBUG(D_tls)
@@ -3044,8 +3073,10 @@ 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. */
@@ -3153,7 +3184,6 @@ unsigned int r;
 int i, needed_len;
 static pid_t pidlast = 0;
 pid_t pidnow;
-uschar *p;
 uschar smallbuf[sizeof(r)];
 
 if (max <= 1)
@@ -3211,11 +3241,8 @@ if (i < 0)
   }
 
 r = 0;
-for (p = smallbuf; needed_len; --needed_len, ++p)
-  {
-  r *= 256;
-  r += *p;
-  }
+for (uschar * 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. */
@@ -3282,7 +3309,7 @@ BOOL
 tls_openssl_options_parse(uschar *option_spec, long *results)
 {
 long result, item;
-uschar *s, *end;
+uschar *end;
 uschar keep_c;
 BOOL adding, item_parsed;
 
@@ -3302,7 +3329,7 @@ if (!option_spec)
   return TRUE;
   }
 
-for (s=option_spec; *s != '\0'; /**/)
+for (uschar * s = option_spec; *s != '\0'; /**/)
   {
   while (isspace(*s)) ++s;
   if (*s == '\0')