Add a bunch of expandables and some DKIM ACL red tape
[users/jgh/exim.git] / src / src / dkim.c
index f0805ac42372209f70ffb41fead695520519252c..a2beafd6cefcc1354a8a8ae9609e17f14f08b6ac 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/dkim.c,v 1.1.2.14 2009/05/27 17:29:35 tom Exp $ */
+/* $Cambridge: exim/src/src/dkim.c,v 1.1.2.15 2009/06/08 21:06:31 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
@@ -18,6 +18,7 @@
 
 pdkim_ctx       *dkim_verify_ctx = NULL;
 pdkim_signature *dkim_signatures = NULL;
+pdkim_signature *dkim_cur_sig    = NULL;
 
 int dkim_exim_query_dns_txt(char *name, char *answer) {
   dns_answer dnsa;
@@ -79,6 +80,7 @@ void dkim_exim_verify_feed(uschar *data, int len) {
 
 
 void dkim_exim_verify_finish(void) {
+  pdkim_signature *sig = NULL;
   int dkim_signing_domains_size = 0;
   int dkim_signing_domains_ptr = 0;
   dkim_signing_domains = NULL;
@@ -99,49 +101,49 @@ void dkim_exim_verify_finish(void) {
   /* Finish DKIM operation and fetch link to signatures chain */
   if (pdkim_feed_finish(dkim_verify_ctx,&dkim_signatures) != PDKIM_OK) return;
 
