Certificate variables and field-extractor expansions. Bug 1358
authorJeremy Harris <jgh146exb@wizmail.org>
Fri, 2 May 2014 17:50:34 +0000 (18:50 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Fri, 2 May 2014 19:05:30 +0000 (20:05 +0100)
38 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/OS/Makefile-Base
src/scripts/MakeLinks
src/src/deliver.c
src/src/expand.c
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/smtp_in.c
src/src/spool_in.c
src/src/spool_out.c
src/src/structs.h
src/src/tls-gnu.c
src/src/tls-openssl.c
src/src/tls.c
src/src/tlscert-gnu.c [new file with mode: 0644]
src/src/tlscert-openssl.c [new file with mode: 0644]
src/src/transports/smtp.c
test/confs/2002
test/confs/2102
test/confs/5750 [new file with mode: 0644]
test/confs/5760 [new file with mode: 0644]
test/log/2002
test/log/2102
test/log/5750 [new file with mode: 0644]
test/log/5760 [new file with mode: 0644]
test/mail/2002.CALLER
test/mail/2102.CALLER
test/scripts/2000-GnuTLS/2002
test/scripts/2100-OpenSSL/2102
test/scripts/5750-GnuTLS-TPDA/5750 [new file with mode: 0644]
test/scripts/5750-GnuTLS-TPDA/REQUIRES [new file with mode: 0644]
test/scripts/5760-OpenSSL-TPDA/5760 [new file with mode: 0644]
test/scripts/5760-OpenSSL-TPDA/REQUIRES [new file with mode: 0644]
test/stdout/2002
test/stdout/2102

index afc15d4330c9c0f40cc84e8b1a867e2f4862f1fc..ec9367582e22cdeeb6c5f7046499b487ae32a550 100644 (file)
@@ -8875,6 +8875,41 @@ the expansion result is an empty string.
 If the ACL returns defer the result is a forced-fail.  Otherwise the expansion fails.
 
 
 If the ACL returns defer the result is a forced-fail.  Otherwise the expansion fails.
 
 
+.new
+.vitem "&*${certextract{*&<&'field'&>&*}{*&<&'certificate'&>&*}&&&
+       {*&<&'string2'&>&*}{*&<&'string3'&>&*}}*&"
+.cindex "expansion" "extracting cerificate fields"
+.cindex "&%certextract%&" "certificate fields"
+.cindex "certificate" "extracting fields"
+The <&'certificate'&> must be a variable of type certificate.
+The field name is expanded and used to retrive the relevant field from
+the certificate.  Supported fields are:
+.display
+version
+serial_number
+subject
+issuer
+notbefore
+notafter
+signature_algorithm
+signature
+subject_altname
+ocsp_uri
+crl_uri
+.endd
+If the field is found,
+<&'string2'&> is expanded, and replaces the whole item;
+otherwise <&'string3'&> is used. During the expansion of <&'string2'&> the
+variable &$value$& contains the value that has been extracted. Afterwards, it
+is restored to any previous value it might have had.
+
+If {<&'string3'&>} is omitted, the item is replaced by an empty string if the
+key is not found. If {<&'string2'&>} is also omitted, the value that was
+extracted is used.
+
+Field values are presented in human-readable form.
+.wen
+
 .vitem "&*${dlfunc{*&<&'file'&>&*}{*&<&'function'&>&*}{*&<&'arg'&>&*}&&&
        {*&<&'arg'&>&*}...}*&"
 .cindex &%dlfunc%&
 .vitem "&*${dlfunc{*&<&'file'&>&*}{*&<&'function'&>&*}{*&<&'arg'&>&*}&&&
        {*&<&'arg'&>&*}...}*&"
 .cindex &%dlfunc%&
@@ -12253,6 +12288,40 @@ on an outbound SMTP connection; the meaning of
 this depends upon the TLS implementation used.
 If TLS has not been negotiated, the value will be 0.
 
 this depends upon the TLS implementation used.
 If TLS has not been negotiated, the value will be 0.
 
+.new
+.vitem &$tls_in_ourcert$&
+.vindex "&$tls_in_ourcert$&"
+This variable refers to the certificate presented to the peer of an
+inbound connection when the message was received.
+It is only useful as the argument of a
+&%certextract%& expansion item or the name for a &%def%& expansion condition.
+.wen
+
+.new
+.vitem &$tls_in_peercert$&
+.vindex "&$tls_in_peercert$&"
+This variable refers to the certificate presented by the peer of an
+inbound connection when the message was received.
+It is only useful as the argument of a
+&%certextract%& expansion item or the name for a &%def%& expansion condition.
+.wen
+
+.new
+.vitem &$tls_out_ourcert$&
+.vindex "&$tls_out_ourcert$&"
+This variable refers to the certificate presented to the peer of an
+outbound connection.  It is only useful as the argument of a
+&%certextract%& expansion item or the name for a &%def%& expansion condition.
+.wen
+
+.new
+.vitem &$tls_out_peercert$&
+.vindex "&$tls_out_peercert$&"
+This variable refers to the certificate presented by the peer of an
+outbound connection.  It is only useful as the argument of a
+&%certextract%& expansion item or the name for a &%def%& expansion condition.
+.wen
+
 .vitem &$tls_in_certificate_verified$&
 .vindex "&$tls_in_certificate_verified$&"
 This variable is set to &"1"& if a TLS certificate was verified when the
 .vitem &$tls_in_certificate_verified$&
 .vindex "&$tls_in_certificate_verified$&"
 This variable is set to &"1"& if a TLS certificate was verified when the
index 172748584d677270bb1889d1f7c1d5aa5abbdc41..c98528884378b30aa444c8a2330a440d4c6508a1 100644 (file)
@@ -104,6 +104,9 @@ TL/10 Bugzilla 1454: New -oMm option to pass message reference to Exim.
       Requires trusted mode and valid format message id, aborts otherwise.
       Patch contributed by Heiko Schlichting.
 
       Requires trusted mode and valid format message id, aborts otherwise.
       Patch contributed by Heiko Schlichting.
 
+JH/20 New expansion variables tls_(in,out)_(our,peer)cert, and expansion item
+      certextract with support for various fields.  Bug 1358.
+
 
 Exim version 4.82
 -----------------
 
 Exim version 4.82
 -----------------
index 33c66ceb971ee75c990c1c5f0e047a1234e5c5c7..b6fc576bd91e1cc591d26659ca203fdd2f7ceea8 100644 (file)
@@ -44,6 +44,9 @@ Version 4.83
 
  9. Support for DNSSEC on outbound connections.
 
 
  9. Support for DNSSEC on outbound connections.
 
+10. New variables "tls_(in,out)_(our,peer)cert" and expansion item
+    "certextract" to extract fields from them.
+
 
 Version 4.82
 ------------
 
 Version 4.82
 ------------
index 8209969f610a4d020047bbe599cc1dcec303ee42..0caf8604bd82c958011a3e8790466413a3700b4f 100644 (file)
@@ -577,7 +577,7 @@ spool_out.o:     $(HDRS) spool_out.c
 std-crypto.o:    $(HDRS) std-crypto.c
 store.o:         $(HDRS) store.c
 string.o:        $(HDRS) string.c
 std-crypto.o:    $(HDRS) std-crypto.c
 store.o:         $(HDRS) store.c
 string.o:        $(HDRS) string.c
-tls.o:           $(HDRS) tls.c tls-gnu.c tls-openssl.c
+tls.o:           $(HDRS) tls.c tls-gnu.c tlscert-gnu.c tls-openssl.c tlscert-openssl.c
 tod.o:           $(HDRS) tod.c
 transport.o:     $(HDRS) transport.c
 tree.o:          $(HDRS) tree.c
 tod.o:           $(HDRS) tod.c
 transport.o:     $(HDRS) transport.c
 tree.o:          $(HDRS) tree.c
index 2eb8a967e8b3eea2fb890c41a96cb2b3357a28c0..01cd21f1c2b411acac73f3ab8b56fc841d22cb1d 100755 (executable)
@@ -233,6 +233,8 @@ ln -s ../src/std-crypto.c      std-crypto.c
 ln -s ../src/store.c           store.c
 ln -s ../src/string.c          string.c
 ln -s ../src/tls.c             tls.c
 ln -s ../src/store.c           store.c
 ln -s ../src/string.c          string.c
 ln -s ../src/tls.c             tls.c
+ln -s ../src/tlscert-gnu.c     tlscert-gnu.c
+ln -s ../src/tlscert-openssl.c tlscert-openssl.c
 ln -s ../src/tls-gnu.c         tls-gnu.c
 ln -s ../src/tls-openssl.c     tls-openssl.c
 ln -s ../src/tod.c             tod.c
 ln -s ../src/tls-gnu.c         tls-gnu.c
 ln -s ../src/tls-openssl.c     tls-openssl.c
 ln -s ../src/tod.c             tod.c
index 1e7a8a18a124fe2ab3111aba1eaedce0774424c0..fff0e2fd088202a054c0d6d92bd4d5b15749d98a 100644 (file)
@@ -1057,7 +1057,7 @@ if (addr->return_file >= 0 && addr->return_filename != NULL)
   (void)close(addr->return_file);
   }
 
   (void)close(addr->return_file);
   }
 
