TLS resumption: restrict session re-use
[exim.git] / src / src / tls-openssl.c
index a16e58791a751df9aeee33d499c5edee60422992..bab02d0560b23868c53906b16f775ba3dbf559c8 100644 (file)
@@ -1446,7 +1446,7 @@ supply_response:
   ocsp_resplist ** op = &state->u_ocsp.server.olist, * oentry;
   while (oentry = *op)
     op = &oentry->next;
   ocsp_resplist ** op = &state->u_ocsp.server.olist, * oentry;
   while (oentry = *op)
     op = &oentry->next;
-  *op = oentry = store_get(sizeof(ocsp_resplist), FALSE);
+  *op = oentry = store_get(sizeof(ocsp_resplist), GET_UNTAINTED);
   oentry->next = NULL;
   oentry->resp = resp;
   }
   oentry->next = NULL;
   oentry->resp = resp;
   }
@@ -2174,7 +2174,7 @@ DEBUG(D_tls) debug_printf("Received TLS SNI \"%s\"%s\n", servername,
 
 /* Make the extension value available for expansion */
 store_pool = POOL_PERM;
 
 /* Make the extension value available for expansion */
 store_pool = POOL_PERM;
-tls_in.sni = string_copy_taint(US servername, TRUE);
+tls_in.sni = string_copy_taint(US servername, GET_TAINTED);
 store_pool = old_pool;
 
 if (!reexpand_tls_files_for_sni)
 store_pool = old_pool;
 
 if (!reexpand_tls_files_for_sni)
@@ -2417,8 +2417,12 @@ int i;
 DEBUG(D_tls) debug_printf("Received TLS status callback (OCSP stapling):\n");
 len = SSL_get_tlsext_status_ocsp_resp(s, &p);
 if(!p)
 DEBUG(D_tls) debug_printf("Received TLS status callback (OCSP stapling):\n");
 len = SSL_get_tlsext_status_ocsp_resp(s, &p);
 if(!p)
- {
-  /* Expect this when we requested ocsp but got none */
+ {                             /* Expect this when we requested ocsp but got none */
+  if (SSL_session_reused(s) && tls_out.ocsp == OCSP_VFIED)
+    {
+    DEBUG(D_tls) debug_printf(" null, but resumed; ocsp vfy stored with session is good\n");
+    return 1;
+    }
   if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher))
     log_write(0, LOG_MAIN, "Required TLS certificate status not received");
   else
   if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher))
     log_write(0, LOG_MAIN, "Required TLS certificate status not received");
   else
@@ -3464,9 +3468,9 @@ See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/ */
   size_t len = SSL_get_peer_finished(ssl, &c, 0);
   int old_pool = store_pool;
 
   size_t len = SSL_get_peer_finished(ssl, &c, 0);
   int old_pool = store_pool;
 
-  SSL_get_peer_finished(ssl, s = store_get((int)len, FALSE), len);
+  SSL_get_peer_finished(ssl, s = store_get((int)len, GET_UNTAINTED), len);
   store_pool = POOL_PERM;
   store_pool = POOL_PERM;
-    tls_in.channelbinding = b64encode_taint(CUS s, (int)len, FALSE);
+    tls_in.channelbinding = b64encode_taint(CUS s, (int)len, GET_UNTAINTED);
   store_pool = old_pool;
   DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p\n", tls_in.channelbinding);
   }
   store_pool = old_pool;
   DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p\n", tls_in.channelbinding);
   }
@@ -3616,21 +3620,21 @@ return DEFER;
 and apply it to the ssl-connection for attempted resumption. */
 
 static void
 and apply it to the ssl-connection for attempted resumption. */
 
 static void
