Fix dmarc build
[exim.git] / src / src / tls-gnu.c
index f5c6a8bd67d6cf6c301c3f53acc2d26a454e9f80..954fd76b1c768d47670db7a2f0806bd397df2a13 100644 (file)
@@ -145,7 +145,7 @@ builtin_macro_create(US"_HAVE_TLS_OCSP");
 # ifdef SUPPORT_SRV_OCSP_STACK
 builtin_macro_create(US"_HAVE_TLS_OCSP_LIST");
 # endif
-#ifdef EXIM_HAVE_INOTIFY
+#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
 builtin_macro_create(US"_HAVE_TLS_CA_CACHE");
 # endif
 }
@@ -345,6 +345,53 @@ tls_server_ticket_cb(gnutls_session_t sess, u_int htype, unsigned when,
 #endif
 
 
+/*************************************************
+*               Handle TLS error                 *
+*************************************************/
+
+/* Called from lots of places when errors occur before actually starting to do
+the TLS handshake, that is, while the session is still in clear. Always returns
+DEFER for a server and FAIL for a client so that most calls can use "return
+tls_error(...)" to do this processing and then give an appropriate return. A
+single function is used for both server and client, because it is called from
+some shared functions.
+
+Argument:
+  prefix    text to include in the logged error
+  msg       additional error string (may be NULL)
+            usually obtained from gnutls_strerror()
+  host      NULL if setting up a server;
+            the connected host if setting up a client
+  errstr    pointer to returned error string
+
+Returns:    OK/DEFER/FAIL
+*/
+
+static int
+tls_error(const uschar *prefix, const uschar *msg, const host_item *host,
+  uschar ** errstr)
+{
+if (errstr)
+  *errstr = string_sprintf("(%s)%s%s", prefix, msg ? ": " : "", msg ? msg : US"");
+return host ? FAIL : DEFER;
+}
+
+
+static int
+tls_error_gnu(const uschar *prefix, int err, const host_item *host,
+  uschar ** errstr)
+{
+return tls_error(prefix, US gnutls_strerror(err), host, errstr);
+}
+
+static int
+tls_error_sys(const uschar *prefix, int err, const host_item *host,
+  uschar ** errstr)
+{
+return tls_error(prefix, US strerror(err), host, errstr);
+}
+
+
 /* ------------------------------------------------------------------------ */
 /* Initialisation */
 
@@ -379,9 +426,10 @@ return FALSE;
 #endif
 
 
-static void
-tls_g_init(void)
+static int
+tls_g_init(uschar ** errstr)
 {
+int rc;
 DEBUG(D_tls) debug_printf("GnuTLS global init required\n");
 
 #if defined(HAVE_GNUTLS_PKCS11) && !defined(GNUTLS_AUTO_PKCS11_MANUAL)
@@ -393,12 +441,12 @@ To prevent this, we init PKCS11 first, which is the documented approach. */
 
 if (!gnutls_allow_auto_pkcs11)
   if ((rc = gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_MANUAL, NULL)))
-    return tls_error_gnu(US"gnutls_pkcs11_init", rc, host, errstr);
+    return tls_error_gnu(US"gnutls_pkcs11_init", rc, NULL, errstr);
 #endif
 
 #ifndef GNUTLS_AUTO_GLOBAL_INIT
 if ((rc = gnutls_global_init()))
-  return tls_error_gnu(US"gnutls_global_init", rc, host, errstr);
+  return tls_error_gnu(US"gnutls_global_init", rc, NULL, errstr);
 #endif
 
 #if EXIM_GNUTLS_LIBRARY_LOG_LEVEL >= 0
@@ -416,6 +464,7 @@ if (tls_ocsp_file && (gnutls_buggy_ocsp = tls_is_buggy_ocsp()))
 #endif
 
 exim_gnutls_base_init_done = TRUE;
+return OK;
 }
 
 
