pdkim_hexprint(const uschar *data, int len)
{
int i;
-for (i = 0 ; i < len; i++) debug_printf("%02x", data[i]);
+if (data) for (i = 0 ; i < len; i++) debug_printf("%02x", data[i]);
+else debug_printf("<NULL>");
debug_printf("\n");
}
switch (*cur_tag)
{
case 'b':
- if (cur_tag[1] == 'h')
- pdkim_decode_base64(cur_val, &sig->bodyhash);
- else
- pdkim_decode_base64(cur_val, &sig->sigdata);
+ pdkim_decode_base64(cur_val,
+ cur_tag[1] == 'h' ? &sig->bodyhash : &sig->sighash);
break;
case 'v':
/* We only support version 1, and that is currently the
"PDKIM >> Raw signature w/o b= tag value >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
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);
+ "PDKIM >> Sig size: %4u bits\n", (unsigned) sig->sighash.len*8);
debug_printf(
"PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
}
-exim_sha_init(&sig->body_hash, sig->algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256);
+exim_sha_init(&sig->body_hash_ctx,
+ sig->algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256);
return sig;
}
/* -------------------------------------------------------------------------- */
static int
-pdkim_update_bodyhash(pdkim_ctx *ctx, const char *data, int len)
+pdkim_update_bodyhash(pdkim_ctx * ctx, const char * data, int len)
{
-pdkim_signature *sig = ctx->sig;
-/* Cache relaxed version of data */
-uschar *relaxed_data = NULL;
-int relaxed_len = 0;
+pdkim_signature * sig;
+uschar * relaxed_data = NULL; /* Cache relaxed version of data */
+int relaxed_len = 0;
/* Traverse all signatures, updating their hashes. */
-while (sig)
+for (sig = ctx->sig; sig; sig = sig->next)
{
/* Defaults to simple canon (no further treatment necessary) */
const uschar *canon_data = CUS data;
if (canon_len > 0)
{
- exim_sha_update(&sig->body_hash, CUS canon_data, canon_len);
+ exim_sha_update(&sig->body_hash_ctx, 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);
{ /* Finish hashes */
blob bh;
- exim_sha_finish(&sig->body_hash, &bh);
+ exim_sha_finish(&sig->body_hash_ctx, &bh);
DEBUG(D_acl)
{
sig->bodylength = -1;
}
- /* VERIFICATION --------------------------------------------------------- */
else
- {
- /* Compare bodyhash */
- if (memcmp(bh.data, sig->bodyhash.data, bh.len) == 0)
+ /* VERIFICATION --------------------------------------------------------- */
+ /* Be careful that the header sig included a bodyash */
+
+ if (sig->bodyhash.data && memcmp(bh.data, sig->bodyhash.data, bh.len) == 0)
{
DEBUG(D_acl) debug_printf("PDKIM [%s] Body hash verified OK\n", sig->domain);
}
DEBUG(D_acl)
{
debug_printf("PDKIM [%s] Body hash signature from headers: ", sig->domain);
- pdkim_hexprint(sig->bodyhash.data,
- exim_sha_hashlen(&sig->body_hash));
+ pdkim_hexprint(sig->bodyhash.data, sig->bodyhash.len);
debug_printf("PDKIM [%s] Body hash did NOT verify\n", sig->domain);
}
sig->verify_status = PDKIM_VERIFY_FAIL;
sig->verify_ext_status = PDKIM_VERIFY_FAIL_BODY;
}
- }
}
}
/* Terminate on EOD marker */
if (ctx->flags & PDKIM_DOT_TERM)
{
- if ( memcmp(p, ".\r\n", 3) == 0)
+ if (memcmp(p, ".\r\n", 3) == 0)
return pdkim_body_complete(ctx);
/* Unstuff dots */
/* DKIM-Signature: headers are added to the verification list */
else
{
+ DEBUG(D_acl)
+ {
+ debug_printf("PDKIM >> raw hdr: ");
+ pdkim_quoteprint(CUS ctx->cur_header, Ustrlen(ctx->cur_header));
+ }
if (strncasecmp(CCS ctx->cur_header,
DKIM_SIGNATURE_HEADERNAME,
Ustrlen(DKIM_SIGNATURE_HEADERNAME)) == 0)
{
- pdkim_signature *new_sig;
+ pdkim_signature * new_sig, * last_sig;
+
+ /* Create and chain new signature block. We could error-check for all
+ required tags here, but prefer to create the internal sig and expicitly
+ fail verification of it later. */
- /* Create and chain new signature block */
DEBUG(D_acl) debug_printf(
"PDKIM >> Found sig, trying to parse >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
- if ((new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header)))
+ new_sig = pdkim_parse_sig_header(ctx, ctx->cur_header);
+
+ if (!(last_sig = ctx->sig))
+ ctx->sig = new_sig;
+ else
{
- pdkim_signature *last_sig = ctx->sig;
- if (!last_sig)
- ctx->sig = new_sig;
- else
- {
- while (last_sig->next) last_sig = last_sig->next;
- last_sig->next = new_sig;
- }
+ while (last_sig->next) last_sig = last_sig->next;
+ last_sig->next = new_sig;
}
- else
- DEBUG(D_acl) debug_printf(
- "Error while parsing signature header\n"
- "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
}
- /* every other header is stored for signature verification */
- else
- ctx->headers = pdkim_prepend_stringlist(ctx->headers, ctx->cur_header);
+ /* all headers are stored for signature verification */
+ ctx->headers = pdkim_prepend_stringlist(ctx->headers, ctx->cur_header);
}
BAIL:
DLLEXPORT int
pdkim_feed(pdkim_ctx *ctx, char *data, int len)
{
-int p;
+int p, rc;
/* Alternate EOD signal, used in non-dotstuffing mode */
if (!data)
if (ctx->flags & PDKIM_PAST_HDRS)
{
+ if (c == '\n' && !(ctx->flags & PDKIM_SEEN_CR)) /* emulate the CR */
+ {
+ ctx->linebuf[ctx->linebuf_offset++] = '\r';
+ if (ctx->linebuf_offset == PDKIM_MAX_BODY_LINE_LEN-1)
+ return PDKIM_ERR_LONG_LINE;
+ }
+
/* Processing body byte */
ctx->linebuf[ctx->linebuf_offset++] = c;
- if (c == '\n')
+ if (c == '\r')
+ ctx->flags |= PDKIM_SEEN_CR;
+ else if (c == '\n')
{
- int rc = pdkim_bodyline_complete(ctx); /* End of line */
- if (rc != PDKIM_OK) return rc;
+ ctx->flags &= ~PDKIM_SEEN_CR;
+ if ((rc = pdkim_bodyline_complete(ctx)) != PDKIM_OK)
+ return rc;
}
- if (ctx->linebuf_offset == (PDKIM_MAX_BODY_LINE_LEN-1))
+
+ if (ctx->linebuf_offset == PDKIM_MAX_BODY_LINE_LEN-1)
return PDKIM_ERR_LONG_LINE;
}
else
{
/* Processing header byte */
- if (c != '\r')
+ if (c == '\r')
+ ctx->flags |= PDKIM_SEEN_CR;
+ else if (c == '\n')
{
- if (c == '\n')
- {
- if (ctx->flags & PDKIM_SEEN_LF)
- {
- int rc = pdkim_header_complete(ctx); /* Seen last header line */
- if (rc != PDKIM_OK) return rc;
+ if (!(ctx->flags & PDKIM_SEEN_CR)) /* emulate the CR */
+ ctx->cur_header = string_catn(ctx->cur_header, &ctx->cur_header_size,
+ &ctx->cur_header_len, CUS "\r", 1);
- 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->flags |= PDKIM_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->flags &= ~PDKIM_SEEN_LF;
+ if (ctx->flags & PDKIM_SEEN_LF) /* Seen last header line */
+ {
+ if ((rc = pdkim_header_complete(ctx)) != PDKIM_OK)
+ return rc;
+
+ ctx->flags = ctx->flags & ~(PDKIM_SEEN_LF|PDKIM_SEEN_CR) | PDKIM_PAST_HDRS;
+ DEBUG(D_acl) debug_printf(
+ "PDKIM >> Body data for hash, canonicalized >>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+ continue;
}
+ else
+ ctx->flags = ctx->flags & ~PDKIM_SEEN_CR | PDKIM_SEEN_LF;
+ }
+ else if (ctx->flags & PDKIM_SEEN_LF)
+ {
+ if (!(c == '\t' || c == ' ')) /* End of header */
+ if ((rc = pdkim_header_complete(ctx)) != PDKIM_OK)
+ return rc;
+ ctx->flags &= ~PDKIM_SEEN_LF;
}
if (ctx->cur_header_len < PDKIM_MAX_HEADER_LEN)
}
/* Preliminary or final version? */
-base64_b = final ? pdkim_encode_base64(&sig->sigdata) : US"";
+base64_b = final ? pdkim_encode_base64(&sig->sighash) : US"";
hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"b=", base64_b);
/* add trailing semicolon: I'm not sure if this is actually needed */
hdata = hhash;
#endif
- if ((errstr = exim_rsa_sign(&sctx, is_sha1, &hdata, &sig->sigdata)))
+ if ((errstr = exim_rsa_sign(&sctx, is_sha1, &hdata, &sig->sighash)))
{
DEBUG(D_acl) debug_printf("signing: %s\n", errstr);
return PDKIM_ERR_RSA_SIGNING;
DEBUG(D_acl)
{
debug_printf( "PDKIM [%s] b computed: ", sig->domain);
- pdkim_hexprint(sig->sigdata.data, sig->sigdata.len);
+ pdkim_hexprint(sig->sighash.data, sig->sighash.len);
}
sig->signature_header = pdkim_create_header(sig, TRUE);
&& sig->selector && *sig->selector
&& sig->headernames && *sig->headernames
&& sig->bodyhash.data
- && sig->sigdata.data
+ && sig->sighash.data
&& sig->algo > -1
&& sig->version
) )
goto NEXT_VERIFY;
/* Check the signature */
- if ((errstr = exim_rsa_verify(&vctx, is_sha1, &hhash, &sig->sigdata)))
+ if ((errstr = exim_rsa_verify(&vctx, is_sha1, &hhash, &sig->sighash)))
{
DEBUG(D_acl) debug_printf("headers verify: %s\n", errstr);
sig->verify_status = PDKIM_VERIFY_FAIL;
}
- /* We have a winner! (if bodydhash was correct earlier) */
+ /* We have a winner! (if bodyhash was correct earlier) */
if (sig->verify_status == PDKIM_VERIFY_NONE)
sig->verify_status = PDKIM_VERIFY_PASS;
sig->rsa_privkey = string_copy(US rsa_privkey);
sig->algo = algo;
-exim_sha_init(&sig->body_hash, algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256);
-
+exim_sha_init(&sig->body_hash_ctx,
+ algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256);
DEBUG(D_acl)
{
pdkim_signature s = *sig;
debug_printf("WARNING: bad dkim key in dns\n");
debug_printf("PDKIM (finished checking verify key)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
}
-
return ctx;
}