PDKIM: Fix use of private-keys having trailing '=' in the base-64. Bug 1781
[exim.git] / src / src / pdkim / pdkim.c
index 90a0584fa80e038aa6b1f4283c35c46ea5c0f367..6e471a6141bb3bc77057c36816466973c33157cd 100644 (file)
@@ -1,7 +1,8 @@
 /*
  *  PDKIM - a RFC4871 (DKIM) implementation
  *
- *  Copyright (C) 2009 - 2015  Tom Kistner <tom@duncanthrax.net>
+ *  Copyright (C) 2009 - 2016  Tom Kistner <tom@duncanthrax.net>
+ *  Copyright (C) 2016  Jeremy Harris <jgh@exim.org>
  *
  *  http://duncanthrax.net/pdkim/
  *
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
+#include "../exim.h"
 
-#include "../mytypes.h"
-#include "pdkim.h"
-#include "pdkim-rsa.h"
 
-#include "polarssl/sha1.h"
-#include "polarssl/sha2.h"
-#include "polarssl/rsa.h"
-#include "polarssl/base64.h"
+#ifndef DISABLE_DKIM   /* entire file */
+
+#ifndef SUPPORT_TLS
+# error Need SUPPORT_TLS for DKIM
+#endif
+
+#include "crypt_ver.h"
+
+#ifdef RSA_OPENSSL
+# include <openssl/rsa.h>
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+#elif defined(RSA_GNUTLS)
+# include <gnutls/gnutls.h>
+# include <gnutls/x509.h>
+# include <gnutls/abstract.h>
+#endif
+
+#ifdef SHA_GNUTLS
+# include <gnutls/crypto.h>
+#elif defined(SHA_POLARSSL)
+# include "polarssl/sha1.h"
+# include "polarssl/sha2.h"
+#endif
+
+#include "pdkim.h"
 
 #define PDKIM_SIGNATURE_VERSION     "1"
 #define PDKIM_PUB_RECORD_VERSION    "DKIM1"
@@ -95,6 +112,7 @@ typedef struct pdkim_combined_canon_entry {
   int canon_headers;
   int canon_body;
 } pdkim_combined_canon_entry;
+
 pdkim_combined_canon_entry pdkim_combined_canons[] = {
   { "simple/simple",    PDKIM_CANON_SIMPLE,   PDKIM_CANON_SIMPLE },
   { "simple/relaxed",   PDKIM_CANON_SIMPLE,   PDKIM_CANON_RELAXED },
@@ -106,7 +124,11 @@ pdkim_combined_canon_entry pdkim_combined_canons[] = {
 };
 
 
-const char *pdkim_verify_status_str(int status) {
+/* -------------------------------------------------------------------------- */
+
+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";
@@ -115,13 +137,17 @@ const char *pdkim_verify_status_str(int status) {
     default:                   return "PDKIM_VERIFY_UNKNOWN";
   }
 }
-const char *pdkim_verify_ext_status_str(int ext_status) {
+
+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";
+    case PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD: return "PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD";
+    case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return "PDKIM_VERIFY_INVALID_PUBKEY_IMPORT";
     default: return "PDKIM_VERIFY_UNKNOWN";
   }
 }
@@ -129,57 +155,53 @@ const char *pdkim_verify_ext_status_str(int ext_status) {
 
 /* -------------------------------------------------------------------------- */
 /* Print debugging functions */
-#ifdef PDKIM_DEBUG
 void
-pdkim_quoteprint(FILE *stream, const char *data, int len, int lf)
+pdkim_quoteprint(const char *data, int len, int lf)
 {
 int i;
 const unsigned char *p = (const unsigned char *)data;
 
-for (i = 0; i<len; i++)
+for (i = 0; i < len; i++)
   {
   const int c = p[i];
   switch (c)
     {
-    case ' ' : fprintf(stream, "{SP}"); break;
-    case '\t': fprintf(stream, "{TB}"); break;
-    case '\r': fprintf(stream, "{CR}"); break;
-    case '\n': fprintf(stream, "{LF}"); break;
-    case '{' : fprintf(stream, "{BO}"); break;
-    case '}' : fprintf(stream, "{BC}"); break;
+    case ' ' : debug_printf("{SP}"); break;
+    case '\t': debug_printf("{TB}"); break;
+    case '\r': debug_printf("{CR}"); break;
+    case '\n': debug_printf("{LF}"); break;
+    case '{' : debug_printf("{BO}"); break;
+    case '}' : debug_printf("{BC}"); break;
     default:
       if ( (c < 32) || (c > 127) )
-       fprintf(stream, "{%02x}", c);
+       debug_printf("{%02x}", c);
       else
-       fputc(c, stream);
+       debug_printf("%c", c);
       break;
     }
   }
 if (lf)
-  fputc('\n', stream);
+  debug_printf("\n");
 }
 
 void
-pdkim_hexprint(FILE *stream, const char *data, int len, int lf)
+pdkim_hexprint(const char *data, int len, int lf)
 {
 int i;
 const unsigned char *p = (const unsigned char *)data;
 
-for (i =0 ; i<len; i++)
-  {
-  const int c = p[i];
-  fprintf(stream, "%02x", c);
-  }
+for (i = 0 ; i < len; i++)
+  debug_printf("%02x", p[i]);
 if (lf)
-  fputc('\n', stream);
+  debug_printf("\n");
 }
-#endif
 
 
-/* -------------------------------------------------------------------------- */
-/* Simple string list implementation for convinience */
-pdkim_stringlist *
-pdkim_append_stringlist(pdkim_stringlist *base, char *str)
+
+/* String package: should be replaced by Exim standard ones */
+
+static pdkim_stringlist *
+pdkim_prepend_stringlist(pdkim_stringlist *base, char *str)
 {
 pdkim_stringlist *new_entry = malloc(sizeof(pdkim_stringlist));
 
@@ -197,24 +219,11 @@ 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) return NULL;
-memset(new_entry, 0, sizeof(pdkim_stringlist));
-if (!(new_entry->value = strdup(str))) return NULL;
-if (base)
-  new_entry->next = base;
-return new_entry;
-}
-
 
 /* -------------------------------------------------------------------------- */
 /* A small "growing string" implementation to escape malloc/realloc hell */
 
-pdkim_str *
+static pdkim_str *
 pdkim_strnew (const char *cstr)
 {
 unsigned int len = cstr ? strlen(cstr) : 0;
@@ -236,7 +245,7 @@ else
 return p;
 }
 
-char *
+static char *
 pdkim_strncat(pdkim_str *str, const char *data, int len)
 {
 if ((str->allocated - str->len) < (len+1))
@@ -255,21 +264,13 @@ str->str[str->len] = '\0';
 return str->str;
 }
 