-tls_retrieve_session(tls_support * tlsp, SSL * ssl, const uschar * key)
+tls_retrieve_session(tls_support * tlsp, SSL * ssl)
 {
 {
-tlsp->resumption |= RESUME_SUPPORTED;
 if (tlsp->host_resumable)
   {
 if (tlsp->host_resumable)
   {
+  const uschar * key = tlsp->resume_index;
   dbdata_tls_session * dt;
   int len;
   open_db dbblock, * dbm_file;
 
   tlsp->resumption |= RESUME_CLIENT_REQUESTED;
   dbdata_tls_session * dt;
   int len;
   open_db dbblock, * dbm_file;
 
   tlsp->resumption |= RESUME_CLIENT_REQUESTED;
-  DEBUG(D_tls) debug_printf("checking for resumable session for %s\n", key);
+  DEBUG(D_tls)
+    debug_printf("checking for resumable session for %s\n", tlsp->resume_index);
   if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
   if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
-    /* key for the db is the IP */
-    if ((dt = dbfn_read_with_length(dbm_file, key, &len)))
+    if ((dt = dbfn_read_with_length(dbm_file, tlsp->resume_index, &len)))
       {
       SSL_SESSION * ss = NULL;
       const uschar * sess_asn1 = dt->session;
       {
       SSL_SESSION * ss = NULL;
       const uschar * sess_asn1 = dt->session;
@@ -3656,24 +3660,21 @@ if (tlsp->host_resumable)
        if (lifetime + dt->time_stamp < time(NULL))
          {
          DEBUG(D_tls) debug_printf("session expired\n");
        if (lifetime + dt->time_stamp < time(NULL))
          {
          DEBUG(D_tls) debug_printf("session expired\n");
-         dbfn_delete(dbm_file, key);
-         }
-       else if (!SSL_set_session(ssl, ss))
-         {
-         DEBUG(D_tls)
-           {
-           ERR_error_string_n(ERR_get_error(),
-             ssl_errstring, sizeof(ssl_errstring));
-           debug_printf("applying session to ssl: %s\n", ssl_errstring);
-           }
+         dbfn_delete(dbm_file, tlsp->resume_index);
          }
          }
-       else
+       else if (SSL_set_session(ssl, ss))
          {
          DEBUG(D_tls) debug_printf("good session\n");
          tlsp->resumption |= RESUME_CLIENT_SUGGESTED;
          tlsp->verify_override = dt->verify_override;
          tlsp->ocsp = dt->ocsp;
          }
          {
          DEBUG(D_tls) debug_printf("good session\n");
          tlsp->resumption |= RESUME_CLIENT_SUGGESTED;
          tlsp->verify_override = dt->verify_override;
          tlsp->ocsp = dt->ocsp;
          }
+       else DEBUG(D_tls)
+         {
+         ERR_error_string_n(ERR_get_error(),
+           ssl_errstring, sizeof(ssl_errstring));
+         debug_printf("applying session to ssl: %s\n", ssl_errstring);
+         }
        }
       }
     else
        }
       }
     else
@@ -3702,7 +3703,7 @@ if (SSL_SESSION_is_resumable(ss))         /* 1.1.1 */
   {
   int len = i2d_SSL_SESSION(ss, NULL);
   int dlen = sizeof(dbdata_tls_session) + len;
   {
   int len = i2d_SSL_SESSION(ss, NULL);
   int dlen = sizeof(dbdata_tls_session) + len;
-  dbdata_tls_session * dt = store_get(dlen, TRUE);
+  dbdata_tls_session * dt = store_get(dlen, GET_TAINTED);
   uschar * s = dt->session;
   open_db dbblock, * dbm_file;
 
   uschar * s = dt->session;
   open_db dbblock, * dbm_file;
 
@@ -3715,9 +3716,7 @@ if (SSL_SESSION_is_resumable(ss))         /* 1.1.1 */
 
   if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
 
   if ((dbm_file = dbfn_open(US"tls", O_RDWR, &dbblock, FALSE, FALSE)))
     {
-    const uschar * key = cbinfo->host->address;
-    dbfn_delete(dbm_file, key);
-    dbfn_write(dbm_file, key, dt, dlen);
+    dbfn_write(dbm_file, tlsp->resume_index, dt, dlen);
     dbfn_close(dbm_file);
     DEBUG(D_tls) debug_printf("wrote session (len %u) to db\n",
                  (unsigned)dlen);
     dbfn_close(dbm_file);
     DEBUG(D_tls) debug_printf("wrote session (len %u) to db\n",
                  (unsigned)dlen);
@@ -3727,21 +3726,20 @@ return 1;
 }
 
 
 }
 
 
+/* Construct a key for session DB lookup, and setup the SSL_CTX for resumption */
+
 static void
 tls_client_ctx_resume_prehandshake(
 static void
 tls_client_ctx_resume_prehandshake(
-  exim_openssl_client_tls_ctx * exim_client_ctx, tls_support * tlsp,
-  smtp_transport_options_block * ob, host_item * host)
+  exim_openssl_client_tls_ctx * exim_client_ctx, smtp_connect_args * conn_args,
+  tls_support * tlsp, smtp_transport_options_block * ob)
 {
 {
-/* Should the client request a session resumption ticket? */
-if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, host) == OK)
-  {
-  tlsp->host_resumable = TRUE;
+tlsp->host_resumable = TRUE;
+tls_client_resmption_key(tlsp, conn_args, ob);
 
 
-  SSL_CTX_set_session_cache_mode(exim_client_ctx->ctx,
-       SSL_SESS_CACHE_CLIENT
-       | SSL_SESS_CACHE_NO_INTERNAL | SSL_SESS_CACHE_NO_AUTO_CLEAR);
-  SSL_CTX_sess_set_new_cb(exim_client_ctx->ctx, tls_save_session_cb);
-  }
+SSL_CTX_set_session_cache_mode(exim_client_ctx->ctx,
+      SSL_SESS_CACHE_CLIENT
+      | SSL_SESS_CACHE_NO_INTERNAL | SSL_SESS_CACHE_NO_AUTO_CLEAR);
+SSL_CTX_sess_set_new_cb(exim_client_ctx->ctx, tls_save_session_cb);
 }
 
 static BOOL
 }
 
 static BOOL
@@ -3765,7 +3763,7 @@ if (tlsp->host_resumable)
 
 tlsp->resumption = RESUME_SUPPORTED;
 /* Pick up a previous session, saved on an old ticket */
 
 tlsp->resumption = RESUME_SUPPORTED;
 /* Pick up a previous session, saved on an old ticket */
-tls_retrieve_session(tlsp, ssl, host->address);
+tls_retrieve_session(tlsp, ssl);
 return TRUE;
 }
 
 return TRUE;
 }
 