-
-  while (dkim_signatures != NULL) {
+  sig = dkim_signatures;
+  while (sig != NULL) {
     int size = 0;
     int ptr = 0;
     /* Log a line for each signature */
     uschar *logmsg = string_append(NULL, &size, &ptr, 5,
 
       string_sprintf( "DKIM: d=%s s=%s c=%s/%s a=%s ",
-                      dkim_signatures->domain,
-                      dkim_signatures->selector,
-                      (dkim_signatures->canon_headers == PDKIM_CANON_SIMPLE)?"simple":"relaxed",
-                      (dkim_signatures->canon_body    == PDKIM_CANON_SIMPLE)?"simple":"relaxed",
-                      (dkim_signatures->algo          == PDKIM_ALGO_RSA_SHA256)?"rsa-sha256":"rsa-sha1"
+                      sig->domain,
+                      sig->selector,
+                      (sig->canon_headers == PDKIM_CANON_SIMPLE)?"simple":"relaxed",
+                      (sig->canon_body    == PDKIM_CANON_SIMPLE)?"simple":"relaxed",
+                      (sig->algo          == PDKIM_ALGO_RSA_SHA256)?"rsa-sha256":"rsa-sha1"
                     ),
-      ((dkim_signatures->identity != NULL)?
-        string_sprintf("i=%s ", dkim_signatures->identity)
+      ((sig->identity != NULL)?
+        string_sprintf("i=%s ", sig->identity)
         :
         US""
       ),
-      ((dkim_signatures->created > 0)?
-        string_sprintf("t=%lu ", dkim_signatures->created)
+      ((sig->created > 0)?
+        string_sprintf("t=%lu ", sig->created)
         :
         US""
       ),
-      ((dkim_signatures->expires > 0)?
-        string_sprintf("x=%lu ", dkim_signatures->expires)
+      ((sig->expires > 0)?
+        string_sprintf("x=%lu ", sig->expires)
         :
         US""
       ),
-      ((dkim_signatures->bodylength > -1)?
-        string_sprintf("l=%lu ", dkim_signatures->bodylength)
+      ((sig->bodylength > -1)?
+        string_sprintf("l=%lu ", sig->bodylength)
         :
         US""
       )
     );
 
-    switch(dkim_signatures->verify_status) {
+    switch(sig->verify_status) {
       case PDKIM_VERIFY_NONE:
         logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]");
       break;
       case PDKIM_VERIFY_INVALID:
         logmsg = string_append(logmsg, &size, &ptr, 1, "[invalid - ");
-        switch (dkim_signatures->verify_ext_status) {
+        switch (sig->verify_ext_status) {
           case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
             logmsg = string_append(logmsg, &size, &ptr, 1, "public key record (currently?) unavailable]");
           break;
@@ -157,7 +159,7 @@ void dkim_exim_verify_finish(void) {
       break;
       case PDKIM_VERIFY_FAIL:
         logmsg = string_append(logmsg, &size, &ptr, 1, "[verification failed - ");
-        switch (dkim_signatures->verify_ext_status) {
+        switch (sig->verify_ext_status) {
           case PDKIM_VERIFY_FAIL_BODY:
             logmsg = string_append(logmsg, &size, &ptr, 1, "body hash mismatch (body probably modified in transit)]");
           break;
@@ -181,12 +183,12 @@ void dkim_exim_verify_finish(void) {
                                          &dkim_signing_domains_size,
                                          &dkim_signing_domains_ptr,
                                          2,
-                                         dkim_signatures->domain,
+                                         sig->domain,
                                          ":"
                                         );
 
     /* Process next signature */
-    dkim_signatures = dkim_signatures->next;
+    sig = sig->next;
   }
 
   /* Chop the last colon from the domain list */
@@ -196,9 +198,161 @@ void dkim_exim_verify_finish(void) {
 }
 
 
-void dkim_exim_verify_result(uschar *domain, uschar **result, uschar **error) {
-  if (dkim_verify_ctx) {
+void dkim_exim_acl_setup(uschar *id) {
+  pdkim_signature *sig = dkim_signatures;
+  dkim_cur_sig = NULL;
+  if (dkim_disable_verify ||
+      !id || !sig ||
+      !dkim_verify_ctx) return;
+  /* Find signature to run ACL on */
+  while (sig != NULL) {
+    uschar *cmp_val = NULL;
+    if (Ustrchr(id,'@') != NULL) cmp_val = (uschar *)sig->identity;
+                            else cmp_val = (uschar *)sig->domain;
+    if (cmp_val && (strcmpic(cmp_val,id) == 0)) {
+      dkim_cur_sig = sig;
+      /* The "dkim_domain" and "dkim_selector" expansion variables have
+         related globals, since they are used in the signing code too.
+         Instead of inventing separate names for verification, we set
+         them here. This is easy since a domain and selector is guaranteed
+         to be in a signature. The other dkim_* expansion items are
+         dynamically fetched from dkim_cur_sig at expansion time (see
+         function below). */
+      dkim_signing_domain   = (uschar *)sig->domain;
+      dkim_signing_selector = (uschar *)sig->selector;
+      return;
+    }
+    sig = sig->next;
+  }
+}
+
+
+uschar *dkim_exim_expand_query(int what) {
+
+  if (!dkim_verify_ctx ||
+      dkim_disable_verify ||
+      !dkim_cur_sig) return dkim_exim_expand_defaults(what);
+
+  switch(what) {
+    case DKIM_ALGO:
+      return dkim_cur_sig->algo?
+              (uschar *)(dkim_cur_sig->algo)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_BODYLENGTH:
+      return (dkim_cur_sig->bodylength >= 0)?
+              (uschar *)string_sprintf(OFF_T_FMT,(LONGLONG_T)dkim_cur_sig->bodylength)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_CANON_BODY:
+      return dkim_cur_sig->canon_body?
+              (uschar *)(dkim_cur_sig->canon_body)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_CANON_HEADERS:
+      return dkim_cur_sig->canon_headers?
+              (uschar *)(dkim_cur_sig->canon_headers)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_COPIEDHEADERS:
+      return dkim_cur_sig->copiedheaders?
+              (uschar *)(dkim_cur_sig->copiedheaders)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_CREATED:
+      return (dkim_cur_sig->created > 0)?
+              (uschar *)string_sprintf("%llu",dkim_cur_sig->created)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_EXPIRES:
+      return (dkim_cur_sig->expires > 0)?
+              (uschar *)string_sprintf("%llu",dkim_cur_sig->expires)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_HEADERNAMES:
+      return dkim_cur_sig->headernames?
+              (uschar *)(dkim_cur_sig->headernames)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_IDENTITY:
+      return dkim_cur_sig->identity?
+              (uschar *)(dkim_cur_sig->identity)
+              :dkim_exim_expand_defaults(what);
+    case DKIM_KEY_GRANULARITY:
+      return dkim_cur_sig->pubkey?
+              (dkim_cur_sig->pubkey->granularity?
+                (uschar *)(dkim_cur_sig->pubkey->granularity)
+                :dkim_exim_expand_defaults(what)
+              )
+              :dkim_exim_expand_defaults(what);
+    case DKIM_KEY_SRVTYPE:
+      return dkim_cur_sig->pubkey?
+              (dkim_cur_sig->pubkey->srvtype?
+                (uschar *)(dkim_cur_sig->pubkey->srvtype)
+                :dkim_exim_expand_defaults(what)
+              )
+              :dkim_exim_expand_defaults(what);
+    case DKIM_KEY_NOTES:
+      return dkim_cur_sig->pubkey?
+              (dkim_cur_sig->pubkey->notes?
+                (uschar *)(dkim_cur_sig->pubkey->notes)
+                :dkim_exim_expand_defaults(what)
+              )
+              :dkim_exim_expand_defaults(what);
+    case DKIM_KEY_TESTING:
+      return dkim_cur_sig->pubkey?
+              (dkim_cur_sig->pubkey->testing?
+                US"1"
+                :dkim_exim_expand_defaults(what)
+              )
+              :dkim_exim_expand_defaults(what);
+    case DKIM_NOSUBDOMAINS:
+      return dkim_cur_sig->pubkey?
+              (dkim_cur_sig->pubkey->no_subdomaining?
+                US"1"
+                :dkim_exim_expand_defaults(what)
+              )
+              :dkim_exim_expand_defaults(what);
+    case DKIM_VERIFY_STATUS:
+      switch(dkim_cur_sig->verify_status) {
+        case PDKIM_VERIFY_INVALID:
+          return US"invalid";
+        case PDKIM_VERIFY_FAIL:
+          return US"fail";
+        case PDKIM_VERIFY_PASS:
+          return US"pass";
+        case PDKIM_VERIFY_NONE:
+        default:
+          return US"none";
+      }
+    case DKIM_VERIFY_REASON:
+      switch (dkim_cur_sig->verify_ext_status) {
+        case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
+          return US"pubkey_unavailable";
+        case PDKIM_VERIFY_INVALID_PUBKEY_PARSING:
+          return US"pubkey_syntax";
+        case PDKIM_VERIFY_FAIL_BODY:
+          return US"bodyhash_mismatch";
+        case PDKIM_VERIFY_FAIL_MESSAGE:
+          return US"signature_incorrect";
+      }
+    default:
+      return US"";
+  }
+}
+
 
+uschar *dkim_exim_expand_defaults(int what) {
+  switch(what) {
+    case DKIM_ALGO:               return US"";
+    case DKIM_BODYLENGTH:         return US"9999999999999";
+    case DKIM_CANON_BODY:         return US"";
+    case DKIM_CANON_HEADERS:      return US"";
+    case DKIM_COPIEDHEADERS:      return US"";
+    case DKIM_CREATED:            return US"0";
+    case DKIM_EXPIRES:            return US"9999999999999";
+    case DKIM_HEADERNAMES:        return US"";
+    case DKIM_IDENTITY:           return US"";
+    case DKIM_KEY_GRANULARITY:    return US"*";
+    case DKIM_KEY_SRVTYPE:        return US"*";
+    case DKIM_KEY_NOTES:          return US"";
+    case DKIM_KEY_TESTING:        return US"0";
+    case DKIM_NOSUBDOMAINS:       return US"0";
+    case DKIM_VERIFY_STATUS:      return US"none";
+    case DKIM_VERIFY_REASON:      return US"";
+    default:                      return US"";
   }
 }