/*
* 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>
+#endif
+
+#include "pdkim.h"
+#include "rsa.h"
#define PDKIM_SIGNATURE_VERSION "1"
#define PDKIM_PUB_RECORD_VERSION "DKIM1"
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 },
};
-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";
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";
}
}
/* -------------------------------------------------------------------------- */
/* Print debugging functions */
-#ifdef PDKIM_DEBUG
-void
-pdkim_quoteprint(FILE *stream, const char *data, int len, int lf)
+static void
+pdkim_quoteprint(const uschar *data, int len)
{
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];
+ const int c = data[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)
+static void
+pdkim_hexprint(const uschar *data, int len)
{
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);
- }
-if (lf)
- fputc('\n',stream);
+for (i = 0 ; i < len; i++) debug_printf("%02x", data[i]);
+debug_printf("\n");
}
-#endif
-/* -------------------------------------------------------------------------- */
-/* Simple string list implementation for convinience */
-pdkim_stringlist *
-pdkim_append_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)
- {
- pdkim_stringlist *last = base;
- while (last->next != NULL) { last = last->next; }
- last->next = new_entry;
- return base;
- }
-else
- return new_entry;
-}
+/* SSS probably want to keep the "stringlist" notion */
-pdkim_stringlist *
+static 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));
+memset(new_entry, 0, sizeof(pdkim_stringlist));
if (!(new_entry->value = strdup(str))) return NULL;
-if (base)
- new_entry->next = base;
+if (base) new_entry->next = base;
return new_entry;
}
/* -------------------------------------------------------------------------- */
/* A small "growing string" implementation to escape malloc/realloc hell */
+/* String package: should be replaced by Exim standard ones */
+/* SSS Ustrcpy */
-pdkim_str *
+static pdkim_str *
pdkim_strnew (const char *cstr)
{
unsigned int len = cstr ? strlen(cstr) : 0;
pdkim_str *p = malloc(sizeof(pdkim_str));
if (!p) return NULL;
-memset(p,0,sizeof(pdkim_str));
+memset(p, 0, sizeof(pdkim_str));
if (!(p->str = malloc(len+1)))
{
free(p);
p->allocated = len+1;
p->len = len;
if (cstr)
- strcpy(p->str,cstr);
+ strcpy(p->str, cstr);
else
p->str[p->len] = '\0';
return p;
}
-char *
+
+/*SSS Ustrncat */
+
+static char *
pdkim_strncat(pdkim_str *str, const char *data, int len)
{
if ((str->allocated - str->len) < (len+1))
return str->str;
}
-char *
+
+/* SSS Ustrcat */
+
+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 *
+
+/* Trim whitespace fore & aft */
+
+static 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 == '\t' || *p == ' ') p++; /* skip whitespace */
+while (*p) {*q = *p; q++; p++;} /* dump the leading whitespace */
*q = '\0';
-while ( (q != str->str) && ( (*q == '\0') || (*q == '\t') || (*q == ' ') ) )
- {
+while (q != str->str && ( (*q == '\0') || (*q == '\t') || (*q == ' ') ) )
+ { /* dump trailing whitespace */
*q = '\0';
q--;
}
return str->str;
}
-char *
+
+
+static char *
pdkim_strclear(pdkim_str *str)
{
str->str[0] = '\0';
return str->str;
}
-void
+
+
+static void
pdkim_strfree(pdkim_str *str)
{
if (!str) return;
}
+static void
+pdkim_stringlist_free(pdkim_stringlist * e)
+{
+while(e)
+ {
+ pdkim_stringlist * c = e;
+ if (e->value) free(e->value);
+ e = e->next;
+ free(c);
+ }
+}
+
+
/* -------------------------------------------------------------------------- */
-void
+static void
pdkim_free_pubkey(pdkim_pubkey *pub)
{
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);
free(pub);
}
}
/* -------------------------------------------------------------------------- */
-void
+static void
pdkim_free_sig(pdkim_signature *sig)
{
if (sig)
{
pdkim_signature *next = (pdkim_signature *)sig->next;
- pdkim_stringlist *e = sig->headers;
- while(e)
- {
- pdkim_stringlist *c = e;
- if (e->value) free(e->value);
- e = e->next;
- free(c);
- }
-
- if (sig->sigdata ) free(sig->sigdata);
- if (sig->bodyhash ) free(sig->bodyhash);
+ pdkim_stringlist_free(sig->headers);
if (sig->selector ) free(sig->selector);
if (sig->domain ) free(sig->domain);
if (sig->identity ) free(sig->identity);
- if (sig->headernames ) free(sig->headernames);
if (sig->copiedheaders ) free(sig->copiedheaders);
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);
{
if (ctx)
{
- pdkim_stringlist *e = ctx->headers;
- while(e)
- {
- pdkim_stringlist *c = e;
- if (e->value) free(e->value);
- e = e->next;
- free(c);
- }
+ pdkim_stringlist_free(ctx->headers);
pdkim_free_sig(ctx->sig);
pdkim_strfree(ctx->cur_header);
free(ctx);
/* -------------------------------------------------------------------------- */
/* Matches the name of the passed raw "header" against
- the passed colon-separated "list", starting at entry
- "start". Returns the position of the header name in
- the list. */
-
-int
-header_name_match(const char *header,
- char *tick,
- int do_tick)
+ the passed colon-separated "tick", and invalidates
+ the entry in tick. Returns OK or fail-code */
+/*XXX might be safer done using a pdkim_stringlist for "tick" */
+
+static int
+header_name_match(const char * header, char * tick)
{
char *hname;
char *lcopy;
int rc = PDKIM_FAIL;
/* Get header name */
-char *hcolon = strchr(header,':');
+char *hcolon = strchr(header, ':');
if (!hcolon) return rc; /* This isn't a header */
if (!(hname = malloc((hcolon-header)+1)))
return PDKIM_ERR_OOM;
-memset(hname,0,(hcolon-header)+1);
-strncpy(hname,header,(hcolon-header));
+memset(hname, 0, (hcolon-header)+1);
+strncpy(hname, header, (hcolon-header));
/* Copy tick-off list locally, so we can punch zeroes into it */
if (!(lcopy = strdup(tick)))
return PDKIM_ERR_OOM;
}
p = lcopy;
-q = strchr(p,':');
+q = strchr(p, ':');
while (q)
{
*q = '\0';
- if (strcasecmp(p,hname) == 0)
+ if (strcasecmp(p, hname) == 0)
{
rc = PDKIM_OK;
/* Invalidate header name instance in tick-off list */
- if (do_tick) tick[p-lcopy] = '_';
+ tick[p-lcopy] = '_';
goto BAIL;
}
p = q+1;
- q = strchr(p,':');
+ q = strchr(p, ':');
}
-if (strcasecmp(p,hname) == 0)
+if (strcasecmp(p, hname) == 0)
{
rc = PDKIM_OK;
/* Invalidate header name instance in tick-off list */
- if (do_tick) tick[p-lcopy] = '_';
+ tick[p-lcopy] = '_';
}
BAIL:
/* 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;
if (q > relaxed && q[-1] == ' ') q--; /* Squash eventual trailing SP */
*q = '\0';
-if (crlf) strcat(relaxed,"\r\n");
+if (crlf) strcat(relaxed, "\r\n");
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;
{
/* 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;
}
/* -------------------------------------------------------------------------- */
-char *
+static char *
pdkim_decode_qp(char *str)
{
int nchar = 0;
/* -------------------------------------------------------------------------- */
-char *
-pdkim_decode_base64(char *str, int *num_decoded)
+static void
+pdkim_decode_base64(uschar *str, blob * b)
{
-int dlen = 0;
-char *res;
-
-base64_decode(NULL, &dlen, (unsigned char *)str, strlen(str));
-
-if (!(res = malloc(dlen+1)))
- return NULL;
-if (base64_decode((unsigned char *)res, &dlen, (unsigned char *)str, strlen(str)) != 0)
- {
- free(res);
- return NULL;
- }
-
-if (num_decoded) *num_decoded = dlen;
-return res;
+int dlen;
+dlen = b64decode(str, &b->data);
+if (dlen < 0) b->data = NULL;
+b->len = dlen;
}
-
/* -------------------------------------------------------------------------- */
-char *
-pdkim_encode_base64(char *str, int num)
+static char *
+pdkim_encode_base64(blob * b)
{
-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(b->data, b->len);
+store_pool = old_pool;
+return ret;
}
#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 ;
-char *p,*q;
+char *p, *q;
pdkim_str *cur_tag = NULL;
pdkim_str *cur_val = NULL;
BOOL past_hname = FALSE;
BOOL in_b_val = FALSE;
int where = PDKIM_HDR_LIMBO;
int i;
+int old_pool = store_pool;
+
+/* There is a store-reset between header & body reception
+so cannot use the main pool. Any allocs done by Exim
+memory-handling must use the perm pool. */
+
+store_pool = POOL_PERM;
if (!(sig = malloc(sizeof(pdkim_signature)))) return NULL;
-memset(sig,0,sizeof(pdkim_signature));
+memset(sig, 0, sizeof(pdkim_signature));
sig->bodylength = -1;
if (!(sig->rawsig_no_b_val = malloc(strlen(raw_hdr)+1)))
{
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])
{
case 'b':
if (cur_tag->str[1] == 'h')
- sig->bodyhash = pdkim_decode_base64(cur_val->str,
- &sig->bodyhash_len);
+ pdkim_decode_base64(US cur_val->str, &sig->bodyhash);
else
- sig->sigdata = pdkim_decode_base64(cur_val->str,
- &sig->sigdata_len);
+ pdkim_decode_base64(US cur_val->str, &sig->sigdata);
break;
case 'v':
/* We only support version 1, and that is currently the
case 'l':
sig->bodylength = strtol(cur_val->str, NULL, 10); break;
case 'h':
- sig->headernames = strdup(cur_val->str); break;
+ sig->headernames = string_copy(US cur_val->str); break;
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;
}
}
*q++ = c;
}
+store_pool = old_pool;
+
/* Make sure the most important bits are there. */
if (!(sig->domain && (*(sig->domain) != '\0') &&
sig->selector && (*(sig->selector) != '\0') &&
sig->headernames && (*(sig->headernames) != '\0') &&
- sig->bodyhash &&
- sig->sigdata &&
sig->version))
{
pdkim_free_sig(sig);
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(US sig->rawsig_no_b_val, strlen(sig->rawsig_no_b_val));
+ debug_printf(
+ "PDKIM >> Sig size: %4u bits\n", (unsigned) sig->sigdata.len*8);
+ debug_printf(
"PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
}
-#endif
-
-if ( !(sig->sha1_body = malloc(sizeof(sha1_context)))
- || !(sig->sha2_body = malloc(sizeof(sha2_context)))
- )
- {
- pdkim_free_sig(sig);
- return NULL;
- }
-
-sha1_starts(sig->sha1_body);
-sha2_starts(sig->sha2_body, 0);
+exim_sha_init(&sig->body_hash, sig->algo == PDKIM_ALGO_RSA_SHA1);
return sig;
}
/* -------------------------------------------------------------------------- */
-pdkim_pubkey *
+static pdkim_pubkey *
pdkim_parse_pubkey_record(pdkim_ctx *ctx, char *raw_record)
{
pdkim_pubkey *pub;
int where = PDKIM_HDR_LIMBO;
if (!(pub = malloc(sizeof(pdkim_pubkey)))) return NULL;
-memset(pub,0,sizeof(pdkim_pubkey));
+memset(pub, 0, sizeof(pdkim_pubkey));
for (p = raw_record; ; p++)
{
if (!cur_val)
cur_val = pdkim_strnew(NULL);
- if (c == '\r' || c == '\n')
- goto NEXT_CHAR;
-
if (c == ';' || c == '\0')
{
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])
{
case 'n':
pub->notes = pdkim_decode_qp(cur_val->str); break;
case 'p':
- pub->key = pdkim_decode_base64(cur_val->str, &(pub->key_len)); break;
+ pdkim_decode_base64(US cur_val->str, &pub->key);
+ break;
case 'k':
pub->hashes = strdup(cur_val->str); break;
case 's':
pub->srvtype = strdup(cur_val->str); break;
case 't':
- if (strchr(cur_val->str,'y') != NULL) pub->testing = 1;
- if (strchr(cur_val->str,'s') != NULL) pub->no_subdomaining = 1;
+ if (strchr(cur_val->str, 'y') != NULL) pub->testing = 1;
+ 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;
}
}
if (!pub->srvtype ) pub->srvtype = strdup("*");
/* p= is required */
-if (pub->key)
+if (pub->key.data)
return pub;
pdkim_free_pubkey(pub);
/* -------------------------------------------------------------------------- */
-int
+static 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;
-int relaxed_len = 0;
+uschar *relaxed_data = NULL;
+int relaxed_len = 0;
/* Traverse all signatures, updating their hashes. */
while (sig)
{
/* Defaults to simple canon (no further treatment necessary) */
- const char *canon_data = data;
- int canon_len = len;
+ const uschar *canon_data = CUS data;
+ int canon_len = len;
if (sig->canon_body == PDKIM_CANON_RELAXED)
{
if (canon_len > 0)
{
- if (sig->algo == PDKIM_ALGO_RSA_SHA1)
- sha1_update(sig->sha1_body,(unsigned char *)canon_data,canon_len);
- else
- sha2_update(sig->sha2_body,(unsigned char *)canon_data,canon_len);
-
+ exim_sha_update(&sig->body_hash, CCS canon_data, canon_len);
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);
}
sig = sig->next;
/* -------------------------------------------------------------------------- */
-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 */
+ blob bh;
- if (sig->algo == PDKIM_ALGO_RSA_SHA1)
- sha1_finish(sig->sha1_body,bh);
- else
- sha2_finish(sig->sha2_body,bh);
+ exim_sha_finish(&sig->body_hash, &bh);
-#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(CUS bh.data, bh.len);
}
-#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 = bh;
/* If bodylength limit is set, and we have received less bytes
than the requested amount, effectively remove the limit tag. */
else
{
/* Compare bodyhash */
- if (memcmp(bh, sig->bodyhash,
- (sig->algo == PDKIM_ALGO_RSA_SHA1)?20:32) == 0)
+ if (memcmp(bh.data, sig->bodyhash.data, bh.len) == 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.data,
+ exim_sha_hashlen(&sig->body_hash));
+ 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;
/* -------------------------------------------------------------------------- */
/* Callback from pdkim_feed below for processing complete body lines */
-int
+static int
pdkim_bodyline_complete(pdkim_ctx *ctx)
{
char *p = ctx->linebuf;
int n = ctx->linebuf_offset;
+pdkim_signature *sig = ctx->sig; /*XXX assumes only one sig */
/* Ignore extra data if we've seen the end-of-data marker */
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 ( ctx->sig && ctx->sig->canon_body == PDKIM_CANON_SIMPLE
- && 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--;
- }
+ /* 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 = 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 */
goto BAIL;
}
-if ( ctx->sig
- && ctx->sig->canon_body == PDKIM_CANON_RELAXED
- )
+if (sig && sig->canon_body == PDKIM_CANON_RELAXED)
{
/* Lines with just spaces need to be buffered too */
char *check = p;
/* 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') )
/* SIGNING -------------------------------------------------------------- */
if (ctx->mode == PDKIM_MODE_SIGN)
- for ( ; sig; sig = sig->next) /* Traverse all signatures */
- if (header_name_match(ctx->cur_header->str,
- sig->sign_headers?
- sig->sign_headers:
- PDKIM_DEFAULT_SIGN_HEADERS, 0) == PDKIM_OK)
- {
- pdkim_stringlist *list;
+ {
+ pdkim_signature *sig;
- /* Add header to the signed headers list (in reverse order) */
- if (!(list = pdkim_prepend_stringlist(sig->headers,
- ctx->cur_header->str)))
- return PDKIM_ERR_OOM;
- sig->headers = list;
- }
+ for (sig = ctx->sig; sig; sig = sig->next) /* Traverse all signatures */
+ {
+ pdkim_stringlist *list;
+
+ /* Add header to the signed headers list (in reverse order) */
+ if (!(list = pdkim_prepend_stringlist(sig->headers,
+ ctx->cur_header->str)))
+ return PDKIM_ERR_OOM;
+ sig->headers = list;
+ }
+ }
+/* VERIFICATION ----------------------------------------------------------- */
/* DKIM-Signature: headers are added to the verification list */
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)))
{
}
}
else
-#ifdef PDKIM_DEBUG
- 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 */
{
pdkim_stringlist *list;
- if (!(list = pdkim_prepend_stringlist(ctx->headers,
- ctx->cur_header->str)))
+ if (!(list = pdkim_prepend_stringlist(ctx->headers, ctx->cur_header->str)))
return PDKIM_ERR_OOM;
ctx->headers = list;
}
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 */
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,
- "PDKIM >> Hashed body data, canonicalized >>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
-#endif
+ DEBUG(D_acl) debug_printf(
+ "PDKIM >> Body data for hash, canonicalized >>>>>>>>>>>>>>>>>>>>>>\n");
continue;
}
else
- ctx->seen_lf = 1;
+ ctx->seen_lf = TRUE;
}
else if (ctx->seen_lf)
{
int rc = pdkim_header_complete(ctx); /* End of header */
if (rc != PDKIM_OK) return rc;
}
- ctx->seen_lf = 0;
+ ctx->seen_lf = FALSE;
}
}
* "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 *
{
size_t sl = strlen(intro);
- pdkim_strncat(str, intro,sl);
+ pdkim_strncat(str, intro, sl);
*col += sl;
l -= sl;
intro = NULL; /* only want this once */
size_t sl = strlen(payload);
size_t chomp = *col+sl < 77 ? sl : 78-*col;
- pdkim_strncat(str, payload,chomp);
+ pdkim_strncat(str, payload, chomp);
*col += chomp;
payload += chomp;
l -= chomp-1;
/* -------------------------------------------------------------------------- */
-char *
-pdkim_create_header(pdkim_signature *sig, int final)
+static char *
+pdkim_create_header(pdkim_signature *sig, BOOL final)
{
char *rc = NULL;
char *base64_bh = NULL;
if (!(canon_all = pdkim_strnew(pdkim_canons[sig->canon_headers])))
goto BAIL;
-if (!(base64_bh = pdkim_encode_base64(sig->bodyhash, sig->bodyhash_len)))
+if (!(base64_bh = pdkim_encode_base64(&sig->bodyhash)))
goto BAIL;
col = strlen(hdr->str);
/* Required and static bits */
-if (
- pdkim_headcat(&col,hdr,";","a=",pdkim_algos[sig->algo]) &&
- pdkim_headcat(&col,hdr,";","q=",pdkim_querymethods[sig->querymethod]) &&
- pdkim_strcat(canon_all,"/") &&
- pdkim_strcat(canon_all,pdkim_canons[sig->canon_body]) &&
- pdkim_headcat(&col,hdr,";","c=",canon_all->str) &&
- pdkim_headcat(&col,hdr,";","d=",sig->domain) &&
- pdkim_headcat(&col,hdr,";","s=",sig->selector)
+if ( pdkim_headcat(&col, hdr, ";", "a=", pdkim_algos[sig->algo])
+ && pdkim_headcat(&col, hdr, ";", "q=", pdkim_querymethods[sig->querymethod])
+ && pdkim_strcat(canon_all, "/")
+ && pdkim_strcat(canon_all, pdkim_canons[sig->canon_body])
+ && pdkim_headcat(&col, hdr, ";", "c=", canon_all->str)
+ && pdkim_headcat(&col, hdr, ";", "d=", sig->domain)
+ && pdkim_headcat(&col, hdr, ";", "s=", sig->selector)
)
{
- /* list of eader names can be split between items. */
+ /* list of header names can be split between items. */
{
- char *n = strdup(sig->headernames);
- char *f = n;
+ char *n = CS string_copy(sig->headernames);
char *i = "h=";
char *s = ";";
if (!n) goto BAIL;
while (*n)
{
- char *c = strchr(n,':');
+ char *c = strchr(n, ':');
if (c) *c ='\0';
if (!i)
- if (!pdkim_headcat(&col,hdr,NULL,NULL,":"))
+ if (!pdkim_headcat(&col, hdr, NULL, NULL, ":"))
{
- free(f);
goto BAIL;
}
- if (!pdkim_headcat(&col,hdr,s,i,n))
+ if (!pdkim_headcat(&col, hdr, s, i, n))
{
- free(f);
goto BAIL;
}
s = NULL;
i = NULL;
}
- free(f);
}
if(!pdkim_headcat(&col, hdr, ";", "bh=", base64_bh))
{
char minibuf[20];
- snprintf(minibuf,20,"%lu",sig->created);
+ snprintf(minibuf, 20, "%lu", sig->created);
if(!pdkim_headcat(&col, hdr, ";", "t=", minibuf))
goto BAIL;
}
{
char minibuf[20];
- snprintf(minibuf,20,"%lu",sig->expires);
+ snprintf(minibuf, 20, "%lu", sig->expires);
if(!pdkim_headcat(&col, hdr, ";", "x=", minibuf))
goto BAIL;
}
{
char minibuf[20];
- snprintf(minibuf,20,"%lu",sig->bodylength);
+ snprintf(minibuf, 20, "%lu", sig->bodylength);
if(!pdkim_headcat(&col, hdr, ";", "l=", minibuf))
goto BAIL;
}
/* Preliminary or final version? */
if (final)
{
- if (!(base64_b = pdkim_encode_base64(sig->sigdata, sig->sigdata_len)))
+ if (!(base64_b = pdkim_encode_base64(&sig->sigdata)))
goto BAIL;
if (!pdkim_headcat(&col, hdr, ";", "b=", base64_b))
goto BAIL;
goto BAIL;
/* add trailing semicolon: I'm not sure if this is actually needed */
- if (!pdkim_headcat(&col,hdr,NULL,";",""))
+ if (!pdkim_headcat(&col, hdr, NULL, ";", ""))
goto BAIL;
}
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;
}
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)
while (sig)
{
- sha1_context sha1_headers;
- sha2_context sha2_headers;
- char *sig_hdr;
- char headerhash[32];
+ BOOL is_sha1 = sig->algo == PDKIM_ALGO_RSA_SHA1;
+ hctx hhash_ctx;
+ char * sig_hdr;
+ blob hhash;
+ blob hdata;
+ int hdata_alloc = 0;
- if (sig->algo == PDKIM_ALGO_RSA_SHA1)
- sha1_starts(&sha1_headers);
- else
- sha2_starts(&sha2_headers,0);
+ hdata.data = NULL;
+ hdata.len = 0;
-#ifdef PDKIM_DEBUG
- if (ctx->debug_stream)
- fprintf(ctx->debug_stream,
- "PDKIM >> Hashed header data, canonicalized, in sequence >>>>>>>>>>>>>>\n");
-#endif
+ exim_sha_init(&hhash_ctx, is_sha1);
+
+ 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. */
+ go, construct a list of the header's names to use for the h= parameter.
+ Then append to that list any remaining header names for which there was no
+ header to sign. */
if (ctx->mode == PDKIM_MODE_SIGN)
{
pdkim_stringlist *p;
+ const uschar * l;
+ uschar * s;
+ int sep = 0;
for (p = sig->headers; p; p = p->next)
- {
- char *rh = NULL;
- /* Collect header names (Note: colon presence is guaranteed here) */
- char *q = strchr(p->value,':');
+ if (header_name_match(p->value, sig->sign_headers) == PDKIM_OK)
+ {
+ uschar * rh;
+ /* Collect header names (Note: colon presence is guaranteed here) */
+ uschar * q = Ustrchr(p->value, ':');
- if (!(pdkim_strncat(headernames, p->value,
- (q-(p->value)) + (p->next ? 1 : 0))))
- return PDKIM_ERR_OOM;
+ if (!(pdkim_strncat(headernames, p->value,
+ (q - US p->value) + (p->next ? 1 : 0))))
+ return PDKIM_ERR_OOM;
- rh = sig->canon_headers == PDKIM_CANON_RELAXED
- ? pdkim_relax_header(p->value,1) /* cook header for relaxed canon */
- : strdup(p->value); /* just copy it for simple canon */
- if (!rh)
- return PDKIM_ERR_OOM;
+ rh = sig->canon_headers == PDKIM_CANON_RELAXED
+ ? US pdkim_relax_header(p->value, 1) /* cook header for relaxed canon */
+ : string_copy(CUS p->value); /* just copy it for simple canon */
+ if (!rh)
+ return PDKIM_ERR_OOM;
- /* Feed header to the hash algorithm */
- if (sig->algo == PDKIM_ALGO_RSA_SHA1)
- sha1_update(&(sha1_headers), (unsigned char *)rh, strlen(rh));
- else
- sha2_update(&(sha2_headers), (unsigned char *)rh, strlen(rh));
+ /* Feed header to the hash algorithm */
+ exim_sha_update(&hhash_ctx, CCS rh, Ustrlen(rh));
-#ifdef PDKIM_DEBUG
- if (ctx->debug_stream)
- pdkim_quoteprint(ctx->debug_stream, rh, strlen(rh), 1);
-#endif
- free(rh);
- }
+ /* Remember headers block for signing (when the library cannot do incremental) */
+ (void) exim_rsa_data_append(&hdata, &hdata_alloc, rh);
+
+ DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
+ }
+
+ l = US sig->sign_headers;
+ while((s = string_nextinlist(&l, &sep, NULL, 0)))
+ if (*s != '_')
+ { /*SSS string_append_listele() */
+ if (headernames->len > 0 && headernames->str[headernames->len-1] != ':')
+ if (!(pdkim_strncat(headernames, ":", 1)))
+ return PDKIM_ERR_OOM;
+ if (!(pdkim_strncat(headernames, CS s, Ustrlen(s))))
+ return PDKIM_ERR_OOM;
+ }
}
/* VERIFICATION ----------------------------------------------------------- */
add the headers to the hash in that order. */
else
{
- char *b = strdup(sig->headernames);
- char *p = b;
- char *q = NULL;
- pdkim_stringlist *hdrs;
+ uschar * b = string_copy(sig->headernames);
+ uschar * p = b;
+ uschar * q;
+ pdkim_stringlist * hdrs;
if (!b) return PDKIM_ERR_OOM;
while(1)
{
- if ((q = strchr(p,':')))
+ if ((q = Ustrchr(p, ':')))
*q = '\0';
+/*XXX walk the list of headers in same order as received. */
for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
if ( hdrs->tag == 0
- && strncasecmp(hdrs->value,p,strlen(p)) == 0
- && (hdrs->value)[strlen(p)] == ':'
+ && strncasecmp(hdrs->value, CS p, Ustrlen(p)) == 0
+ && (hdrs->value)[Ustrlen(p)] == ':'
)
{
- char *rh;
-
- rh = sig->canon_headers == PDKIM_CANON_RELAXED
- ? pdkim_relax_header(hdrs->value,1) /* cook header for relaxed canon */
- : strdup(hdrs->value); /* just copy it for simple canon */
+ uschar * rh = sig->canon_headers == PDKIM_CANON_RELAXED
+ ? US pdkim_relax_header(hdrs->value, 1) /* cook header for relaxed canon */
+ : string_copy(CUS hdrs->value); /* just copy it for simple canon */
if (!rh)
return PDKIM_ERR_OOM;
/* Feed header to the hash algorithm */
- if (sig->algo == PDKIM_ALGO_RSA_SHA1)
- sha1_update(&(sha1_headers), (unsigned char *)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);
-#endif
- free(rh);
+ exim_sha_update(&hhash_ctx, CCS rh, Ustrlen(rh));
+
+ DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
hdrs->tag = 1;
break;
}
if (!q) break;
p = q+1;
}
- 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)
{
/* Copy headernames to signature struct */
- sig->headernames = strdup(headernames->str);
+ sig->headernames = string_copy(US headernames->str);
pdkim_strfree(headernames);
/* Create signature header with b= omitted */
- sig_hdr = pdkim_create_header(ctx->sig, 0);
+ sig_hdr = pdkim_create_header(sig, FALSE);
}
/* VERIFICATION ----------------------------------------------------------- */
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(CUS sig_hdr, strlen(sig_hdr));
+ debug_printf(
"PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
}
-#endif
/* Finalize header hash */
- if (sig->algo == PDKIM_ALGO_RSA_SHA1)
- {
- sha1_update(&(sha1_headers), (unsigned char *)sig_hdr, strlen(sig_hdr));
- sha1_finish(&(sha1_headers), (unsigned char *)headerhash);
+ exim_sha_update(&hhash_ctx, sig_hdr, strlen(sig_hdr));
+ exim_sha_finish(&hhash_ctx, &hhash);
-#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
- }
- else
+ DEBUG(D_acl)
{
- 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);
- }
-#endif
+ debug_printf("PDKIM [%s] hh computed: ", sig->domain);
+ pdkim_hexprint(hhash.data, hhash.len);
}
+ /* Remember headers block for signing (when the library cannot do incremental) */
+ if (ctx->mode == PDKIM_MODE_SIGN)
+ (void) exim_rsa_data_append(&hdata, &hdata_alloc, US sig_hdr);
+
free(sig_hdr);
/* SIGNING ---------------------------------------------------------------- */
if (ctx->mode == PDKIM_MODE_SIGN)
{
- rsa_context rsa;
+ es_ctx sctx;
+ const uschar * errstr;
- rsa_init(&rsa, RSA_PKCS_V15, 0);
-
- /* Perform private key operation */
- if (rsa_parse_key(&rsa, (unsigned char *)sig->rsa_privkey,
- strlen(sig->rsa_privkey), NULL, 0) != 0)
+ /* Import private key */
+ if ((errstr = exim_rsa_signing_init(US sig->rsa_privkey, &sctx)))
+ {
+ DEBUG(D_acl) debug_printf("signing_init: %s\n", errstr);
return PDKIM_ERR_RSA_PRIVKEY;
+ }
- sig->sigdata_len = mpi_size(&(rsa.N));
- if (!(sig->sigdata = malloc(sig->sigdata_len)))
- return PDKIM_ERR_OOM;
+ /* Do signing. With OpenSSL we are signing the hash of headers just
+ calculated, with GnuTLS we have to sign an entire block of headers
+ (due to available interfaces) and it recalculates the hash internally. */
- 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)
- return PDKIM_ERR_RSA_SIGNING;
+#if defined(RSA_OPENSSL) || defined(RSA_GCRYPT)
+ hdata = hhash;
+#endif
- rsa_free(&rsa);
+ if ((errstr = exim_rsa_sign(&sctx, is_sha1, &hdata, &sig->sigdata)))
+ {
+ DEBUG(D_acl) debug_printf("signing: %s\n", errstr);
+ return PDKIM_ERR_RSA_SIGNING;
+ }
-#ifdef PDKIM_DEBUG
- if (ctx->debug_stream)
+ DEBUG(D_acl)
{
- fprintf(ctx->debug_stream, "PDKIM [%s] b computed: ", sig->domain);
- pdkim_hexprint(ctx->debug_stream, sig->sigdata, sig->sigdata_len, 1);
+ debug_printf( "PDKIM [%s] b computed: ", sig->domain);
+ pdkim_hexprint(sig->sigdata.data, sig->sigdata.len);
}
-#endif
- if (!(sig->signature_header = pdkim_create_header(ctx->sig,1)))
+ if (!(sig->signature_header = pdkim_create_header(sig, TRUE)))
return PDKIM_ERR_OOM;
+
+ /* We only ever sign with one sig, and we free'd "headernames"
+ above. So to keep static-analysers happy, exit the loop explicitly.
+ Perhaps the code would be more clear if signing and verification
+ loops were separated? */
+
+ break;
}
/* VERIFICATION ----------------------------------------------------------- */
else
{
- rsa_context rsa;
+ ev_ctx vctx;
+ const uschar * errstr;
+
char *dns_txt_name, *dns_txt_reply;
- rsa_init(&rsa, RSA_PKCS_V15, 0);
+ /* Fetch public key for signing domain, from DNS */
if (!(dns_txt_name = malloc(PDKIM_DNS_TXT_MAX_NAMELEN)))
return PDKIM_ERR_OOM;
memset(dns_txt_reply, 0, PDKIM_DNS_TXT_MAX_RECLEN);
memset(dns_txt_name , 0, PDKIM_DNS_TXT_MAX_NAMELEN);
- if (snprintf(dns_txt_name,PDKIM_DNS_TXT_MAX_NAMELEN,
+ if (snprintf(dns_txt_name, PDKIM_DNS_TXT_MAX_NAMELEN,
"%s._domainkey.%s.",
- sig->selector,sig->domain) >= PDKIM_DNS_TXT_MAX_NAMELEN)
+ sig->selector, sig->domain) >= PDKIM_DNS_TXT_MAX_NAMELEN)
{
sig->verify_status = PDKIM_VERIFY_INVALID;
sig->verify_ext_status = PDKIM_VERIFY_INVALID_BUFFER_SIZE;
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(CUS dns_txt_reply, strlen(dns_txt_reply));
}
-#endif
- if (!(sig->pubkey = pdkim_parse_pubkey_record(ctx,dns_txt_reply)))
+ 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,
- "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
-#endif
+ DEBUG(D_acl) debug_printf(
+ "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
- if (rsa_parse_public_key(&rsa,
- (unsigned char *)sig->pubkey->key,
- sig->pubkey->key_len) != 0)
+ /* Import public key */
+ if ((errstr = exim_rsa_verify_init(&sig->pubkey->key, &vctx)))
{
+ DEBUG(D_acl) debug_printf("verify_init: %s\n", errstr);
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)
+ if ((errstr = exim_rsa_verify(&vctx, is_sha1, &hhash, &sig->sigdata)))
{
+ DEBUG(D_acl) debug_printf("headers verify: %s\n", errstr);
sig->verify_status = PDKIM_VERIFY_FAIL;
sig->verify_ext_status = PDKIM_VERIFY_FAIL_MESSAGE;
goto NEXT_VERIFY;
}
+
/* 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)
+ 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);
free(dns_txt_name);
free(dns_txt_reply);
}
/* -------------------------------------------------------------------------- */
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));
if (!ctx)
return NULL;
-memset(ctx,0,sizeof(pdkim_ctx));
+memset(ctx, 0, sizeof(pdkim_ctx));
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;
/* -------------------------------------------------------------------------- */
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;
if (!(ctx = malloc(sizeof(pdkim_ctx))))
return NULL;
-memset(ctx,0,sizeof(pdkim_ctx));
+memset(ctx, 0, sizeof(pdkim_ctx));
if (!(ctx->linebuf = malloc(PDKIM_MAX_BODY_LINE_LEN)))
{
free(ctx);
return NULL;
}
-memset(sig,0,sizeof(pdkim_signature));
+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);
+sig->domain = strdup(domain);
+sig->selector = strdup(selector);
+sig->rsa_privkey = strdup(rsa_privkey);
+sig->algo = algo;
-if (!ctx->sig->domain || !ctx->sig->selector || !ctx->sig->rsa_privkey)
+if (!sig->domain || !sig->selector || !sig->rsa_privkey)
goto BAIL;
-if (!(ctx->sig->sha1_body = malloc(sizeof(sha1_context))))
- goto BAIL;
-sha1_starts(ctx->sig->sha1_body);
-
-if (!(ctx->sig->sha2_body = malloc(sizeof(sha2_context))))
- goto BAIL;
-sha2_starts(ctx->sig->sha2_body,0);
-
+exim_sha_init(&sig->body_hash, algo == PDKIM_ALGO_RSA_SHA1);
return ctx;
BAIL:
return NULL;
}
+
/* -------------------------------------------------------------------------- */
DLLEXPORT int
int canon_headers,
int canon_body,
long bodylength,
- int algo,
unsigned long created,
unsigned long expires)
{
+pdkim_signature * sig = ctx->sig;
if (identity)
- if (!(ctx->sig->identity = strdup(identity)))
+ if (!(sig->identity = strdup(identity)))
return PDKIM_ERR_OOM;
-if (sign_headers)
- if (!(ctx->sig->sign_headers = strdup(sign_headers)))
- return PDKIM_ERR_OOM;
+if (!(sig->sign_headers = strdup(sign_headers
+ ? sign_headers : PDKIM_DEFAULT_SIGN_HEADERS)))
+ return PDKIM_ERR_OOM;
-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;
+sig->canon_headers = canon_headers;
+sig->canon_body = canon_body;
+sig->bodylength = bodylength;
+sig->created = created;
+sig->expires = expires;
return PDKIM_OK;
}
-/* -------------------------------------------------------------------------- */
-#ifdef PDKIM_DEBUG
-DLLEXPORT void
-pdkim_set_debug_stream(pdkim_ctx *ctx, FILE *debug_stream)
+void
+pdkim_init(void)
{
-ctx->debug_stream = debug_stream;
+exim_rsa_init();
}
-#endif
+
+
+#endif /*DISABLE_DKIM*/