@@ -432,10 +481,11 @@ tls_per_lib_daemon_tick(void)
 static void
 tls_per_lib_daemon_init(void)
 {
+uschar * dummy_errstr;
 static BOOL once = FALSE;
 
 if (!exim_gnutls_base_init_done)
-  tls_g_init();
+  tls_g_init(&dummy_errstr);
 
 if (!once)
   {
@@ -456,54 +506,6 @@ if (!once)
 }
 
 /* ------------------------------------------------------------------------ */
-/* Static functions */
-
-/*************************************************
-*               Handle TLS error                 *
-*************************************************/
-
-/* Called from lots of places when errors occur before actually starting to do
-the TLS handshake, that is, while the session is still in clear. Always returns
-DEFER for a server and FAIL for a client so that most calls can use "return
-tls_error(...)" to do this processing and then give an appropriate return. A
-single function is used for both server and client, because it is called from
-some shared functions.
-
-Argument:
-  prefix    text to include in the logged error
-  msg       additional error string (may be NULL)
-            usually obtained from gnutls_strerror()
-  host      NULL if setting up a server;
-            the connected host if setting up a client
-  errstr    pointer to returned error string
-
-Returns:    OK/DEFER/FAIL
-*/
-
-static int
-tls_error(const uschar *prefix, const uschar *msg, const host_item *host,
-  uschar ** errstr)
-{
-if (errstr)
-  *errstr = string_sprintf("(%s)%s%s", prefix, msg ? ": " : "", msg ? msg : US"");
-return host ? FAIL : DEFER;
-}
-
-
-static int
-tls_error_gnu(const uschar *prefix, int err, const host_item *host,
-  uschar ** errstr)
-{
-return tls_error(prefix, US gnutls_strerror(err), host, errstr);
-}
-
-static int
-tls_error_sys(const uschar *prefix, int err, const host_item *host,
-  uschar ** errstr)
-{
-return tls_error(prefix, US strerror(err), host, errstr);
-}
-
 
 /*************************************************
 *    Deal with logging errors during I/O         *
@@ -849,7 +851,7 @@ if (rc < 0)
     return tls_error(US"Filename too long to generate replacement",
         filename, NULL, errstr);
 
-  temp_fn = string_copy(US"%s.XXXXXXX");
+  temp_fn = string_copy(US"exim-dh.XXXXXXX");
   if ((fd = mkstemp(CS temp_fn)) < 0)  /* modifies temp_fn */
     return tls_error_sys(US"Unable to open temp file", errno, NULL, errstr);
   (void)exim_chown(temp_fn, exim_uid, exim_gid);   /* Probably not necessary */
@@ -927,7 +929,7 @@ return OK;
 
 
 
-/* Create and install a selfsigned certificate, for use in server mode */
+/* Create and install a selfsigned certificate, for use in server mode. */
 
 static int
 tls_install_selfsign(exim_gnutls_state_st * state, uschar ** errstr)
@@ -944,6 +946,7 @@ rc = GNUTLS_E_NO_CERTIFICATE_FOUND;
 if (TRUE) goto err;
 #endif
 
+DEBUG(D_tls) debug_printf("TLS: generating selfsigned server cert\n");
 where = US"initialising pkey";
 if ((rc = gnutls_x509_privkey_init(&pkey))) goto err;
 
@@ -968,7 +971,7 @@ now = 1;
 if (  (rc = gnutls_x509_crt_set_version(cert, 3))
    || (rc = gnutls_x509_crt_set_serial(cert, &now, sizeof(now)))
    || (rc = gnutls_x509_crt_set_activation_time(cert, now = time(NULL)))
-   || (rc = gnutls_x509_crt_set_expiration_time(cert, now + 60 * 60)) /* 1 hr */
+   || (rc = gnutls_x509_crt_set_expiration_time(cert, (long)2 * 60 * 60))      /* 2 hour */
    || (rc = gnutls_x509_crt_set_key(cert, pkey))
 
    || (rc = gnutls_x509_crt_set_dn_by_oid(cert,
@@ -1078,6 +1081,7 @@ return gnutls_ext_raw_parse(NULL, tls_server_clienthello_ext, msg,
 }
 
 
+# ifdef notdef_crashes
 /* Make a note that we saw a status-response */
 static int
 tls_server_servercerts_ext(void * ctx, unsigned tls_id,
@@ -1093,6 +1097,7 @@ if (FALSE && tls_id == 5) /* status_request */
   }
 return 0;
 }
+# endif
 
 /* Callback for certificates packet, on server, if we think we might serve stapled-OCSP */
 static int
@@ -1100,12 +1105,12 @@ tls_server_servercerts_cb(gnutls_session_t session, unsigned int htype,
   unsigned when, unsigned int incoming, const gnutls_datum_t * msg)
 {
 /* Call fn for each extension seen.  3.6.3 onwards */
-#ifdef notdef
-/*XXX crashes */
+# ifdef notdef_crashes
+                               /*XXX crashes */
 return gnutls_ext_raw_parse(NULL, tls_server_servercerts_ext, msg, 0);
-#endif
+# endif
 }
-#endif
+#endif /*SUPPORT_GNUTLS_EXT_RAW_PARSE*/
 
 /*XXX in tls1.3 the cert-status travel as an extension next to the cert, in the
  "Handshake Protocol: Certificate" record.
@@ -1417,47 +1422,66 @@ return gnutls_priority_init( (gnutls_priority_t *) &state->lib_state.pri_cache,
   CCS p, errpos);
 }
 
-static void
+static unsigned
 tls_server_creds_init(void)
 {
 uschar * dummy_errstr;
+unsigned lifetime = 0;
 
 state_server.lib_state = null_tls_preload;
 if (gnutls_certificate_allocate_credentials(
       (gnutls_certificate_credentials_t *) &state_server.lib_state.x509_cred))
   {
   state_server.lib_state.x509_cred = NULL;
-  return;
+  return lifetime;
   }
 creds_basic_init(state_server.lib_state.x509_cred, TRUE);
 
-#ifdef EXIM_HAVE_INOTIFY
+#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
 /* If tls_certificate has any $ indicating expansions, it is not good.
 If tls_privatekey is set but has $, not good.  Likewise for tls_ocsp_file.
-If all good (and tls_certificate set), load the cert(s).  Do not try
-to handle selfsign generation for now (tls_certificate null/empty;
-XXX will want to do that later though) due to the lifetime/expiry issue. */
+If all good (and tls_certificate set), load the cert(s). */
 
 if (  opt_set_and_noexpand(tls_certificate)
-   && opt_unset_or_noexpand(tls_privatekey)
-   && opt_unset_or_noexpand(tls_ocsp_file))
+# ifndef DISABLE_OCSP
+   && opt_unset_or_noexpand(tls_ocsp_file)
+# endif
+   && opt_unset_or_noexpand(tls_privatekey))
   {
   /* Set watches on the filenames.  The implementation does de-duplication
   so we can just blindly do them all.
   */
 
   if (  tls_set_watch(tls_certificate, TRUE)
-     && tls_set_watch(tls_privatekey, TRUE)
+# ifndef DISABLE_OCSP
      && tls_set_watch(tls_ocsp_file, TRUE)
-     )
+# endif
+     && tls_set_watch(tls_privatekey, TRUE))
     {
     DEBUG(D_tls) debug_printf("TLS: preloading server certs\n");
     if (creds_load_server_certs(&state_server, tls_certificate,
          tls_privatekey && *tls_privatekey ? tls_privatekey : tls_certificate,
-         tls_ocsp_file, &dummy_errstr) == 0)
+# ifdef DISABLE_OCSP
+         NULL,
+# else
+         tls_ocsp_file,
+# endif
+         &dummy_errstr) == 0)
       state_server.lib_state.conn_certs = TRUE;
     }
   }