-/* The sucess case happens only after delivery by a transport. */
+/* The success case happens only after delivery by a transport. */
 
 if (result == OK)
   {
 
 if (result == OK)
   {
@@ -1073,10 +1073,8 @@ if (result == OK)
   DEBUG(D_deliver) debug_printf("%s delivered\n", addr->address);
 
   if (addr->parent == NULL)
   DEBUG(D_deliver) debug_printf("%s delivered\n", addr->address);
 
   if (addr->parent == NULL)
-    {
     deliver_msglog("%s %s: %s%s succeeded\n", now, addr->address,
       driver_name, driver_kind);
     deliver_msglog("%s %s: %s%s succeeded\n", now, addr->address,
       driver_name, driver_kind);
-    }
   else
     {
     deliver_msglog("%s %s <%s>: %s%s succeeded\n", now, addr->address,
   else
     {
     deliver_msglog("%s %s <%s>: %s%s succeeded\n", now, addr->address,
@@ -1084,7 +1082,28 @@ if (result == OK)
     child_done(addr, now);
     }
 
     child_done(addr, now);
     }
 
+  /* Certificates for logging (via TPDA) */
+  #ifdef SUPPORT_TLS
+  tls_out.ourcert = addr->ourcert;
+  addr->ourcert = NULL;
+  tls_out.peercert = addr->peercert;
+  addr->peercert = NULL;
+  #endif
+
   delivery_log(LOG_MAIN, addr, logchar, NULL);
   delivery_log(LOG_MAIN, addr, logchar, NULL);
+
+  #ifdef SUPPORT_TLS
+  if (tls_out.ourcert)
+    {
+    tls_free_cert(tls_out.ourcert);
+    tls_out.ourcert = NULL;
+    }
+  if (tls_out.peercert)
+    {
+    tls_free_cert(tls_out.peercert);
+    tls_out.peercert = NULL;
+    }
+  #endif
   }
 
 
   }
 
 
@@ -2957,27 +2976,51 @@ while (!done)
 
     #ifdef SUPPORT_TLS
     case 'X':
 
     #ifdef SUPPORT_TLS
     case 'X':
-    if (addr == NULL) goto ADDR_MISMATCH;            /* Below, in 'A' handler */
-    addr->cipher = (*ptr)? string_copy(ptr) : NULL;
-    while (*ptr++);
-    addr->peerdn = (*ptr)? string_copy(ptr) : NULL;
+    if (addr == NULL) goto ADDR_MISMATCH;          /* Below, in 'A' handler */
+    switch (*ptr++)
+      {
+      case '1':
+      addr->cipher = NULL;
+      addr->peerdn = NULL;
+
+      if (*ptr)
+       addr->cipher = string_copy(ptr);
+      while (*ptr++);
+      if (*ptr)
+       {
+       addr->peerdn = string_copy(ptr);
+       }
+      break;
+
+      case '2':
+      addr->peercert = NULL;
+      if (*ptr)
+       (void) tls_import_cert(ptr, &addr->peercert);
+      break;
+
+      case '3':
+      addr->ourcert = NULL;
+      if (*ptr)
+       (void) tls_import_cert(ptr, &addr->ourcert);
+      break;
+      }
     while (*ptr++);
     break;
     #endif
 
     case 'C':  /* client authenticator information */
     switch (*ptr++)
     while (*ptr++);
     break;
     #endif
 
     case 'C':  /* client authenticator information */
     switch (*ptr++)
-    {
-    case '1':
-      addr->authenticator = (*ptr)? string_copy(ptr) : NULL;
-      break;
-    case '2':
-      addr->auth_id = (*ptr)? string_copy(ptr) : NULL;
-      break;
-    case '3':
-      addr->auth_sndr = (*ptr)? string_copy(ptr) : NULL;
-      break;
-    }
+      {
+      case '1':
+       addr->authenticator = (*ptr)? string_copy(ptr) : NULL;
+       break;
+      case '2':
+       addr->auth_id = (*ptr)? string_copy(ptr) : NULL;
+       break;
+      case '3':
+       addr->auth_sndr = (*ptr)? string_copy(ptr) : NULL;
+       break;
+      }
     while (*ptr++);
     break;
 
     while (*ptr++);
     break;
 
@@ -4054,18 +4097,41 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
       /* Use an X item only if there's something to send */
 
       #ifdef SUPPORT_TLS
       /* Use an X item only if there's something to send */
 
       #ifdef SUPPORT_TLS
-      if (addr->cipher != NULL)
+      if (addr->cipher)
         {
         ptr = big_buffer;
         {
         ptr = big_buffer;
-        sprintf(CS ptr, "X%.128s", addr->cipher);
+        sprintf(CS ptr, "X1%.128s", addr->cipher);
         while(*ptr++);
         while(*ptr++);
-        if (addr->peerdn == NULL) *ptr++ = 0; else
+        if (!addr->peerdn)
+         *ptr++ = 0;
+       else
           {
           sprintf(CS ptr, "%.512s", addr->peerdn);
           while(*ptr++);
           }
           {
           sprintf(CS ptr, "%.512s", addr->peerdn);
           while(*ptr++);
           }
+
         rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
         }
         rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
         }
+      if (addr->peercert)
+       {
+        ptr = big_buffer;
+       *ptr++ = 'X'; *ptr++ = '2';
+       if (!tls_export_cert(ptr, big_buffer_size-2, addr->peercert))
+         while(*ptr++);
+       else
+         *ptr++ = 0;
+        rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
+       }
+      if (addr->ourcert)
+       {
+        ptr = big_buffer;
+       *ptr++ = 'X'; *ptr++ = '3';
+       if (!tls_export_cert(ptr, big_buffer_size-2, addr->ourcert))
+         while(*ptr++);
+       else
+         *ptr++ = 0;
+        rmt_dlv_checked_write(fd, big_buffer, ptr - big_buffer);
+       }
       #endif
 
       if (client_authenticator)
       #endif
 
       if (client_authenticator)
index 54b3abc5498c4017f8ff4494fdec284d3afd4bed..34fb0346effe91d7160ed7ceecad80a2a91b2429 100644 (file)
@@ -93,6 +93,9 @@ bcrypt ({CRYPT}$2a$).
 
 
 
 
 
 
+#ifndef nelements
+# define nelements(arr) (sizeof(arr) / sizeof(*arr))
+#endif
 
 /*************************************************
 *            Local statics and tables            *
 
 /*************************************************
 *            Local statics and tables            *
@@ -103,6 +106,7 @@ alphabetical order. */
 
 static uschar *item_table[] = {
   US"acl",
 
 static uschar *item_table[] = {
   US"acl",
+  US"certextract",
   US"dlfunc",
   US"extract",
   US"filter",
   US"dlfunc",
   US"extract",
   US"filter",
@@ -127,6 +131,7 @@ static uschar *item_table[] = {
 
 enum {
   EITEM_ACL,
 
 enum {
   EITEM_ACL,
+  EITEM_CERTEXTRACT,
   EITEM_DLFUNC,
   EITEM_EXTRACT,
   EITEM_FILTER,
   EITEM_DLFUNC,
   EITEM_EXTRACT,
   EITEM_FILTER,
@@ -387,7 +392,8 @@ enum {
   vtype_host_lookup,    /* value not used; get host name */
   vtype_load_avg,       /* value not used; result is int from os_getloadavg */
   vtype_pspace,         /* partition space; value is T/F for spool/log */
   vtype_host_lookup,    /* value not used; get host name */
   vtype_load_avg,       /* value not used; result is int from os_getloadavg */
   vtype_pspace,         /* partition space; value is T/F for spool/log */
-  vtype_pinodes         /* partition inodes; value is T/F for spool/log */
+  vtype_pinodes,        /* partition inodes; value is T/F for spool/log */
+  vtype_cert           /* SSL certificate */
   #ifndef DISABLE_DKIM
   ,vtype_dkim           /* Lookup of value in DKIM signature */
   #endif
   #ifndef DISABLE_DKIM
   ,vtype_dkim           /* Lookup of value in DKIM signature */
   #endif
@@ -665,6 +671,8 @@ static var_entry var_table[] = {
   { "tls_in_bits",         vtype_int,         &tls_in.bits },
   { "tls_in_certificate_verified", vtype_int, &tls_in.certificate_verified },
   { "tls_in_cipher",       vtype_stringptr,   &tls_in.cipher },
   { "tls_in_bits",         vtype_int,         &tls_in.bits },
   { "tls_in_certificate_verified", vtype_int, &tls_in.certificate_verified },
   { "tls_in_cipher",       vtype_stringptr,   &tls_in.cipher },
+  { "tls_in_ourcert",      vtype_cert,        &tls_in.ourcert },
+  { "tls_in_peercert",     vtype_cert,        &tls_in.peercert },
   { "tls_in_peerdn",       vtype_stringptr,   &tls_in.peerdn },
 #if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
   { "tls_in_sni",          vtype_stringptr,   &tls_in.sni },
   { "tls_in_peerdn",       vtype_stringptr,   &tls_in.peerdn },
 #if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
   { "tls_in_sni",          vtype_stringptr,   &tls_in.sni },
@@ -672,6 +680,8 @@ static var_entry var_table[] = {
   { "tls_out_bits",        vtype_int,         &tls_out.bits },
   { "tls_out_certificate_verified", vtype_int,&tls_out.certificate_verified },
   { "tls_out_cipher",      vtype_stringptr,   &tls_out.cipher },
   { "tls_out_bits",        vtype_int,         &tls_out.bits },
   { "tls_out_certificate_verified", vtype_int,&tls_out.certificate_verified },
   { "tls_out_cipher",      vtype_stringptr,   &tls_out.cipher },
+  { "tls_out_ourcert",     vtype_cert,        &tls_out.ourcert },
+  { "tls_out_peercert",    vtype_cert,        &tls_out.peercert },
   { "tls_out_peerdn",      vtype_stringptr,   &tls_out.peerdn },
 #if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
   { "tls_out_sni",         vtype_stringptr,   &tls_out.sni },
   { "tls_out_peerdn",      vtype_stringptr,   &tls_out.peerdn },
 #if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
   { "tls_out_sni",         vtype_stringptr,   &tls_out.sni },
@@ -1065,6 +1075,23 @@ return NULL;
 
 
 
 
 
 
+static var_entry *
+find_var_ent(uschar * name)
+{
+int first = 0;
+int last = var_table_size;
+
+while (last > first)
+  {
+  int middle = (first + last)/2;
+  int c = Ustrcmp(name, var_table[middle].name);
+
+  if (c > 0) { first = middle + 1; continue; }
+  if (c < 0) { last = middle; continue; }
+  return &var_table[middle];
+  }
+return NULL;
+}
 
 /*************************************************
 *   Extract numbered subfield from string        *
 
 /*************************************************
 *   Extract numbered subfield from string        *
@@ -1140,7 +1167,7 @@ return fieldtext;
 
 
 static uschar *
 
 
 static uschar *
-expand_getlistele (int field, uschar *list)
+expand_getlistele(int field, uschar * list)
 {
 uschar * tlist= list;
 int sep= 0;
 {
 uschar * tlist= list;
 int sep= 0;
@@ -1156,6 +1183,68 @@ while(--field>0 && (string_nextinlist(&list, &sep, &dummy, 1))) ;
 return string_nextinlist(&list, &sep, NULL, 0);
 }
 
 return string_nextinlist(&list, &sep, NULL, 0);
 }
 
+
+/* Certificate fields, by name.  Worry about by-OID later */
+
+#ifdef SUPPORT_TLS
+typedef struct
+{
+uschar * name;
+uschar * (*getfn)(void * cert);
+} certfield;
+static certfield certfields[] =
+{                      /* linear search; no special order */
+  { US"version",       &tls_cert_version },
+  { US"serial_number", &tls_cert_serial_number },
+  { US"subject",       &tls_cert_subject },
+  { US"notbefore",     &tls_cert_not_before },
+  { US"notafter",      &tls_cert_not_after },
+  { US"issuer",                &tls_cert_issuer },
+  { US"signature",     &tls_cert_signature },
+  { US"signature_algorithm",   &tls_cert_signature_algorithm },
+  { US"subject_altname",       &tls_cert_subject_altname },
+  { US"ocsp_uri",      &tls_cert_ocsp_uri },
+  { US"crl_uri",       &tls_cert_crl_uri },
+};
+
+static uschar *
+expand_getcertele(uschar * field, uschar * certvar)
+{
+var_entry * vp;
+certfield * cp;
+
+if (!(vp = find_var_ent(certvar)))
+  {
+  expand_string_message = 
+    string_sprintf("no variable named \"%s\"", certvar);
+  return NULL;          /* Unknown variable name */
+  }
+/* NB this stops us passing certs around in variable.  Might
+want to do that in future */
+if (vp->type != vtype_cert)
+  {
+  expand_string_message = 
+    string_sprintf("\"%s\" is not a certificate", certvar);
+  return NULL;          /* Unknown variable name */
+  }
+if (!*(void **)vp->value)
+  return NULL;
+
+if (*field >= '0' && *field <= '9')
+  return tls_cert_ext_by_oid(*(void **)vp->value, field, 0);
+
+for(cp = certfields;
+    cp < certfields + nelements(certfields);
+    cp++)
+  if (Ustrcmp(cp->name, field) == 0)
+    return (*cp->getfn)( *(void **)vp->value );
+
+expand_string_message = 
+  string_sprintf("bad field selector \"%s\" for certextract", field);
+return NULL;
+}
+#endif /*SUPPORT_TLS*/
+
 /*************************************************
 *        Extract a substring from a string       *
 *************************************************/
 /*************************************************
 *        Extract a substring from a string       *
 *************************************************/
@@ -1551,8 +1640,10 @@ Returns:        NULL if the variable does not exist, or
 static uschar *
 find_variable(uschar *name, BOOL exists_only, BOOL skipping, int *newsize)
 {
 static uschar *
 find_variable(uschar *name, BOOL exists_only, BOOL skipping, int *newsize)
 {
-int first = 0;
-int last = var_table_size;
+var_entry * vp;
+uschar *s, *domain;
+uschar **ss;
+void * val;
 
 /* Handle ACL variables, whose names are of the form acl_cxxx or acl_mxxx.
 Originally, xxx had to be a number in the range 0-9 (later 0-19), but from
 
 /* Handle ACL variables, whose names are of the form acl_cxxx or acl_mxxx.
 Originally, xxx had to be a number in the range 0-9 (later 0-19), but from
@@ -1585,203 +1676,198 @@ if (Ustrncmp(name, "auth", 4) == 0)
 
 /* For all other variables, search the table */
 
 
 /* For all other variables, search the table */
 
-while (last > first)
-  {
-  uschar *s, *domain;
-  uschar **ss;
-  int middle = (first + last)/2;
-  int c = Ustrcmp(name, var_table[middle].name);
-
-  if (c > 0) { first = middle + 1; continue; }
-  if (c < 0) { last = middle; continue; }
+if (!(vp = find_var_ent(name)))
+  return NULL;          /* Unknown variable name */
 
 
-  /* Found an existing variable. If in skipping state, the value isn't needed,
-  and we want to avoid processing (such as looking up the host name). */
+/* Found an existing variable. If in skipping state, the value isn't needed,
+and we want to avoid processing (such as looking up the host name). */
 
 
-  if (skipping) return US"";
+if (skipping)
+  return US"";
 
 
-  switch (var_table[middle].type)
+val = vp->value;
+switch (vp->type)
+  {
+  case vtype_filter_int:
+  if (!filter_running) return NULL;
+  /* Fall through */
+  /* VVVVVVVVVVVV */
+  case vtype_int:
+  sprintf(CS var_buffer, "%d", *(int *)(val)); /* Integer */
+  return var_buffer;
+
+  case vtype_ino:
+  sprintf(CS var_buffer, "%ld", (long int)(*(ino_t *)(val))); /* Inode */
+  return var_buffer;
+
+  case vtype_gid:
+  sprintf(CS var_buffer, "%ld", (long int)(*(gid_t *)(val))); /* gid */
+  return var_buffer;
+
+  case vtype_uid:
+  sprintf(CS var_buffer, "%ld", (long int)(*(uid_t *)(val))); /* uid */
+  return var_buffer;
+
+  case vtype_bool:
+  sprintf(CS var_buffer, "%s", *(BOOL *)(val) ? "yes" : "no"); /* bool */
+  return var_buffer;
+
+  case vtype_stringptr:                      /* Pointer to string */
+  s = *((uschar **)(val));
+  return (s == NULL)? US"" : s;
+
+  case vtype_pid:
+  sprintf(CS var_buffer, "%d", (int)getpid()); /* pid */
+  return var_buffer;
+
+  case vtype_load_avg:
+  sprintf(CS var_buffer, "%d", OS_GETLOADAVG()); /* load_average */
+  return var_buffer;
+
+  case vtype_host_lookup:                    /* Lookup if not done so */
+  if (sender_host_name == NULL && sender_host_address != NULL &&
+      !host_lookup_failed && host_name_lookup() == OK)
+    host_build_sender_fullhost();
+  return (sender_host_name == NULL)? US"" : sender_host_name;
+
+  case vtype_localpart:                      /* Get local part from address */
+  s = *((uschar **)(val));
+  if (s == NULL) return US"";
+  domain = Ustrrchr(s, '@');
+  if (domain == NULL) return s;
+  if (domain - s > sizeof(var_buffer) - 1)
+    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "local part longer than " SIZE_T_FMT
+       " in string expansion", sizeof(var_buffer));
+  Ustrncpy(var_buffer, s, domain - s);
+  var_buffer[domain - s] = 0;
+  return var_buffer;
+
+  case vtype_domain:                         /* Get domain from address */
+  s = *((uschar **)(val));
+  if (s == NULL) return US"";
+  domain = Ustrrchr(s, '@');
+  return (domain == NULL)? US"" : domain + 1;
+
+  case vtype_msgheaders:
+  return find_header(NULL, exists_only, newsize, FALSE, NULL);
+
+  case vtype_msgheaders_raw:
+  return find_header(NULL, exists_only, newsize, TRUE, NULL);
+
+  case vtype_msgbody:                        /* Pointer to msgbody string */
+  case vtype_msgbody_end:                    /* Ditto, the end of the msg */
+  ss = (uschar **)(val);
+  if (*ss == NULL && deliver_datafile >= 0)  /* Read body when needed */
     {
     {
-    case vtype_filter_int:
-    if (!filter_running) return NULL;
-    /* Fall through */
-    /* VVVVVVVVVVVV */
-    case vtype_int:
-    sprintf(CS var_buffer, "%d", *(int *)(var_table[middle].value)); /* Integer */
-    return var_buffer;
-
-    case vtype_ino:
-    sprintf(CS var_buffer, "%ld", (long int)(*(ino_t *)(var_table[middle].value))); /* Inode */
-    return var_buffer;
-
-    case vtype_gid:
-    sprintf(CS var_buffer, "%ld", (long int)(*(gid_t *)(var_table[middle].value))); /* gid */
-    return var_buffer;
-
-    case vtype_uid:
-    sprintf(CS var_buffer, "%ld", (long int)(*(uid_t *)(var_table[middle].value))); /* uid */
-    return var_buffer;
-
-    case vtype_bool:
-    sprintf(CS var_buffer, "%s", *(BOOL *)(var_table[middle].value) ? "yes" : "no"); /* bool */
-    return var_buffer;
-
-    case vtype_stringptr:                      /* Pointer to string */
-    s = *((uschar **)(var_table[middle].value));
-    return (s == NULL)? US"" : s;
-
-    case vtype_pid:
-    sprintf(CS var_buffer, "%d", (int)getpid()); /* pid */
-    return var_buffer;
-
-    case vtype_load_avg:
-    sprintf(CS var_buffer, "%d", OS_GETLOADAVG()); /* load_average */
-    return var_buffer;
-
-    case vtype_host_lookup:                    /* Lookup if not done so */
-    if (sender_host_name == NULL && sender_host_address != NULL &&
-        !host_lookup_failed && host_name_lookup() == OK)
-      host_build_sender_fullhost();
-    return (sender_host_name == NULL)? US"" : sender_host_name;
-
-    case vtype_localpart:                      /* Get local part from address */
-    s = *((uschar **)(var_table[middle].value));
-    if (s == NULL) return US"";
-    domain = Ustrrchr(s, '@');
-    if (domain == NULL) return s;
-    if (domain - s > sizeof(var_buffer) - 1)
-      log_write(0, LOG_MAIN|LOG_PANIC_DIE, "local part longer than " SIZE_T_FMT
-          " in string expansion", sizeof(var_buffer));
-    Ustrncpy(var_buffer, s, domain - s);
-    var_buffer[domain - s] = 0;
-    return var_buffer;
-
-    case vtype_domain:                         /* Get domain from address */
-    s = *((uschar **)(var_table[middle].value));
-    if (s == NULL) return US"";
-    domain = Ustrrchr(s, '@');
-    return (domain == NULL)? US"" : domain + 1;
-
-    case vtype_msgheaders:
-    return find_header(NULL, exists_only, newsize, FALSE, NULL);
-
-    case vtype_msgheaders_raw:
-    return find_header(NULL, exists_only, newsize, TRUE, NULL);
-
-    case vtype_msgbody:                        /* Pointer to msgbody string */
-    case vtype_msgbody_end:                    /* Ditto, the end of the msg */
-    ss = (uschar **)(var_table[middle].value);
-    if (*ss == NULL && deliver_datafile >= 0)  /* Read body when needed */
+    uschar *body;
+    off_t start_offset = SPOOL_DATA_START_OFFSET;
+    int len = message_body_visible;
+    if (len > message_size) len = message_size;
+    *ss = body = store_malloc(len+1);
+    body[0] = 0;
+    if (vp->type == vtype_msgbody_end)
       {
       {
-      uschar *body;
-      off_t start_offset = SPOOL_DATA_START_OFFSET;
-      int len = message_body_visible;
-      if (len > message_size) len = message_size;
-      *ss = body = store_malloc(len+1);
-      body[0] = 0;
-      if (var_table[middle].type == vtype_msgbody_end)
-        {
-        struct stat statbuf;
-        if (fstat(deliver_datafile, &statbuf) == 0)
-          {
-          start_offset = statbuf.st_size - len;
-          if (start_offset < SPOOL_DATA_START_OFFSET)
-            start_offset = SPOOL_DATA_START_OFFSET;
-          }
-        }
-      lseek(deliver_datafile, start_offset, SEEK_SET);
-      len = read(deliver_datafile, body, len);
-      if (len > 0)
-        {
-        body[len] = 0;
-        if (message_body_newlines)   /* Separate loops for efficiency */
-          {
-          while (len > 0)
-            { if (body[--len] == 0) body[len] = ' '; }
-          }
-        else
-          {
-          while (len > 0)
-            { if (body[--len] == '\n' || body[len] == 0) body[len] = ' '; }
-          }
-        }
+      struct stat statbuf;
+      if (fstat(deliver_datafile, &statbuf) == 0)
+       {
+       start_offset = statbuf.st_size - len;
+       if (start_offset < SPOOL_DATA_START_OFFSET)
+         start_offset = SPOOL_DATA_START_OFFSET;
+       }
+      }
+    lseek(deliver_datafile, start_offset, SEEK_SET);
+    len = read(deliver_datafile, body, len);
+    if (len > 0)
+      {
+      body[len] = 0;
+      if (message_body_newlines)   /* Separate loops for efficiency */
+       {
+       while (len > 0)
+         { if (body[--len] == 0) body[len] = ' '; }
+       }
+      else
+       {
+       while (len > 0)
+         { if (body[--len] == '\n' || body[len] == 0) body[len] = ' '; }
+       }
       }
       }
-    return (*ss == NULL)? US"" : *ss;
+    }
+  return (*ss == NULL)? US"" : *ss;
 
 
-    case vtype_todbsdin:                       /* BSD inbox time of day */
-    return tod_stamp(tod_bsdin);
+  case vtype_todbsdin:                       /* BSD inbox time of day */
+  return tod_stamp(tod_bsdin);
 
 
-    case vtype_tode:                           /* Unix epoch time of day */
-    return tod_stamp(tod_epoch);
+  case vtype_tode:                           /* Unix epoch time of day */
+  return tod_stamp(tod_epoch);
 
 
-    case vtype_todel:                          /* Unix epoch/usec time of day */
-    return tod_stamp(tod_epoch_l);
+  case vtype_todel:                          /* Unix epoch/usec time of day */
+  return tod_stamp(tod_epoch_l);
 
 
-    case vtype_todf:                           /* Full time of day */
-    return tod_stamp(tod_full);
+  case vtype_todf:                           /* Full time of day */
+  return tod_stamp(tod_full);
 
 
-    case vtype_todl:                           /* Log format time of day */
-    return tod_stamp(tod_log_bare);            /* (without timezone) */
+  case vtype_todl:                           /* Log format time of day */
+  return tod_stamp(tod_log_bare);            /* (without timezone) */
 
 
-    case vtype_todzone:                        /* Time zone offset only */
-    return tod_stamp(tod_zone);
+  case vtype_todzone:                        /* Time zone offset only */
+  return tod_stamp(tod_zone);
 
 
-    case vtype_todzulu:                        /* Zulu time */
-    return tod_stamp(tod_zulu);
+  case vtype_todzulu:                        /* Zulu time */
+  return tod_stamp(tod_zulu);
 
 
-    case vtype_todlf:                          /* Log file datestamp tod */
-    return tod_stamp(tod_log_datestamp_daily);
+  case vtype_todlf:                          /* Log file datestamp tod */
+  return tod_stamp(tod_log_datestamp_daily);
 
 
-    case vtype_reply:                          /* Get reply address */
-    s = find_header(US"reply-to:", exists_only, newsize, TRUE,
-      headers_charset);
-    if (s != NULL) while (isspace(*s)) s++;
-    if (s == NULL || *s == 0)
-      {
-      *newsize = 0;                            /* For the *s==0 case */
-      s = find_header(US"from:", exists_only, newsize, TRUE, headers_charset);
-      }
-    if (s != NULL)
-      {
-      uschar *t;
-      while (isspace(*s)) s++;
-      for (t = s; *t != 0; t++) if (*t == '\n') *t = ' ';
-      while (t > s && isspace(t[-1])) t--;
-      *t = 0;
-      }
-    return (s == NULL)? US"" : s;
+  case vtype_reply:                          /* Get reply address */
+  s = find_header(US"reply-to:", exists_only, newsize, TRUE,
+    headers_charset);
+  if (s != NULL) while (isspace(*s)) s++;
+  if (s == NULL || *s == 0)
+    {
+    *newsize = 0;                            /* For the *s==0 case */
+    s = find_header(US"from:", exists_only, newsize, TRUE, headers_charset);
+    }
+  if (s != NULL)
+    {
+    uschar *t;
+    while (isspace(*s)) s++;
+    for (t = s; *t != 0; t++) if (*t == '\n') *t = ' ';
+    while (t > s && isspace(t[-1])) t--;
+    *t = 0;
+    }
+  return (s == NULL)? US"" : s;
 
 
-    case vtype_string_func:
-      {
-      uschar * (*fn)() = var_table[middle].value;
-      return fn();
-      }
+  case vtype_string_func:
+    {
+    uschar * (*fn)() = val;
+    return fn();
+    }
 
 
-    case vtype_pspace:
-      {
-      int inodes;
-      sprintf(CS var_buffer, "%d",
-        receive_statvfs(var_table[middle].value == (void *)TRUE, &inodes));
-      }
-    return var_buffer;
+  case vtype_pspace:
+    {
+    int inodes;
+    sprintf(CS var_buffer, "%d",
+      receive_statvfs(val == (void *)TRUE, &inodes));
+    }
+  return var_buffer;
 
 
-    case vtype_pinodes:
-      {
-      int inodes;
-      (void) receive_statvfs(var_table[middle].value == (void *)TRUE, &inodes);
-      sprintf(CS var_buffer, "%d", inodes);
-      }
-    return var_buffer;
+  case vtype_pinodes:
+    {
+    int inodes;
+    (void) receive_statvfs(val == (void *)TRUE, &inodes);
+    sprintf(CS var_buffer, "%d", inodes);
+    }
+  return var_buffer;
 
 
-    #ifndef DISABLE_DKIM
-    case vtype_dkim:
-    return dkim_exim_expand_query((int)(long)var_table[middle].value);
-    #endif
+  case vtype_cert:
+  return *(void **)val ? US"<cert>" : US"";
 
 
-    }
-  }
+  #ifndef DISABLE_DKIM
+  case vtype_dkim:
+  return dkim_exim_expand_query((int)(long)val);
+  #endif
 
 
-return NULL;          /* Unknown variable name */
+  }
 }
 
 
 }
 
 
@@ -1790,21 +1876,8 @@ return NULL;          /* Unknown variable name */
 void
 modify_variable(uschar *name, void * value)
 {
 void
 modify_variable(uschar *name, void * value)
 {
-int first = 0;
-int last = var_table_size;
-
-while (last > first)
-  {
-  int middle = (first + last)/2;
-  int c = Ustrcmp(name, var_table[middle].name);
-
-  if (c > 0) { first = middle + 1; continue; }
-  if (c < 0) { last = middle; continue; }
-
-  /* Found an existing variable; change the item it refers to */
-  var_table[middle].value = value;
-  return;
-  }
+var_entry * vp;
+if ((vp = find_var_ent(name))) vp->value = value;
 return;          /* Unknown variable name, fail silently */
 }
 
 return;          /* Unknown variable name, fail silently */
 }
 
@@ -5279,6 +5352,79 @@ while (*s != 0)
       continue;
       }
 
       continue;
       }
 
+#ifdef SUPPORT_TLS
+    case EITEM_CERTEXTRACT:
+      {
+      int i;
+      int field_number = 1;
+      uschar *save_lookup_value = lookup_value;
+      uschar *sub[2];
+      int save_expand_nmax =
+        save_expand_strings(save_expand_nstring, save_expand_nlength);
+
+      /* Read the field argument */
+      while (isspace(*s)) s++;
+      if (*s != '{')                                   /*}*/
+       goto EXPAND_FAILED_CURLY;
+      sub[0] = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
+      if (!sub[0])     goto EXPAND_FAILED;             /*{*/
+      if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+      /* strip spaces fore & aft */
+      {
+      int len;
+      int x = 0;
+      uschar *p = sub[0];
+
+      while (isspace(*p)) p++;
+      sub[0] = p;
+
+      len = Ustrlen(p);
+      while (len > 0 && isspace(p[len-1])) len--;
+      p[len] = 0;
+      }
+
+      /* inspect the cert argument */
+      while (isspace(*s)) s++;
+      if (*s != '{')                                   /*}*/
+       goto EXPAND_FAILED_CURLY;
+      if (*++s != '$')
+        {
+       expand_string_message = US"second argument of \"certextract\" must "
+         "be a certificate variable";
+       goto EXPAND_FAILED;
+       }
+      sub[1] = expand_string_internal(s+1, TRUE, &s, skipping, FALSE, &resetok);
+      if (!sub[1])     goto EXPAND_FAILED;             /*{*/
+      if (*s++ != '}') goto EXPAND_FAILED_CURLY;
+
+      if (skipping)
+       lookup_value = NULL;
+      else
+       {
+       lookup_value = expand_getcertele(sub[0], sub[1]);
+       if (*expand_string_message) goto EXPAND_FAILED;
+       }
+      switch(process_yesno(
+               skipping,                     /* were previously skipping */
+               lookup_value != NULL,         /* success/failure indicator */
+               save_lookup_value,            /* value to reset for string2 */
+               &s,                           /* input pointer */
+               &yield,                       /* output pointer */
+               &size,                        /* output size */
+               &ptr,                         /* output current point */
+               US"extract",                  /* condition type */
+              &resetok))
+        {
+        case 1: goto EXPAND_FAILED;          /* when all is well, the */
+        case 2: goto EXPAND_FAILED_CURLY;    /* returned value is 0 */
+        }
+
+      restore_expand_strings(save_expand_nmax, save_expand_nstring,
+        save_expand_nlength);
+      continue;
+      }
+#endif /*SUPPORT_TLS*/
+
     /* Handle list operations */
 
     case EITEM_FILTER:
     /* Handle list operations */
 
     case EITEM_FILTER:
index 599afd206cee4b1e50dc0cd70ddc1d52788da92f..8751a006e4b060ca0257bc67c2f49c01e259da59 100644 (file)
@@ -25,6 +25,20 @@ extern const char *
                std_dh_prime_default(void);
 extern const char *
                std_dh_prime_named(const uschar *);
                std_dh_prime_default(void);
 extern const char *
                std_dh_prime_named(const uschar *);
+
+extern uschar * tls_cert_crl_uri(void *);
+extern uschar * tls_cert_ext_by_oid(void *, uschar *, int);
+extern uschar * tls_cert_issuer(void *);
+extern uschar * tls_cert_not_before(void *);
+extern uschar * tls_cert_not_after(void *);
+extern uschar * tls_cert_ocsp_uri(void *);
+extern uschar * tls_cert_serial_number(void *);
+extern uschar * tls_cert_signature(void *);
+extern uschar * tls_cert_signature_algorithm(void *);
+extern uschar * tls_cert_subject(void *);
+extern uschar * tls_cert_subject_altname(void *);
+extern uschar * tls_cert_version(void *);
+
 extern int     tls_client_start(int, host_item *, address_item *,
                  uschar *, uschar *, uschar *, uschar *, uschar *, uschar *,
 # ifdef EXPERIMENTAL_OCSP
 extern int     tls_client_start(int, host_item *, address_item *,
                  uschar *, uschar *, uschar *, uschar *, uschar *, uschar *,
 # ifdef EXPERIMENTAL_OCSP
@@ -32,9 +46,12 @@ extern int     tls_client_start(int, host_item *, address_item *,
 # endif
                  int, int, uschar *, uschar *);
 extern void    tls_close(BOOL, BOOL);
 # endif
                  int, int, uschar *, uschar *);
 extern void    tls_close(BOOL, BOOL);
+extern int     tls_export_cert(uschar *, size_t, void *);
 extern int     tls_feof(void);
 extern int     tls_ferror(void);
 extern int     tls_feof(void);
 extern int     tls_ferror(void);
+extern void    tls_free_cert(void *);
 extern int     tls_getc(void);
 extern int     tls_getc(void);
+extern int     tls_import_cert(const uschar *, void **);
 extern int     tls_read(BOOL, uschar *, size_t);
 extern int     tls_server_start(const uschar *);
 extern BOOL    tls_smtp_buffered(void);
 extern int     tls_read(BOOL, uschar *, size_t);
 extern int     tls_server_start(const uschar *);
 extern BOOL    tls_smtp_buffered(void);
@@ -421,4 +438,6 @@ extern void    version_init(void);
 
 extern ssize_t write_to_fd_buf(int, const uschar *, size_t);
 
 
 extern ssize_t write_to_fd_buf(int, const uschar *, size_t);
 
+/* vi: aw
+*/
 /* End of functions.h */
 /* End of functions.h */
index da81b8db52d5c8133d060cea6ca47878dfdbc79d..7b591e42a12875317226ad89eddf99029773ae8e 100644 (file)
@@ -106,6 +106,8 @@ tls_support tls_in = {
  NULL, /* tls_cipher */
  FALSE,/* tls_on_connect */
  NULL, /* tls_on_connect_ports */
  NULL, /* tls_cipher */
  FALSE,/* tls_on_connect */
  NULL, /* tls_on_connect_ports */
+ NULL, /* tls_ourcert */
+ NULL, /* tls_peercert */
  NULL, /* tls_peerdn */
  NULL  /* tls_sni */
 };
  NULL, /* tls_peerdn */
  NULL  /* tls_sni */
 };
@@ -116,6 +118,8 @@ tls_support tls_out = {
  NULL, /* tls_cipher */
  FALSE,/* tls_on_connect */
  NULL, /* tls_on_connect_ports */
  NULL, /* tls_cipher */
  FALSE,/* tls_on_connect */
  NULL, /* tls_on_connect_ports */
+ NULL, /* tls_ourcert */
+ NULL, /* tls_peercert */
  NULL, /* tls_peerdn */
  NULL  /* tls_sni */
 };
  NULL, /* tls_peerdn */
  NULL  /* tls_sni */
 };
@@ -332,6 +336,8 @@ address_item address_defaults = {
   NULL,                 /* shadow_message */
   #ifdef SUPPORT_TLS
   NULL,                 /* cipher */
   NULL,                 /* shadow_message */
   #ifdef SUPPORT_TLS
   NULL,                 /* cipher */
+  NULL,                        /* ourcert */
+  NULL,                        /* peercert */
   NULL,                 /* peerdn */
   #endif
   NULL,                        /* authenticator */
   NULL,                 /* peerdn */
   #endif
   NULL,                        /* authenticator */
index 79bf38caa5df3f98dce3b0c03803a6da380e666e..584d1bd09ea2a480db9fdf71e6a9f4ef3d72b347 100644 (file)
@@ -85,6 +85,8 @@ typedef struct {
   uschar *cipher;             /* Cipher used */
   BOOL    on_connect;         /* For older MTAs that don't STARTTLS */
   uschar *on_connect_ports;   /* Ports always tls-on-connect */
   uschar *cipher;             /* Cipher used */
   BOOL    on_connect;         /* For older MTAs that don't STARTTLS */
   uschar *on_connect_ports;   /* Ports always tls-on-connect */
+  void   *ourcert;            /* Certificate we presented, binary */
+  void  *peercert;           /* Certificate of peer, binary */
   uschar *peerdn;             /* DN from peer */
   uschar *sni;                /* Server Name Indication */
 } tls_support;
   uschar *peerdn;             /* DN from peer */
   uschar *sni;                /* Server Name Indication */
 } tls_support;
index 27ff137850e5bd82975d272ca904d1c3bd5ed3cc..6810d25e3d3c47cca2d96591d656ecd827d796a5 100644 (file)
@@ -1818,6 +1818,8 @@ authenticated_by = NULL;
 
 #ifdef SUPPORT_TLS
 tls_in.cipher = tls_in.peerdn = NULL;
 
 #ifdef SUPPORT_TLS
 tls_in.cipher = tls_in.peerdn = NULL;
+tls_in.ourcert = tls_in.peercert = NULL;
+tls_in.sni = NULL;
 tls_advertised = FALSE;
 #endif
 
 tls_advertised = FALSE;
 #endif
 
index a546b65216c57bd88c3a4774bfdaf2e9fda10ca5..2006e1b02a429146080a51602a1c8ba06afff13c 100644 (file)
@@ -285,6 +285,8 @@ dkim_collect_input = FALSE;
 #ifdef SUPPORT_TLS
 tls_in.certificate_verified = FALSE;
 tls_in.cipher = NULL;
 #ifdef SUPPORT_TLS
 tls_in.certificate_verified = FALSE;
 tls_in.cipher = NULL;
+tls_in.ourcert = NULL;
+tls_in.peercert = NULL;
 tls_in.peerdn = NULL;
 tls_in.sni = NULL;
 #endif
 tls_in.peerdn = NULL;
 tls_in.sni = NULL;
 #endif
@@ -548,6 +550,12 @@ for (;;)
       tls_in.certificate_verified = TRUE;
     else if (Ustrncmp(p, "ls_cipher", 9) == 0)
       tls_in.cipher = string_copy(big_buffer + 12);
       tls_in.certificate_verified = TRUE;
     else if (Ustrncmp(p, "ls_cipher", 9) == 0)
       tls_in.cipher = string_copy(big_buffer + 12);
+#ifndef COMPILE_UTILITY
+    else if (Ustrncmp(p, "ls_ourcert", 10) == 0)
+      (void) tls_import_cert(big_buffer + 13, &tls_in.ourcert);
+    else if (Ustrncmp(p, "ls_peercert", 11) == 0)
+      (void) tls_import_cert(big_buffer + 14, &tls_in.peercert);
+#endif
     else if (Ustrncmp(p, "ls_peerdn", 9) == 0)
       tls_in.peerdn = string_unprinting(string_copy(big_buffer + 12));
     else if (Ustrncmp(p, "ls_sni", 6) == 0)
     else if (Ustrncmp(p, "ls_peerdn", 9) == 0)
       tls_in.peerdn = string_unprinting(string_copy(big_buffer + 12));
     else if (Ustrncmp(p, "ls_sni", 6) == 0)
@@ -799,4 +807,6 @@ errno = ERRNO_SPOOLFORMAT;
 return inheader? spool_read_hdrerror : spool_read_enverror;
 }
 
 return inheader? spool_read_hdrerror : spool_read_enverror;
 }
 
+/* vi: aw ai sw=2
+*/
 /* End of spool_in.c */
 /* End of spool_in.c */
index ce25a564efd4d18c4c21fdc6e168bd21a771a0de..7bbd42df0f137a411773ef6a84f62677ba7e7fe1 100644 (file)
@@ -229,9 +229,19 @@ if (bmi_verdicts != NULL) fprintf(f, "-bmi_verdicts %s\n", bmi_verdicts);
 
 #ifdef SUPPORT_TLS
 if (tls_in.certificate_verified) fprintf(f, "-tls_certificate_verified\n");
 
 #ifdef SUPPORT_TLS
 if (tls_in.certificate_verified) fprintf(f, "-tls_certificate_verified\n");
-if (tls_in.cipher != NULL)       fprintf(f, "-tls_cipher %s\n", tls_in.cipher);
-if (tls_in.peerdn != NULL)       fprintf(f, "-tls_peerdn %s\n", string_printing(tls_in.peerdn));
-if (tls_in.sni != NULL)                 fprintf(f, "-tls_sni %s\n",    string_printing(tls_in.sni));
+if (tls_in.cipher)       fprintf(f, "-tls_cipher %s\n", tls_in.cipher);
+if (tls_in.peercert)
+  {
+  (void) tls_export_cert(big_buffer, big_buffer_size, tls_in.peercert);
+  fprintf(f, "-tls_peercert %s\n", CS big_buffer);
+  }
+if (tls_in.peerdn)       fprintf(f, "-tls_peerdn %s\n", string_printing(tls_in.peerdn));
+if (tls_in.sni)                 fprintf(f, "-tls_sni %s\n",    string_printing(tls_in.sni));
+if (tls_in.ourcert)
+  {
+  (void) tls_export_cert(big_buffer, big_buffer_size, tls_in.ourcert);
+  fprintf(f, "-tls_ourcert %s\n", CS big_buffer);
+  }
 #endif
 
 /* To complete the envelope, write out the tree of non-recipients, followed by
 #endif
 
 /* To complete the envelope, write out the tree of non-recipients, followed by
index eb430851df9669f8078d58b64e323618d0dd4600..a6c78f4fc1fb4ce1880b2d23e556896401c2b9f4 100644 (file)
@@ -540,6 +540,8 @@ typedef struct address_item {
 
   #ifdef SUPPORT_TLS
   uschar *cipher;                 /* Cipher used for transport */
 
   #ifdef SUPPORT_TLS
   uschar *cipher;                 /* Cipher used for transport */
+  void   *ourcert;                /* Certificate offered to peer, binary */
+  void   *peercert;               /* Certificate from peer, binary */
   uschar *peerdn;                 /* DN of server's certificate */
   #endif
 
   uschar *peerdn;                 /* DN of server's certificate */
   #endif
 
index ace59633a565f36e7b15a30e7877e6abadd060df..880aaeb14831565a9ae4b752bfe2506f687c2c72 100644 (file)
@@ -74,19 +74,20 @@ Not handled here: global tls_channelbinding_b64.
 */
 
 typedef struct exim_gnutls_state {
 */
 
 typedef struct exim_gnutls_state {
-  gnutls_session_t session;
+  gnutls_session_t     session;
   gnutls_certificate_credentials_t x509_cred;
   gnutls_certificate_credentials_t x509_cred;
-  gnutls_priority_t priority_cache;
+  gnutls_priority_t    priority_cache;
   enum peer_verify_requirement verify_requirement;
   enum peer_verify_requirement verify_requirement;
-  int fd_in;
-  int fd_out;
-  BOOL peer_cert_verified;
-  BOOL trigger_sni_changes;
-  BOOL have_set_peerdn;
+  int                  fd_in;
+  int                  fd_out;
+  BOOL                 peer_cert_verified;
+  BOOL                 trigger_sni_changes;
+  BOOL                 have_set_peerdn;
   const struct host_item *host;
   const struct host_item *host;
-  uschar *peerdn;
-  uschar *ciphersuite;
-  uschar *received_sni;
+  gnutls_x509_crt_t    peercert;
+  uschar               *peerdn;
+  uschar               *ciphersuite;
+  uschar               *received_sni;
 
   const uschar *tls_certificate;
   const uschar *tls_privatekey;
 
   const uschar *tls_certificate;
   const uschar *tls_privatekey;
@@ -288,12 +289,34 @@ tls_error(when, msg, state->host);
 *        Set various Exim expansion vars         *
 *************************************************/
 
 *        Set various Exim expansion vars         *
 *************************************************/
 
+#define exim_gnutls_cert_err(Label) do { \
+  if (rc != GNUTLS_E_SUCCESS) { \
+    DEBUG(D_tls) debug_printf("TLS: cert problem: %s: %s\n", (Label), gnutls_strerror(rc)); \
+    return rc; } } while (0)
+
+static int
+import_cert(const gnutls_datum * cert, gnutls_x509_crt_t * crtp)
+{
+int rc;
+
+rc = gnutls_x509_crt_init(crtp);
+exim_gnutls_cert_err(US"gnutls_x509_crt_init (crt)");
+
+rc = gnutls_x509_crt_import(*crtp, cert, GNUTLS_X509_FMT_DER);
+exim_gnutls_cert_err(US"failed to import certificate [gnutls_x509_crt_import(cert)]");
+
+return rc;
+}
+
+#undef exim_gnutls_cert_err
+
+
 /* We set various Exim global variables from the state, once a session has
 been established.  With TLS callouts, may need to change this to stack
 variables, or just re-call it with the server state after client callout
 has finished.
 
 /* We set various Exim global variables from the state, once a session has
 been established.  With TLS callouts, may need to change this to stack
 variables, or just re-call it with the server state after client callout
 has finished.
 
-Make sure anything set here is inset in tls_getc().
+Make sure anything set here is unset in tls_getc().
 
 Sets:
   tls_active                fd
 
 Sets:
   tls_active                fd
@@ -301,15 +324,17 @@ Sets:
   tls_certificate_verified  bool indicator
   tls_channelbinding_b64    for some SASL mechanisms
   tls_cipher                a string
   tls_certificate_verified  bool indicator
   tls_channelbinding_b64    for some SASL mechanisms
   tls_cipher                a string
+  tls_peercert              pointer to library internal
   tls_peerdn                a string
   tls_sni                   a (UTF-8) string
   tls_peerdn                a string
   tls_sni                   a (UTF-8) string
+  tls_ourcert               pointer to library internal
 
 Argument:
   state      the relevant exim_gnutls_state_st *
 */
 
 static void
 
 Argument:
   state      the relevant exim_gnutls_state_st *
 */
 
 static void
-extract_exim_vars_from_tls_state(exim_gnutls_state_st *state, BOOL is_server)
+extract_exim_vars_from_tls_state(exim_gnutls_state_st * state)
 {
 gnutls_cipher_algorithm_t cipher;
 #ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING
 {
 gnutls_cipher_algorithm_t cipher;
 #ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING
@@ -317,18 +342,19 @@ int old_pool;
 int rc;
 gnutls_datum_t channel;
 #endif
 int rc;
 gnutls_datum_t channel;
 #endif
+tls_support * tlsp = state->tlsp;
 
 
-state->tlsp->active = state->fd_out;
+tlsp->active = state->fd_out;
 
 cipher = gnutls_cipher_get(state->session);
 /* returns size in "bytes" */
 
 cipher = gnutls_cipher_get(state->session);
 /* returns size in "bytes" */
-state->tlsp->bits = gnutls_cipher_get_key_size(cipher) * 8;
+tlsp->bits = gnutls_cipher_get_key_size(cipher) * 8;
 
 
-state->tlsp->cipher = state->ciphersuite;
+tlsp->cipher = state->ciphersuite;
 
 DEBUG(D_tls) debug_printf("cipher: %s\n", state->ciphersuite);
 
 
 DEBUG(D_tls) debug_printf("cipher: %s\n", state->ciphersuite);
 
-state->tlsp->certificate_verified = state->peer_cert_verified;
+tlsp->certificate_verified = state->peer_cert_verified;
 
 /* note that tls_channelbinding_b64 is not saved to the spool file, since it's
 only available for use for authenticators while this TLS session is running. */
 
 /* note that tls_channelbinding_b64 is not saved to the spool file, since it's
 only available for use for authenticators while this TLS session is running. */
@@ -349,8 +375,17 @@ if (rc) {
 }
 #endif
 
 }
 #endif
 
-state->tlsp->peerdn = state->peerdn;
-state->tlsp->sni =    state->received_sni;
+/* peercert is set in peer_status() */
+tlsp->peerdn = state->peerdn;
+tlsp->sni =    state->received_sni;
+
+/* record our certificate */
+  {
+  const gnutls_datum * cert = gnutls_certificate_get_ours(state->session);
+  gnutls_x509_crt_t crt;
+
+  tlsp->ourcert = cert && import_cert(cert, &crt)==0 ? crt : NULL;
+  }
 }
 
 
 }
 
 
@@ -1099,7 +1134,6 @@ return OK;
 
 
 
 
 
 
-
 /*************************************************
 *            Extract peer information            *
 *************************************************/
 /*************************************************
 *            Extract peer information            *
 *************************************************/
@@ -1205,11 +1239,11 @@ if (ct != GNUTLS_CRT_X509)
     if (state->verify_requirement == VERIFY_REQUIRED) { return tls_error((Label), gnutls_strerror(rc), state->host); } \
     return OK; } } while (0)
 
     if (state->verify_requirement == VERIFY_REQUIRED) { return tls_error((Label), gnutls_strerror(rc), state->host); } \
     return OK; } } while (0)
 