-char *
+static 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 *
+static char *
 pdkim_strtrim(pdkim_str *str)
 {
 char *p = str->str;
@@ -286,7 +287,7 @@ str->len = strlen(str->str);
 return str->str;
 }
 
-char *
+static char *
 pdkim_strclear(pdkim_str *str)
 {
 str->str[0] = '\0';
@@ -294,7 +295,7 @@ str->len = 0;
 return str->str;
 }
 
-void
+static void
 pdkim_strfree(pdkim_str *str)
 {
 if (!str) return;
@@ -306,7 +307,7 @@ free(str);
 
 /* -------------------------------------------------------------------------- */
 
-void
+static void
 pdkim_free_pubkey(pdkim_pubkey *pub)
 {
 if (pub)
@@ -317,7 +318,7 @@ if (pub)
   if (pub->keytype    ) free(pub->keytype);
   if (pub->srvtype    ) free(pub->srvtype);
   if (pub->notes      ) free(pub->notes);
-  if (pub->key        ) free(pub->key);
+/*  if (pub->key        ) free(pub->key); */
   free(pub);
   }
 }
@@ -325,7 +326,7 @@ if (pub)
 
 /* -------------------------------------------------------------------------- */
 
-void
+static void
 pdkim_free_sig(pdkim_signature *sig)
 {
 if (sig)
@@ -341,8 +342,6 @@ if (sig)
     free(c);
     }
 
-  if (sig->sigdata         ) free(sig->sigdata);
-  if (sig->bodyhash        ) free(sig->bodyhash);
   if (sig->selector        ) free(sig->selector);
   if (sig->domain          ) free(sig->domain);
   if (sig->identity        ) free(sig->identity);
@@ -351,8 +350,6 @@ if (sig)
   if (sig->rsa_privkey     ) free(sig->rsa_privkey);
   if (sig->sign_headers    ) free(sig->sign_headers);
   if (sig->signature_header) free(sig->signature_header);
-  if (sig->sha1_body       ) free(sig->sha1_body);
-  if (sig->sha2_body       ) free(sig->sha2_body);
 
   if (sig->pubkey) pdkim_free_pubkey(sig->pubkey);
 
@@ -390,7 +387,7 @@ if (ctx)
    "start". Returns the position of the header name in
    the list. */
 
-int
+static int
 header_name_match(const char *header,
                       char       *tick,
                       int         do_tick)
@@ -453,7 +450,7 @@ return rc;
 /* Performs "relaxed" canonicalization of a header. The returned pointer needs
    to be free()d. */
 
