[exim-cvs] Enable operator md5 and sha1 use on certificate v…

Top Page
Delete this message
Reply to this message
Author: Exim Git Commits Mailing List
Date:  
To: exim-cvs
Subject: [exim-cvs] Enable operator md5 and sha1 use on certificate variables. Bug 1170
Gitweb: http://git.exim.org/exim.git/commitdiff/6a8a60e0f273b61a24a48afd3217ff84106779bd
Commit:     6a8a60e0f273b61a24a48afd3217ff84106779bd
Parent:     4466248715466b6f251454283642b74de65e9d9a
Author:     Jeremy Harris <jgh146exb@???>
AuthorDate: Thu May 8 20:38:46 2014 +0100
Committer:  Jeremy Harris <jgh146exb@???>
CommitDate: Thu May 8 20:38:46 2014 +0100


    Enable operator md5 and sha1 use on certificate variables.  Bug 1170
---
 doc/doc-docbook/spec.xfpt |   12 ++++--
 doc/doc-txt/ChangeLog     |    5 ++-
 doc/doc-txt/NewStuff      |    3 +-
 src/src/expand.c          |   91 +++++++++++++++++++++++++++++++-------------
 src/src/functions.h       |    3 +
 src/src/tlscert-gnu.c     |   47 +++++++++++++++++++++++
 src/src/tlscert-openssl.c |   36 ++++++++++++++++++
 test/confs/2002           |    2 +
 test/confs/2102           |    2 +
 test/log/2002             |    2 +
 test/log/2102             |    2 +
 11 files changed, 172 insertions(+), 33 deletions(-)


diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index 6497157..3dd72e9 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -12308,7 +12308,8 @@ If TLS has not been negotiated, the value will be 0.
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.
+&%certextract%& expansion item, &%md5%& or &%sha1%& operator,
+or a &%def%& condition.
.wen

.new
@@ -12317,7 +12318,8 @@ It is only useful as the argument of a
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.
+&%certextract%& expansion item, &%md5%& or &%sha1%& operator,
+or a &%def%& condition.
.wen

.new
@@ -12325,7 +12327,8 @@ It is only useful as the argument of a
.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.
+&%certextract%& expansion item, &%md5%& or &%sha1%& operator,
+or a &%def%& condition.
.wen

.new
@@ -12333,7 +12336,8 @@ outbound connection. It is only useful as the argument of a
.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.
+&%certextract%& expansion item, &%md5%& or &%sha1%& operator,
+or a &%def%& condition.
.wen

.vitem &$tls_in_certificate_verified$&
diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index ebf2ead..9704295 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -109,7 +109,10 @@ JH/20 New expansion variables tls_(in,out)_(our,peer)cert, and expansion item

 JH/21 Observability of OCSP via variables tls_(in,out)_ocsp.  Stapling
       is requested by default, modifiable by smtp transport option
-      hosts_request_ocsp;
+      hosts_request_ocsp.
+
+JH/22 Expansion operators ${md5:string} and ${sha1::string} can now
+      operate on certificate variables to give certificate fingerprints



Exim version 4.82
diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff
index b6fc576..87bb8a3 100644
--- a/doc/doc-txt/NewStuff
+++ b/doc/doc-txt/NewStuff
@@ -45,7 +45,8 @@ Version 4.83
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.
+    "certextract" to extract fields from them. Hash operators md5 and sha1
+    work over them for generating fingerprints.



 Version 4.82
