Moved pdkim declaration to satisfy older compilers
[exim.git] / src / src / pdkim / pdkim.c
index e269f7796455f8b000e5da25fc3d5ad3c0d11a36..4f0da3f7120eac1711de68c0bca745c44a1ff293 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *  PDKIM - a RFC4871 (DKIM) implementation
  *
- *  Copyright (C) 2009  Tom Kistner <tom@duncanthrax.net>
+ *  Copyright (C) 2009 - 2012  Tom Kistner <tom@duncanthrax.net>
  *
  *  http://duncanthrax.net/pdkim/
  *
@@ -20,8 +20,6 @@
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-/* $Cambridge: exim/src/src/pdkim/pdkim.c,v 1.2 2009/06/10 07:34:05 tom Exp $ */
-
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -53,6 +51,7 @@
 /* -------------------------------------------------------------------------- */
 struct pdkim_stringlist {
   char *value;
+  int  tag;
   void *next;
 };
 
@@ -65,32 +64,32 @@ struct pdkim_str {
 
 /* -------------------------------------------------------------------------- */
 /* A bunch of list constants */
-char *pdkim_querymethods[] = {
+const char *pdkim_querymethods[] = {
   "dns/txt",
   NULL
 };
-char *pdkim_algos[] = {
+const char *pdkim_algos[] = {
   "rsa-sha256",
   "rsa-sha1",
   NULL
 };
-char *pdkim_canons[] = {
+const char *pdkim_canons[] = {
   "simple",
   "relaxed",
   NULL
 };
-char *pdkim_hashes[] = {
+const char *pdkim_hashes[] = {
   "sha256",
   "sha1",
   NULL
 };
-char *pdkim_keytypes[] = {
+const char *pdkim_keytypes[] = {
   "rsa",
   NULL
 };
 
 typedef struct pdkim_combined_canon_entry {
-  char *str;
+  const char *str;
   int canon_headers;
   int canon_body;
 } pdkim_combined_canon_entry;
@@ -105,15 +104,36 @@ pdkim_combined_canon_entry pdkim_combined_canons[] = {
 };
 
 
+const char *pdkim_verify_status_str(int status) {
+  switch(status) {
+    case PDKIM_VERIFY_NONE:    return "PDKIM_VERIFY_NONE";
+    case PDKIM_VERIFY_INVALID: return "PDKIM_VERIFY_INVALID";
+    case PDKIM_VERIFY_FAIL:    return "PDKIM_VERIFY_FAIL";
+    case PDKIM_VERIFY_PASS:    return "PDKIM_VERIFY_PASS";
+    default:                   return "PDKIM_VERIFY_UNKNOWN";
+  }
+}
+const char *pdkim_verify_ext_status_str(int ext_status) {
+  switch(ext_status) {
+    case PDKIM_VERIFY_FAIL_BODY: return "PDKIM_VERIFY_FAIL_BODY";
+    case PDKIM_VERIFY_FAIL_MESSAGE: return "PDKIM_VERIFY_FAIL_MESSAGE";
+    case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE: return "PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE";
+    case PDKIM_VERIFY_INVALID_BUFFER_SIZE: return "PDKIM_VERIFY_INVALID_BUFFER_SIZE";
+    case PDKIM_VERIFY_INVALID_PUBKEY_PARSING: return "PDKIM_VERIFY_INVALID_PUBKEY_PARSING";
+    default: return "PDKIM_VERIFY_UNKNOWN";
+  }
+}
+
+
 /* -------------------------------------------------------------------------- */
 /* Print debugging functions */
 #ifdef PDKIM_DEBUG
-void pdkim_quoteprint(FILE *stream, char *data, int len, int lf) {
+void pdkim_quoteprint(FILE *stream, const char *data, int len, int lf) {
   int i;
-  unsigned char *p = (unsigned char *)data;
+  const unsigned char *p = (const unsigned char *)data;
 
   for (i=0;i<len;i++) {
-    int c = p[i];
+    const int c = p[i];
     switch (c) {
       case ' ' : fprintf(stream,"{SP}"); break;
       case '\t': fprintf(stream,"{TB}"); break;
@@ -132,12 +152,12 @@ void pdkim_quoteprint(FILE *stream, char *data, int len, int lf) {
   if (lf)
     fputc('\n',stream);
 }
-void pdkim_hexprint(FILE *stream, char *data, int len, int lf) {
+void pdkim_hexprint(FILE *stream, const char *data, int len, int lf) {
   int i;
-  unsigned char *p = (unsigned char *)data;
+  const unsigned char *p = (const unsigned char *)data;
 
   for (i=0;i<len;i++) {
-    int c = p[i];
+    const int c = p[i];
     fprintf(stream,"%02x",c);
   }
   if (lf)
@@ -156,17 +176,28 @@ pdkim_stringlist *pdkim_append_stringlist(pdkim_stringlist *base, char *str) {
   if (new_entry->value == NULL) return NULL;
   if (base != NULL) {
     pdkim_stringlist *last = base;
-    while (last->next != NULL) { last = last->next; };
+    while (last->next != NULL) { last = last->next; }
     last->next = new_entry;
     return base;
   }
   else return new_entry;
-};
+}
+pdkim_stringlist *pdkim_prepend_stringlist(pdkim_stringlist *base, char *str) {
+  pdkim_stringlist *new_entry = malloc(sizeof(pdkim_stringlist));
+  if (new_entry == NULL) return NULL;
+  memset(new_entry,0,sizeof(pdkim_stringlist));
+  new_entry->value = strdup(str);
+  if (new_entry->value == NULL) return NULL;
+  if (base != NULL) {
+    new_entry->next = base;
+  }
+  return new_entry;
+}
 
 
 /* -------------------------------------------------------------------------- */
 /* A small "growing string" implementation to escape malloc/realloc hell */
-pdkim_str *pdkim_strnew (char *cstr) {
+pdkim_str *pdkim_strnew (const char *cstr) {
   unsigned int len = cstr?strlen(cstr):0;
   pdkim_str *p = malloc(sizeof(pdkim_str));
   if (p == NULL) return NULL;
@@ -179,9 +210,10 @@ pdkim_str *pdkim_strnew (char *cstr) {
   p->allocated=(len+1);
   p->len=len;
   if (cstr) strcpy(p->str,cstr);
+  else p->str[p->len] = '\0';
   return p;
-};
-char *pdkim_strncat(pdkim_str *str, char *data, int len) {
+}
+char *pdkim_strncat(pdkim_str *str, const char *data, int len) {
   if ((str->allocated - str->len) < (len+1)) {
     /* Extend the buffer */
     int num_frags = ((len+1)/PDKIM_STR_ALLOC_FRAG)+1;
@@ -195,20 +227,20 @@ char *pdkim_strncat(pdkim_str *str, char *data, int len) {
   str->len+=len;
   str->str[str->len] = '\0';
   return str->str;
-};
-char *pdkim_strcat(pdkim_str *str, char *cstr) {
+}
+char *pdkim_strcat(pdkim_str *str, const char *cstr) {
   return pdkim_strncat(str, cstr, strlen(cstr));
-};
+}
 char *pdkim_numcat(pdkim_str *str, unsigned long num) {
   char minibuf[20];
   snprintf(minibuf,20,"%lu",num);
   return pdkim_strcat(str,minibuf);
-};
+}
 char *pdkim_strtrim(pdkim_str *str) {
   char *p = str->str;
   char *q = str->str;
   while ( (*p != '\0') && ((*p == '\t') || (*p == ' ')) ) p++;
-  while (*p != '\0') {*q = *p; q++; p++;};
+  while (*p != '\0') {*q = *p; q++; p++;}
   *q = '\0';
   while ( (q != str->str) && ( (*q == '\0') || (*q == '\t') || (*q == ' ') ) ) {
     *q = '\0';
@@ -216,17 +248,17 @@ char *pdkim_strtrim(pdkim_str *str) {
   }
   str->len = strlen(str->str);
   return str->str;
-};
+}
 char *pdkim_strclear(pdkim_str *str) {
   str->str[0] = '\0';
   str->len = 0;
   return str->str;
-};
+}
 void pdkim_strfree(pdkim_str *str) {
   if (str == NULL) return;
   if (str->str != NULL) free(str->str);
   free(str);
-};
+}
 
 
 
@@ -270,24 +302,30 @@ void pdkim_free_sig(pdkim_signature *sig) {
     if (sig->signature_header != NULL) free(sig->signature_header);
     if (sig->sha1_body        != NULL) free(sig->sha1_body);
     if (sig->sha2_body        != NULL) free(sig->sha2_body);
-    if (sig->hnames_check     != NULL) free(sig->hnames_check);
 
     if (sig->pubkey != NULL) pdkim_free_pubkey(sig->pubkey);
 
     free(sig);
     if (next != NULL) pdkim_free_sig(next);
   }
-};
+}
 
 
 /* -------------------------------------------------------------------------- */
 DLLEXPORT void pdkim_free_ctx(pdkim_ctx *ctx) {
   if (ctx) {
+    pdkim_stringlist *e = ctx->headers;
+    while(e != NULL) {
+      pdkim_stringlist *c = e;
+      if (e->value != NULL) free(e->value);
+      e = e->next;
+      free(c);
+    }
     pdkim_free_sig(ctx->sig);
     pdkim_strfree(ctx->cur_header);
     free(ctx);
   }
-};
+}
 
 
 /* -------------------------------------------------------------------------- */
@@ -295,9 +333,9 @@ DLLEXPORT void pdkim_free_ctx(pdkim_ctx *ctx) {
    the passed colon-separated "list", starting at entry
    "start". Returns the position of the header name in
    the list. */
-int header_name_match(char *header,
-                      char *tick,
-                      int   do_tick) {
+int header_name_match(const char *header,
+                      char       *tick,
+                      int         do_tick) {
   char *hname;
   char *lcopy;
   char *p;
@@ -355,7 +393,7 @@ char *pdkim_relax_header (char *header, int crlf) {
   int seen_wsp = 0;
   char *p = header;
   char *q;
-  char *relaxed = malloc(strlen(header));
+  char *relaxed = malloc(strlen(header)+3);
   if (relaxed == NULL) return NULL;
   q = relaxed;
   while (*p != '\0') {
@@ -387,10 +425,11 @@ char *pdkim_relax_header (char *header, int crlf) {
     p++;
     q++;
   }
+  if ((q>relaxed) && (*(q-1) == ' ')) q--; /* Squash eventual trailing SP */
   *q = '\0';
   if (crlf) strcat(relaxed,"\r\n");
   return relaxed;
-};
+}
 
 
 /* -------------------------------------------------------------------------- */
@@ -405,12 +444,12 @@ char *pdkim_decode_qp_char(char *qp_p, int *c) {
   if (isxdigit(*qp_p) && isxdigit(qp_p[1])) {
     /* Do hex conversion */
     if (isdigit(*qp_p)) {*c = *qp_p - '0';}
-    else {*c = toupper(*qp_p) - 'A' + 10;};
+    else {*c = toupper(*qp_p) - 'A' + 10;}
     *c <<= 4;
     if (isdigit(qp_p[1])) {*c |= qp_p[1] - '0';}
-    else {*c |= toupper(qp_p[1]) - 'A' + 10;};
+    else {*c |= toupper(qp_p[1]) - 'A' + 10;}
     return qp_p + 2;
-  };
+  }
 
   /* Illegal char here */
   *c = PDKIM_QP_ERROR_DECODE;
@@ -669,9 +708,6 @@ pdkim_signature *pdkim_parse_sig_header(pdkim_ctx *ctx, char *raw_hdr) {
     return NULL;
   }
 
-  /* Copy header list to 'tick-off' header list */
-  sig->hnames_check = strdup(sig->headernames);
-
   *q = '\0';
   /* Chomp raw header. The final newline must not be added to the signature. */
   q--;
@@ -790,7 +826,7 @@ pdkim_pubkey *pdkim_parse_pubkey_record(pdkim_ctx *ctx, char *raw_record) {
               pub->srvtype = strdup(cur_val->str);
             break;
             case 't':
-              if (strchr(cur_val->str,'t') != NULL) pub->testing = 1;
+              if (strchr(cur_val->str,'y') != NULL) pub->testing = 1;
               if (strchr(cur_val->str,'s') != NULL) pub->no_subdomaining = 1;
             break;
             default:
@@ -831,7 +867,7 @@ pdkim_pubkey *pdkim_parse_pubkey_record(pdkim_ctx *ctx, char *raw_record) {
 
 
 /* -------------------------------------------------------------------------- */
-int pdkim_update_bodyhash(pdkim_ctx *ctx, char *data, int len) {
+int pdkim_update_bodyhash(pdkim_ctx *ctx, const char *data, int len) {
   pdkim_signature *sig = ctx->sig;
   /* Cache relaxed version of data */
   char *relaxed_data = NULL;
@@ -840,20 +876,23 @@ int pdkim_update_bodyhash(pdkim_ctx *ctx, char *data, int len) {
   /* Traverse all signatures, updating their hashes. */
   while (sig != NULL) {
     /* Defaults to simple canon (no further treatment necessary) */
-    char *canon_data = data;
-    int   canon_len = len;
+    const char *canon_data = data;
+    int         canon_len = len;
 
     if (sig->canon_body == PDKIM_CANON_RELAXED) {
       /* Relax the line if not done already */
       if (relaxed_data == NULL) {
         int seen_wsp = 0;
-        char *p = data;
+        const char *p = data;
         int q = 0;
         relaxed_data = malloc(len+1);
         if (relaxed_data == NULL) return PDKIM_ERR_OOM;
         while (*p != '\0') {
           char c = *p;
-          if ( (c == '\t') || (c == ' ') ) {
+          if (c == '\r') {
+            if ( (q > 0) && (relaxed_data[q-1] == ' ') ) q--;
+          }
+          else if ( (c == '\t') || (c == ' ') ) {
             c = ' '; /* Turns WSP into SP */
             if (seen_wsp) {
               p++;
@@ -894,7 +933,7 @@ int pdkim_update_bodyhash(pdkim_ctx *ctx, char *data, int len) {
 
   if (relaxed_data != NULL) free(relaxed_data);
   return PDKIM_OK;
-};
+}
 
 
 /* -------------------------------------------------------------------------- */
@@ -962,7 +1001,7 @@ int pdkim_finish_bodyhash(pdkim_ctx *ctx) {
   }
 
   return PDKIM_OK;
-};
+}
 
 
 
@@ -1027,72 +1066,76 @@ int pdkim_header_complete(pdkim_ctx *ctx) {
   ctx->num_headers++;
   if (ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL;
 
-  /* Traverse all signatures */
-  while (sig != NULL) {
-    pdkim_stringlist *list;
+  /* SIGNING -------------------------------------------------------------- */
+  if (ctx->mode == PDKIM_MODE_SIGN) {
+    /* Traverse all signatures */
+    while (sig != NULL) {
+      pdkim_stringlist *list;
 
-    /* SIGNING -------------------------------------------------------------- */
-    if (ctx->mode == PDKIM_MODE_SIGN) {
       if (header_name_match(ctx->cur_header->str,
                             sig->sign_headers?
                               sig->sign_headers:
                               PDKIM_DEFAULT_SIGN_HEADERS, 0) != PDKIM_OK) goto NEXT_SIG;
-    }
-    /* VERIFICATION --------------------------------------------------------- */
-    else {
-      /* Header is not included or all instances were already 'ticked off' */
-      if (header_name_match(ctx->cur_header->str,
-                            sig->hnames_check, 1) != PDKIM_OK) goto NEXT_SIG;
-    }
 
-    /* Add header to the signed headers list */
-    list = pdkim_append_stringlist(sig->headers,
-                                   ctx->cur_header->str);
-    if (list == NULL) return PDKIM_ERR_OOM;
-    sig->headers = list;
-
-    NEXT_SIG:
-    sig = sig->next;
+      /* Add header to the signed headers list (in reverse order) */
+      list = pdkim_prepend_stringlist(sig->headers,
+                                      ctx->cur_header->str);
+      if (list == NULL) return PDKIM_ERR_OOM;
+      sig->headers = list;
+  
+      NEXT_SIG:
+      sig = sig->next;
+    }
   }
 
   /* DKIM-Signature: headers are added to the verification list */
-  if ( (ctx->mode == PDKIM_MODE_VERIFY) &&
-       (strncasecmp(ctx->cur_header->str,
+  if (ctx->mode == PDKIM_MODE_VERIFY) {
+    if (strncasecmp(ctx->cur_header->str,
                     DKIM_SIGNATURE_HEADERNAME,
-                    strlen(DKIM_SIGNATURE_HEADERNAME)) == 0) ) {
-     pdkim_signature *new_sig;
-    /* Create and chain new signature block */
-    #ifdef PDKIM_DEBUG
-    if (ctx->debug_stream)
-      fprintf(ctx->debug_stream,
-        "PDKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
-    #endif
-    new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header->str);
-    if (new_sig != NULL) {
-      pdkim_signature *last_sig = ctx->sig;
-      if (last_sig == NULL) {
-        ctx->sig = new_sig;
+                    strlen(DKIM_SIGNATURE_HEADERNAME)) == 0) {
+      pdkim_signature *new_sig;
+      /* Create and chain new signature block */
+      #ifdef PDKIM_DEBUG
+      if (ctx->debug_stream)
+        fprintf(ctx->debug_stream,
+          "PDKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+      #endif
+      new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header->str);
+      if (new_sig != NULL) {
+        pdkim_signature *last_sig = ctx->sig;
+        if (last_sig == NULL) {
+          ctx->sig = new_sig;
+        }
+        else {
+          while (last_sig->next != NULL) { last_sig = last_sig->next; }
+          last_sig->next = new_sig;
+        }
       }
       else {
-        while (last_sig->next != NULL) { last_sig = last_sig->next; };
-        last_sig->next = new_sig;
+        #ifdef PDKIM_DEBUG
+        if (ctx->debug_stream) {
+          fprintf(ctx->debug_stream,"Error while parsing signature header\n");
+          fprintf(ctx->debug_stream,
+            "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+        }
+        #endif
       }
     }
+    /* every other header is stored for signature verification */
     else {
-      #ifdef PDKIM_DEBUG
-      if (ctx->debug_stream) {
-        fprintf(ctx->debug_stream,"Error while parsing signature header\n");
-        fprintf(ctx->debug_stream,
-          "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
-      }
-      #endif
+      pdkim_stringlist *list;
+
+      list = pdkim_prepend_stringlist(ctx->headers,
+                                      ctx->cur_header->str);
+      if (list == NULL) return PDKIM_ERR_OOM;
+      ctx->headers = list;
     }
   }
 
   BAIL:
   pdkim_strclear(ctx->cur_header); /* Re-use existing pdkim_str */
   return PDKIM_OK;
-};
+}
 
 
 
@@ -1150,7 +1193,7 @@ DLLEXPORT int pdkim_feed (pdkim_ctx *ctx,
     }
   }
   return PDKIM_OK;
-};
+}
 
 
 /* -------------------------------------------------------------------------- */
@@ -1253,7 +1296,7 @@ DLLEXPORT int pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatu
   /* Check if we must still flush a (partial) header. If that is the
      case, the message has no body, and we must compute a body hash
      out of '<CR><LF>' */
-  if (ctx->cur_header->len) {
+  if (ctx->cur_header && ctx->cur_header->len) {
     int rc = pdkim_header_complete(ctx);
     if (rc != PDKIM_OK) return rc;
     pdkim_update_bodyhash(ctx,"\r\n",2);
@@ -1269,7 +1312,7 @@ DLLEXPORT int pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatu
     #ifdef PDKIM_DEBUG
     if (ctx->debug_stream)
       fprintf(ctx->debug_stream,
-        "\nPDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+        "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
     #endif
   }
 
@@ -1340,14 +1383,24 @@ DLLEXPORT int pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatu
       char *b = strdup(sig->headernames);
       char *p = b;
       char *q = NULL;
+      pdkim_stringlist *hdrs = ctx->headers;
+
       if (b == NULL) return PDKIM_ERR_OOM;
 
+      /* clear tags */
+      while (hdrs != NULL) {
+        hdrs->tag = 0;
+        hdrs = hdrs->next;
+      }
+
       while(1) {
-        pdkim_stringlist *hdrs = sig->headers;
+        hdrs = ctx->headers;
         q = strchr(p,':');
         if (q != NULL) *q = '\0';
         while (hdrs != NULL) {
-          if (strncasecmp(hdrs->value,p,strlen(p)) == 0) {
+          if ( (hdrs->tag == 0) &&
+               (strncasecmp(hdrs->value,p,strlen(p)) == 0) &&
+               ((hdrs->value)[strlen(p)] == ':') ) {
             char *rh = NULL;
             if (sig->canon_headers == PDKIM_CANON_RELAXED)
               rh = pdkim_relax_header(hdrs->value,1); /* cook header for relaxed canon */
@@ -1364,6 +1417,8 @@ DLLEXPORT int pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatu
               pdkim_quoteprint(ctx->debug_stream, rh, strlen(rh), 1);
             #endif
             free(rh);
+            hdrs->tag = 1;
+            break;
           }
           hdrs = hdrs->next;
         }
@@ -1442,7 +1497,7 @@ DLLEXPORT int pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatu
     if (ctx->mode == PDKIM_MODE_SIGN) {
       rsa_context rsa;
 
-      rsa_init(&rsa,RSA_PKCS_V15,0,NULL,NULL);
+      rsa_init(&rsa,RSA_PKCS_V15,0);
 
       /* Perform private key operation */
       if (rsa_parse_key(&rsa, (unsigned char *)sig->rsa_privkey,
@@ -1456,7 +1511,7 @@ DLLEXPORT int pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatu
 
       if (rsa_pkcs1_sign( &rsa, RSA_PRIVATE,
                           ((sig->algo == PDKIM_ALGO_RSA_SHA1)?
-                             RSA_SHA1:RSA_SHA256),
+                             SIG_RSA_SHA1:SIG_RSA_SHA256),
                           0,
                           (unsigned char *)headerhash,
                           (unsigned char *)sig->sigdata ) != 0) {
@@ -1481,7 +1536,7 @@ DLLEXPORT int pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatu
       rsa_context rsa;
       char *dns_txt_name, *dns_txt_reply;
 
-      rsa_init(&rsa,RSA_PKCS_V15,0,NULL,NULL);
+      rsa_init(&rsa,RSA_PKCS_V15,0);
 
       dns_txt_name  = malloc(PDKIM_DNS_TXT_MAX_NAMELEN);
       if (dns_txt_name == NULL) return PDKIM_ERR_OOM;
@@ -1499,7 +1554,7 @@ DLLEXPORT int pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatu
         sig->verify_status =      PDKIM_VERIFY_INVALID;
         sig->verify_ext_status =  PDKIM_VERIFY_INVALID_BUFFER_SIZE;
         goto NEXT_VERIFY;
-      };
+      }
 
       if ((ctx->dns_txt_callback(dns_txt_name, dns_txt_reply) != PDKIM_OK) ||
           (dns_txt_reply[0] == '\0')) {
@@ -1550,32 +1605,36 @@ DLLEXPORT int pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatu
       if (rsa_pkcs1_verify(&rsa,
                         RSA_PUBLIC,
                         ((sig->algo == PDKIM_ALGO_RSA_SHA1)?
-                             RSA_SHA1:RSA_SHA256),
+                             SIG_RSA_SHA1:SIG_RSA_SHA256),
                         0,
                         (unsigned char *)headerhash,
                         (unsigned char *)sig->sigdata) != 0) {
         sig->verify_status =      PDKIM_VERIFY_FAIL;
         sig->verify_ext_status =  PDKIM_VERIFY_FAIL_MESSAGE;
-        #ifdef PDKIM_DEBUG
-        if (ctx->debug_stream) {
-          fprintf(ctx->debug_stream, "PDKIM [%s] signature did NOT verify OK\n",
-                  sig->domain);
-        }
-        #endif
         goto NEXT_VERIFY;
       }
 
-      /* We have a winner! */
-      sig->verify_status = PDKIM_VERIFY_PASS;
+      /* We have a winner! (if bodydhash was correct earlier) */
+      if (sig->verify_status == PDKIM_VERIFY_NONE) {
+        sig->verify_status = PDKIM_VERIFY_PASS;
+      }
+
+      NEXT_VERIFY:
 
       #ifdef PDKIM_DEBUG
       if (ctx->debug_stream) {
-        fprintf(ctx->debug_stream, "PDKIM [%s] signature verified OK\n",
-                sig->domain);
+        fprintf(ctx->debug_stream, "PDKIM [%s] signature status: %s",
+                sig->domain, pdkim_verify_status_str(sig->verify_status));
+        if (sig->verify_ext_status > 0) {
+          fprintf(ctx->debug_stream, " (%s)\n",
+                  pdkim_verify_ext_status_str(sig->verify_ext_status));
+        }
+        else {
+          fprintf(ctx->debug_stream, "\n");
+        }
       }
       #endif
 
-      NEXT_VERIFY:
       rsa_free(&rsa);
       free(dns_txt_name);
       free(dns_txt_reply);
@@ -1672,14 +1731,14 @@ DLLEXPORT pdkim_ctx *pdkim_init_sign(int input_mode,
   sha2_starts(ctx->sig->sha2_body,0);
 
   return ctx;
-};
+}
 
 #ifdef PDKIM_DEBUG
 /* -------------------------------------------------------------------------- */
 DLLEXPORT void pdkim_set_debug_stream(pdkim_ctx *ctx,
                             FILE *debug_stream) {
   ctx->debug_stream = debug_stream;
-};
+}
 #endif
 
 /* -------------------------------------------------------------------------- */
@@ -1711,4 +1770,4 @@ DLLEXPORT int pdkim_set_optional(pdkim_ctx *ctx,
   ctx->sig->expires = expires;
 
   return PDKIM_OK;
-};
+}