-rc = gnutls_x509_crt_init(&crt);
-exim_gnutls_peer_err(US"gnutls_x509_crt_init (crt)");
+rc = import_cert(&cert_list[0], &crt);
+exim_gnutls_peer_err(US"cert 0");
+
+state->tlsp->peercert = state->peercert = crt;
 
 
-rc = gnutls_x509_crt_import(crt, &cert_list[0], GNUTLS_X509_FMT_DER);
-exim_gnutls_peer_err(US"failed to import certificate [gnutls_x509_crt_import(cert 0)]");
 sz = 0;
 rc = gnutls_x509_crt_get_dn(crt, NULL, &sz);
 if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER)
 sz = 0;
 rc = gnutls_x509_crt_get_dn(crt, NULL, &sz);
 if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER)
@@ -1220,6 +1254,7 @@ if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER)
 dn_buf = store_get_perm(sz);
 rc = gnutls_x509_crt_get_dn(crt, CS dn_buf, &sz);
 exim_gnutls_peer_err(US"failed to extract certificate DN [gnutls_x509_crt_get_dn(cert 0)]");
 dn_buf = store_get_perm(sz);
 rc = gnutls_x509_crt_get_dn(crt, CS dn_buf, &sz);
 exim_gnutls_peer_err(US"failed to extract certificate DN [gnutls_x509_crt_get_dn(cert 0)]");