+else if (  !tls_certificate && !tls_privatekey
+# ifndef DISABLE_OCSP
+       && !tls_ocsp_file
+# endif
+       )
+  {            /* Generate & preload a selfsigned cert. No files to watch. */
+  if ((tls_install_selfsign(&state_server, &dummy_errstr)) == OK)
+    {
+    state_server.lib_state.conn_certs = TRUE;
+    lifetime = f.running_in_test_harness ? 2 : 60 * 60;                /* 1 hour */
+    }
+  }
 else
   DEBUG(D_tls) debug_printf("TLS: not preloading server certs\n");
 
@@ -1470,7 +1494,7 @@ if (opt_set_and_noexpand(tls_verify_certificates))
     DEBUG(D_tls) debug_printf("TLS: preloading CA bundle for server\n");
     if (creds_load_cabundle(&state_server, tls_verify_certificates,
                            NULL, &dummy_errstr) != OK)
-      return;
+      return lifetime;
     state_server.lib_state.cabundle = TRUE;
 
     /* If CAs loaded and tls_crl is non-empty and has no $, load it */
@@ -1481,7 +1505,7 @@ if (opt_set_and_noexpand(tls_verify_certificates))
        {
        DEBUG(D_tls) debug_printf("TLS: preloading CRL for server\n");
        if (creds_load_crl(&state_server, tls_crl, &dummy_errstr) != OK)
-         return;
+         return lifetime;
        state_server.lib_state.crl = TRUE;
        }
       }