@@ -3785,16 +3783,19 @@ if (SSL_session_reused(exim_client_ctx->ssl))
 #ifdef EXIM_HAVE_ALPN
 /* Expand and convert an Exim list to an ALPN list.  False return for fail.
 NULL plist return for silent no-ALPN.
 #ifdef EXIM_HAVE_ALPN
 /* Expand and convert an Exim list to an ALPN list.  False return for fail.
 NULL plist return for silent no-ALPN.
+
+Overwite the passed-in list with the expanded version.
 */
 
 static BOOL
 */
 
 static BOOL
-tls_alpn_plist(const uschar * tls_alpn, const uschar ** plist, unsigned * plen,
+tls_alpn_plist(uschar ** tls_alpn, const uschar ** plist, unsigned * plen,
   uschar ** errstr)
 {
 uschar * exp_alpn;
 
   uschar ** errstr)
 {
 uschar * exp_alpn;
 
-if (!expand_check(tls_alpn, US"tls_alpn", &exp_alpn, errstr))
+if (!expand_check(*tls_alpn, US"tls_alpn", &exp_alpn, errstr))
   return FALSE;
   return FALSE;
+*tls_alpn = exp_alpn;
 
 if (!exp_alpn)
   {
 
 if (!exp_alpn)
   {
@@ -3807,7 +3808,7 @@ else
   but it's little extra code complexity in the client. */
 
   const uschar * list = exp_alpn;
   but it's little extra code complexity in the client. */
 
   const uschar * list = exp_alpn;
-  uschar * p = store_get(Ustrlen(exp_alpn), is_tainted(exp_alpn)), * s, * t;
+  uschar * p = store_get(Ustrlen(exp_alpn), exp_alpn), * s, * t;
   int sep = 0;
   uschar len;
 
   int sep = 0;
   uschar len;
 
@@ -3861,7 +3862,7 @@ BOOL require_ocsp = FALSE;
 
 rc = store_pool;
 store_pool = POOL_PERM;
 
 rc = store_pool;
 store_pool = POOL_PERM;
-exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx), FALSE);
+exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx), GET_UNTAINTED);
 exim_client_ctx->corked = NULL;
 store_pool = rc;
 
 exim_client_ctx->corked = NULL;
 store_pool = rc;
 
@@ -3975,39 +3976,20 @@ if (tls_client_basic_ctx_init(exim_client_ctx->ctx, host, ob,
       client_static_state, errstr) != OK)
   return FALSE;
 
       client_static_state, errstr) != OK)
   return FALSE;
 