+
 state->peerdn = dn_buf;
 
 return OK;
 state->peerdn = dn_buf;
 
 return OK;
@@ -1484,7 +1519,7 @@ mode, the fflush() happens when smtp_getc() is called. */
 if (!state->tlsp->on_connect)
   {
   smtp_printf("220 TLS go ahead\r\n");
 if (!state->tlsp->on_connect)
   {
   smtp_printf("220 TLS go ahead\r\n");
-  fflush(smtp_out);            /*XXX JGH */
+  fflush(smtp_out);
   }
 
 /* Now negotiate the TLS session. We put our own timer on it, since it seems
   }
 
 /* Now negotiate the TLS session. We put our own timer on it, since it seems
@@ -1526,22 +1561,17 @@ DEBUG(D_tls) debug_printf("gnutls_handshake was successful\n");
 
 /* Verify after the fact */
 
 
 /* Verify after the fact */
 
-if (state->verify_requirement != VERIFY_NONE)
+if (  state->verify_requirement != VERIFY_NONE
+   && !verify_certificate(state, &error))
   {
   {
-  if (!verify_certificate(state, &error))
+  if (state->verify_requirement != VERIFY_OPTIONAL)
     {
     {
-    if (state->verify_requirement == VERIFY_OPTIONAL)
-      {
-      DEBUG(D_tls)
-        debug_printf("TLS: continuing on only because verification was optional, after: %s\n",
-            error);
-      }
-    else
-      {
-      tls_error(US"certificate verification failed", error, NULL);
-      return FAIL;
-      }
+    tls_error(US"certificate verification failed", error, NULL);
+    return FAIL;
     }
     }
+  DEBUG(D_tls)
+    debug_printf("TLS: continuing on only because verification was optional, after: %s\n",
+       error);
   }
 
 /* Figure out peer DN, and if authenticated, etc. */
   }
 
 /* Figure out peer DN, and if authenticated, etc. */
@@ -1551,7 +1581,7 @@ if (rc != OK) return rc;
 
 /* Sets various Exim expansion variables; always safe within server */
 
 
 /* Sets various Exim expansion variables; always safe within server */
 
-extract_exim_vars_from_tls_state(state, TRUE);
+extract_exim_vars_from_tls_state(state);
 
 /* TLS has been set up. Adjust the input functions to read via TLS,
 and initialize appropriately. */
 
 /* TLS has been set up. Adjust the input functions to read via TLS,
 and initialize appropriately. */
@@ -1664,16 +1694,22 @@ else
   }
 
 #ifdef EXPERIMENTAL_OCSP       /* since GnuTLS 3.1.3 */
   }
 
 #ifdef EXPERIMENTAL_OCSP       /* since GnuTLS 3.1.3 */
-if (require_ocsp &&
-    (rc = gnutls_ocsp_status_request_enable_client(state->session, NULL, 0, NULL))
-    != OK)
-  return tls_error(US"cert-status-req", gnutls_strerror(rc), state->host);
+if (require_ocsp)
+  {
+  DEBUG(D_tls) debug_printf("TLS: will request OCSP stapling\n");
+  rc = gnutls_ocsp_status_request_enable_client(state->session,
+                   NULL, 0, NULL);
+  if (rc != OK)
+    return tls_error(US"cert-status-req",
+                   gnutls_strerror(rc), state->host);
+  }
 #endif
 
 gnutls_transport_set_ptr(state->session, (gnutls_transport_ptr)fd);
 state->fd_in = fd;
 state->fd_out = fd;
 
 #endif
 
 gnutls_transport_set_ptr(state->session, (gnutls_transport_ptr)fd);
 state->fd_in = fd;
 state->fd_out = fd;
 
+DEBUG(D_tls) debug_printf("about to gnutls_handshake\n");
 /* There doesn't seem to be a built-in timeout on connection. */
 
 sigalrm_seen = FALSE;
 /* There doesn't seem to be a built-in timeout on connection. */
 
 sigalrm_seen = FALSE;
@@ -1732,7 +1768,7 @@ if ((rc = peer_status(state)) != OK)
 
 /* Sets various Exim expansion variables; may need to adjust for ACL callouts */
 
 
 /* Sets various Exim expansion variables; may need to adjust for ACL callouts */
 
-extract_exim_vars_from_tls_state(state, FALSE);
+extract_exim_vars_from_tls_state(state);
 
 return OK;
 }
 
 return OK;
 }
@@ -1830,8 +1866,9 @@ if (state->xfer_buffer_lwm >= state->xfer_buffer_hwm)
     state->tlsp->active = -1;
     state->tlsp->bits = 0;
     state->tlsp->certificate_verified = FALSE;
     state->tlsp->active = -1;
     state->tlsp->bits = 0;
     state->tlsp->certificate_verified = FALSE;
-    tls_channelbinding_b64 = NULL;     /*XXX JGH */
+    tls_channelbinding_b64 = NULL;
     state->tlsp->cipher = NULL;
     state->tlsp->cipher = NULL;
+    state->tlsp->peercert = NULL;
     state->tlsp->peerdn = NULL;
 
     return smtp_getc();
     state->tlsp->peerdn = NULL;
 
     return smtp_getc();