@@ -1508,6 +1532,7 @@ if (!tls_require_ciphers || opt_set_and_noexpand(tls_require_ciphers))
   }
 else
   DEBUG(D_tls) debug_printf("TLS: not preloading cipher list for server\n");
+return lifetime;
 }
 
 
@@ -1522,8 +1547,9 @@ exim_gnutls_state_st tpt_dummy_state;
 host_item * dummy_host = (host_item *)1;
 uschar * dummy_errstr;
 
-if (!exim_gnutls_base_init_done)
-  tls_g_init();
+if (  !exim_gnutls_base_init_done
+   && tls_g_init(&dummy_errstr) != OK)
+  return;
 
 ob->tls_preload = null_tls_preload;
 if (gnutls_certificate_allocate_credentials(
@@ -1537,7 +1563,7 @@ creds_basic_init(ob->tls_preload.x509_cred, FALSE);
 tpt_dummy_state.session = NULL;
 tpt_dummy_state.lib_state = ob->tls_preload;
 
-#ifdef EXIM_HAVE_INOTIFY
+#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
 if (  opt_set_and_noexpand(ob->tls_certificate)
    && opt_unset_or_noexpand(ob->tls_privatekey))
   {
@@ -1601,7 +1627,7 @@ depends on DANE or plain usage. */
 }
 
 
-#ifdef EXIM_HAVE_INOTIFY
+#if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
 /* Invalidate the creds cached, by dropping the current ones.
 Call when we notice one of the source files has changed. */
  
@@ -1750,7 +1776,13 @@ if (!state->lib_state.conn_certs)
          ? creds_load_client_certs(state, host, state->exp_tls_certificate,
                              state->exp_tls_privatekey, errstr)
          : creds_load_server_certs(state, state->exp_tls_certificate,
-                             state->exp_tls_privatekey, tls_ocsp_file, errstr)
+                             state->exp_tls_privatekey,
+#ifdef DISABLE_OCSP
+                             NULL,
+#else
+                             tls_ocsp_file,
+#endif
+                             errstr)
        )  ) return rc;
     }
   }
@@ -1763,9 +1795,11 @@ else
   state->exp_tls_certificate = US state->tls_certificate;
   state->exp_tls_privatekey = US state->tls_privatekey;
 
+#ifdef SUPPORT_GNUTLS_EXT_RAW_PARSE
   if (state->lib_state.ocsp_hook)
      gnutls_handshake_set_hook_function(state->session,
        GNUTLS_HANDSHAKE_ANY, GNUTLS_HOOK_POST, tls_server_hook_cb);
+#endif
   }
 
 