-#ifndef DISABLE_TLS_RESUME
-tls_client_ctx_resume_prehandshake(exim_client_ctx, tlsp, ob, host);
-#endif
-
-
-if (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx)))
-  {
-  tls_error(US"SSL_new", host, NULL, errstr);
-  return FALSE;
-  }
-SSL_set_session_id_context(exim_client_ctx->ssl, sid_ctx, Ustrlen(sid_ctx));
-
-SSL_set_fd(exim_client_ctx->ssl, cctx->sock);
-SSL_set_connect_state(exim_client_ctx->ssl);
-
 if (ob->tls_sni)
   {
   if (!expand_check(ob->tls_sni, US"tls_sni", &tlsp->sni, errstr))
     return FALSE;
   if (!tlsp->sni)
 if (ob->tls_sni)
   {
   if (!expand_check(ob->tls_sni, US"tls_sni", &tlsp->sni, errstr))
     return FALSE;
   if (!tlsp->sni)
-    {
-    DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n");
-    }
+    { DEBUG(D_tls) debug_printf("Setting TLS SNI forced to fail, not sending\n"); }
   else if (!Ustrlen(tlsp->sni))
     tlsp->sni = NULL;
   else
     {
   else if (!Ustrlen(tlsp->sni))
     tlsp->sni = NULL;
   else
     {
-#ifdef EXIM_HAVE_OPENSSL_TLSEXT
-    DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tlsp->sni);
-    SSL_set_tlsext_host_name(exim_client_ctx->ssl, tlsp->sni);
-#else
+#ifndef EXIM_HAVE_OPENSSL_TLSEXT
     log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n",
           tlsp->sni);
     log_write(0, LOG_MAIN, "SNI unusable with this OpenSSL library version; ignoring \"%s\"\n",
           tlsp->sni);
+    tlsp->sni = NULL;
 #endif
     }
   }
 #endif
     }
   }
@@ -4018,10 +4000,10 @@ if (ob->tls_alpn)
   const uschar * plist;
   unsigned plen;
 
   const uschar * plist;
   unsigned plen;
 
-  if (!tls_alpn_plist(ob->tls_alpn, &plist, &plen, errstr))
+  if (!tls_alpn_plist(&ob->tls_alpn, &plist, &plen, errstr))
     return FALSE;
   if (plist)
     return FALSE;
   if (plist)
-    if (SSL_set_alpn_protos(exim_client_ctx->ssl, plist, plen) != 0)
+    if (SSL_CTX_set_alpn_protos(exim_client_ctx->ctx, plist, plen) != 0)
       {
       tls_error(US"alpn init", host, NULL, errstr);
       return FALSE;
       {
       tls_error(US"alpn init", host, NULL, errstr);
       return FALSE;
@@ -4034,6 +4016,29 @@ if (ob->tls_alpn)
           ob->tls_alpn);
 #endif
 
           ob->tls_alpn);
 #endif
 
+#ifndef DISABLE_TLS_RESUME
+if (verify_check_given_host(CUSS &ob->tls_resumption_hosts, host) == OK)
+  tls_client_ctx_resume_prehandshake(exim_client_ctx, conn_args, tlsp, ob);
+#endif
+
+
+if (!(exim_client_ctx->ssl = SSL_new(exim_client_ctx->ctx)))
+  {
+  tls_error(US"SSL_new", host, NULL, errstr);
+  return FALSE;
+  }
+SSL_set_session_id_context(exim_client_ctx->ssl, sid_ctx, Ustrlen(sid_ctx));
+SSL_set_fd(exim_client_ctx->ssl, cctx->sock);
+SSL_set_connect_state(exim_client_ctx->ssl);
+
+#ifdef EXIM_HAVE_OPENSSL_TLSEXT
+if (tlsp->sni)
+  {
+  DEBUG(D_tls) debug_printf("Setting TLS SNI \"%s\"\n", tlsp->sni);
+  SSL_set_tlsext_host_name(exim_client_ctx->ssl, tlsp->sni);
+  }
+#endif
+
 #ifdef SUPPORT_DANE
 if (conn_args->dane)
   if (dane_tlsa_load(exim_client_ctx->ssl, host, &conn_args->tlsa_dnsa, errstr) != OK)
 #ifdef SUPPORT_DANE
 if (conn_args->dane)
   if (dane_tlsa_load(exim_client_ctx->ssl, host, &conn_args->tlsa_dnsa, errstr) != OK)
@@ -4147,9 +4152,9 @@ tlsp->cipher_stdname = cipher_stdname_ssl(exim_client_ctx->ssl);
   size_t len = SSL_get_finished(exim_client_ctx->ssl, &c, 0);
   int old_pool = store_pool;
 
   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);