index bdf910acc584e29fb0f5f8dc22f0da83418fd46e..2f08e43c66cd887e6d1fb74bf73bfd6168958f2e 100644 (file)
@@ -276,7 +276,11 @@ if (state == 0)
     txt);
   tlsp->certificate_verified = FALSE;
   *calledp = TRUE;
     txt);
   tlsp->certificate_verified = FALSE;
   *calledp = TRUE;
-  if (!*optionalp) return 0;    /* reject */
+  if (!*optionalp)
+    {
+    tlsp->peercert = X509_dup(x509ctx->current_cert);
+    return 0;                      /* reject */
+    }
   DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in "
     "tls_try_verify_hosts)\n");
   return 1;                          /* accept */
   DEBUG(D_tls) debug_printf("SSL verify failure overridden (host in "
     "tls_try_verify_hosts)\n");
   return 1;                          /* accept */
@@ -303,6 +307,7 @@ else
   DEBUG(D_tls) debug_printf("SSL%s peer: %s\n",
     *calledp ? "" : " authenticated", txt);
   tlsp->peerdn = txt;
   DEBUG(D_tls) debug_printf("SSL%s peer: %s\n",
     *calledp ? "" : " authenticated", txt);
   tlsp->peerdn = txt;
+  tlsp->peercert = X509_dup(x509ctx->current_cert);
   }
 
 /*XXX JGH: this looks bogus - we set "verified" first time through, which
   }
 
 /*XXX JGH: this looks bogus - we set "verified" first time through, which
@@ -1433,6 +1438,11 @@ DEBUG(D_tls)
     debug_printf("Shared ciphers: %s\n", buf);
   }
 
     debug_printf("Shared ciphers: %s\n", buf);
   }
 
+/* Record the certificate we presented */
+  {
+  X509 * crt = SSL_get_certificate(server_ssl);
+  tls_in.ourcert = crt ? X509_dup(crt) : NULL;
+  }
 
 /* Only used by the server-side tls (tls_in), including tls_getc.
    Client-side (tls_out) reads (seem to?) go via
 
 /* Only used by the server-side tls (tls_in), including tls_getc.
    Client-side (tls_out) reads (seem to?) go via
@@ -1597,12 +1607,13 @@ if (rc <= 0)
 DEBUG(D_tls) debug_printf("SSL_connect succeeded\n");
 
 /* Beware anonymous ciphers which lead to server_cert being NULL */
 DEBUG(D_tls) debug_printf("SSL_connect succeeded\n");
 
 /* Beware anonymous ciphers which lead to server_cert being NULL */
+/*XXX server_cert is never freed... use X509_free() */
 server_cert = SSL_get_peer_certificate (client_ssl);
 if (server_cert)
   {
   tls_out.peerdn = US X509_NAME_oneline(X509_get_subject_name(server_cert),
     CS txt, sizeof(txt));
 server_cert = SSL_get_peer_certificate (client_ssl);
 if (server_cert)
   {
   tls_out.peerdn = US X509_NAME_oneline(X509_get_subject_name(server_cert),
     CS txt, sizeof(txt));
-  tls_out.peerdn = txt;
+  tls_out.peerdn = txt;                /*XXX a static buffer... */
   }
 else
   tls_out.peerdn = NULL;
   }
 else
   tls_out.peerdn = NULL;
@@ -1610,6 +1621,12 @@ else
 construct_cipher_name(client_ssl, cipherbuf, sizeof(cipherbuf), &tls_out.bits);
 tls_out.cipher = cipherbuf;
 
 construct_cipher_name(client_ssl, cipherbuf, sizeof(cipherbuf), &tls_out.bits);
 tls_out.cipher = cipherbuf;
 
+/* Record the certificate we presented */
+  {
+  X509 * crt = SSL_get_certificate(client_ssl);
+  tls_out.ourcert = crt ? X509_dup(crt) : NULL;
+  }
+
 tls_out.active = fd;
 return OK;
 }
 tls_out.active = fd;
 return OK;
 }
@@ -2250,4 +2267,6 @@ for (s=option_spec; *s != '\0'; /**/)
 return TRUE;
 }
 
 return TRUE;
 }
 
+/* vi: aw ai sw=2
+*/
 /* End of tls-openssl.c */
 /* End of tls-openssl.c */
index 972785284b05c923c6b9d3ea6191a8cefd9f4b0c..ad7fe609c26d06e9bcb47f437e31a73cfe08f3cb 100644 (file)
@@ -85,6 +85,7 @@ return TRUE;
 
 #ifdef USE_GNUTLS
 #include "tls-gnu.c"
 
 #ifdef USE_GNUTLS
 #include "tls-gnu.c"
+#include "tlscert-gnu.c"
 
 #define ssl_xfer_buffer (state_server.xfer_buffer)
 #define ssl_xfer_buffer_lwm (state_server.xfer_buffer_lwm)
 
 #define ssl_xfer_buffer (state_server.xfer_buffer)
 #define ssl_xfer_buffer_lwm (state_server.xfer_buffer_lwm)
@@ -94,6 +95,7 @@ return TRUE;
 
 #else
 #include "tls-openssl.c"
 
 #else
 #include "tls-openssl.c"
+#include "tlscert-openssl.c"
 #endif
 
 
 #endif
 
 
diff --git a/src/src/tlscert-gnu.c b/src/src/tlscert-gnu.c
new file mode 100644 (file)
index 0000000..649e93a
--- /dev/null
@@ -0,0 +1,325 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* Copyright (c) Jeremy Harris 2014 */
+
+/* This file provides TLS/SSL support for Exim using the GnuTLS library,
+one of the available supported implementations.  This file is #included into
+tls.c when USE_GNUTLS has been set.
+*/
+
+#include <gnutls/gnutls.h>
+/* needed for cert checks in verification and DN extraction: */
+#include <gnutls/x509.h>
+/* needed to disable PKCS11 autoload unless requested */
+#if GNUTLS_VERSION_NUMBER >= 0x020c00
+# include <gnutls/pkcs11.h>
+#endif
+
+
+/*****************************************************
+*  Export/import a certificate, binary/printable
+*****************************************************/
+int
+tls_export_cert(uschar * buf, size_t buflen, void * cert)
+{
+size_t sz = buflen;
+void * reset_point = store_get(0);
+int fail = 0;
+uschar * cp;
+
+if (gnutls_x509_crt_export((gnutls_x509_crt_t)cert,
+    GNUTLS_X509_FMT_PEM, buf, &sz))
+  return 1;
+if ((cp = string_printing(buf)) != buf)
+  {
+  Ustrncpy(buf, cp, buflen);
+  if (buf[buflen-1])
+    fail = 1;
+  }
+store_reset(reset_point);
+return fail;
+}
+
+int
+tls_import_cert(const uschar * buf, void ** cert)
+{
+void * reset_point = store_get(0);
+gnutls_datum_t datum;
+gnutls_x509_crt_t crt;
+int fail = 0;
+
+gnutls_global_init();
+gnutls_x509_crt_init(&crt);
+
+datum.data = string_unprinting(US buf);
+datum.size = Ustrlen(datum.data);
+if (gnutls_x509_crt_import(crt, &datum, GNUTLS_X509_FMT_PEM))
+  fail = 1;
+else
+  *cert = (void *)crt;
+
+store_reset(reset_point);
+return fail;
+}
+
+void
+tls_free_cert(void * cert)
+{
+gnutls_x509_crt_deinit((gnutls_x509_crt_t) cert);
+gnutls_global_deinit();
+}
+
+/*****************************************************
+*  Certificate field extraction routines
+*****************************************************/
+static uschar *
+time_copy(time_t t)
+{
+uschar * cp = store_get(32);
+struct tm * tp = gmtime(&t);
+size_t len = strftime(CS cp, 32, "%b %e %T %Y %Z", tp);
+return len > 0 ? cp : NULL;
+}
+
+/**/
+
+uschar *
+tls_cert_issuer(void * cert)
+{
+uschar txt[256];
+size_t sz = sizeof(txt);
+return ( gnutls_x509_crt_get_issuer_dn(cert, CS txt, &sz) == 0 )
+  ? string_copy(txt) : NULL;
+}
+
+uschar *
+tls_cert_not_after(void * cert)
+{
+return time_copy(
+  gnutls_x509_crt_get_expiration_time((gnutls_x509_crt_t)cert));
+}
+
+uschar *
+tls_cert_not_before(void * cert)
+{
+return time_copy(
+  gnutls_x509_crt_get_activation_time((gnutls_x509_crt_t)cert));
+}
+
+uschar *
+tls_cert_serial_number(void * cert)
+{
+uschar bin[50], txt[150];
+size_t sz = sizeof(bin);
+uschar * sp;
+uschar * dp;
+
+if (gnutls_x509_crt_get_serial((gnutls_x509_crt_t)cert,
+    bin, &sz) || sz > sizeof(bin))
+  return NULL;
+for(dp = txt, sp = bin; sz; dp += 2, sp++, sz--)
+  sprintf(dp, "%.2x", *sp);
+for(sp = txt; sp[0]=='0' && sp[1]; ) sp++;     /* leading zeroes */
+return string_copy(sp);
+}
+
+uschar *
+tls_cert_signature(void * cert)
+{
+uschar * cp1;
+uschar * cp2;
+uschar * cp3;
+size_t len = 0;
+int ret;
+
+if ((ret = gnutls_x509_crt_get_signature((gnutls_x509_crt_t)cert, cp1, &len)) !=
+       GNUTLS_E_SHORT_MEMORY_BUFFER)
+  {
+  fprintf(stderr, "%s: gs0 fail: %s\n", __FUNCTION__, gnutls_strerror(ret));
+  return NULL;
+  }
+
+cp1 = store_get(len*4+1);
+
+if (gnutls_x509_crt_get_signature((gnutls_x509_crt_t)cert, cp1, &len) != 0)
+  {
+  fprintf(stderr, "%s: gs1 fail\n", __FUNCTION__);
+  return NULL;
+  }
+
+for(cp3 = cp2 = cp1+len; cp1 < cp2; cp3 += 3, cp1++)
+  sprintf(cp3, "%.2x ", *cp1);
+cp3[-1]= '\0';
+
+return cp2;
+}
+
+uschar *
+tls_cert_signature_algorithm(void * cert)
+{
+gnutls_sign_algorithm_t algo =
+  gnutls_x509_crt_get_signature_algorithm((gnutls_x509_crt_t)cert);
+return algo < 0 ? NULL : string_copy(gnutls_sign_get_name(algo));
+}
+
+uschar *
+tls_cert_subject(void * cert)
+{
+static uschar txt[256];
+size_t sz = sizeof(txt);
+return ( gnutls_x509_crt_get_dn(cert, CS txt, &sz) == 0 )
+  ? string_copy(txt) : NULL;
+}
+
+uschar *
+tls_cert_version(void * cert)
+{
+return string_sprintf("%d", gnutls_x509_crt_get_version(cert));
+}
+
+uschar *
+tls_cert_ext_by_oid(void * cert, uschar * oid, int idx)
+{
+uschar * cp1 = NULL;
+uschar * cp2;
+uschar * cp3;
+size_t siz = 0;
+unsigned int crit;
+int ret;
+
+ret = gnutls_x509_crt_get_extension_by_oid ((gnutls_x509_crt_t)cert,
+  oid, idx, cp1, &siz, &crit);
+if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+  {
+  fprintf(stderr, "%s: ge0 fail: %s\n", __FUNCTION__, gnutls_strerror(ret));
+  return NULL;
+  }
+
+cp1 = store_get(siz*4 + 1);
+
+ret = gnutls_x509_crt_get_extension_by_oid ((gnutls_x509_crt_t)cert,
+  oid, idx, cp1, &siz, &crit);
+if (ret < 0)
+  {
+  fprintf(stderr, "%s: ge1 fail: %s\n", __FUNCTION__, gnutls_strerror(ret));
+  return NULL;
+  }
+
+/* binary data, DER encoded */
+
+/* just dump for now */
+for(cp3 = cp2 = cp1+siz; cp1 < cp2; cp3 += 3, cp1++)
+  sprintf(cp3, "%.2x ", *cp1);
+cp3[-1]= '\0';
+
+return cp2;
+}
+
+uschar *
+tls_cert_subject_altname(void * cert)
+{
+uschar * cp = NULL;
+size_t siz = 0;
+unsigned int crit;
+int ret;
+
+ret = gnutls_x509_crt_get_subject_alt_name ((gnutls_x509_crt_t)cert,
+  0, cp, &siz, &crit);
+switch(ret)
+  {
+  case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE:
+    return NULL;
+  case GNUTLS_E_SHORT_MEMORY_BUFFER:
+    break;
+  default:
+    expand_string_message = 
+      string_sprintf("%s: gs0 fail: %d %s\n", __FUNCTION__,
+       ret, gnutls_strerror(ret));
+    return NULL;
+  }
+
+cp = store_get(siz+1);
+ret = gnutls_x509_crt_get_subject_alt_name ((gnutls_x509_crt_t)cert,
+  0, cp, &siz, &crit);
+if (ret < 0)
+  {
+  expand_string_message = 
+    string_sprintf("%s: gs1 fail: %d %s\n", __FUNCTION__,
+      ret, gnutls_strerror(ret));
+  return NULL;
+  }
+cp[siz] = '\0';
+return cp;
+}
+
+uschar *
+tls_cert_ocsp_uri(void * cert)
+{
+#if GNUTLS_VERSION_NUMBER >= 0x030000
+gnutls_datum_t uri;
+unsigned int crit;
+int ret = gnutls_x509_crt_get_authority_info_access((gnutls_x509_crt_t)cert,
+       0, GNUTLS_IA_OCSP_URI, &uri, &crit);
+
+if (ret >= 0)
+  return string_copyn(uri.data, uri.size);
+
+if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+  expand_string_message = 
+    string_sprintf("%s: gai fail: %d %s\n", __FUNCTION__,
+      ret, gnutls_strerror(ret));
+
+return NULL;
+
+#else
+
+expand_string_message = 
+  string_sprintf("%s: OCSP support with GnuTLS requires version 3.0.0\n",
+    __FUNCTION__);
+return NULL;
+
+#endif
+}
+
+uschar *
+tls_cert_crl_uri(void * cert)
+{
+int ret;
+uschar * cp = NULL;
+size_t siz = 0;
+
+ret = gnutls_x509_crt_get_crl_dist_points ((gnutls_x509_crt_t)cert,
+  0, cp, &siz, NULL, NULL);
+switch(ret)
+  {
+  case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE:
+    return NULL;
+  case GNUTLS_E_SHORT_MEMORY_BUFFER:
+    break;
+  default:
+    expand_string_message = 
+      string_sprintf("%s: gc0 fail: %d %s\n", __FUNCTION__,
+       ret, gnutls_strerror(ret));
+    return NULL;
+  }
+
+cp = store_get(siz+1);
+ret = gnutls_x509_crt_get_crl_dist_points ((gnutls_x509_crt_t)cert,
+  0, cp, &siz, NULL, NULL);
+if (ret < 0)
+  {
+  expand_string_message = 
+    string_sprintf("%s: gs1 fail: %d %s\n", __FUNCTION__,
+      ret, gnutls_strerror(ret));
+  return NULL;
+  }
+cp[siz] = '\0';
+return cp;
+}
+
+
+/* vi: aw ai sw=2
+*/
+/* End of tlscert-gnu.c */
diff --git a/src/src/tlscert-openssl.c b/src/src/tlscert-openssl.c
new file mode 100644 (file)
index 0000000..008cf54
--- /dev/null
@@ -0,0 +1,293 @@
+/*************************************************
+*     Exim - an Internet mail transport agent    *
+*************************************************/
+
+/* Copyright (c) Jeremy Harris 2014 */
+
+/* This module provides TLS (aka SSL) support for Exim using the OpenSSL
+library. It is #included into the tls.c file when that library is used.
+*/
+
+
+/* Heading stuff */
+
+#include <openssl/lhash.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+
+
+/*****************************************************
+*  Export/import a certificate, binary/printable
+*****************************************************/
+int
+tls_export_cert(uschar * buf, size_t buflen, void * cert)
+{
+BIO * bp = BIO_new(BIO_s_mem());
+int fail;
+
+if ((fail = PEM_write_bio_X509(bp, (X509 *)cert) ? 0 : 1))
+  log_write(0, LOG_MAIN, "TLS error in certificate export: %s",
+    ERR_error_string(ERR_get_error(), NULL));
+else
+  {
+  char * cp = CS buf;
+  int n;
+  buflen -= 2;
+  for(;;)
+    {
+    if ((n = BIO_gets(bp, cp, (int)buflen)) <= 0) break;
+    cp += n+1;
+    buflen -= n+1;
+    cp[-2] = '\\'; cp[-1] = 'n'; /* newline->"\n" */
+    }                           /* compat with string_printing() */
+  *cp = '\0';
+  }
+
+BIO_free(bp);
+return fail;
+}
+
+int
+tls_import_cert(const uschar * buf, void ** cert)
+{
+void * reset_point = store_get(0);
+const uschar * cp = string_unprinting(US buf);
+BIO * bp;
+X509 * x;
+
+bp = BIO_new_mem_buf(US cp, -1);
+x = PEM_read_bio_X509(bp, NULL, 0, NULL);
+int fail = 0;
+if (!x)
+  fail = 1;
+else
+  *cert = (void *)x;
+BIO_free(bp);
+store_reset(reset_point);
+return fail;
+}
+
+void
+tls_free_cert(void * cert)
+{
+X509_free((X509 *)cert);
+}
+
+/*****************************************************
+*  Certificate field extraction routines
+*****************************************************/
+static uschar *
+bio_string_copy(BIO * bp, int len)
+{
+uschar * cp = "";
+len = len > 0 ? (int) BIO_get_mem_data(bp, &cp) : 0;
+cp = string_copyn(cp, len);
+BIO_free(bp);
+return cp;
+}
+
+static uschar *
+asn1_time_copy(const ASN1_TIME * time)
+{
+BIO * bp = BIO_new(BIO_s_mem());
+int len = ASN1_TIME_print(bp, time);
+return bio_string_copy(bp, len);
+}
+
+static uschar *
+x509_name_copy(X509_NAME * name)
+{
+BIO * bp = BIO_new(BIO_s_mem());
+int len_good =
+  X509_NAME_print_ex(bp, name, 0, XN_FLAG_RFC2253) >= 0
+  ? 1 : 0;
+return bio_string_copy(bp, len_good);
+}
+
+/**/
+
+uschar *
+tls_cert_issuer(void * cert)
+{
+return x509_name_copy(X509_get_issuer_name((X509 *)cert));
+}
+
+uschar *
+tls_cert_not_before(void * cert)
+{
+return asn1_time_copy(X509_get_notBefore((X509 *)cert));
+}
+
+uschar *
+tls_cert_not_after(void * cert)
+{
+return asn1_time_copy(X509_get_notAfter((X509 *)cert));
+}
+
+uschar *
+tls_cert_serial_number(void * cert)
+{
+uschar txt[256];
+BIO * bp = BIO_new(BIO_s_mem());
+int len = i2a_ASN1_INTEGER(bp, X509_get_serialNumber((X509 *)cert));
+
+if (len < sizeof(txt))
+  BIO_read(bp, txt, len);
+else
+  len = 0;
+BIO_free(bp);
+return string_copynlc(txt, len);       /* lowercase */
+}
+
+uschar *
+tls_cert_signature(void * cert)
+{
+BIO * bp = BIO_new(BIO_s_mem());
+uschar * cp = NULL;
+
+if (X509_print_ex(bp, (X509 *)cert, 0,
+  X509_FLAG_NO_HEADER | X509_FLAG_NO_VERSION | X509_FLAG_NO_SERIAL | 
+  X509_FLAG_NO_SIGNAME | X509_FLAG_NO_ISSUER | X509_FLAG_NO_VALIDITY | 
+  X509_FLAG_NO_SUBJECT | X509_FLAG_NO_PUBKEY | X509_FLAG_NO_EXTENSIONS | 
+  /* X509_FLAG_NO_SIGDUMP is the missing one */
+  X509_FLAG_NO_AUX) == 1)
+  {
+  long len = BIO_get_mem_data(bp, &cp);
+  cp = string_copyn(cp, len);
+  }
+BIO_free(bp);
+return cp;
+}
+
+uschar *
+tls_cert_signature_algorithm(void * cert)
+{
+return string_copy(OBJ_nid2ln(X509_get_signature_type((X509 *)cert)));
+}
+
+uschar *
+tls_cert_subject(void * cert)
+{
+return x509_name_copy(X509_get_subject_name((X509 *)cert));
+}
+
+uschar *
+tls_cert_version(void * cert)
+{
+return string_sprintf("%d", X509_get_version((X509 *)cert));
+}
+
+uschar *
+tls_cert_ext_by_oid(void * cert, uschar * oid, int idx)
+{
+int nid = OBJ_create(oid, "", "");
+int nidx = X509_get_ext_by_NID((X509 *)cert, nid, idx);
+X509_EXTENSION * ex = X509_get_ext((X509 *)cert, nidx);
+ASN1_OCTET_STRING * adata = X509_EXTENSION_get_data(ex);
+BIO * bp = BIO_new(BIO_s_mem());
+long len;
+uschar * cp1;
+uschar * cp2;
+uschar * cp3;
+
+M_ASN1_OCTET_STRING_print(bp, adata);
+/* binary data, DER encoded */
+
+/* just dump for now */
+len = BIO_get_mem_data(bp, &cp1);
+cp3 = cp2 = store_get(len*3+1);
+
+while(len)
+  {
+  sprintf(cp2, "%.2x ", *cp1++);
+  cp2 += 3;
+  len--;
+  }
+cp2[-1] = '\0';
+
+return cp3;
+}
+
+uschar *
+tls_cert_subject_altname(void * cert)
+{
+uschar * cp;
+STACK_OF(GENERAL_NAME) * san = (STACK_OF(GENERAL_NAME) *)
+  X509_get_ext_d2i((X509 *)cert, NID_subject_alt_name, NULL, NULL);
+
+if (!san) return NULL;
+
+while (sk_GENERAL_NAME_num(san) > 0)
+  {
+  GENERAL_NAME * namePart = sk_GENERAL_NAME_pop(san);
+  switch (namePart->type)
+    {
+    case GEN_URI:
+      cp = string_sprintf("URI=%s",
+           ASN1_STRING_data(namePart->d.uniformResourceIdentifier));
+      return cp;
+    case GEN_EMAIL:
+      cp = string_sprintf("email=%s",
+           ASN1_STRING_data(namePart->d.rfc822Name));
+      return cp;
+    default:
+      cp = string_sprintf("Unrecognisable");
+      return cp;
+    }
+  }
+
+/* sk_GENERAL_NAME_pop_free(gen_names, GENERAL_NAME_free);  ??? */
+return cp;
+}
+
+uschar *
+tls_cert_ocsp_uri(void * cert)
+{
+STACK_OF(ACCESS_DESCRIPTION) * ads = (STACK_OF(ACCESS_DESCRIPTION) *)
+  X509_get_ext_d2i((X509 *)cert, NID_info_access, NULL, NULL);
+int adsnum = sk_ACCESS_DESCRIPTION_num(ads);
+int i;
+
+for (i = 0; i < adsnum; i++)
+  {
+  ACCESS_DESCRIPTION * ad = sk_ACCESS_DESCRIPTION_value(ads, i);
+
+  if (ad && OBJ_obj2nid(ad->method) == NID_ad_OCSP)
+    return string_copy( ASN1_STRING_data(ad->location->d.ia5) );
+  }
+
+return NULL;
+}
+
+uschar *
+tls_cert_crl_uri(void * cert)
+{
+STACK_OF(DIST_POINT) * dps = (STACK_OF(DIST_POINT) *)
+  X509_get_ext_d2i((X509 *)cert,  NID_crl_distribution_points,
+    NULL, NULL);
+DIST_POINT * dp;
+int dpsnum = sk_DIST_POINT_num(dps);
+int i;
+
+if (dps) for (i = 0; i < dpsnum; i++)
+  if ((dp = sk_DIST_POINT_value(dps, i)))
+    {
+    STACK_OF(GENERAL_NAME) * names = dp->distpoint->name.fullname;
+    GENERAL_NAME * np;
+    int nnum = sk_GENERAL_NAME_num(names);
+    int j;
+
+    for (j = 0; j < nnum; j++)
+      if (  (np = sk_GENERAL_NAME_value(names, j))
+        && np->type == GEN_URI
+        )
+       return string_copy(ASN1_STRING_data(
+         np->d.uniformResourceIdentifier));
+    }
+return NULL;
+}
+
+/* vi: aw ai sw=2
+*/
+/* End of tlscert-openssl.c */
index 9e0ab15569ae50a2471ae3aaac29bf675773e597..16c2b601166ff8ceb02e01e342e5bb5bee029e0b 100644 (file)
@@ -1221,6 +1221,8 @@ outblock.authenticating = FALSE;
 
 tls_out.bits = 0;
 tls_out.cipher = NULL; /* the one we may use for this transport */
 
 tls_out.bits = 0;
 tls_out.cipher = NULL; /* the one we may use for this transport */
