#include "rsa.h"
#define PDKIM_SIGNATURE_VERSION "1"
-#define PDKIM_PUB_RECORD_VERSION "DKIM1"
+#define PDKIM_PUB_RECORD_VERSION US "DKIM1"
#define PDKIM_MAX_HEADER_LEN 65536
#define PDKIM_MAX_HEADERS 512
/* -------------------------------------------------------------------------- */
struct pdkim_stringlist {
- char *value;
- int tag;
- void *next;
+ uschar * value;
+ int tag;
+ void * next;
};
/* -------------------------------------------------------------------------- */
/* A bunch of list constants */
-const char *pdkim_querymethods[] = {
- "dns/txt",
+const uschar * pdkim_querymethods[] = {
+ US"dns/txt",
NULL
};
-const char *pdkim_algos[] = {
- "rsa-sha256",
- "rsa-sha1",
+const uschar * pdkim_algos[] = {
+ US"rsa-sha256",
+ US"rsa-sha1",
NULL
};
-const char *pdkim_canons[] = {
- "simple",
- "relaxed",
+const uschar * pdkim_canons[] = {
+ US"simple",
+ US"relaxed",
NULL
};
-const char *pdkim_hashes[] = {
- "sha256",
- "sha1",
+const uschar * pdkim_hashes[] = {
+ US"sha256",
+ US"sha1",
NULL
};
-const char *pdkim_keytypes[] = {
- "rsa",
+const uschar * pdkim_keytypes[] = {
+ US"rsa",
NULL
};
typedef struct pdkim_combined_canon_entry {
- const char *str;
+ const uschar * str;
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 },
- { "relaxed/simple", PDKIM_CANON_RELAXED, PDKIM_CANON_SIMPLE },
- { "relaxed/relaxed", PDKIM_CANON_RELAXED, PDKIM_CANON_RELAXED },
- { "simple", PDKIM_CANON_SIMPLE, PDKIM_CANON_SIMPLE },
- { "relaxed", PDKIM_CANON_RELAXED, PDKIM_CANON_SIMPLE },
- { NULL, 0, 0 }
+ { US"simple/simple", PDKIM_CANON_SIMPLE, PDKIM_CANON_SIMPLE },
+ { US"simple/relaxed", PDKIM_CANON_SIMPLE, PDKIM_CANON_RELAXED },
+ { US"relaxed/simple", PDKIM_CANON_RELAXED, PDKIM_CANON_SIMPLE },
+ { US"relaxed/relaxed", PDKIM_CANON_RELAXED, PDKIM_CANON_RELAXED },
+ { US"simple", PDKIM_CANON_SIMPLE, PDKIM_CANON_SIMPLE },
+ { US"relaxed", PDKIM_CANON_RELAXED, PDKIM_CANON_SIMPLE },
+ { NULL, 0, 0 }
};
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";
+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_DNSRECORD: return "PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD";
- case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return "PDKIM_VERIFY_INVALID_PUBKEY_IMPORT";
- default: return "PDKIM_VERIFY_UNKNOWN";
+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_DNSRECORD: return "PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD";
+ case PDKIM_VERIFY_INVALID_PUBKEY_IMPORT: return "PDKIM_VERIFY_INVALID_PUBKEY_IMPORT";
+ case PDKIM_VERIFY_INVALID_SIGNATURE_ERROR: return "PDKIM_VERIFY_INVALID_SIGNATURE_ERROR";
+ case PDKIM_VERIFY_INVALID_DKIM_VERSION: return "PDKIM_VERIFY_INVALID_DKIM_VERSION";
+ default: return "PDKIM_VERIFY_UNKNOWN";
+ }
+}
+
+const char *
+pdkim_errstr(int status)
+{
+switch(status)
+ {
+ case PDKIM_OK: return "OK";
+ case PDKIM_FAIL: return "FAIL";
+ case PDKIM_ERR_RSA_PRIVKEY: return "RSA_PRIVKEY";
+ case PDKIM_ERR_RSA_SIGNING: return "RSA SIGNING";
+ case PDKIM_ERR_LONG_LINE: return "RSA_LONG_LINE";
+ case PDKIM_ERR_BUFFER_TOO_SMALL: return "BUFFER_TOO_SMALL";
+ case PDKIM_SIGN_PRIVKEY_WRAP: return "PRIVKEY_WRAP";
+ case PDKIM_SIGN_PRIVKEY_B64D: return "PRIVKEY_B64D";
+ default: return "(unknown)";
}
}
/*XXX might be safer done using a pdkim_stringlist for "tick" */
static int
-header_name_match(const uschar * header, char * tick)
+header_name_match(const uschar * header, uschar * tick)
{
uschar * hname;
uschar * lcopy;
/* -------------------------------------------------------------------------- */
#define PDKIM_QP_ERROR_DECODE -1
-static char *
-pdkim_decode_qp_char(char *qp_p, int *c)
+static uschar *
+pdkim_decode_qp_char(uschar *qp_p, int *c)
{
-char *initial_pos = qp_p;
+uschar *initial_pos = qp_p;
/* Advance one char */
qp_p++;
/* -------------------------------------------------------------------------- */
-static char *
+static uschar *
pdkim_decode_qp(uschar * str)
{
int nchar = 0;
memset(sig, 0, sizeof(pdkim_signature));
sig->bodylength = -1;
+/* Set so invalid/missing data error display is accurate */
+sig->algo = -1;
+sig->version = 0;
+
q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1);
for (p = raw_hdr; ; p++)
if (where == PDKIM_HDR_TAG)
{
if (c >= 'a' && c <= 'z')
- cur_tag = string_cat(cur_tag, &ts, &tl, p, 1);
+ cur_tag = string_catn(cur_tag, &ts, &tl, p, 1);
if (c == '=')
{
case 'v':
/* We only support version 1, and that is currently the
only version there is. */
- if (Ustrcmp(cur_val, PDKIM_SIGNATURE_VERSION) == 0)
- sig->version = 1;
+ sig->version =
+ Ustrcmp(cur_val, PDKIM_SIGNATURE_VERSION) == 0 ? 1 : -1;
break;
case 'a':
for (i = 0; pdkim_algos[i]; i++)
where = PDKIM_HDR_LIMBO;
}
else
- cur_val = string_cat(cur_val, &vs, &vl, p, 1);
+ cur_val = string_catn(cur_val, &vs, &vl, p, 1);
}
NEXT_CHAR:
*q++ = c;
}
-/* Make sure the most important bits are there. */
-if (!sig->version)
- return NULL;
-
*q = '\0';
/* Chomp raw header. The final newline must not be added to the signature. */
while (--q > sig->rawsig_no_b_val && (*q == '\r' || *q == '\n'))
{
debug_printf(
"PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
- pdkim_quoteprint(US sig->rawsig_no_b_val, strlen(sig->rawsig_no_b_val));
+ pdkim_quoteprint(US sig->rawsig_no_b_val, Ustrlen(sig->rawsig_no_b_val));
debug_printf(
"PDKIM >> Sig size: %4u bits\n", (unsigned) sig->sigdata.len*8);
debug_printf(
"PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
}
-exim_sha_init(&sig->body_hash, sig->algo == PDKIM_ALGO_RSA_SHA1);
+exim_sha_init(&sig->body_hash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256);
return sig;
}
/* -------------------------------------------------------------------------- */
static pdkim_pubkey *
-pdkim_parse_pubkey_record(pdkim_ctx *ctx, char *raw_record)
+pdkim_parse_pubkey_record(pdkim_ctx *ctx, const uschar *raw_record)
{
pdkim_pubkey *pub;
-char *p;
+const uschar *p;
uschar * cur_tag = NULL; int ts = 0, tl = 0;
uschar * cur_val = NULL; int vs = 0, vl = 0;
int where = PDKIM_HDR_LIMBO;
if (where == PDKIM_HDR_TAG)
{
if (c >= 'a' && c <= 'z')
- cur_tag = string_cat(cur_tag, &ts, &tl, p, 1);
+ cur_tag = string_catn(cur_tag, &ts, &tl, p, 1);
if (c == '=')
{
case 's':
pub->srvtype = string_copy(cur_val); break;
case 't':
- if (strchr(cur_val, 'y') != NULL) pub->testing = 1;
- if (strchr(cur_val, 's') != NULL) pub->no_subdomaining = 1;
+ if (Ustrchr(cur_val, 'y') != NULL) pub->testing = 1;
+ if (Ustrchr(cur_val, 's') != NULL) pub->no_subdomaining = 1;
break;
default:
DEBUG(D_acl) debug_printf(" Unknown tag encountered\n");
where = PDKIM_HDR_LIMBO;
}
else
- cur_val = string_cat(cur_val, &vs, &vl, p, 1);
+ cur_val = string_catn(cur_val, &vs, &vl, p, 1);
}
NEXT_CHAR:
/* Set fallback defaults */
if (!pub->version ) pub->version = string_copy(PDKIM_PUB_RECORD_VERSION);
if (!pub->granularity) pub->granularity = string_copy(US"*");
-if (!pub->keytype ) pub->keytype = string_copy("rsa");
+if (!pub->keytype ) pub->keytype = string_copy(US"rsa");
if (!pub->srvtype ) pub->srvtype = string_copy(US"*");
/* p= is required */
const char *p;
int q = 0;
- relaxed_data = store_get(len+1);
+ /* We want to be able to free this else we allocate
+ for the entire message which could be many MB. Since
+ we don't know what allocations the SHA routines might
+ do, not safe to use store_get()/store_reset(). */
+
+ relaxed_data = store_malloc(len+1);
for (p = data; *p; p++)
{
if (canon_len > 0)
{
- exim_sha_update(&sig->body_hash, CCS canon_data, canon_len);
+ exim_sha_update(&sig->body_hash, CUS canon_data, canon_len);
sig->signed_body_bytes += canon_len;
DEBUG(D_acl) pdkim_quoteprint(canon_data, canon_len);
}
sig = sig->next;
}
+if (relaxed_data) store_free(relaxed_data);
return PDKIM_OK;
}
}
/* SIGNING -------------------------------------------------------------- */
- if (ctx->mode == PDKIM_MODE_SIGN)
+ if (ctx->flags & PDKIM_MODE_SIGN)
{
sig->bodyhash = bh;
+static int
+pdkim_body_complete(pdkim_ctx * ctx)
+{
+pdkim_signature * sig = ctx->sig; /*XXX assumes only one sig */
+
+/* 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->flags |= PDKIM_SEEN_EOD;
+ctx->linebuf_offset = 0;
+return PDKIM_OK;
+}
+
+
+
/* -------------------------------------------------------------------------- */
-/* Callback from pdkim_feed below for processing complete body lines */
+/* Call from pdkim_feed below for processing complete body lines */
static int
pdkim_bodyline_complete(pdkim_ctx *ctx)
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;
+if (ctx->flags & PDKIM_SEEN_EOD) goto BAIL;
/* We've always got one extra byte to stuff a zero ... */
ctx->linebuf[ctx->linebuf_offset] = '\0';
/* Terminate on EOD marker */
-if (memcmp(p, ".\r\n", 3) == 0)
+if (ctx->flags & PDKIM_DOT_TERM)
{
- /* 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);
+ if ( memcmp(p, ".\r\n", 3) == 0)
+ return pdkim_body_complete(ctx);
- ctx->seen_eod = TRUE;
- goto BAIL;
- }
-/* Unstuff dots */
-if (memcmp(p, "..", 2) == 0)
- {
- p++;
- n--;
+ /* Unstuff dots */
+ if (memcmp(p, "..", 2) == 0)
+ {
+ p++;
+ n--;
+ }
}
/* Empty lines need to be buffered until we find a non-empty line */
if (ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL;
/* SIGNING -------------------------------------------------------------- */
-if (ctx->mode == PDKIM_MODE_SIGN)
+if (ctx->flags & PDKIM_MODE_SIGN)
{
pdkim_signature *sig;
/* VERIFICATION ----------------------------------------------------------- */
/* DKIM-Signature: headers are added to the verification list */
-if (ctx->mode == PDKIM_MODE_VERIFY)
+else
{
- if (strncasecmp(ctx->cur_header,
+ if (strncasecmp(CCS ctx->cur_header,
DKIM_SIGNATURE_HEADERNAME,
- strlen(DKIM_SIGNATURE_HEADERNAME)) == 0)
+ Ustrlen(DKIM_SIGNATURE_HEADERNAME)) == 0)
{
pdkim_signature *new_sig;
{
int p;
-for (p = 0; p<len; p++)
+/* Alternate EOD signal, used in non-dotstuffing mode */
+if (!data)
+ pdkim_body_complete(ctx);
+
+else for (p = 0; p<len; p++)
{
uschar c = data[p];
- if (ctx->past_headers)
+ if (ctx->flags & PDKIM_PAST_HDRS)
{
/* Processing body byte */
ctx->linebuf[ctx->linebuf_offset++] = c;
{
if (c == '\n')
{
- if (ctx->seen_lf)
+ if (ctx->flags & PDKIM_SEEN_LF)
{
int rc = pdkim_header_complete(ctx); /* Seen last header line */
if (rc != PDKIM_OK) return rc;
- ctx->past_headers = TRUE;
- ctx->seen_lf = 0;
+ ctx->flags = ctx->flags & ~PDKIM_SEEN_LF | PDKIM_PAST_HDRS;
DEBUG(D_acl) debug_printf(
"PDKIM >> Body data for hash, canonicalized >>>>>>>>>>>>>>>>>>>>>>\n");
continue;
}
else
- ctx->seen_lf = TRUE;
+ ctx->flags |= PDKIM_SEEN_LF;
}
- else if (ctx->seen_lf)
+ else if (ctx->flags & PDKIM_SEEN_LF)
{
if (!(c == '\t' || c == ' '))
{
int rc = pdkim_header_complete(ctx); /* End of header */
if (rc != PDKIM_OK) return rc;
}
- ctx->seen_lf = FALSE;
+ ctx->flags &= ~PDKIM_SEEN_LF;
}
}
if (ctx->cur_header_len < PDKIM_MAX_HEADER_LEN)
- ctx->cur_header = string_cat(ctx->cur_header, &ctx->cur_header_size,
- &ctx->cur_header_len, &data[p], 1);
+ ctx->cur_header = string_catn(ctx->cur_header, &ctx->cur_header_size,
+ &ctx->cur_header_len, CUS &data[p], 1);
}
}
return PDKIM_OK;
pdkim_hdr_cont(uschar * str, int * size, int * ptr, int * col)
{
*col = 1;
-return string_cat(str, size, ptr, US"\r\n\t", 3);
+return string_catn(str, size, ptr, US"\r\n\t", 3);
}
l = Ustrlen(pad);
if (*col + l > 78)
str = pdkim_hdr_cont(str, size, ptr, col);
- str = string_cat(str, size, ptr, pad, l);
+ str = string_catn(str, size, ptr, pad, l);
*col += l;
}
{ /* this fragment will not fit on a single line */
if (pad)
{
- str = string_cat(str, size, ptr, US" ", 1);
+ str = string_catn(str, size, ptr, US" ", 1);
*col += 1;
pad = NULL; /* only want this once */
l--;
{
size_t sl = Ustrlen(intro);
- str = string_cat(str, size, ptr, intro, sl);
+ str = string_catn(str, size, ptr, intro, sl);
*col += sl;
l -= sl;
intro = NULL; /* only want this once */
size_t sl = Ustrlen(payload);
size_t chomp = *col+sl < 77 ? sl : 78-*col;
- str = string_cat(str, size, ptr, payload, chomp);
+ str = string_catn(str, size, ptr, payload, chomp);
*col += chomp;
payload += chomp;
l -= chomp-1;
if (pad)
{
- str = string_cat(str, size, ptr, US" ", 1);
+ str = string_catn(str, size, ptr, US" ", 1);
*col += 1;
pad = NULL;
}
{
size_t sl = Ustrlen(intro);
- str = string_cat(str, size, ptr, intro, sl);
+ str = string_catn(str, size, ptr, intro, sl);
*col += sl;
l -= sl;
intro = NULL;
{
size_t sl = Ustrlen(payload);
- str = string_cat(str, size, ptr, payload, sl);
+ str = string_catn(str, size, ptr, payload, sl);
*col += sl;
}
uschar * hdr; int hdr_size = 0, hdr_len = 0;
uschar * canon_all; int can_size = 0, can_len = 0;
-canon_all = string_cat(NULL, &can_size, &can_len,
- pdkim_canons[sig->canon_headers], -1);
-canon_all = string_cat(canon_all, &can_size, &can_len, US"/", 1);
-canon_all = string_cat(canon_all, &can_size, &can_len,
- pdkim_canons[sig->canon_body], -1);
+canon_all = string_cat (NULL, &can_size, &can_len,
+ pdkim_canons[sig->canon_headers]);
+canon_all = string_catn(canon_all, &can_size, &can_len, US"/", 1);
+canon_all = string_cat (canon_all, &can_size, &can_len,
+ pdkim_canons[sig->canon_body]);
canon_all[can_len] = '\0';
hdr = string_cat(NULL, &hdr_size, &hdr_len,
- "DKIM-Signature: v="PDKIM_SIGNATURE_VERSION, -1);
+ US"DKIM-Signature: v="PDKIM_SIGNATURE_VERSION);
col = hdr_len;
/* Required and static bits */
/* list of header names can be split between items. */
{
- uschar * n = CS string_copy(sig->headernames);
+ uschar * n = string_copy(sig->headernames);
uschar * i = US"h=";
uschar * s = US";";
if (sig->created > 0)
{
- char minibuf[20];
+ uschar minibuf[20];
- snprintf(minibuf, 20, "%lu", sig->created);
+ snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->created);
hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"t=", minibuf);
}
if (sig->expires > 0)
{
- char minibuf[20];
+ uschar minibuf[20];
- snprintf(minibuf, 20, "%lu", sig->expires);
+ snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->expires);
hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"x=", minibuf);
}
if (sig->bodylength >= 0)
{
- char minibuf[20];
+ uschar minibuf[20];
- snprintf(minibuf, 20, "%lu", sig->bodylength);
+ snprintf(CS minibuf, sizeof(minibuf), "%lu", sig->bodylength);
hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"l=", minibuf);
}
pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatures)
{
pdkim_signature *sig = ctx->sig;
-uschar * headernames = NULL; /* Collected signed header names */
-int hs = 0, hl = 0;
/* 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
{
BOOL is_sha1 = sig->algo == PDKIM_ALGO_RSA_SHA1;
hctx hhash_ctx;
- uschar * sig_hdr;
+ uschar * sig_hdr = US"";
blob hhash;
blob hdata;
int hdata_alloc = 0;
hdata.data = NULL;
hdata.len = 0;
- exim_sha_init(&hhash_ctx, is_sha1);
+ exim_sha_init(&hhash_ctx, is_sha1 ? HASH_SHA1 : HASH_SHA256);
DEBUG(D_acl) debug_printf(
"PDKIM >> Hashed header data, canonicalized, in sequence >>>>>>>>>>>>>>\n");
Then append to that list any remaining header names for which there was no
header to sign. */
- if (ctx->mode == PDKIM_MODE_SIGN)
+ if (ctx->flags & PDKIM_MODE_SIGN)
{
+ uschar * headernames = NULL; /* Collected signed header names */
+ int hs = 0, hl = 0;
pdkim_stringlist *p;
const uschar * l;
uschar * s;
/* Collect header names (Note: colon presence is guaranteed here) */
uschar * q = Ustrchr(p->value, ':');
- headernames = string_cat(headernames, &hs, &hl,
+ headernames = string_catn(headernames, &hs, &hl,
p->value, (q - US p->value) + (p->next ? 1 : 0));
rh = sig->canon_headers == PDKIM_CANON_RELAXED
: string_copy(CUS p->value); /* just copy it for simple canon */
/* Feed header to the hash algorithm */
- exim_sha_update(&hhash_ctx, CCS rh, Ustrlen(rh));
+ exim_sha_update(&hhash_ctx, CUS rh, Ustrlen(rh));
/* Remember headers block for signing (when the library cannot do incremental) */
(void) exim_rsa_data_append(&hdata, &hdata_alloc, rh);
if (*s != '_')
{ /*SSS string_append_listele() */
if (hl > 0 && headernames[hl-1] != ':')
- headernames = string_cat(headernames, &hs, &hl, US":", 1);
+ headernames = string_catn(headernames, &hs, &hl, US":", 1);
- headernames = string_cat(headernames, &hs, &hl, s, -1);
+ headernames = string_cat(headernames, &hs, &hl, s);
}
headernames[hl] = '\0';
/* Copy headernames to signature struct */
sig->headernames = headernames;
- headernames = NULL, hs = hl = 0;
/* Create signature header with b= omitted */
sig_hdr = pdkim_create_header(sig, FALSE);
add the headers to the hash in that order. */
else
{
- uschar * b = string_copy(sig->headernames);
- uschar * p = b;
+ uschar * p = sig->headernames;
uschar * q;
pdkim_stringlist * hdrs;
- /* clear tags */
- for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
- hdrs->tag = 0;
-
- while(1)
+ if (p)
{
- if ((q = Ustrchr(p, ':')))
- *q = '\0';
-
-/*XXX walk the list of headers in same order as received. */
+ /* clear tags */
for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next)
- if ( hdrs->tag == 0
- && strncasecmp(hdrs->value, CS p, Ustrlen(p)) == 0
- && (hdrs->value)[Ustrlen(p)] == ':'
- )
- {
- uschar * rh = sig->canon_headers == PDKIM_CANON_RELAXED
- ? pdkim_relax_header(hdrs->value, 1) /* cook header for relaxed canon */
- : string_copy(CUS hdrs->value); /* just copy it for simple canon */
+ hdrs->tag = 0;
- /* Feed header to the hash algorithm */
- exim_sha_update(&hhash_ctx, CCS rh, Ustrlen(rh));
+ p = string_copy(p);
+ while(1)
+ {
+ 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(CCS hdrs->value, CCS p, Ustrlen(p)) == 0
+ && (hdrs->value)[Ustrlen(p)] == ':'
+ )
+ {
+ /* cook header for relaxed canon, or just copy it for simple */
+
+ uschar * rh = sig->canon_headers == PDKIM_CANON_RELAXED
+ ? pdkim_relax_header(hdrs->value, 1)
+ : string_copy(CUS hdrs->value);
+
+ /* Feed header to the hash algorithm */
+ exim_sha_update(&hhash_ctx, CUS rh, Ustrlen(rh));
+
+ DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
+ hdrs->tag = 1;
+ break;
+ }
- DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh));
- hdrs->tag = 1;
- break;
- }
+ if (!q) break;
+ p = q+1;
+ }
- if (!q) break;
- p = q+1;
+ sig_hdr = string_copy(sig->rawsig_no_b_val);
}
-
- sig_hdr = string_copy(sig->rawsig_no_b_val);
}
DEBUG(D_acl) debug_printf(
{
debug_printf(
"PDKIM >> Signed DKIM-Signature header, canonicalized >>>>>>>>>>>>>>>>>\n");
- pdkim_quoteprint(CUS sig_hdr, strlen(sig_hdr));
+ pdkim_quoteprint(CUS sig_hdr, Ustrlen(sig_hdr));
debug_printf(
"PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
}
/* Finalize header hash */
- exim_sha_update(&hhash_ctx, sig_hdr, strlen(sig_hdr));
+ exim_sha_update(&hhash_ctx, CUS sig_hdr, Ustrlen(sig_hdr));
exim_sha_finish(&hhash_ctx, &hhash);
DEBUG(D_acl)
}
/* Remember headers block for signing (when the library cannot do incremental) */
- if (ctx->mode == PDKIM_MODE_SIGN)
+ if (ctx->flags & PDKIM_MODE_SIGN)
(void) exim_rsa_data_append(&hdata, &hdata_alloc, US sig_hdr);
/* SIGNING ---------------------------------------------------------------- */
- if (ctx->mode == PDKIM_MODE_SIGN)
+ if (ctx->flags & PDKIM_MODE_SIGN)
{
es_ctx sctx;
const uschar * errstr;
uschar *dns_txt_name, *dns_txt_reply;
+ /* Make sure we have all required signature tags */
+ if (!( sig->domain && *sig->domain
+ && sig->selector && *sig->selector
+ && sig->headernames && *sig->headernames
+ && sig->bodyhash.data
+ && sig->sigdata.data
+ && sig->algo > -1
+ && sig->version
+ ) )
+ {
+ sig->verify_status = PDKIM_VERIFY_INVALID;
+ sig->verify_ext_status = PDKIM_VERIFY_INVALID_SIGNATURE_ERROR;
+
+ DEBUG(D_acl) debug_printf(
+ " Error in DKIM-Signature header: tags missing or invalid\n"
+ "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+ goto NEXT_VERIFY;
+ }
+
+ /* Make sure sig uses supported DKIM version (only v1) */
+ if (sig->version != 1)
+ {
+ sig->verify_status = PDKIM_VERIFY_INVALID;
+ sig->verify_ext_status = PDKIM_VERIFY_INVALID_DKIM_VERSION;
+
+ DEBUG(D_acl) debug_printf(
+ " Error in DKIM-Signature header: unsupported DKIM version\n"
+ "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+ goto NEXT_VERIFY;
+ }
+
/* Fetch public key for signing domain, from DNS */
dns_txt_name = string_sprintf("%s._domainkey.%s.",
pdkim_quoteprint(CUS dns_txt_reply, Ustrlen(dns_txt_reply));
}
- if (!(sig->pubkey = pdkim_parse_pubkey_record(ctx, dns_txt_reply)))
+ if (!(sig->pubkey = pdkim_parse_pubkey_record(ctx, CUS dns_txt_reply)))
{
sig->verify_status = PDKIM_VERIFY_INVALID;
sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD;
/* -------------------------------------------------------------------------- */
DLLEXPORT pdkim_ctx *
-pdkim_init_verify(int(*dns_txt_callback)(char *, char *))
+pdkim_init_verify(int(*dns_txt_callback)(char *, char *), BOOL dot_stuffing)
{
pdkim_ctx * ctx;
ctx = store_get(sizeof(pdkim_ctx));
memset(ctx, 0, sizeof(pdkim_ctx));
+if (dot_stuffing) ctx->flags = PDKIM_DOT_TERM;
ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN);
-ctx->mode = PDKIM_MODE_VERIFY;
ctx->dns_txt_callback = dns_txt_callback;
return ctx;
/* -------------------------------------------------------------------------- */
DLLEXPORT pdkim_ctx *
-pdkim_init_sign(char *domain, char *selector, char *rsa_privkey, int algo)
+pdkim_init_sign(char *domain, char *selector, char *rsa_privkey, int algo,
+ BOOL dot_stuffed)
{
pdkim_ctx *ctx;
pdkim_signature *sig;
ctx = store_get(sizeof(pdkim_ctx));
memset(ctx, 0, sizeof(pdkim_ctx));
+ctx->flags = dot_stuffed ? PDKIM_MODE_SIGN | PDKIM_DOT_TERM : PDKIM_MODE_SIGN;
ctx->linebuf = store_get(PDKIM_MAX_BODY_LINE_LEN);
sig = store_get(sizeof(pdkim_signature));
memset(sig, 0, sizeof(pdkim_signature));
sig->bodylength = -1;
-
-ctx->mode = PDKIM_MODE_SIGN;
ctx->sig = sig;
-sig->domain = string_copy(domain);
-sig->selector = string_copy(selector);
-sig->rsa_privkey = string_copy(rsa_privkey);
+sig->domain = string_copy(US domain);
+sig->selector = string_copy(US selector);
+sig->rsa_privkey = string_copy(US rsa_privkey);
sig->algo = algo;
-exim_sha_init(&sig->body_hash, algo == PDKIM_ALGO_RSA_SHA1);
+exim_sha_init(&sig->body_hash, algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256);
return ctx;
}
pdkim_signature * sig = ctx->sig;
if (identity)
- sig->identity = string_copy(identity);
+ sig->identity = string_copy(US identity);
sig->sign_headers = string_copy(sign_headers
? US sign_headers : US PDKIM_DEFAULT_SIGN_HEADERS);