diff --git a/src/src/expand.c b/src/src/expand.c
index de911db..9532d9d 100644
--- a/src/src/expand.c
+++ b/src/src/expand.c
@@ -5724,19 +5724,16 @@ while (*s != 0)
     {
     int c;
     uschar *arg = NULL;
-    uschar *sub = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
-    if (sub == NULL) goto EXPAND_FAILED;
-    s++;
+    uschar *sub;
+    var_entry *vp;


     /* Owing to an historical mis-design, an underscore may be part of the
     operator name, or it may introduce arguments.  We therefore first scan the
     table of names that contain underscores. If there is no match, we cut off
     the arguments and then scan the main table. */


-    c = chop_match(name, op_table_underscore,
-      sizeof(op_table_underscore)/sizeof(uschar *));
-
-    if (c < 0)
+    if ((c = chop_match(name, op_table_underscore,
+    sizeof(op_table_underscore)/sizeof(uschar *))) < 0)
       {
       arg = Ustrchr(name, '_');
       if (arg != NULL) *arg = 0;
@@ -5746,6 +5743,34 @@ while (*s != 0)
       if (arg != NULL) *arg++ = '_';   /* Put back for error messages */
       }


+    /* Deal specially with operators that might take a certificate variable
+    as we do not want to do the usual expansion. For most, expand the string.*/
+    switch(c)
+      {
+      case EOP_SHA1:
+      case EOP_MD5:
+    if (s[1] == '$')
+      {
+      uschar * s1 = s;
+      sub = expand_string_internal(s+2, TRUE, &s1, skipping,
+          FALSE, &resetok);
+      if (!sub)       goto EXPAND_FAILED;        /*{*/
+      if (*s1 != '}') goto EXPAND_FAILED_CURLY;
+      if ((vp = find_var_ent(sub)) && vp->type == vtype_cert)
+        {
+        s = s1+1;
+        break;
+        }
+      }
+    vp = NULL;
+        /*FALLTHROUGH*/
+      default:
+    sub = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok);
+    if (!sub) goto EXPAND_FAILED;
+    s++;
+    break;
+      }
+
     /* If we are skipping, we don't need to perform the operation at all.
     This matters for operations like "mask", because the data may not be
     in the correct format when skipping. For example, the expression may test
@@ -5830,30 +5855,42 @@ while (*s != 0)
         }


       case EOP_MD5:
-        {
-        md5 base;
-        uschar digest[16];
-        int j;
-        char st[33];
-        md5_start(&base);
-        md5_end(&base, sub, Ustrlen(sub), digest);
-        for(j = 0; j < 16; j++) sprintf(st+2*j, "%02x", digest[j]);
-        yield = string_cat(yield, &size, &ptr, US st, (int)strlen(st));
+    if (vp && *(void **)vp->value)
+      {
+      uschar * cp = tls_cert_fprt_md5(*(void **)vp->value);
+      yield = string_cat(yield, &size, &ptr, cp, (int)strlen(cp));
+      }
+    else
+      {
+      md5 base;
+      uschar digest[16];
+      int j;
+      char st[33];
+      md5_start(&base);
+      md5_end(&base, sub, Ustrlen(sub), digest);
+      for(j = 0; j < 16; j++) sprintf(st+2*j, "%02x", digest[j]);
+      yield = string_cat(yield, &size, &ptr, US st, (int)strlen(st));
+      }
         continue;
-        }


       case EOP_SHA1:
-        {
-        sha1 base;
-        uschar digest[20];
-        int j;
-        char st[41];
-        sha1_start(&base);
-        sha1_end(&base, sub, Ustrlen(sub), digest);
-        for(j = 0; j < 20; j++) sprintf(st+2*j, "%02X", digest[j]);
-        yield = string_cat(yield, &size, &ptr, US st, (int)strlen(st));
+    if (vp && *(void **)vp->value)
+      {
+      uschar * cp = tls_cert_fprt_sha1(*(void **)vp->value);
+      yield = string_cat(yield, &size, &ptr, cp, (int)strlen(cp));
+      }
+    else
+      {
+      sha1 base;
+      uschar digest[20];
+      int j;
+      char st[41];
+      sha1_start(&base);
+      sha1_end(&base, sub, Ustrlen(sub), digest);
+      for(j = 0; j < 20; j++) sprintf(st+2*j, "%02X", digest[j]);
+      yield = string_cat(yield, &size, &ptr, US st, (int)strlen(st));
+      }
         continue;
-        }


       /* Convert hex encoding to base64 encoding */


diff --git a/src/src/functions.h b/src/src/functions.h
index 8d249b8..38ba7f3 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -39,6 +39,9 @@ extern uschar * tls_cert_subject(void *, uschar * mod);
extern uschar * tls_cert_subject_altname(void *, uschar * mod);
extern uschar * tls_cert_version(void *, uschar * mod);

+extern uschar * tls_cert_fprt_md5(void *);
+extern uschar * tls_cert_fprt_sha1(void *);
+
 extern int     tls_client_start(int, host_item *, address_item *,
          void *);
 extern void    tls_close(BOOL, BOOL);
diff --git a/src/src/tlscert-gnu.c b/src/src/tlscert-gnu.c
index 61b526a..32b1986 100644
--- a/src/src/tlscert-gnu.c
+++ b/src/src/tlscert-gnu.c
@@ -375,6 +375,53 @@ for(index = 0;; index++)
 }



