Fix build with older GnuTLS
[exim.git] / src / src / tls-gnu.c
index ad55f95cdae975fc8085b43e4b54fdff4265eab0..e08381344dd51e3967d5e7bf90b9378e016710a6 100644 (file)
@@ -256,8 +256,10 @@ static BOOL gnutls_buggy_ocsp = FALSE;
 
 /* Set this to control gnutls_global_set_log_level(); values 0 to 9 will setup
 the library logging; a value less than 0 disables the calls to set up logging
-callbacks.  Possibly GNuTLS also looks for an environment variable
-"GNUTLS_DEBUG_LEVEL". */
+callbacks.  GNuTLS also looks for an environment variable - except not for
+setuid binaries, making it useless - "GNUTLS_DEBUG_LEVEL".
+Allegedly the testscript line "GNUTLS_DEBUG_LEVEL=9 sudo exim ..." would work,
+but the env var must be added to /etc/sudoers too. */
 #ifndef EXIM_GNUTLS_LIBRARY_LOG_LEVEL
 # define EXIM_GNUTLS_LIBRARY_LOG_LEVEL -1
 #endif
@@ -705,7 +707,7 @@ if (rc < 0)
   temp_fn = string_copy(US"%s.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)fchown(fd, exim_uid, exim_gid);   /* Probably not necessary */
+  (void)exim_chown(temp_fn, exim_uid, exim_gid);   /* Probably not necessary */
 
   /* GnuTLS overshoots!
    * If we ask for 2236, we might get 2237 or more.
@@ -1514,11 +1516,44 @@ state->peerdn = NULL;
 cipher = gnutls_cipher_get(state->session);
 protocol = gnutls_protocol_get_version(state->session);
 mac = gnutls_mac_get(state->session);
-kx = gnutls_kx_get(state->session);
+kx =
+#ifdef GNUTLS_TLS1_3
+    protocol >= GNUTLS_TLS1_3 ? 0 :
+#endif
+  gnutls_kx_get(state->session);
 
 old_pool = store_pool;
   {
   store_pool = POOL_PERM;
+
+#ifdef SUPPORT_GNUTLS_SESS_DESC
+    {
+    gstring * g = NULL;
+    uschar * s = US gnutls_session_get_desc(state->session), c;
+
+    /* Nikos M suggests we use this by preference.  It returns like:
+    (TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(AES-256-GCM)
+
+    For partial back-compat, put a colon after the TLS version, replace the
+    )-( grouping with __, replace in-group - with _ and append the :keysize. */
+
+    /* debug_printf("peer_status: gnutls_session_get_desc %s\n", s); */
+
+    for (s++; (c = *s) && c != ')'; s++) g = string_catn(g, s, 1);
+    g = string_catn(g, US":", 1);
+    if (*s) s++;               /* now on _ between groups */
+    while ((c = *s))
+      {
+      for (*++s && ++s; (c = *s) && c != ')'; s++) g = string_catn(g, c == '-' ? US"_" : s, 1);
+      /* now on ) closing group */
+      if ((c = *s) && *++s == '-') g = string_catn(g, US"__", 2);
+      /* now on _ between groups */
+      }
+    g = string_catn(g, US":", 1);
+    g = string_cat(g, string_sprintf("%d", (int) gnutls_cipher_get_key_size(cipher) * 8));
+    state->ciphersuite = string_from_gstring(g);
+    }
+#else
   state->ciphersuite = string_sprintf("%s:%s:%d",
       gnutls_protocol_get_name(protocol),
       gnutls_cipher_suite_get_name(kx, cipher, mac),
@@ -1529,7 +1564,12 @@ old_pool = store_pool;
   releases did return "TLS 1.0"; play it safe, just in case. */
 
   for (uschar * p = state->ciphersuite; *p; p++) if (isspace(*p)) *p = '-';
+#endif
+
+/* debug_printf("peer_status: ciphersuite %s\n", state->ciphersuite); */
+
   state->tlsp->cipher = state->ciphersuite;
+  state->tlsp->bits = gnutls_cipher_get_key_size(cipher) * 8;
 
   state->tlsp->cipher_stdname = cipher_stdname_kcm(kx, cipher, mac);
   }