@@ -1871,12 +1905,11 @@ client-side params. */
 
 if (!state->host)
   {
-/*XXX DDD done-bit */
   if (!dh_server_params)
     if ((rc = init_server_dh(errstr)) != OK) return rc;
 
   /* Unnecessary & discouraged with 3.6.0 or later */
-  gnutls_certificate_set_dh_params(state->.lib_statex509_cred, dh_server_params);
+  gnutls_certificate_set_dh_params(state->lib_state.x509_cred, dh_server_params);
   }
 #endif
 
@@ -1920,8 +1953,9 @@ exim_gnutls_state_st * state;
 int rc;
 size_t sz;
 
-if (!exim_gnutls_base_init_done)
-  tls_g_init();
+if (  !exim_gnutls_base_init_done
+   && (rc = tls_g_init(errstr)) != OK)
+  return rc;
 
 if (host)
   {
@@ -1969,7 +2003,7 @@ state->tls_require_ciphers =      require_ciphers;
 state->host = host;
 
 /* This handles the variables that might get re-expanded after TLS SNI;
-that's tls_certificate, tls_privatekey, tls_verify_certificates, tls_crl */
+tls_certificate, tls_privatekey, tls_verify_certificates, tls_crl */
 
 DEBUG(D_tls)
   debug_printf("Expanding various TLS configuration options for session credentials\n");
@@ -3455,6 +3489,25 @@ return TRUE;
 
 
 
+/*
+Arguments:
+  ct_ctx       client TLS context pointer, or NULL for the one global server context
+*/
+
+void
+tls_shutdown_wr(void * ct_ctx)
+{
+exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server;
+tls_support * tlsp = state->tlsp;
+
+if (!tlsp || tlsp->active.sock < 0) return;  /* TLS was not active */
+
+tls_write(ct_ctx, NULL, 0, FALSE);     /* flush write buffer */
+
+HDEBUG(D_transport|D_tls|D_acl|D_v) debug_printf_indent("  SMTP(TLS shutdown)>>\n");
+gnutls_bye(state->session, GNUTLS_SHUT_WR);
+}
+
 /*************************************************
 *         Close down a TLS session               *
 *************************************************/
@@ -3465,27 +3518,30 @@ would tamper with the TLS session in the parent process).
 
 Arguments:
   ct_ctx       client context pointer, or NULL for the one global server context
-  shutdown     1 if TLS close-alert is to be sent,
-               2 if also response to be waited for
+  do_shutdown  0 no data-flush or TLS close-alert
+               1 if TLS close-alert is to be sent,
+               2 if also response to be waited for (2s timeout)
 
 Returns:     nothing
 */
 
 void
-tls_close(void * ct_ctx, int shutdown)
+tls_close(void * ct_ctx, int do_shutdown)
 {
 exim_gnutls_state_st * state = ct_ctx ? ct_ctx : &state_server;
 tls_support * tlsp = state->tlsp;
 
 if (!tlsp || tlsp->active.sock < 0) return;  /* TLS was not active */
 
-if (shutdown)
+if (do_shutdown)
   {
   DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n",
-    shutdown > 1 ? " (with response-wait)" : "");
+    do_shutdown > 1 ? " (with response-wait)" : "");
+
+  tls_write(ct_ctx, NULL, 0, FALSE);   /* flush write buffer */
 
   ALARM(2);
-  gnutls_bye(state->session, shutdown > 1 ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR);
+  gnutls_bye(state->session, do_shutdown > 1 ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR);
   ALARM_CLR(0);
   }
 
@@ -3633,7 +3689,7 @@ return buf;
 
 
 void
-tls_get_cache()
+tls_get_cache(void)
 {
 #ifndef DISABLE_DKIM
 exim_gnutls_state_st * state = &state_server;
@@ -3652,8 +3708,6 @@ return state_server.xfer_buffer_lwm < state_server.xfer_buffer_hwm
 }
 
 
-
-
 /*************************************************
 *          Read bytes from TLS channel           *
 *************************************************/