+tls_out.ourcert = NULL;
+tls_out.peercert = NULL;
 tls_out.peerdn = NULL;
 #if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
 tls_out.sni = NULL;
 tls_out.peerdn = NULL;
 #if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
 tls_out.sni = NULL;
@@ -1480,6 +1482,8 @@ if (tls_offered && !suppress_tls &&
       if (addr->transport_return == PENDING_DEFER)
         {
         addr->cipher = tls_out.cipher;
       if (addr->transport_return == PENDING_DEFER)
         {
         addr->cipher = tls_out.cipher;
+        addr->ourcert = tls_out.ourcert;
+        addr->peercert = tls_out.peercert;
         addr->peerdn = tls_out.peerdn;
         }
       }
         addr->peerdn = tls_out.peerdn;
         }
       }
@@ -2417,8 +2421,6 @@ tls_close(FALSE, TRUE);
 #endif
 
 /* Close the socket, and return the appropriate value, first setting
 #endif
 
 /* Close the socket, and return the appropriate value, first setting
-continue_transport and continue_hostname NULL to prevent any other addresses
-that may include the host from trying to re-use a continuation socket. This
 works because the NULL setting is passed back to the calling process, and
 remote_max_parallel is forced to 1 when delivering over an existing connection,
 
 works because the NULL setting is passed back to the calling process, and
 remote_max_parallel is forced to 1 when delivering over an existing connection,
 
@@ -2519,6 +2521,8 @@ for (addr = addrlist; addr != NULL; addr = addr->next)
   addr->message = NULL;
   #ifdef SUPPORT_TLS
   addr->cipher = NULL;
   addr->message = NULL;
   #ifdef SUPPORT_TLS
   addr->cipher = NULL;
+  addr->ourcert = NULL;
+  addr->peercert = NULL;
   addr->peerdn = NULL;
   #endif
   }
   addr->peerdn = NULL;
   #endif
   }
index e8358da258d8fed19fc2718b5397c21e3cf6cfa3..b4d0348cadb4658218e11213eb00fa59fe126307 100644 (file)
@@ -20,11 +20,11 @@ queue_run_in_order
 
 tls_advertise_hosts = 127.0.0.1 : HOSTIPV4
 
 
 tls_advertise_hosts = 127.0.0.1 : HOSTIPV4
 
-tls_certificate = DIR/aux-fixed/cert1
-tls_privatekey = DIR/aux-fixed/cert1
+tls_certificate = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.pem
+tls_privatekey = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.unlocked.key
 
 tls_verify_hosts = HOSTIPV4
 
 tls_verify_hosts = HOSTIPV4
-tls_verify_certificates = DIR/aux-fixed/cert2
+tls_verify_certificates = DIR/aux-fixed/exim-ca/example.com/server2.example.com/ca_chain.pem
 
 
 # ------ ACL ------
 
 
 # ------ ACL ------
@@ -41,7 +41,23 @@ check_recipient:
                       DHE_RSA_AES_256_CBC_SHA1 : \
                       DHE_RSA_3DES_EDE_CBC_SHA : \
                       RSA_AES_256_CBC_SHA1
                       DHE_RSA_AES_256_CBC_SHA1 : \
                       DHE_RSA_3DES_EDE_CBC_SHA : \
                       RSA_AES_256_CBC_SHA1
-  accept
+  warn    logwrite =  ${if def:tls_in_ourcert \
+               {Our cert SN: <${certextract{subject}{$tls_in_ourcert}}>} \
+               {We did not present a cert}}
+  accept  condition = ${if !def:tls_in_peercert}
+         logwrite =  Peer did not present a cert
+  accept  logwrite =  Peer cert:
+          logwrite =  ver ${certextract {version}{$tls_in_peercert}}
+         logwrite =  SR  <${certextract {serial_number}{$tls_in_peercert}}>
+         logwrite =  SN  <${certextract {subject}      {$tls_in_peercert}}>
+          logwrite =  IN  <${certextract {issuer}      {$tls_in_peercert}}>
+          logwrite =  NB  <${certextract {notbefore}   {$tls_in_peercert}}>
+          logwrite =  NA  <${certextract {notafter}    {$tls_in_peercert}}>
+          logwrite =  SA  <${certextract {signature_algorithm}{$tls_in_peercert}}>
+          logwrite =  SG  <${certextract {signature}   {$tls_in_peercert}}>
+         logwrite =       ${certextract {subject_altname}{$tls_in_peercert} {SAN <$value>}{(no SAN)}}
+#        logwrite =       ${certextract {ocsp_uri}     {$tls_in_peercert} {OCU <$value>}{(no OCU)}}
+         logwrite =       ${certextract {crl_uri}      {$tls_in_peercert} {CRU <$value>}{(no CRU)}}
 
 
 # ----- Routers -----
 
 
 # ----- Routers -----
index 7f5771c0ed01babe0a19f3e14ad86158e666f427..5332801dc6c0bc6d202d0f795c1419949e48775b 100644 (file)
@@ -20,11 +20,11 @@ queue_run_in_order
 
 tls_advertise_hosts = 127.0.0.1 : HOSTIPV4
 
 
 tls_advertise_hosts = 127.0.0.1 : HOSTIPV4
 
-tls_certificate = DIR/aux-fixed/cert1
-tls_privatekey = DIR/aux-fixed/cert1
+tls_certificate = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.pem
+tls_privatekey = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.unlocked.key
 
 tls_verify_hosts = HOSTIPV4
 
 tls_verify_hosts = HOSTIPV4
-tls_verify_certificates = DIR/aux-fixed/cert2
+tls_verify_certificates = DIR/aux-fixed/exim-ca/example.com/server2.example.com/ca_chain.pem
 
 
 # ------ ACL ------
 
 
 # ------ ACL ------
@@ -42,7 +42,23 @@ check_recipient:
                      DHE-RSA-AES256-GCM-SHA384 : \
                       DHE_RSA_AES_256_CBC_SHA1 : \
                       DHE_RSA_3DES_EDE_CBC_SHA
                      DHE-RSA-AES256-GCM-SHA384 : \
                       DHE_RSA_AES_256_CBC_SHA1 : \
                       DHE_RSA_3DES_EDE_CBC_SHA
-  accept
+  warn    logwrite =  ${if def:tls_in_ourcert \
+               {Our cert SN: <${certextract{subject}{$tls_in_ourcert}}>} \
+               {We did not present a cert}}
+  accept  condition = ${if !def:tls_in_peercert}
+         logwrite =  Peer did not present a cert
+  accept  logwrite =  Peer cert:
+          logwrite =  ver ${certextract {version}{$tls_in_peercert}}
+         logwrite =  SR  <${certextract {serial_number}{$tls_in_peercert}}>
+         logwrite =  SN  <${certextract {subject}      {$tls_in_peercert}}>
+          logwrite =  IN  <${certextract {issuer}      {$tls_in_peercert}}>
+          logwrite =  NB  <${certextract {notbefore}   {$tls_in_peercert}}>
+          logwrite =  NA  <${certextract {notafter}    {$tls_in_peercert}}>
+          logwrite =  SA  <${certextract {signature_algorithm}{$tls_in_peercert}}>
+          logwrite =  SG  <${certextract {signature}   {$tls_in_peercert}}>
+         logwrite =       ${certextract {subject_altname}{$tls_in_peercert} {SAN <$value>}{(no SAN)}}
+         logwrite =       ${certextract {ocsp_uri}     {$tls_in_peercert} {OCU <$value>}{(no OCU)}}
+         logwrite =       ${certextract {crl_uri}      {$tls_in_peercert} {CRU <$value>}{(no CRU)}}
 
 
 # ----- Routers -----
 
 
 # ----- Routers -----
diff --git a/test/confs/5750 b/test/confs/5750
new file mode 100644 (file)
index 0000000..a4762bd
--- /dev/null
@@ -0,0 +1,95 @@
+# Exim test configuration 5750 (dup of 5760)
+# $tls_out_peercert - GnuTLS
+
+SERVER=
+
+exim_path = EXIM_PATH
+host_lookup_order = bydns
+primary_hostname = myhost.test.ex
+rfc1413_query_timeout = 0s
+spool_directory = DIR/spool
+log_file_path = DIR/spool/log/SERVER%slog
+gecos_pattern = ""
+gecos_name = CALLER_NAME
+
+# ----- Main settings -----
+
+acl_smtp_rcpt = accept
+
+log_selector =  +tls_peerdn
+
+queue_only
+queue_run_in_order
+
+tls_advertise_hosts = *
+
+tls_certificate = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.pem
+tls_privatekey = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.unlocked.key
+
+tls_verify_hosts = *
+tls_verify_certificates = DIR/aux-fixed/exim-ca/example.com/server2.example.com/ca_chain.pem
+
+#
+
+begin acl
+logger:
+  warn   logwrite =  $acl_arg1 $tpda_delivery_local_part
+  warn   logwrite =  ${if !def:tls_out_ourcert \
+               {NO CLENT CERT presented} \
+               {Our cert SN: ${certextract{subject}{$tls_out_ourcert}}}}
+  accept condition = ${if !def:tls_out_peercert}
+        logwrite =  No Peer cert
+  accept logwrite = Peer cert:
+        logwrite =  ver <${certextract {version}       {$tls_out_peercert}}>
+        logwrite =  SN  <${certextract {subject}       {$tls_out_peercert}}>
+         logwrite =  IN  <${certextract {issuer}       {$tls_out_peercert}}>
+         logwrite =  NB  <${certextract {notbefore}    {$tls_out_peercert}}>
+         logwrite =  NA  <${certextract {notafter}     {$tls_out_peercert}}>
+         logwrite =  SA  <${certextract {signature_algorithm}{$tls_out_peercert}}>
+         logwrite =  SG  <${certextract {signature}    {$tls_out_peercert}}>
+        logwrite =       ${certextract {subject_altname}{$tls_out_peercert}{SAN <$value>}{(no SAN)}}
+        logwrite =       ${certextract {ocsp_uri}      {$tls_out_peercert} {OCU <$value>}{(no OCU)}}
+        logwrite =       ${certextract {crl_uri}       {$tls_out_peercert} {CRU <$value>}{(no CRU)}}
+
+
+# ----- Routers -----
+
+begin routers
+
+client:
+  driver = accept
+  condition = ${if eq {SERVER}{server}{no}{yes}}
+  retry_use_local_part
+  transport = send_to_server
+
+
+# ----- Transports -----
+
+begin transports
+
+send_to_server:
+  driver = smtp
+  allow_localhost
+  hosts = 127.0.0.1
+  port = PORT_D
+
+  tls_certificate = DIR/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.pem
+  tls_privatekey = DIR/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.unlocked.key
+
+  tls_verify_certificates = DIR/aux-fixed/exim-ca/\
+       ${if eq {$local_part}{good}\
+{example.com/server1.example.com/ca_chain.pem}\
+{example.net/server1.example.net/ca_chain.pem}}
+
+  tpda_delivery_action =   ${acl {logger} {delivery} {$domain} }
+  tpda_host_defer_action = ${acl {logger} {deferral} {$domain} }
+
+# ----- Retry -----
+
+
+begin retry
+
+* * F,5d,10s
+
+
+# End
diff --git a/test/confs/5760 b/test/confs/5760
new file mode 100644 (file)
index 0000000..0e11ab0
--- /dev/null
@@ -0,0 +1,95 @@
+# Exim test configuration 5760 (dup of 5750)
+# $tls_out_peercert - OpenSSL
+
+SERVER=
+
+exim_path = EXIM_PATH
+host_lookup_order = bydns
+primary_hostname = myhost.test.ex
+rfc1413_query_timeout = 0s
+spool_directory = DIR/spool
+log_file_path = DIR/spool/log/SERVER%slog
+gecos_pattern = ""
+gecos_name = CALLER_NAME
+
+# ----- Main settings -----
+
+acl_smtp_rcpt = accept
+
+log_selector =  +tls_peerdn
+
+queue_only
+queue_run_in_order
+
+tls_advertise_hosts = *
+
+tls_certificate = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.pem
+tls_privatekey = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.unlocked.key
+
+tls_verify_hosts = *
+tls_verify_certificates = DIR/aux-fixed/exim-ca/example.com/server2.example.com/ca_chain.pem
+
+#
+
+begin acl
+logger:
+  warn   logwrite =  $acl_arg1 $tpda_delivery_local_part
+  warn   logwrite =  ${if !def:tls_out_ourcert \
+               {NO CLENT CERT presented} \
+               {Our cert SN: ${certextract{subject}{$tls_out_ourcert}}}}
+  accept condition = ${if !def:tls_out_peercert}
+        logwrite =  No Peer cert
+  accept logwrite = Peer cert:
+        logwrite =  ver <${certextract {version}       {$tls_out_peercert}}>
+        logwrite =  SN  <${certextract {subject}       {$tls_out_peercert}}>
+         logwrite =  IN  <${certextract {issuer}       {$tls_out_peercert}}>
+         logwrite =  NB  <${certextract {notbefore}    {$tls_out_peercert}}>
+         logwrite =  NA  <${certextract {notafter}     {$tls_out_peercert}}>
+         logwrite =  SA  <${certextract {signature_algorithm}{$tls_out_peercert}}>
+         logwrite =  SG  <${certextract {signature}    {$tls_out_peercert}}>
+        logwrite =       ${certextract {subject_altname}{$tls_out_peercert}{SAN <$value>}{(no SAN)}}
+        logwrite =       ${certextract {ocsp_uri}      {$tls_out_peercert} {OCU <$value>}{(no OCU)}}
+        logwrite =       ${certextract {crl_uri}       {$tls_out_peercert} {CRU <$value>}{(no CRU)}}
+
+
+# ----- Routers -----
+
+begin routers
+
+client:
+  driver = accept
+  condition = ${if eq {SERVER}{server}{no}{yes}}
+  retry_use_local_part
+  transport = send_to_server
+
+
+# ----- Transports -----
+
+begin transports
+
+send_to_server:
+  driver = smtp
+  allow_localhost
+  hosts = 127.0.0.1
+  port = PORT_D
+
+  tls_certificate = DIR/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.pem
+  tls_privatekey = DIR/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.unlocked.key
+
+  tls_verify_certificates = DIR/aux-fixed/exim-ca/\
+       ${if eq {$local_part}{good}\
+{example.com/server1.example.com/ca_chain.pem}\
+{example.net/server1.example.net/ca_chain.pem}}
+
+  tpda_delivery_action =   ${acl {logger} {delivery} {$domain} }
+  tpda_host_defer_action = ${acl {logger} {deferral} {$domain} }
+
+# ----- Retry -----
+
+
+begin retry
+
+* * F,5d,10s
+
+
+# End
index 774495514786280fd66c8596a50c3dfc935ae7c3..e2777b459a6de8f9bd9efd6aa72dd67ae4f906c1 100644 (file)
@@ -1,8 +1,24 @@
 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 Our cert SN: <CN=server1.example.com>
+1999-03-02 09:44:33 Peer did not present a cert
 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex H=[127.0.0.1] P=smtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 S=sss
 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex H=[127.0.0.1] P=smtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 S=sss
+1999-03-02 09:44:33 Our cert SN: <CN=server1.example.com>
+1999-03-02 09:44:33 Peer did not present a cert
 1999-03-02 09:44:33 10HmaY-0005vi-00 <= "name with spaces"@test.ex H=[127.0.0.1] P=smtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 S=sss
 1999-03-02 09:44:33 TLS error on connection from (rhu.barb) [ip4.ip4.ip4.ip4] (gnutls_handshake): The peer did not send any certificate.
 1999-03-02 09:44:33 10HmaY-0005vi-00 <= "name with spaces"@test.ex H=[127.0.0.1] P=smtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 S=sss
 1999-03-02 09:44:33 TLS error on connection from (rhu.barb) [ip4.ip4.ip4.ip4] (gnutls_handshake): The peer did not send any certificate.
-1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex H=[ip4.ip4.ip4.ip4] P=smtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 DN="C=UK,O=The Exim Maintainers,OU=Test Suite,CN=Phil Pennock" S=sss
+1999-03-02 09:44:33 Our cert SN: <CN=server1.example.com>
+1999-03-02 09:44:33 Peer cert:
+1999-03-02 09:44:33 ver 3
+1999-03-02 09:44:33 SR  <c9>
+1999-03-02 09:44:33 SN  <CN=server2.example.com>
+1999-03-02 09:44:33 IN  <O=example.com,CN=clica Signing Cert>
+1999-03-02 09:44:33 NB  <Nov  1 12:34:06 2012 GMT>
+1999-03-02 09:44:33 NA  <Jan  1 12:34:06 2038 GMT>
+1999-03-02 09:44:33 SA  <RSA-SHA1>
+1999-03-02 09:44:33 SG  <6c 37 41 26 4d 5d f4 b5 31 10 67 ca fb 64 b6 22 98 62 f7 1e 95 7b 6c e6 74 47 21 f4 5e 89 36 3e b9 9c 8a c5 52 bb c4 af 12 93 26 3b d7 3d e0 56 71 1e 1d 21 20 02 ed f0 4e d5 5e 45 42 fd 3c 38 41 54 83 86 0b 3b bf c5 47 39 ff 15 ea 93 dc fd c7 3d 18 58 59 ca dd 2a d8 b9 f9 2f b9 76 93 f4 ae e3 91 56 80 2f 8c 04 2f ad 57 ef d2 51 19 f4 b4 ef 32 9c ac 3a 7c 0d b8 39 db b1 e3 30 73 1a>
+1999-03-02 09:44:33 SAN <server2.example.com>
+1999-03-02 09:44:33 CRU <http://crl.example.com/latest.crl>
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex H=[ip4.ip4.ip4.ip4] P=smtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 DN="CN=server2.example.com" S=sss
 1999-03-02 09:44:33 Start queue run: pid=pppp -qf
 1999-03-02 09:44:33 10HmaX-0005vi-00 => CALLER <CALLER@test.ex> R=abc T=local_delivery
 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
 1999-03-02 09:44:33 Start queue run: pid=pppp -qf
 1999-03-02 09:44:33 10HmaX-0005vi-00 => CALLER <CALLER@test.ex> R=abc T=local_delivery
 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
index da4ee49d721e183ae725dce51f4ce00a12566bbd..6e0713f41757a562c65dec64757b34bf2b36a357 100644 (file)
@@ -1,9 +1,26 @@
 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
 1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 Our cert SN: <CN=server1.example.com>
+1999-03-02 09:44:33 Peer did not present a cert
 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex H=[127.0.0.1] P=smtps X=TLSv1:AES256-SHA:256 S=sss
 1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex H=[127.0.0.1] P=smtps X=TLSv1:AES256-SHA:256 S=sss
+1999-03-02 09:44:33 Our cert SN: <CN=server1.example.com>
+1999-03-02 09:44:33 Peer did not present a cert
 1999-03-02 09:44:33 10HmaY-0005vi-00 <= "name with spaces"@test.ex H=[127.0.0.1] P=smtps X=TLSv1:AES256-SHA:256 S=sss
 1999-03-02 09:44:33 TLS error on connection from (rhu.barb) [ip4.ip4.ip4.ip4] (SSL_accept): error: <<detail omitted>>
 1999-03-02 09:44:33 TLS client disconnected cleanly (rejected our certificate?)
 1999-03-02 09:44:33 10HmaY-0005vi-00 <= "name with spaces"@test.ex H=[127.0.0.1] P=smtps X=TLSv1:AES256-SHA:256 S=sss
 1999-03-02 09:44:33 TLS error on connection from (rhu.barb) [ip4.ip4.ip4.ip4] (SSL_accept): error: <<detail omitted>>
 1999-03-02 09:44:33 TLS client disconnected cleanly (rejected our certificate?)
-1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex H=[ip4.ip4.ip4.ip4] P=smtps X=TLSv1:AES256-SHA:256 DN="/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock" S=sss
+1999-03-02 09:44:33 Our cert SN: <CN=server1.example.com>
+1999-03-02 09:44:33 Peer cert:
+1999-03-02 09:44:33 ver 2
+1999-03-02 09:44:33 SR  <c9>
+1999-03-02 09:44:33 SN  <CN=server2.example.com>
+1999-03-02 09:44:33 IN  <CN=clica Signing Cert,O=example.com>
+1999-03-02 09:44:33 NB  <Nov  1 12:34:06 2012 GMT>
+1999-03-02 09:44:33 NA  <Jan  1 12:34:06 2038 GMT>
+1999-03-02 09:44:33 SA  <undefined>
+1999-03-02 09:44:33 SG  <    Signature Algorithm: sha1WithRSAEncryption\n         6c:37:41:26:4d:5d:f4:b5:31:10:67:ca:fb:64:b6:22:98:62:\n         f7:1e:95:7b:6c:e6:74:47:21:f4:5e:89:36:3e:b9:9c:8a:c5:\n         52:bb:c4:af:12:93:26:3b:d7:3d:e0:56:71:1e:1d:21:20:02:\n         ed:f0:4e:d5:5e:45:42:fd:3c:38:41:54:83:86:0b:3b:bf:c5:\n         47:39:ff:15:ea:93:dc:fd:c7:3d:18:58:59:ca:dd:2a:d8:b9:\n         f9:2f:b9:76:93:f4:ae:e3:91:56:80:2f:8c:04:2f:ad:57:ef:\n         d2:51:19:f4:b4:ef:32:9c:ac:3a:7c:0d:b8:39:db:b1:e3:30:\n         73:1a\n>
+1999-03-02 09:44:33 SAN <Unrecognisable>
+1999-03-02 09:44:33 OCU <http://oscp/example.com/>
+1999-03-02 09:44:33 CRU <http://crl.example.com/latest.crl>
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex H=[ip4.ip4.ip4.ip4] P=smtps X=TLSv1:AES256-SHA:256 DN="/CN=server2.example.com" S=sss
 1999-03-02 09:44:33 Start queue run: pid=pppp -qf
 1999-03-02 09:44:33 10HmaX-0005vi-00 => CALLER <CALLER@test.ex> R=abc T=local_delivery
 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
 1999-03-02 09:44:33 Start queue run: pid=pppp -qf
 1999-03-02 09:44:33 10HmaX-0005vi-00 => CALLER <CALLER@test.ex> R=abc T=local_delivery
 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
diff --git a/test/log/5750 b/test/log/5750
new file mode 100644 (file)
index 0000000..8c98b5b
--- /dev/null
@@ -0,0 +1,46 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 Start queue run: pid=pppp -qf
+1999-03-02 09:44:33 10HmaX-0005vi-00 TLS error on connection to 127.0.0.1 [127.0.0.1] (certificate verification failed): certificate invalid
+1999-03-02 09:44:33 10HmaX-0005vi-00 deferral bad
+1999-03-02 09:44:33 10HmaX-0005vi-00 NO CLENT CERT presented
+1999-03-02 09:44:33 10HmaX-0005vi-00 Peer cert:
+1999-03-02 09:44:33 10HmaX-0005vi-00 ver <3>
+1999-03-02 09:44:33 10HmaX-0005vi-00 SN  <CN=server1.example.com>
+1999-03-02 09:44:33 10HmaX-0005vi-00 IN  <O=example.com,CN=clica Signing Cert>
+1999-03-02 09:44:33 10HmaX-0005vi-00 NB  <Nov  1 12:34:05 2012 GMT>
+1999-03-02 09:44:33 10HmaX-0005vi-00 NA  <Jan  1 12:34:05 2038 GMT>
+1999-03-02 09:44:33 10HmaX-0005vi-00 SA  <RSA-SHA1>
+1999-03-02 09:44:33 10HmaX-0005vi-00 SG  <56 3a a4 3c cb eb b8 27 c2 90 08 74 13 88 dc 48 c6 b5 2c e5 26 be 5b 91 d4 67 e7 3c 49 12 d7 47 30 df 98 db 58 ed 18 a8 7d 4b db 97 48 f5 5c 7f 70 b9 37 63 33 f1 24 62 72 92 60 f5 6e da b6 bc 73 c8 c2 dc d6 95 9a bd 16 16 a2 ef 0a f1 d7 41 68 f6 ad 98 5a d0 ff d9 1b 51 9f 59 ce 2f 3d 84 d0 ee e8 2b eb 9b 32 1a 0e 02 3e cc 30 89 44 09 2a 75 81 46 a7 b6 ed 7d 41 eb 5a 63 fa 9c 58 ef>
+1999-03-02 09:44:33 10HmaX-0005vi-00 SAN <alternatename.server1.example.com>
+1999-03-02 09:44:33 10HmaX-0005vi-00 OCU <http://oscp/example.com/>
+1999-03-02 09:44:33 10HmaX-0005vi-00 CRU <http://crl.example.com/latest.crl>
+1999-03-02 09:44:33 10HmaX-0005vi-00 TLS session failure: delivering unencrypted to 127.0.0.1 [127.0.0.1] (not in hosts_require_tls)
+1999-03-02 09:44:33 10HmaX-0005vi-00 => bad@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] C="250 OK id=10HmaZ-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 delivery bad
+1999-03-02 09:44:33 10HmaX-0005vi-00 NO CLENT CERT presented
+1999-03-02 09:44:33 10HmaX-0005vi-00 No Peer cert
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaY-0005vi-00 => good@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 DN="CN=server1.example.com" C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaY-0005vi-00 delivery good
+1999-03-02 09:44:33 10HmaY-0005vi-00 Our cert SN: CN=server2.example.com
+1999-03-02 09:44:33 10HmaY-0005vi-00 Peer cert:
+1999-03-02 09:44:33 10HmaY-0005vi-00 ver <3>
+1999-03-02 09:44:33 10HmaY-0005vi-00 SN  <CN=server1.example.com>
+1999-03-02 09:44:33 10HmaY-0005vi-00 IN  <O=example.com,CN=clica Signing Cert>
+1999-03-02 09:44:33 10HmaY-0005vi-00 NB  <Nov  1 12:34:05 2012 GMT>
+1999-03-02 09:44:33 10HmaY-0005vi-00 NA  <Jan  1 12:34:05 2038 GMT>
+1999-03-02 09:44:33 10HmaY-0005vi-00 SA  <RSA-SHA1>
+1999-03-02 09:44:33 10HmaY-0005vi-00 SG  <56 3a a4 3c cb eb b8 27 c2 90 08 74 13 88 dc 48 c6 b5 2c e5 26 be 5b 91 d4 67 e7 3c 49 12 d7 47 30 df 98 db 58 ed 18 a8 7d 4b db 97 48 f5 5c 7f 70 b9 37 63 33 f1 24 62 72 92 60 f5 6e da b6 bc 73 c8 c2 dc d6 95 9a bd 16 16 a2 ef 0a f1 d7 41 68 f6 ad 98 5a d0 ff d9 1b 51 9f 59 ce 2f 3d 84 d0 ee e8 2b eb 9b 32 1a 0e 02 3e cc 30 89 44 09 2a 75 81 46 a7 b6 ed 7d 41 eb 5a 63 fa 9c 58 ef>
+1999-03-02 09:44:33 10HmaY-0005vi-00 SAN <alternatename.server1.example.com>
+1999-03-02 09:44:33 10HmaY-0005vi-00 OCU <http://oscp/example.com/>
+1999-03-02 09:44:33 10HmaY-0005vi-00 CRU <http://crl.example.com/latest.crl>
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp -qf
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 TLS error on connection from localhost [127.0.0.1] (recv): A TLS fatal alert has been received.: Certificate is bad
+1999-03-02 09:44:33 TLS error on connection from localhost [127.0.0.1] (send): The specified session has been invalidated for some reason.
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 DN="CN=server2.example.com" S=sss id=E10HmaY-0005vi-00@myhost.test.ex
diff --git a/test/log/5760 b/test/log/5760
new file mode 100644 (file)
index 0000000..0b74e24
--- /dev/null
@@ -0,0 +1,47 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 Start queue run: pid=pppp -qf
+1999-03-02 09:44:33 10HmaX-0005vi-00 SSL verify error: depth=2 error=self signed certificate in certificate chain cert=/O=example.com/CN=clica CA
+1999-03-02 09:44:33 10HmaX-0005vi-00 TLS error on connection to 127.0.0.1 [127.0.0.1] (SSL_connect): error: <<detail omitted>>
+1999-03-02 09:44:33 10HmaX-0005vi-00 deferral bad
+1999-03-02 09:44:33 10HmaX-0005vi-00 NO CLENT CERT presented
+1999-03-02 09:44:33 10HmaX-0005vi-00 Peer cert:
+1999-03-02 09:44:33 10HmaX-0005vi-00 ver <2>
+1999-03-02 09:44:33 10HmaX-0005vi-00 SN  <CN=clica CA,O=example.com>
+1999-03-02 09:44:33 10HmaX-0005vi-00 IN  <CN=clica CA,O=example.com>
+1999-03-02 09:44:33 10HmaX-0005vi-00 NB  <Nov  1 12:34:04 2012 GMT>
+1999-03-02 09:44:33 10HmaX-0005vi-00 NA  <Jan  1 12:34:04 2038 GMT>
+1999-03-02 09:44:33 10HmaX-0005vi-00 SA  <undefined>
+1999-03-02 09:44:33 10HmaX-0005vi-00 SG  <    Signature Algorithm: sha1WithRSAEncryption\n         89:fd:fb:cb:b2:42:d6:aa:f2:c0:44:a2:14:e5:ab:22:50:41:\n         e6:64:e7:1c:5a:20:b6:0f:fe:b0:88:c5:cf:b3:e5:f8:0e:87:\n         eb:ac:07:d6:9d:6a:20:f6:dd:13:ee:b8:3f:cf:d9:cd:d4:a8:\n         72:50:5a:a2:14:4e:ee:3a:78:e2:a7:f4:ae:d7:ee:77:48:1f:\n         75:a7:68:2f:ee:e2:7c:ac:2f:e4:88:02:e8:3b:db:f9:35:04:\n         05:46:35:0b:f2:35:03:21:b6:1e:82:7d:94:e0:63:4b:60:71:\n         2d:19:45:21:f2:85:b4:c3:d0:77:a2:24:32:36:f3:50:68:38:\n         98:e6\n>
+1999-03-02 09:44:33 10HmaX-0005vi-00 (no SAN)
+1999-03-02 09:44:33 10HmaX-0005vi-00 (no OCU)
+1999-03-02 09:44:33 10HmaX-0005vi-00 (no CRU)
+1999-03-02 09:44:33 10HmaX-0005vi-00 TLS session failure: delivering unencrypted to 127.0.0.1 [127.0.0.1] (not in hosts_require_tls)
+1999-03-02 09:44:33 10HmaX-0005vi-00 => bad@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] C="250 OK id=10HmaZ-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 delivery bad
+1999-03-02 09:44:33 10HmaX-0005vi-00 NO CLENT CERT presented
+1999-03-02 09:44:33 10HmaX-0005vi-00 No Peer cert
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaY-0005vi-00 => good@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] X=TLSv1:AES256-SHA:256 DN="/CN=server1.example.com" C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaY-0005vi-00 delivery good
+1999-03-02 09:44:33 10HmaY-0005vi-00 Our cert SN: CN=server2.example.com
+1999-03-02 09:44:33 10HmaY-0005vi-00 Peer cert:
+1999-03-02 09:44:33 10HmaY-0005vi-00 ver <2>
+1999-03-02 09:44:33 10HmaY-0005vi-00 SN  <CN=server1.example.com>
+1999-03-02 09:44:33 10HmaY-0005vi-00 IN  <CN=clica Signing Cert,O=example.com>
+1999-03-02 09:44:33 10HmaY-0005vi-00 NB  <Nov  1 12:34:05 2012 GMT>
+1999-03-02 09:44:33 10HmaY-0005vi-00 NA  <Jan  1 12:34:05 2038 GMT>
+1999-03-02 09:44:33 10HmaY-0005vi-00 SA  <undefined>
+1999-03-02 09:44:33 10HmaY-0005vi-00 SG  <    Signature Algorithm: sha1WithRSAEncryption\n         56:3a:a4:3c:cb:eb:b8:27:c2:90:08:74:13:88:dc:48:c6:b5:\n         2c:e5:26:be:5b:91:d4:67:e7:3c:49:12:d7:47:30:df:98:db:\n         58:ed:18:a8:7d:4b:db:97:48:f5:5c:7f:70:b9:37:63:33:f1:\n         24:62:72:92:60:f5:6e:da:b6:bc:73:c8:c2:dc:d6:95:9a:bd:\n         16:16:a2:ef:0a:f1:d7:41:68:f6:ad:98:5a:d0:ff:d9:1b:51:\n         9f:59:ce:2f:3d:84:d0:ee:e8:2b:eb:9b:32:1a:0e:02:3e:cc:\n         30:89:44:09:2a:75:81:46:a7:b6:ed:7d:41:eb:5a:63:fa:9c:\n         58:ef\n>
+1999-03-02 09:44:33 10HmaY-0005vi-00 SAN <Unrecognisable>
+1999-03-02 09:44:33 10HmaY-0005vi-00 OCU <http://oscp/example.com/>
+1999-03-02 09:44:33 10HmaY-0005vi-00 CRU <http://crl.example.com/latest.crl>
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp -qf
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 TLS error on connection from localhost (myhost.test.ex) [127.0.0.1] (SSL_accept): error: <<detail omitted>>
+1999-03-02 09:44:33 TLS client disconnected cleanly (rejected our certificate?)
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtp S=sss id=E10HmaX-0005vi-00@myhost.test.ex
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLSv1:AES256-SHA:256 DN="/CN=server2.example.com" S=sss id=E10HmaY-0005vi-00@myhost.test.ex
index a4e0dd526a79c506329f241ed387d49c3f0ea753..23b5f61a5a7922324fd509cf48a4725e9e5ab217 100644 (file)
@@ -30,7 +30,7 @@ Received: from [ip4.ip4.ip4.ip4]
        id 10HmaZ-0005vi-00
        for CALLER@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
 tls-certificate-verified: 1
        id 10HmaZ-0005vi-00
        for CALLER@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
 tls-certificate-verified: 1
-TLS: cipher=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 peerdn=C=UK,O=The Exim Maintainers,OU=Test Suite,CN=Phil Pennock
+TLS: cipher=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 peerdn=CN=server2.example.com
 
 This is a test encrypted message from a verified host.
 
 
 This is a test encrypted message from a verified host.
 
index e4be6a342d1f0a059e1f030da08ff30d14d1958a..42c189f78ff07b0459b3038c9cc9dabf421af238 100644 (file)
@@ -30,7 +30,7 @@ Received: from [ip4.ip4.ip4.ip4]
        id 10HmaZ-0005vi-00
        for CALLER@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
 tls-certificate-verified: 1
        id 10HmaZ-0005vi-00
        for CALLER@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
 tls-certificate-verified: 1
-TLS: cipher=TLSv1:AES256-SHA:256 peerdn=/C=UK/O=The Exim Maintainers/OU=Test Suite/CN=Phil Pennock
+TLS: cipher=TLSv1:AES256-SHA:256 peerdn=/CN=server2.example.com
 
 This is a test encrypted message from a verified host.
 
 
 This is a test encrypted message from a verified host.
 
index 06a7b31d0165d8be16640f922911847ad7e6a9b8..49f841e56ac81997d80ecf6e4cd07696720b8a69 100644 (file)
@@ -1,4 +1,4 @@
-# TLS server: general
+# TLS server: general ops and certificate extractions
 gnutls
 exim -DSERVER=server -bd -oX PORT_D
 ****
 gnutls
 exim -DSERVER=server -bd -oX PORT_D
 ****
@@ -60,7 +60,7 @@ ehlo rhu.barb
 starttls
 ??? 220
 ****
 starttls
 ??? 220
 ****
-client-gnutls HOSTIPV4 PORT_D DIR/aux-fixed/cert2 DIR/aux-fixed/cert2
+client-gnutls HOSTIPV4 PORT_D DIR/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.pem DIR/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.unlocked.key
 ??? 220
 ehlo rhu.barb
 ??? 250-
 ??? 220
 ehlo rhu.barb
 ??? 250-
index 2e7dca0a65e82976b512588b46a2aa194be4a606..cbb9ce393dd0b266cb83af9e2e297ef07b576548 100644 (file)
@@ -1,4 +1,4 @@
-# TLS server: general
+# TLS server: general ops and certificate extractions
 exim -DSERVER=server -bd -oX PORT_D
 ****
 client-ssl 127.0.0.1 PORT_D
 exim -DSERVER=server -bd -oX PORT_D
 ****
 client-ssl 127.0.0.1 PORT_D
@@ -59,7 +59,7 @@ ehlo rhu.barb
 starttls
 ??? 220
 ****
 starttls
 ??? 220
 ****
-client-ssl HOSTIPV4 PORT_D DIR/aux-fixed/cert2 DIR/aux-fixed/cert2
+client-ssl HOSTIPV4 PORT_D DIR/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.pem DIR/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.unlocked.key
 ??? 220
 ehlo rhu.barb
 ??? 250-
 ??? 220
 ehlo rhu.barb
 ??? 250-
diff --git a/test/scripts/5750-GnuTLS-TPDA/5750 b/test/scripts/5750-GnuTLS-TPDA/5750
new file mode 100644 (file)
index 0000000..903c795
--- /dev/null
@@ -0,0 +1,13 @@
+# TLS client: GnuTLS $tls_out_peercert
+exim -DSERVER=server -bd -oX PORT_D
+****
+exim bad@test.ex
+Testing
+****
+exim good@test.ex
+Testing
+****
+exim -qf
+****
+killdaemon
+no_msglog_check
diff --git a/test/scripts/5750-GnuTLS-TPDA/REQUIRES b/test/scripts/5750-GnuTLS-TPDA/REQUIRES
new file mode 100644 (file)
index 0000000..af1eb46
--- /dev/null
@@ -0,0 +1,2 @@
+support Experimental_TPDA
+support GnuTLS
diff --git a/test/scripts/5760-OpenSSL-TPDA/5760 b/test/scripts/5760-OpenSSL-TPDA/5760
new file mode 100644 (file)
index 0000000..8fa8bd0
--- /dev/null
@@ -0,0 +1,13 @@
+# TLS client: OpenSSL certificates and extractions
+exim -DSERVER=server -bd -oX PORT_D
+****
+exim bad@test.ex
+Testing
+****
+exim good@test.ex
+Testing
+****
+exim -qf
+****
+killdaemon
+no_msglog_check
diff --git a/test/scripts/5760-OpenSSL-TPDA/REQUIRES b/test/scripts/5760-OpenSSL-TPDA/REQUIRES
new file mode 100644 (file)
index 0000000..5b48920
--- /dev/null
@@ -0,0 +1,2 @@
+support Experimental_TPDA
+support OpenSSL
index a248be7c0a2aee565bc71d09814e96755f664f6b..ec3c1f9540388e640863840340ff1c047fcc5c50 100644 (file)
@@ -97,8 +97,8 @@ Attempting to start TLS
 Failed to start TLS
 End of script
 Connecting to ip4.ip4.ip4.ip4 port 1225 ... connected
 Failed to start TLS
 End of script
 Connecting to ip4.ip4.ip4.ip4 port 1225 ... connected
-Certificate file = TESTSUITE/aux-fixed/cert2
-Key file = TESTSUITE/aux-fixed/cert2
+Certificate file = TESTSUITE/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.pem
+Key file = TESTSUITE/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.unlocked.key
 ??? 220
 <<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
 >>> ehlo rhu.barb
 ??? 220
 <<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
 >>> ehlo rhu.barb
index 23c39cdf4e041a7b1b743d8316ea4cd69920db3e..77ae109b2b0c2a8ec44655b1cb5276dc5a45021b 100644 (file)
@@ -145,8 +145,8 @@ pppp:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure:s
 Failed to start TLS
 End of script
 Connecting to ip4.ip4.ip4.ip4 port 1225 ... connected
 Failed to start TLS
 End of script
 Connecting to ip4.ip4.ip4.ip4 port 1225 ... connected
-Certificate file = TESTSUITE/aux-fixed/cert2
-Key file = TESTSUITE/aux-fixed/cert2
+Certificate file = TESTSUITE/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.pem
+Key file = TESTSUITE/aux-fixed/exim-ca/example.com/server2.example.com/server2.example.com.unlocked.key
 ??? 220
 <<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
 >>> ehlo rhu.barb
 ??? 220
 <<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
 >>> ehlo rhu.barb