@@ -2022,6 +2062,34 @@ for (unsigned i = d->size; i > 0; i--, s++)
 return g;
 }
 
+static void
+post_handshake_debug(exim_gnutls_state_st * state)
+{
+debug_printf("gnutls_handshake was successful\n");
+#ifdef SUPPORT_GNUTLS_SESS_DESC
+debug_printf("%s\n", gnutls_session_get_desc(state->session));
+#endif
+#ifdef SUPPORT_GNUTLS_KEYLOG
+if (gnutls_protocol_get_version(state->session) < GNUTLS_TLS1_3)
+  {
+  gnutls_datum_t c, s;
+  gstring * gc, * gs;
+  /* we only want the client random and the master secret */
+  gnutls_session_get_random(state->session, &c, &s);
+  gnutls_session_get_master_secret(state->session, &s);
+  gc = ddump(&c);
+  gs = ddump(&s);
+  debug_printf("CLIENT_RANDOM %.*s %.*s\n", (int)gc->ptr, gc->s, (int)gs->ptr, gs->s);
+  }
+else
+  debug_printf("To get keying info for TLS1.3 is hard:\n"
+    " set environment variable SSLKEYLOGFILE to a filename writable by uid exim\n"
+    " add SSLKEYLOGFILE to keep_environment in the exim config\n"
+    " run exim as root\n"
+    " if using sudo, add SSLKEYLOGFILE to env_keep in /etc/sudoers\n");
+#endif
+}
+
 /* ------------------------------------------------------------------------ */
 /* Exported functions */
 
@@ -2168,24 +2236,7 @@ if (rc != GNUTLS_E_SUCCESS)
   return FAIL;
   }
 
-DEBUG(D_tls)
-  {
-  debug_printf("gnutls_handshake was successful\n");
-#ifdef SUPPORT_GNUTLS_SESS_DESC
-  debug_printf("%s\n", gnutls_session_get_desc(state->session));
-#endif
-#ifdef SUPPORT_GNUTLS_KEYLOG
-  {
-  gnutls_datum_t c, s;
-  gstring * gc, * gs;
-  gnutls_session_get_random(state->session, &c, &s);
-  gnutls_session_get_master_secret(state->session, &s);
-  gc = ddump(&c);
-  gs = ddump(&s);
-  debug_printf("CLIENT_RANDOM %.*s %.*s\n", (int)gc->ptr, gc->s, (int)gs->ptr, gs->s);
-  }
-#endif
-  }
+DEBUG(D_tls) post_handshake_debug(state);
 
 /* Verify after the fact */
 
@@ -2492,24 +2543,7 @@ if (rc != GNUTLS_E_SUCCESS)
   return FALSE;
   }
 
-DEBUG(D_tls)
-  {
-  debug_printf("gnutls_handshake was successful\n");
-#ifdef SUPPORT_GNUTLS_SESS_DESC
-  debug_printf("%s\n", gnutls_session_get_desc(state->session));
-#endif
-#ifdef SUPPORT_GNUTLS_KEYLOG
-  {
-  gnutls_datum_t c, s;
-  gstring * gc, * gs;
-  gnutls_session_get_random(state->session, &c, &s);
-  gnutls_session_get_master_secret(state->session, &s);
-  gc = ddump(&c);
-  gs = ddump(&s);
-  debug_printf("CLIENT_RANDOM %.*s %.*s\n", (int)gc->ptr, gc->s, (int)gs->ptr, gs->s);
-  }
-#endif
-  }
+DEBUG(D_tls) post_handshake_debug(state);
 
 /* Verify late */