+/*****************************************************
+*  Certificate operator routines
+*****************************************************/
+static uschar *
+fingerprint(gnutls_x509_crt_t cert, gnutls_digest_algorithm_t algo)
+{
+int ret;
+size_t siz = 0;
+uschar * cp;
+uschar * cp2;
+uschar * cp3;
+
+if ((ret = gnutls_x509_crt_get_fingerprint(cert, algo, NULL, &siz))
+    != GNUTLS_E_SHORT_MEMORY_BUFFER)
+  {
+  expand_string_message = 
+    string_sprintf("%s: gf0 fail: %d %s\n", __FUNCTION__,
+      ret, gnutls_strerror(ret));
+  return NULL;
+  }
+cp = store_get(siz*3+1);
+if ((ret = gnutls_x509_crt_get_fingerprint(cert, algo, cp, &siz)) < 0)
+  {
+  expand_string_message = 
+    string_sprintf("%s: gf1 fail: %d %s\n", __FUNCTION__,
+      ret, gnutls_strerror(ret));
+  return NULL;
+  }
+for (cp3 = cp2 = cp+siz; cp < cp2; cp++, cp3+=2)
+  sprintf(cp3, "%02X",*cp);
+return cp2;
+}
+
+
+uschar *
+tls_cert_fprt_md5(void * cert)
+{
+return fingerprint((gnutls_x509_crt_t)cert, GNUTLS_DIG_MD5);
+}
+
+uschar *
+tls_cert_fprt_sha1(void * cert)
+{
+return fingerprint((gnutls_x509_crt_t)cert, GNUTLS_DIG_SHA1);
+}
+
+
 /* vi: aw ai sw=2
 */
 /* End of tlscert-gnu.c */
diff --git a/src/src/tlscert-openssl.c b/src/src/tlscert-openssl.c
index 53945a1..cddde4c 100644
--- a/src/src/tlscert-openssl.c
+++ b/src/src/tlscert-openssl.c
@@ -322,6 +322,42 @@ if (dps) for (i = 0; i < dpsnum; i++)
 return list;
 }


+
+
+/*****************************************************
+*  Certificate operator routines
+*****************************************************/
+static uschar *
+fingerprint(X509 * cert, const EVP_MD * fdig)
+{
+int j;
+unsigned int n;
+uschar md[EVP_MAX_MD_SIZE];
+uschar * cp;
+
+if (!X509_digest(cert,fdig,md,&n))
+  {
+  expand_string_message = US"tls_cert_fprt: out of mem\n";
+  return NULL;
+  }
+cp = store_get(n*2+1);
+for (j = 0; j < (int)n; j++) sprintf(cp+2*j, "%02X", md[j]);
+return(cp);
+}
+
+uschar * 
+tls_cert_fprt_md5(void * cert)
+{
+return fingerprint((X509 *)cert, EVP_md5());
+}
+
+uschar * 
+tls_cert_fprt_sha1(void * cert)
+{
+return fingerprint((X509 *)cert, EVP_sha1());
+}
+
+
 /* vi: aw ai sw=2
 */
 /* End of tlscert-openssl.c */
diff --git a/test/confs/2002 b/test/confs/2002
index 9d7deb7..dfb4fee 100644
--- a/test/confs/2002
+++ b/test/confs/2002
@@ -58,6 +58,8 @@ check_recipient:
       logwrite =       ${certextract {subj_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)}}
+      logwrite =  md5 fingerprint ${md5:$tls_in_peercert}
+      logwrite =  sha1 fingerprint ${sha1:$tls_in_peercert}



 # ----- Routers -----
diff --git a/test/confs/2102 b/test/confs/2102
index 27cb805..6f29298 100644
--- a/test/confs/2102
+++ b/test/confs/2102
@@ -59,6 +59,8 @@ check_recipient:
       logwrite =       ${certextract {subj_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)}}
+      logwrite =  md5 fingerprint ${md5:$tls_in_peercert}
+      logwrite =  sha1 fingerprint ${sha1:$tls_in_peercert}



# ----- Routers -----
diff --git a/test/log/2002 b/test/log/2002
index c4aa1d0..96762c1 100644
--- a/test/log/2002
+++ b/test/log/2002
@@ -18,6 +18,8 @@
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 <DNS=server2.example.com>
1999-03-02 09:44:33 CRU <http://crl.example.com/latest.crl>
+1999-03-02 09:44:33 md5 fingerprint C5FA6C8B1BE926DBC4E436AF08F92B55
+1999-03-02 09:44:33 sha1 fingerprint 40B2135E6B67AE36A397696DA328423685E74CE3
1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@??? 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@???> R=abc T=local_delivery
diff --git a/test/log/2102 b/test/log/2102
index e5bf8f6..77ef150 100644
--- a/test/log/2102
+++ b/test/log/2102
@@ -20,6 +20,8 @@
1999-03-02 09:44:33 SAN <DNS=server2.example.com>
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 md5 fingerprint C5FA6C8B1BE926DBC4E436AF08F92B55
+1999-03-02 09:44:33 sha1 fingerprint 40B2135E6B67AE36A397696DA328423685E74CE3
1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@??? 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@???> R=abc T=local_delivery