+  SSL_get_finished(exim_client_ctx->ssl, s = store_get((int)len, GET_TAINTED), len);
   store_pool = POOL_PERM;
   store_pool = POOL_PERM;
-    tlsp->channelbinding = b64encode_taint(CUS s, (int)len, TRUE);
+    tlsp->channelbinding = b64encode_taint(CUS s, (int)len, GET_TAINTED);
   store_pool = old_pool;
   DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp);
   }
   store_pool = old_pool;
   DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage %p %p\n", tlsp->channelbinding, tlsp);
   }
@@ -4519,22 +4524,25 @@ int * fdp = o_ctx ? &tls_out.active.sock : &tls_in.active.sock;
 
 if (*fdp < 0) return;  /* TLS was not active */
 
 
 if (*fdp < 0) return;  /* TLS was not active */
 
-if (do_shutdown)
+if (do_shutdown > TLS_NO_SHUTDOWN)
   {
   int rc;
   DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n",
   {
   int rc;
   DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n",
-    do_shutdown > 1 ? " (with response-wait)" : "");
+    do_shutdown > TLS_SHUTDOWN_NOWAIT ? " (with response-wait)" : "");
 
   tls_write(ct_ctx, NULL, 0, FALSE);   /* flush write buffer */
 
 
   tls_write(ct_ctx, NULL, 0, FALSE);   /* flush write buffer */
 
-  if (  (rc = SSL_shutdown(*sslp)) == 0        /* send "close notify" alert */
-     && do_shutdown > 1)
+  if (  (  do_shutdown >= TLS_SHUTDOWN_WONLY
+       || (rc = SSL_shutdown(*sslp)) == 0      /* send "close notify" alert */
+       )
+     && do_shutdown > TLS_SHUTDOWN_NOWAIT
+     )
     {
 #ifdef EXIM_TCP_CORK
     (void) setsockopt(*fdp, IPPROTO_TCP, EXIM_TCP_CORK, US &off, sizeof(off));
 #endif
     ALARM(2);
     {
 #ifdef EXIM_TCP_CORK
     (void) setsockopt(*fdp, IPPROTO_TCP, EXIM_TCP_CORK, US &off, sizeof(off));
 #endif
     ALARM(2);
-    rc = SSL_shutdown(*sslp);          /* wait for response */
+    rc = SSL_shutdown(*sslp);                  /* wait for response */
     ALARM_CLR(0);
     }
 
     ALARM_CLR(0);
     }
 
@@ -4585,8 +4593,8 @@ Returns:     NULL on success, or error message
 uschar *
 tls_validate_require_cipher(void)
 {
 uschar *
 tls_validate_require_cipher(void)
 {
-SSL_CTX *ctx;
-uschar *s, *expciphers, *err;
+SSL_CTX * ctx;
+uschar * expciphers, * err;
 
 tls_openssl_init();
 
 
 tls_openssl_init();
 
@@ -4638,21 +4646,22 @@ number/string, and the version date remains unchanged.  The _build_ date
 will change, so we can more usefully assist with version diagnosis by also
 reporting the build date.
 
 will change, so we can more usefully assist with version diagnosis by also
 reporting the build date.
 
-Arguments:   a FILE* to print the results to
-Returns:     nothing
+Arguments:   string to append to
+Returns:     string
 */
 
 */
 
-void
-tls_version_report(FILE *f)
+gstring *
+tls_version_report(gstring * g)
 {
 {
-fprintf(f, "Library version: OpenSSL: Compile: %s\n"
-           "                          Runtime: %s\n"
-           "                                 : %s\n",
-           OPENSSL_VERSION_TEXT,
-           SSLeay_version(SSLEAY_VERSION),
-           SSLeay_version(SSLEAY_BUILT_ON));
-/* third line is 38 characters for the %s and the line is 73 chars long;
-the OpenSSL output includes a "built on: " prefix already. */
+return string_fmt_append(g,
+    "Library version: OpenSSL: Compile: %s\n"
+    "                          Runtime: %s\n"
+    "                                 : %s\n",
+            OPENSSL_VERSION_TEXT,
+            SSLeay_version(SSLEAY_VERSION),
+            SSLeay_version(SSLEAY_BUILT_ON));
+  /* third line is 38 characters for the %s and the line is 73 chars long;
+  the OpenSSL output includes a "built on: " prefix already. */
 }
 
 
 }