-char *
+static char *
 pdkim_relax_header (char *header, int crlf)
 {
 BOOL past_field_name = FALSE;
@@ -504,7 +501,7 @@ return relaxed;
 /* -------------------------------------------------------------------------- */
 #define PDKIM_QP_ERROR_DECODE -1
 
-char *
+static char *
 pdkim_decode_qp_char(char *qp_p, int *c)
 {
 char *initial_pos = qp_p;
@@ -517,7 +514,7 @@ if (isxdigit(*qp_p) && isxdigit(qp_p[1]))
   {
   /* Do hex conversion */
   *c = (isdigit(*qp_p) ? *qp_p - '0' : toupper(*qp_p) - 'A' + 10) << 4;
-  *c != isdigit(qp_p[1]) ? qp_p[1] - '0' : toupper(qp_p[1]) - 'A' + 10;
+  *c |= isdigit(qp_p[1]) ? qp_p[1] - '0' : toupper(qp_p[1]) - 'A' + 10;
   return qp_p + 2;
   }
 
@@ -529,7 +526,7 @@ return initial_pos;
 
 /* -------------------------------------------------------------------------- */
 
-char *
+static char *
 pdkim_decode_qp(char *str)
 {
 int nchar = 0;
@@ -563,21 +560,21 @@ return n;
 
 /* -------------------------------------------------------------------------- */
 
-char *
+static char *
 pdkim_decode_base64(char *str, int *num_decoded)
 {
 int dlen = 0;
 char *res;
+int old_pool = store_pool;
 
-base64_decode(NULL, &dlen, (unsigned char *)str, strlen(str));
+/* There is a store-reset between header & body reception
+so cannot use the main pool */
 
-if (!(res = malloc(dlen+1)))
-   return NULL;
-if (base64_decode((unsigned char *)res, &dlen, (unsigned char *)str, strlen(str)) != 0)
-  {
-  free(res);
-  return NULL;
-  }
+store_pool = POOL_PERM;
+dlen = b64decode(US str, USS &res);
+store_pool = old_pool;
+
+if (dlen < 0) return NULL;
 
 if (num_decoded) *num_decoded = dlen;
 return res;
@@ -586,22 +583,16 @@ return res;
 
 /* -------------------------------------------------------------------------- */
 
-char *
+static char *
 pdkim_encode_base64(char *str, int num)
 {
-int dlen = 0;
-char *res;
-
-base64_encode(NULL, &dlen, (unsigned char *)str, num);
+char * ret;
+int old_pool = store_pool;
 
-if (!(res = malloc(dlen+1)))
-  return NULL;
-if (base64_encode((unsigned char *)res, &dlen, (unsigned char *)str, num) != 0)
-  {
-  free(res);
-  return NULL;
-  }
-return res;
+store_pool = POOL_PERM;
+ret = CS b64encode(US str, num);
+store_pool = old_pool;
+return ret;
 }
 
 
@@ -610,7 +601,7 @@ return res;
 #define PDKIM_HDR_TAG   1
 #define PDKIM_HDR_VALUE 2
 
-pdkim_signature *
+static pdkim_signature *
 pdkim_parse_sig_header(pdkim_ctx *ctx, char *raw_hdr)
 {
 pdkim_signature *sig ;
@@ -692,10 +683,7 @@ for (p = raw_hdr; ; p++)
         {
        pdkim_strtrim(cur_val);
 
-#ifdef PDKIM_DEBUG
-       if (ctx->debug_stream)
-         fprintf(ctx->debug_stream, " %s=%s\n", cur_tag->str, cur_val->str);
-#endif
+       DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag->str, cur_val->str);
 
        switch (cur_tag->str[0])
          {
@@ -755,10 +743,7 @@ for (p = raw_hdr; ; p++)
          case 'z':
            sig->copiedheaders = pdkim_decode_qp(cur_val->str); break;
          default:
-#ifdef PDKIM_DEBUG
-           if (ctx->debug_stream)
-             fprintf(ctx->debug_stream, " Unknown tag encountered\n");
-#endif
+           DEBUG(D_acl) debug_printf(" Unknown tag encountered\n");
            break;
          }
        }
@@ -797,20 +782,28 @@ q--;
 while (q > sig->rawsig_no_b_val  && (*q == '\r' || *q == '\n'))
   *q = '\0'; q--;      /*XXX questionable code layout; possible bug */
 
-#ifdef PDKIM_DEBUG
-if (ctx->debug_stream)
+DEBUG(D_acl)
   {
-  fprintf(ctx->debug_stream,
+  debug_printf(
          "PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
-  pdkim_quoteprint(ctx->debug_stream,
-                  sig->rawsig_no_b_val,
-                  strlen(sig->rawsig_no_b_val), 1);
-  fprintf(ctx->debug_stream,
+  pdkim_quoteprint(sig->rawsig_no_b_val, strlen(sig->rawsig_no_b_val), 1);
+  debug_printf(
          "PDKIM >> Sig size: %4d bits\n", sig->sigdata_len*8);
-  fprintf(ctx->debug_stream,
+  debug_printf(
          "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
   }
-#endif
+
+#ifdef SHA_OPENSSL
+
+SHA1_Init  (&sig->sha1_body);
+SHA256_Init(&sig->sha2_body);
+
+#elif defined(SHA_GNUTLS)
+
+gnutls_hash_init(&sig->sha_body,
+  sig->algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256);
+
+#elif defined(SHA_POLARSSL)
 
 if (  !(sig->sha1_body = malloc(sizeof(sha1_context)))
    || !(sig->sha2_body = malloc(sizeof(sha2_context)))
@@ -823,13 +816,15 @@ if (  !(sig->sha1_body = malloc(sizeof(sha1_context)))
 sha1_starts(sig->sha1_body);
 sha2_starts(sig->sha2_body, 0);
 
+#endif /* SHA impl */
+
 return sig;
 }
 
 
 /* -------------------------------------------------------------------------- */
 
-pdkim_pubkey *
+static pdkim_pubkey *
 pdkim_parse_pubkey_record(pdkim_ctx *ctx, char *raw_record)
 {
 pdkim_pubkey *pub;
@@ -886,10 +881,7 @@ for (p = raw_record; ; p++)
       if (cur_tag->len > 0)
         {
        pdkim_strtrim(cur_val);
-#ifdef PDKIM_DEBUG
-       if (ctx->debug_stream)
-         fprintf(ctx->debug_stream, " %s=%s\n", cur_tag->str, cur_val->str);
-#endif
+       DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag->str, cur_val->str);
 
        switch (cur_tag->str[0])
          {
@@ -916,10 +908,7 @@ for (p = raw_record; ; p++)
            if (strchr(cur_val->str, 's') != NULL) pub->no_subdomaining = 1;
            break;
          default:
-#ifdef PDKIM_DEBUG
-           if (ctx->debug_stream)
-             fprintf(ctx->debug_stream, " Unknown tag encountered\n");
-#endif
+           DEBUG(D_acl) debug_printf(" Unknown tag encountered\n");
            break;
          }
        }
@@ -952,7 +941,7 @@ return NULL;
 
 /* -------------------------------------------------------------------------- */
 
-int
+static int
 pdkim_update_bodyhash(pdkim_ctx *ctx, const char *data, int len)
 {
 pdkim_signature *sig = ctx->sig;
@@ -1013,16 +1002,23 @@ while (sig)
 
   if (canon_len > 0)
     {
+#ifdef SHA_GNUTLS
+    gnutls_hash(sig->sha_body, canon_data, canon_len);
+#else
     if (sig->algo == PDKIM_ALGO_RSA_SHA1)
-      sha1_update(sig->sha1_body, (unsigned char *)canon_data, canon_len);
+# ifdef SHA_OPENSSL
+      SHA1_Update  (&sig->sha1_body, canon_data, canon_len);
     else
-      sha2_update(sig->sha2_body, (unsigned char *)canon_data, canon_len);
+      SHA256_Update(&sig->sha2_body, canon_data, canon_len);
+# elif defined(SHA_POLARSSL)
+      sha1_update(sig->sha1_body, US canon_data, canon_len);
+    else
+      sha2_update(sig->sha2_body, US canon_data, canon_len);
+# endif
+#endif
 
     sig->signed_body_bytes += canon_len;
-#ifdef PDKIM_DEBUG
-    if (ctx->debug_stream)
-      pdkim_quoteprint(ctx->debug_stream, canon_data, canon_len, 1);
-#endif
+    DEBUG(D_acl) pdkim_quoteprint(canon_data, canon_len, 1);
     }
 
   sig = sig->next;
@@ -1035,40 +1031,44 @@ return PDKIM_OK;
 
 /* -------------------------------------------------------------------------- */
 
-int
+static int
 pdkim_finish_bodyhash(pdkim_ctx *ctx)
 {
-pdkim_signature *sig = ctx->sig;
+pdkim_signature *sig;
 
 /* Traverse all signatures */
-while (sig)
+for (sig = ctx->sig; sig; sig = sig->next)
   {                                    /* Finish hashes */
-  unsigned char bh[32]; /* SHA-256 = 32 Bytes,  SHA-1 = 20 Bytes */
+  uschar bh[32]; /* SHA-256 = 32 Bytes,  SHA-1 = 20 Bytes */
 
+#ifdef SHA_GNUTLS
+  gnutls_hash_output(sig->sha_body, bh);
+#else
   if (sig->algo == PDKIM_ALGO_RSA_SHA1)
+# ifdef SHA_OPENSSL
+    SHA1_Final  (bh, &sig->sha1_body);
+  else
+    SHA256_Final(bh, &sig->sha2_body);
+# elif defined(SHA_POLARSSL)
     sha1_finish(sig->sha1_body, bh);
   else
     sha2_finish(sig->sha2_body, bh);
+# endif
+#endif
 
-#ifdef PDKIM_DEBUG
-  if (ctx->debug_stream)
+  DEBUG(D_acl)
     {
-    fprintf(ctx->debug_stream, "PDKIM [%s] Body bytes hashed: %lu\n",
-      sig->domain, sig->signed_body_bytes);
-    fprintf(ctx->debug_stream, "PDKIM [%s] bh  computed: ", sig->domain);
-    pdkim_hexprint(ctx->debug_stream, (char *)bh,
-                  (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32, 1);
+    debug_printf("PDKIM [%s] Body bytes hashed: %lu\n"
+                "PDKIM [%s] bh  computed: ",
+               sig->domain, sig->signed_body_bytes, sig->domain);
+    pdkim_hexprint((char *)bh, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32, 1);
     }
-#endif
 
   /* SIGNING -------------------------------------------------------------- */
   if (ctx->mode == PDKIM_MODE_SIGN)
     {
-    sig->bodyhash_len = (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32;
-
-    if (!(sig->bodyhash = malloc(sig->bodyhash_len)))
-      return PDKIM_ERR_OOM;
-    memcpy(sig->bodyhash, bh, sig->bodyhash_len);
+    sig->bodyhash_len = sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20:32;
+    sig->bodyhash = CS string_copyn(US bh, sig->bodyhash_len);
 
     /* If bodylength limit is set, and we have received less bytes
        than the requested amount, effectively remove the limit tag. */
@@ -1083,30 +1083,21 @@ while (sig)
     if (memcmp(bh, sig->bodyhash,
               (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32) == 0)
       {
-#ifdef PDKIM_DEBUG
-      if (ctx->debug_stream)
-       fprintf(ctx->debug_stream, "PDKIM [%s] Body hash verified OK\n",
-               sig->domain);
-#endif
+      DEBUG(D_acl) debug_printf("PDKIM [%s] Body hash verified OK\n", sig->domain);
       }
     else
       {
-#ifdef PDKIM_DEBUG
-      if (ctx->debug_stream)
+      DEBUG(D_acl)
         {
-       fprintf(ctx->debug_stream, "PDKIM [%s] bh signature: ", sig->domain);
-       pdkim_hexprint(ctx->debug_stream, sig->bodyhash,
-                        (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32, 1);
-       fprintf(ctx->debug_stream, "PDKIM [%s] Body hash did NOT verify\n",
-               sig->domain);
+       debug_printf("PDKIM [%s] bh signature: ", sig->domain);
+       pdkim_hexprint(sig->bodyhash,
+                        sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32, 1);
+       debug_printf("PDKIM [%s] Body hash did NOT verify\n", sig->domain);
        }
-#endif
       sig->verify_status     = PDKIM_VERIFY_FAIL;
       sig->verify_ext_status = PDKIM_VERIFY_FAIL_BODY;
       }
     }
-
-  sig = sig->next;
   }
 
 return PDKIM_OK;
@@ -1117,7 +1108,7 @@ return PDKIM_OK;
 /* -------------------------------------------------------------------------- */
 /* Callback from pdkim_feed below for processing complete body lines */
 
-int
+static int
 pdkim_bodyline_complete(pdkim_ctx *ctx)
 {
 char *p = ctx->linebuf;
@@ -1128,30 +1119,30 @@ pdkim_signature *sig = ctx->sig;        /*XXX assumes only one sig */
 if (ctx->seen_eod) goto BAIL;
 
 /* We've always got one extra byte to stuff a zero ... */
-ctx->linebuf[(ctx->linebuf_offset)] = '\0';
+ctx->linebuf[ctx->linebuf_offset] = '\0';
 
-if (ctx->input_mode == PDKIM_INPUT_SMTP)
+/* Terminate on EOD marker */
+if (memcmp(p, ".\r\n", 3) == 0)
   {
-  /* Terminate on EOD marker */
-  if (memcmp(p, ".\r\n", 3) == 0)
-    {
-    /* In simple body mode, if any empty lines were buffered,
-    replace with one. rfc 4871 3.4.3 */
-    if (  sig && sig->canon_body == PDKIM_CANON_SIMPLE
-       && sig->signed_body_bytes == 0
-       && ctx->num_buffered_crlf > 0
-       )
-      pdkim_update_bodyhash(ctx, "\r\n", 2);
+  /* In simple body mode, if any empty lines were buffered,
+  replace with one. rfc 4871 3.4.3 */
+  /*XXX checking the signed-body-bytes is a gross hack; I think
+  it indicates that all linebreaks should be buffered, including
+  the one terminating a text line */
+  if (  sig && sig->canon_body == PDKIM_CANON_SIMPLE
+     && sig->signed_body_bytes == 0
+     && ctx->num_buffered_crlf > 0
+     )
+    pdkim_update_bodyhash(ctx, "\r\n", 2);
 
-    ctx->seen_eod = 1;
-    goto BAIL;
-    }
-  /* Unstuff dots */
-  if (memcmp(p, "..", 2) == 0)
-    {
-    p++;
-    n--;
-    }
+  ctx->seen_eod = TRUE;
+  goto BAIL;
+  }
+/* Unstuff dots */
+if (memcmp(p, "..", 2) == 0)
+  {
+  p++;
+  n--;
   }
 
 /* Empty lines need to be buffered until we find a non-empty line */
@@ -1198,11 +1189,9 @@ return PDKIM_OK;
 /* Callback from pdkim_feed below for processing complete headers */
 #define DKIM_SIGNATURE_HEADERNAME "DKIM-Signature:"
 
-int
+static int
 pdkim_header_complete(pdkim_ctx *ctx)
 {
-pdkim_signature *sig = ctx->sig;
-
 /* Special case: The last header can have an extra \r appended */
 if ( (ctx->cur_header->len > 1) &&
      (ctx->cur_header->str[(ctx->cur_header->len)-1] == '\r') )
@@ -1216,7 +1205,10 @@ if (ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL;
 
 /* SIGNING -------------------------------------------------------------- */
 if (ctx->mode == PDKIM_MODE_SIGN)
-  for ( ; sig; sig = sig->next)                        /* Traverse all signatures */
+  {
+  pdkim_signature *sig;
+
+  for (sig = ctx->sig; sig; sig = sig->next)                   /* Traverse all signatures */
     if (header_name_match(ctx->cur_header->str,
                          sig->sign_headers?
                            sig->sign_headers:
@@ -1230,7 +1222,9 @@ if (ctx->mode == PDKIM_MODE_SIGN)
        return PDKIM_ERR_OOM;
       sig->headers = list;
       }
+  }
 
+/* VERIFICATION ----------------------------------------------------------- */
 /* DKIM-Signature: headers are added to the verification list */
 if (ctx->mode == PDKIM_MODE_VERIFY)
   {
@@ -1241,11 +1235,8 @@ if (ctx->mode == PDKIM_MODE_VERIFY)
     pdkim_signature *new_sig;
 
     /* Create and chain new signature block */
-#ifdef PDKIM_DEBUG
-    if (ctx->debug_stream)
-      fprintf(ctx->debug_stream,
+    DEBUG(D_acl) debug_printf(
        "PDKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
-#endif
 
     if ((new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header->str)))
       {
@@ -1258,15 +1249,10 @@ if (ctx->mode == PDKIM_MODE_VERIFY)
        last_sig->next = new_sig;
        }
       }
-#ifdef PDKIM_DEBUG
     else
-      if (ctx->debug_stream)
-        {
-       fprintf(ctx->debug_stream, "Error while parsing signature header\n");
-       fprintf(ctx->debug_stream,
+      DEBUG(D_acl) debug_printf(
+         "Error while parsing signature header\n"
          "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
-       }
-#endif
     }
 
   /* every other header is stored for signature verification */
@@ -1302,7 +1288,7 @@ for (p = 0; p<len; p++)
   if (ctx->past_headers)
     {
     /* Processing body byte */
-    ctx->linebuf[(ctx->linebuf_offset)++] = c;
+    ctx->linebuf[ctx->linebuf_offset++] = c;
     if (c == '\n')
       {
       int rc = pdkim_bodyline_complete(ctx); /* End of line */
@@ -1323,17 +1309,14 @@ for (p = 0; p<len; p++)
          int rc = pdkim_header_complete(ctx); /* Seen last header line */
          if (rc != PDKIM_OK) return rc;
 
-         ctx->past_headers = 1;
+         ctx->past_headers = TRUE;
          ctx->seen_lf = 0;
-#ifdef PDKIM_DEBUG
-         if (ctx->debug_stream)
-           fprintf(ctx->debug_stream,
+         DEBUG(D_acl) debug_printf(
              "PDKIM >> Hashed body data, canonicalized >>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
-#endif
          continue;
          }
        else
-         ctx->seen_lf = 1;
+         ctx->seen_lf = TRUE;
        }
       else if (ctx->seen_lf)
         {
@@ -1342,7 +1325,7 @@ for (p = 0; p<len; p++)
          int rc = pdkim_header_complete(ctx); /* End of header */
          if (rc != PDKIM_OK) return rc;
          }
-       ctx->seen_lf = 0;
+       ctx->seen_lf = FALSE;
        }
       }
 
@@ -1376,7 +1359,7 @@ return PDKIM_OK;
  * "pad"
  *
  * no guarantees are made for output given out-of range input. like tag
- * names loinger than 78, or bogus col. Input is assumed to be free of line breaks.
+ * names longer than 78, or bogus col. Input is assumed to be free of line breaks.
  */
 
 static char *
@@ -1482,7 +1465,7 @@ return str->str;
 
 /* -------------------------------------------------------------------------- */
 
-char *
+static char *
 pdkim_create_header(pdkim_signature *sig, int final)
 {
 char *rc = NULL;
@@ -1607,8 +1590,6 @@ rc = strdup(hdr->str);
 BAIL:
 pdkim_strfree(hdr);
 if (canon_all) pdkim_strfree(canon_all);
-if (base64_bh) free(base64_bh);
-if (base64_b ) free(base64_b);
 return rc;
 }
 
@@ -1631,21 +1612,8 @@ if (ctx->cur_header && ctx->cur_header->len)
   pdkim_update_bodyhash(ctx, "\r\n", 2);
   }
 else
-  {
-  /* For non-smtp input, check if there's an unfinished line in the
-     body line buffer. If that is the case, we must add a CRLF to the
-     hash to properly terminate the message. */
-  if ((ctx->input_mode == PDKIM_INPUT_NORMAL) && ctx->linebuf_offset)
-    {
-    pdkim_update_bodyhash(ctx, ctx->linebuf, ctx->linebuf_offset);
-    pdkim_update_bodyhash(ctx, "\r\n", 2);
-    }
-#ifdef PDKIM_DEBUG
-  if (ctx->debug_stream)
-    fprintf(ctx->debug_stream,
+  DEBUG(D_acl) debug_printf(
       "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
-#endif
-  }
 
 /* Build (and/or evaluate) body hash */
 if (pdkim_finish_bodyhash(ctx) != PDKIM_OK)
@@ -1659,22 +1627,44 @@ if (ctx->mode == PDKIM_MODE_SIGN)
 
 while (sig)
   {
+#ifdef SHA_OPENSSL
+  SHA_CTX    sha1_headers;
+  SHA256_CTX sha2_headers;
+#elif defined(SHA_GNUTLS)
+  gnutls_hash_hd_t sha_headers;
+#elif defined(SHA_POLARSSL)
   sha1_context sha1_headers;
   sha2_context sha2_headers;
+#endif
+
   char *sig_hdr;
   char headerhash[32];
 
+#ifdef RSA_GNUTLS
+  uschar * hdata = NULL;
+  int hdata_alloc = 0;
+  int hdata_size = 0;
+#endif
+
+#ifdef SHA_GNUTLS
+  gnutls_hash_init(&sha_headers,
+    sig->algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256);
+#else
   if (sig->algo == PDKIM_ALGO_RSA_SHA1)
+# ifdef SHA_OPENSSL
+    SHA1_Init(&sha1_headers);
+  else
+    SHA256_Init(&sha2_headers);
+# elif defined(SHA_POLARSSL)
     sha1_starts(&sha1_headers);
   else
     sha2_starts(&sha2_headers, 0);
-
-#ifdef PDKIM_DEBUG
-  if (ctx->debug_stream)
-    fprintf(ctx->debug_stream,
-           "PDKIM >> Hashed header data, canonicalized, in sequence >>>>>>>>>>>>>>\n");
+# endif
 #endif
 
+  DEBUG(D_acl) debug_printf(
+      "PDKIM >> Hashed header data, canonicalized, in sequence >>>>>>>>>>>>>>\n");
+
   /* SIGNING ---------------------------------------------------------------- */
   /* When signing, walk through our header list and add them to the hash. As we
      go, construct a list of the header's names to use for the h= parameter. */
@@ -1700,15 +1690,27 @@ while (sig)
        return PDKIM_ERR_OOM;
 
       /* Feed header to the hash algorithm */
+#ifdef SHA_GNUTLS
+      gnutls_hash(sha_headers, rh, strlen(rh));
+#else
       if (sig->algo == PDKIM_ALGO_RSA_SHA1)
-       sha1_update(&(sha1_headers), (unsigned char *)rh, strlen(rh));
+# ifdef SHA_OPENSSL
+       SHA1_Update  (&sha1_headers, rh, strlen(rh));
+      else
+       SHA256_Update(&sha2_headers, rh, strlen(rh));
+# elif defined(SHA_POLARSSL)
+       sha1_update(&sha1_headers, US rh, strlen(rh));
       else
-       sha2_update(&(sha2_headers), (unsigned char *)rh, strlen(rh));
+       sha2_update(&sha2_headers, US rh, strlen(rh));
+# endif
+#endif
 
-#ifdef PDKIM_DEBUG
-      if (ctx->debug_stream)
-       pdkim_quoteprint(ctx->debug_stream, rh, strlen(rh), 1);
+#ifdef RSA_GNUTLS
+      /* Remember headers block for signing */
+      hdata = string_append(hdata, &hdata_alloc, &hdata_size, 1, rh);
 #endif
+
+      DEBUG(D_acl) pdkim_quoteprint(rh, strlen(rh), 1);
       free(rh);
       }
     }
@@ -1749,15 +1751,22 @@ while (sig)
            return PDKIM_ERR_OOM;
 
          /* Feed header to the hash algorithm */
+#ifdef SHA_GNUTLS
+         gnutls_hash(sha_headers, rh, strlen(rh));
+#else
          if (sig->algo == PDKIM_ALGO_RSA_SHA1)
-           sha1_update(&sha1_headers, (unsigned char *)rh, strlen(rh));
+# ifdef SHA_OPENSSL
+           SHA1_Update  (&sha1_headers, rh, strlen(rh));
          else
-           sha2_update(&sha2_headers, (unsigned char *)rh, strlen(rh));
-
-#ifdef PDKIM_DEBUG
-         if (ctx->debug_stream)
-           pdkim_quoteprint(ctx->debug_stream, rh, strlen(rh), 1);
+           SHA256_Update(&sha2_headers, rh, strlen(rh));
+# elif defined(SHA_POLARSSL)
+           sha1_update(&sha1_headers, US rh, strlen(rh));
+         else
+           sha2_update(&sha2_headers, US rh, strlen(rh));
+# endif
 #endif
+
+         DEBUG(D_acl) pdkim_quoteprint(rh, strlen(rh), 1);
          free(rh);
          hdrs->tag = 1;
          break;
@@ -1769,11 +1778,8 @@ while (sig)
     free(b);
     }
 
-#ifdef PDKIM_DEBUG
-  if (ctx->debug_stream)
-    fprintf(ctx->debug_stream,
+  DEBUG(D_acl) debug_printf(
            "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
-#endif
 
   /* SIGNING ---------------------------------------------------------------- */
   if (ctx->mode == PDKIM_MODE_SIGN)
@@ -1805,81 +1811,156 @@ while (sig)
     sig_hdr = relaxed_hdr;
     }
 
-#ifdef PDKIM_DEBUG
-  if (ctx->debug_stream)
+  DEBUG(D_acl)
     {
-    fprintf(ctx->debug_stream,
+    debug_printf(
            "PDKIM >> Signed DKIM-Signature header, canonicalized >>>>>>>>>>>>>>>>>\n");
-    pdkim_quoteprint(ctx->debug_stream, sig_hdr, strlen(sig_hdr), 1);
-    fprintf(ctx->debug_stream,
+    pdkim_quoteprint(sig_hdr, strlen(sig_hdr), 1);
+    debug_printf(
            "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
     }
-#endif
 
   /* Finalize header hash */
+#ifdef SHA_GNUTLS
+  gnutls_hash(sha_headers, sig_hdr, strlen(sig_hdr));
+  gnutls_hash_output(sha_headers, headerhash);
+#else
   if (sig->algo == PDKIM_ALGO_RSA_SHA1)
+# ifdef SHA_OPENSSL
     {
-    sha1_update(&sha1_headers, (unsigned char *)sig_hdr, strlen(sig_hdr));
-    sha1_finish(&sha1_headers, (unsigned char *)headerhash);
-
-#ifdef PDKIM_DEBUG
-    if (ctx->debug_stream)
-      {
-      fprintf(ctx->debug_stream, "PDKIM [%s] hh computed: ", sig->domain);
-      pdkim_hexprint(ctx->debug_stream, headerhash, 20, 1);
-      }
-#endif
+    SHA1_Update(&sha1_headers, sig_hdr, strlen(sig_hdr));
+    SHA1_Final(US headerhash, &sha1_headers);
     }
   else
     {
-    sha2_update(&sha2_headers, (unsigned char *)sig_hdr, strlen(sig_hdr));
-    sha2_finish(&sha2_headers, (unsigned char *)headerhash);
-
-#ifdef PDKIM_DEBUG
-    if (ctx->debug_stream)
-      {
-      fprintf(ctx->debug_stream, "PDKIM [%s] hh computed: ", sig->domain);
-      pdkim_hexprint(ctx->debug_stream, headerhash, 32, 1);
-      }
+    SHA256_Update(&sha2_headers, sig_hdr, strlen(sig_hdr));
+    SHA256_Final(US headerhash, &sha2_headers);
+    }
+# elif defined(SHA_POLARSSL)
+    {
+    sha1_update(&sha1_headers, US sig_hdr, strlen(sig_hdr));
+    sha1_finish(&sha1_headers, US headerhash);
+    }
+  else
+    {
+    sha2_update(&sha2_headers, US sig_hdr, strlen(sig_hdr));
+    sha2_finish(&sha2_headers, US headerhash);
+    }
+# endif
 #endif
+
+  DEBUG(D_acl)
+    {
+    debug_printf("PDKIM [%s] hh computed: ", sig->domain);
+    pdkim_hexprint(headerhash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20:32, 1);
     }
 
+#ifdef RSA_GNUTLS
+  if (ctx->mode == PDKIM_MODE_SIGN)
+    hdata = string_append(hdata, &hdata_alloc, &hdata_size, 1, sig_hdr);
+#endif
+
   free(sig_hdr);
 
   /* SIGNING ---------------------------------------------------------------- */
   if (ctx->mode == PDKIM_MODE_SIGN)
     {
-    rsa_context rsa;
+#ifdef RSA_OPENSSL
+    RSA * rsa;
+    uschar * p, * q;
+    int len;
+#elif defined(RSA_GNUTLS)
+    gnutls_x509_privkey_t rsa;
+    gnutls_datum_t k;
+    int rc;
+    size_t sigsize;
+#endif
 
-    rsa_init(&rsa, RSA_PKCS_V15, 0);
+    /* Import private key */
+#ifdef RSA_OPENSSL
 
-    /* Perform private key operation */
-    if (rsa_parse_key(&rsa, (unsigned char *)sig->rsa_privkey,
-                     strlen(sig->rsa_privkey), NULL, 0) != 0)
+    if (  !(p = Ustrstr(sig->rsa_privkey, "-----BEGIN RSA PRIVATE KEY-----"))
+       || !(q = Ustrstr(p+=31, "-----END RSA PRIVATE KEY-----"))
+       )
+      return PDKIM_SIGN_PRIVKEY_WRAP;
+    *q = '\0';
+    if ((len = b64decode(p, &p)) < 0)
+      {
+      DEBUG(D_acl) debug_printf("b64decode failed\n");
+      return PDKIM_SIGN_PRIVKEY_B64D;
+      }
+    if (!(rsa = d2i_RSAPrivateKey(NULL, CUSS &p, len)))
+      {
+      DEBUG(D_acl)
+       {
+       char ssl_errstring[256];
+       ERR_error_string(ERR_get_error(), ssl_errstring);
+       debug_printf("d2i_RSAPrivateKey: %s\n", ssl_errstring);
+       }
       return PDKIM_ERR_RSA_PRIVKEY;
+      }
 
-    sig->sigdata_len = mpi_size(&(rsa.N));
-    if (!(sig->sigdata = malloc(sig->sigdata_len)))
-      return PDKIM_ERR_OOM;
+#elif defined(RSA_GNUTLS)
 
-    if (rsa_pkcs1_sign( &rsa, RSA_PRIVATE,
-                       ((sig->algo == PDKIM_ALGO_RSA_SHA1)?
-                          SIG_RSA_SHA1:SIG_RSA_SHA256),
-                       0,
-                       (unsigned char *)headerhash,
-                       (unsigned char *)sig->sigdata ) != 0)
+    k.data = sig->rsa_privkey;
+    k.size = strlen(sig->rsa_privkey);
+    if (  (rc = gnutls_x509_privkey_init(&rsa)) != GNUTLS_E_SUCCESS
+       || (rc = gnutls_x509_privkey_import2(rsa, &k,
+             GNUTLS_X509_FMT_PEM, NULL, GNUTLS_PKCS_PLAIN)) != GNUTLS_E_SUCCESS
+       )
+      {
+      DEBUG(D_acl) debug_printf("gnutls_x509_privkey_import2: %s\n",
+       gnutls_strerror(rc));
+      return PDKIM_ERR_RSA_PRIVKEY;
+      }
+
+#endif
+
+
+    /* Allocate mem for signature */
+#ifdef RSA_OPENSSL
+    sig->sigdata = store_get(RSA_size(rsa));
+#elif defined(RSA_GNUTLS)
+    k.data = hdata;
+    k.size = hdata_size;
+    (void) gnutls_x509_privkey_sign_data(rsa,
+      sig->algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256,
+      0, &k, NULL, &sigsize);
+    sig->sigdata = store_get(sig->sigdata_len = sigsize);
+#endif
+
+    /* Do signing */
+#ifdef RSA_OPENSSL
+
+    if (RSA_sign(sig->algo == PDKIM_ALGO_RSA_SHA1 ? NID_sha1 : NID_sha256,
+         CUS headerhash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32,
+         US sig->sigdata, (unsigned int *)&sig->sigdata_len,
+         rsa) != 1)
       return PDKIM_ERR_RSA_SIGNING;
+    RSA_free(rsa);
 
-    rsa_free(&rsa);
+#elif defined(RSA_GNUTLS)
 
-#ifdef PDKIM_DEBUG
-    if (ctx->debug_stream)
+    if ((rc = gnutls_x509_privkey_sign_data(rsa,
+         sig->algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256,
+         0, &k, sig->sigdata, &sigsize)) != GNUTLS_E_SUCCESS
+       )
       {
-      fprintf(ctx->debug_stream, "PDKIM [%s] b computed: ", sig->domain);
-      pdkim_hexprint(ctx->debug_stream, sig->sigdata, sig->sigdata_len, 1);
+      DEBUG(D_acl) debug_printf("gnutls_x509_privkey_sign_data: %s\n",
+       gnutls_strerror(rc));
+      return PDKIM_ERR_RSA_SIGNING;
       }
+    gnutls_x509_privkey_deinit(rsa);
+
 #endif
 
+
+    DEBUG(D_acl)
+      {
+      debug_printf( "PDKIM [%s] b computed: ", sig->domain);
+      pdkim_hexprint(sig->sigdata, sig->sigdata_len, 1);
+      }
+
     if (!(sig->signature_header = pdkim_create_header(ctx->sig, 1)))
       return PDKIM_ERR_OOM;
     }
@@ -1887,10 +1968,21 @@ while (sig)
   /* VERIFICATION ----------------------------------------------------------- */
   else
     {
-    rsa_context rsa;
+#ifdef RSA_OPENSSL
+    RSA * rsa;
+    const unsigned char * p;
+#elif defined(RSA_GNUTLS)
+    gnutls_pubkey_t rsa;
+    gnutls_datum_t k, s;
+    int rc;
+#endif
     char *dns_txt_name, *dns_txt_reply;
 
-    rsa_init(&rsa, RSA_PKCS_V15, 0);
+#ifdef RSA_GNUTLS
+    gnutls_pubkey_init(&rsa);
+#endif
+
+    /* Fetch public key for signing domain, from DNS */
 
     if (!(dns_txt_name  = malloc(PDKIM_DNS_TXT_MAX_NAMELEN)))
       return PDKIM_ERR_OOM;
@@ -1921,56 +2013,84 @@ while (sig)
       goto NEXT_VERIFY;
       }
 
-#ifdef PDKIM_DEBUG
-    if (ctx->debug_stream)
+    DEBUG(D_acl)
       {
-      fprintf(ctx->debug_stream,
-             "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
-      fprintf(ctx->debug_stream, " Raw record: ");
-      pdkim_quoteprint(ctx->debug_stream, dns_txt_reply, strlen(dns_txt_reply), 1);
+      debug_printf(
+             "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
+             " Raw record: ");
+      pdkim_quoteprint(dns_txt_reply, strlen(dns_txt_reply), 1);
       }
-#endif
 
     if (!(sig->pubkey = pdkim_parse_pubkey_record(ctx, dns_txt_reply)))
       {
       sig->verify_status =      PDKIM_VERIFY_INVALID;
-      sig->verify_ext_status =  PDKIM_VERIFY_INVALID_PUBKEY_PARSING;
+      sig->verify_ext_status =  PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD;
 
-#ifdef PDKIM_DEBUG
-      if (ctx->debug_stream)
-        {
-       fprintf(ctx->debug_stream, " Error while parsing public key record\n");
-       fprintf(ctx->debug_stream,
+      DEBUG(D_acl) debug_printf(
+         " Error while parsing public key record\n"
          "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
-       }
-#endif
       goto NEXT_VERIFY;
       }
 
-#ifdef PDKIM_DEBUG
-    if (ctx->debug_stream)
-      fprintf(ctx->debug_stream,
+    DEBUG(D_acl) debug_printf(
        "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
-#endif
 
-    if (rsa_parse_public_key(&rsa,
-                           (unsigned char *)sig->pubkey->key,
-                            sig->pubkey->key_len) != 0)
+    /* Import public key */
+#ifdef RSA_OPENSSL
+
+    p = CUS sig->pubkey->key;
+    if (!(rsa = d2i_RSA_PUBKEY(NULL, &p, (long) sig->pubkey->key_len)))
+
+#elif defined(RSA_GNUTLS)
+
+    k.data = sig->pubkey->key;
+    k.size = sig->pubkey->key_len;
+    if ((rc = gnutls_pubkey_import(rsa, &k, GNUTLS_X509_FMT_DER))
+       != GNUTLS_E_SUCCESS)
+
+#endif
       {
+      DEBUG(D_acl)
+       {
+#ifdef RSA_OPENSSL
+       long e;
+       ERR_load_crypto_strings();      /*XXX move to a startup routine */
+       while ((e = ERR_get_error()))
+         debug_printf("Az: %.120s\n", ERR_error_string(e, NULL));
+#elif defined(RSA_GNUTLS)
+       debug_printf("gnutls_pubkey_import: %s\n", gnutls_strerror(rc));
+#endif
+       }
+
       sig->verify_status =      PDKIM_VERIFY_INVALID;
-      sig->verify_ext_status =  PDKIM_VERIFY_INVALID_PUBKEY_PARSING;
+      sig->verify_ext_status =  PDKIM_VERIFY_INVALID_PUBKEY_IMPORT;
       goto NEXT_VERIFY;
       }
 
     /* Check the signature */
-    if (rsa_pkcs1_verify(&rsa,
-                     RSA_PUBLIC,
-                     ((sig->algo == PDKIM_ALGO_RSA_SHA1)?
-                          SIG_RSA_SHA1:SIG_RSA_SHA256),
-                     0,
-                     (unsigned char *)headerhash,
-                     (unsigned char *)sig->sigdata) != 0)
+#ifdef RSA_OPENSSL
+
+    if (RSA_verify(sig->algo == PDKIM_ALGO_RSA_SHA1 ? NID_sha1 : NID_sha256,
+         CUS headerhash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32,
+         US sig->sigdata, (unsigned int)sig->sigdata_len,
+         rsa) != 1)
+
+#elif defined(RSA_GNUTLS)
+
+    k.data = headerhash;
+    k.size = sig->algo == PDKIM_ALGO_RSA_SHA1 ? 20 : 32;
+    s.data = sig->sigdata;
+    s.size = sig->sigdata_len;
+    if ((rc = gnutls_pubkey_verify_hash2(rsa,
+               sig->algo == PDKIM_ALGO_RSA_SHA1
+                 ? GNUTLS_SIGN_RSA_SHA1 : GNUTLS_SIGN_RSA_SHA256,
+               0, &k, &s)) < 0)
+
+#endif
       {
+#if defined(RSA_GNUTLS)
+      debug_printf("gnutls_pubkey_verify_hash2: %s\n", gnutls_strerror(rc));
+#endif
       sig->verify_status =      PDKIM_VERIFY_FAIL;
       sig->verify_ext_status =  PDKIM_VERIFY_FAIL_MESSAGE;
       goto NEXT_VERIFY;
@@ -1982,20 +2102,20 @@ while (sig)
 
 NEXT_VERIFY:
 
-#ifdef PDKIM_DEBUG
-    if (ctx->debug_stream)
+    DEBUG(D_acl)
       {
-      fprintf(ctx->debug_stream, "PDKIM [%s] signature status: %s",
+      debug_printf("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",
+       debug_printf(" (%s)\n",
                pdkim_verify_ext_status_str(sig->verify_ext_status));
       else
-       fprintf(ctx->debug_stream, "\n");
+       debug_printf("\n");
       }
-#endif
 
-    rsa_free(&rsa);
+#ifdef RSA_GNUTLS
+    gnutls_pubkey_deinit(rsa);
+#endif
     free(dns_txt_name);
     free(dns_txt_reply);
     }
@@ -2014,7 +2134,7 @@ return PDKIM_OK;
 /* -------------------------------------------------------------------------- */
 
 DLLEXPORT pdkim_ctx *
-pdkim_init_verify(int input_mode, int(*dns_txt_callback)(char *, char *))
+pdkim_init_verify(int(*dns_txt_callback)(char *, char *))
 {
 pdkim_ctx *ctx = malloc(sizeof(pdkim_ctx));
 
@@ -2029,7 +2149,6 @@ if (!(ctx->linebuf = malloc(PDKIM_MAX_BODY_LINE_LEN)))
   }
 
 ctx->mode = PDKIM_MODE_VERIFY;
-ctx->input_mode = input_mode;
 ctx->dns_txt_callback = dns_txt_callback;
 
 return ctx;
@@ -2039,7 +2158,7 @@ return ctx;
 /* -------------------------------------------------------------------------- */
 
 DLLEXPORT pdkim_ctx *
-pdkim_init_sign(int input_mode, char *domain, char *selector, char *rsa_privkey)
+pdkim_init_sign(char *domain, char *selector, char *rsa_privkey, int algo)
 {
 pdkim_ctx *ctx;
 pdkim_signature *sig;
@@ -2068,16 +2187,25 @@ memset(sig, 0, sizeof(pdkim_signature));
 sig->bodylength = -1;
 
 ctx->mode = PDKIM_MODE_SIGN;
-ctx->input_mode = input_mode;
 ctx->sig = sig;
 
 ctx->sig->domain = strdup(domain);
 ctx->sig->selector = strdup(selector);
 ctx->sig->rsa_privkey = strdup(rsa_privkey);
+ctx->sig->algo = algo;
 
 if (!ctx->sig->domain || !ctx->sig->selector || !ctx->sig->rsa_privkey)
   goto BAIL;
 
+#ifdef SHA_OPENSSL
+SHA1_Init  (&ctx->sig->sha1_body);
+SHA256_Init(&ctx->sig->sha2_body);
+
+#elif defined(SHA_GNUTLS)
+gnutls_hash_init(&ctx->sig->sha_body,
+    algo == PDKIM_ALGO_RSA_SHA1 ? GNUTLS_DIG_SHA1 : GNUTLS_DIG_SHA256);
+
+#elif defined(SHA_POLARSSL)
 if (!(ctx->sig->sha1_body = malloc(sizeof(sha1_context))))
   goto BAIL;
 sha1_starts(ctx->sig->sha1_body);
@@ -2086,6 +2214,8 @@ if (!(ctx->sig->sha2_body = malloc(sizeof(sha2_context))))
   goto BAIL;
 sha2_starts(ctx->sig->sha2_body, 0);
 
+#endif
+
 return ctx;
 
 BAIL:
@@ -2093,6 +2223,7 @@ BAIL:
   return NULL;
 }
 
+
 /* -------------------------------------------------------------------------- */
 
 DLLEXPORT int
@@ -2102,11 +2233,9 @@ pdkim_set_optional(pdkim_ctx *ctx,
                        int canon_headers,
                        int canon_body,
                        long bodylength,
-                       int algo,
                        unsigned long created,
                        unsigned long expires)
 {
-
 if (identity)
   if (!(ctx->sig->identity = strdup(identity)))
     return PDKIM_ERR_OOM;
@@ -2118,20 +2247,11 @@ if (sign_headers)
 ctx->sig->canon_headers = canon_headers;
 ctx->sig->canon_body = canon_body;
 ctx->sig->bodylength = bodylength;
-ctx->sig->algo = algo;
 ctx->sig->created = created;
 ctx->sig->expires = expires;
 
 return PDKIM_OK;
 }
 
-/* -------------------------------------------------------------------------- */
-
-#ifdef PDKIM_DEBUG
-DLLEXPORT void
-pdkim_set_debug_stream(pdkim_ctx *ctx, FILE *debug_stream)
-{
-ctx->debug_stream = debug_stream;
-}
-#endif
 
+#endif /*DISABLE_DKIM*/