X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/4dc2379ac1ab6c21f265abed06dd9aaa214976af..78a3bbd585b7ee0961dee45163c36904a419388b:/src/src/pdkim/pdkim.c diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 7bfcdf4aa..e4384d7f7 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -2,7 +2,7 @@ * PDKIM - a RFC4871 (DKIM) implementation * * Copyright (C) 2009 - 2016 Tom Kistner - * Copyright (C) 2016 Jeremy Harris + * Copyright (C) 2016 - 2017 Jeremy Harris * * http://duncanthrax.net/pdkim/ * @@ -142,19 +142,19 @@ switch(ext_status) } } -const char * +const uschar * 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"; + case PDKIM_OK: return US"OK"; + case PDKIM_FAIL: return US"FAIL"; + case PDKIM_ERR_RSA_PRIVKEY: return US"RSA_PRIVKEY"; + case PDKIM_ERR_RSA_SIGNING: return US"RSA SIGNING"; + case PDKIM_ERR_LONG_LINE: return US"RSA_LONG_LINE"; + case PDKIM_ERR_BUFFER_TOO_SMALL: return US"BUFFER_TOO_SMALL"; + case PDKIM_SIGN_PRIVKEY_WRAP: return US"PRIVKEY_WRAP"; + case PDKIM_SIGN_PRIVKEY_B64D: return US"PRIVKEY_B64D"; default: return "(unknown)"; } } @@ -192,7 +192,8 @@ static void 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(""); debug_printf("\n"); } @@ -415,7 +416,7 @@ return b64encode(b->data, b->len); static pdkim_signature * pdkim_parse_sig_header(pdkim_ctx *ctx, uschar * raw_hdr) { -pdkim_signature *sig ; +pdkim_signature * sig; uschar *p, *q; uschar * cur_tag = NULL; int ts = 0, tl = 0; uschar * cur_val = NULL; int vs = 0, vl = 0; @@ -493,10 +494,8 @@ for (p = raw_hdr; ; p++) 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 @@ -578,12 +577,17 @@ DEBUG(D_acl) "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); +if (!exim_sha_init(&sig->body_hash_ctx, + sig->algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256)) + { + DEBUG(D_acl) debug_printf("PDKIM: hash init internal error\n"); + return NULL; + } return sig; } @@ -603,90 +607,87 @@ pub = store_get(sizeof(pdkim_pubkey)); memset(pub, 0, sizeof(pdkim_pubkey)); for (p = raw_record; ; p++) - { - char c = *p; - - /* Ignore FWS */ - if (c == '\r' || c == '\n') - goto NEXT_CHAR; - - if (where == PDKIM_HDR_LIMBO) { - /* In limbo, just wait for a tag-char to appear */ - if (!(c >= 'a' && c <= 'z')) - goto NEXT_CHAR; - - where = PDKIM_HDR_TAG; - } - - if (where == PDKIM_HDR_TAG) - { - if (c >= 'a' && c <= 'z') - cur_tag = string_catn(cur_tag, &ts, &tl, p, 1); + uschar c = *p; - if (c == '=') + /* Ignore FWS */ + if (c != '\r' && c != '\n') switch (where) { - cur_tag[tl] = '\0'; - where = PDKIM_HDR_VALUE; - goto NEXT_CHAR; - } - } + case PDKIM_HDR_LIMBO: /* In limbo, just wait for a tag-char to appear */ + if (!(c >= 'a' && c <= 'z')) + break; + where = PDKIM_HDR_TAG; + /*FALLTHROUGH*/ - if (where == PDKIM_HDR_VALUE) - { - if (c == ';' || c == '\0') - { - if (tl && vl) - { - cur_val[vl] = '\0'; - pdkim_strtrim(cur_val); - DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag, cur_val); + case PDKIM_HDR_TAG: + if (c >= 'a' && c <= 'z') + cur_tag = string_catn(cur_tag, &ts, &tl, p, 1); - switch (cur_tag[0]) + if (c == '=') { - case 'v': - /* This tag isn't evaluated because: - - We only support version DKIM1. - - Which is the default for this value (set below) - - Other versions are currently not specified. */ - break; - case 'h': - case 'k': - pub->hashes = string_copy(cur_val); break; - case 'g': - pub->granularity = string_copy(cur_val); break; - case 'n': - pub->notes = pdkim_decode_qp(cur_val); break; - case 'p': - pdkim_decode_base64(US cur_val, &pub->key); - break; - case 's': - pub->srvtype = string_copy(cur_val); break; - case 't': - 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"); - break; + cur_tag[tl] = '\0'; + where = PDKIM_HDR_VALUE; } - } - tl = 0; - vl = 0; - where = PDKIM_HDR_LIMBO; + break; + + case PDKIM_HDR_VALUE: + if (c == ';' || c == '\0') + { + if (tl && vl) + { + cur_val[vl] = '\0'; + pdkim_strtrim(cur_val); + DEBUG(D_acl) debug_printf(" %s=%s\n", cur_tag, cur_val); + + switch (cur_tag[0]) + { + case 'v': + pub->version = string_copy(cur_val); break; + case 'h': + case 'k': +/* This field appears to never be used. Also, unclear why +a 'k' (key-type_ would go in this field name. There is a field +"keytype", also never used. + pub->hashes = string_copy(cur_val); +*/ + break; + case 'g': + pub->granularity = string_copy(cur_val); break; + case 'n': + pub->notes = pdkim_decode_qp(cur_val); break; + case 'p': + pdkim_decode_base64(US cur_val, &pub->key); break; + case 's': + pub->srvtype = string_copy(cur_val); break; + case 't': + 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"); + break; + } + } + tl = 0; + vl = 0; + where = PDKIM_HDR_LIMBO; + } + else + cur_val = string_catn(cur_val, &vs, &vl, p, 1); + break; } - else - cur_val = string_catn(cur_val, &vs, &vl, p, 1); - } -NEXT_CHAR: - if (c == '\0') break; - } + if (c == '\0') break; + } /* Set fallback defaults */ if (!pub->version ) pub->version = string_copy(PDKIM_PUB_RECORD_VERSION); +else if (Ustrcmp(pub->version, PDKIM_PUB_RECORD_VERSION) != 0) return NULL; + if (!pub->granularity) pub->granularity = string_copy(US"*"); +/* if (!pub->keytype ) pub->keytype = string_copy(US"rsa"); +*/ if (!pub->srvtype ) pub->srvtype = string_copy(US"*"); /* p= is required */ @@ -700,15 +701,14 @@ return NULL; /* -------------------------------------------------------------------------- */ 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; @@ -764,12 +764,10 @@ while (sig) 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); @@ -789,12 +787,12 @@ for (sig = ctx->sig; sig; sig = sig->next) { /* Finish hashes */ blob bh; - exim_sha_finish(&sig->body_hash, &bh); + exim_sha_finish(&sig->body_hash_ctx, &bh); DEBUG(D_acl) { debug_printf("PDKIM [%s] Body bytes hashed: %lu\n" - "PDKIM [%s] bh computed: ", + "PDKIM [%s] Body hash computed: ", sig->domain, sig->signed_body_bytes, sig->domain); pdkim_hexprint(CUS bh.data, bh.len); } @@ -810,11 +808,11 @@ for (sig = ctx->sig; sig; sig = sig->next) 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); } @@ -822,15 +820,13 @@ for (sig = ctx->sig; sig; sig = sig->next) { DEBUG(D_acl) { - 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 signature from headers: ", sig->domain); + 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; } - } } } @@ -879,7 +875,7 @@ ctx->linebuf[ctx->linebuf_offset] = '\0'; /* 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 */ @@ -935,68 +931,65 @@ return PDKIM_OK; #define DKIM_SIGNATURE_HEADERNAME "DKIM-Signature:" static int -pdkim_header_complete(pdkim_ctx *ctx) +pdkim_header_complete(pdkim_ctx * ctx) { +pdkim_signature * sig, * last_sig; + /* Special case: The last header can have an extra \r appended */ if ( (ctx->cur_header_len > 1) && (ctx->cur_header[(ctx->cur_header_len)-1] == '\r') ) --ctx->cur_header_len; ctx->cur_header[ctx->cur_header_len] = '\0'; -ctx->num_headers++; -if (ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL; +if (++ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL; /* SIGNING -------------------------------------------------------------- */ if (ctx->flags & PDKIM_MODE_SIGN) - { - pdkim_signature *sig; - for (sig = ctx->sig; sig; sig = sig->next) /* Traverse all signatures */ /* Add header to the signed headers list (in reverse order) */ sig->headers = pdkim_prepend_stringlist(sig->headers, ctx->cur_header); - } /* VERIFICATION ----------------------------------------------------------- */ /* DKIM-Signature: headers are added to the verification list */ else { +#ifdef notdef + DEBUG(D_acl) + { + debug_printf("PDKIM >> raw hdr: "); + pdkim_quoteprint(CUS ctx->cur_header, ctx->cur_header_len); + } +#endif if (strncasecmp(CCS ctx->cur_header, DKIM_SIGNATURE_HEADERNAME, Ustrlen(DKIM_SIGNATURE_HEADERNAME)) == 0) { - pdkim_signature *new_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))) + sig = pdkim_parse_sig_header(ctx, ctx->cur_header); + + if (!(last_sig = ctx->sig)) + ctx->sig = 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 = 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: -*ctx->cur_header = '\0'; -ctx->cur_header_len = 0; /* leave buffer for reuse */ +ctx->cur_header[ctx->cur_header_len = 0] = '\0'; /* leave buffer for reuse */ return PDKIM_OK; } @@ -1008,7 +1001,7 @@ return PDKIM_OK; 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) @@ -1020,45 +1013,57 @@ else for (p = 0; pflags & 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) @@ -1292,7 +1297,7 @@ if (sig->bodylength >= 0) } /* 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 */ @@ -1303,10 +1308,79 @@ return hdr; } +/* -------------------------------------------------------------------------- */ + +static pdkim_pubkey * +pdkim_key_from_dns(pdkim_ctx * ctx, pdkim_signature * sig, ev_ctx * vctx, + const uschar ** errstr) +{ +uschar * dns_txt_name, * dns_txt_reply; +pdkim_pubkey * p; + +/* Fetch public key for signing domain, from DNS */ + +dns_txt_name = string_sprintf("%s._domainkey.%s.", sig->selector, sig->domain); + +dns_txt_reply = store_get(PDKIM_DNS_TXT_MAX_RECLEN); +memset(dns_txt_reply, 0, PDKIM_DNS_TXT_MAX_RECLEN); + +if ( ctx->dns_txt_callback(CS dns_txt_name, CS dns_txt_reply) != PDKIM_OK + || dns_txt_reply[0] == '\0' + ) + { + sig->verify_status = PDKIM_VERIFY_INVALID; + sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE; + return NULL; + } + +DEBUG(D_acl) + { + debug_printf( + "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" + " Raw record: "); + pdkim_quoteprint(CUS dns_txt_reply, Ustrlen(dns_txt_reply)); + } + +if ( !(p = pdkim_parse_pubkey_record(ctx, CUS dns_txt_reply)) + || (Ustrcmp(p->srvtype, "*") != 0 && Ustrcmp(p->srvtype, "email") != 0) + ) + { + sig->verify_status = PDKIM_VERIFY_INVALID; + sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD; + + DEBUG(D_acl) + { + if (p) + debug_printf(" Invalid public key service type '%s'\n", p->srvtype); + else + debug_printf(" Error while parsing public key record\n"); + debug_printf( + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + } + return NULL; + } + +DEBUG(D_acl) debug_printf( + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + +/* Import public key */ +if ((*errstr = exim_rsa_verify_init(&p->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_IMPORT; + return NULL; + } + +return p; +} + + /* -------------------------------------------------------------------------- */ DLLEXPORT int -pdkim_feed_finish(pdkim_ctx *ctx, pdkim_signature **return_signatures) +pdkim_feed_finish(pdkim_ctx * ctx, pdkim_signature ** return_signatures, + const uschar ** err) { pdkim_signature *sig = ctx->sig; @@ -1338,10 +1412,14 @@ while (sig) hdata.data = NULL; hdata.len = 0; - exim_sha_init(&hhash_ctx, is_sha1 ? HASH_SHA1 : HASH_SHA256); + if (!exim_sha_init(&hhash_ctx, is_sha1 ? HASH_SHA1 : HASH_SHA256)) + { + DEBUG(D_acl) debug_printf("PDKIM: hask setup internal error\n"); + break; + } DEBUG(D_acl) debug_printf( - "PDKIM >> Hashed header data, canonicalized, in sequence >>>>>>>>>>>>>>\n"); + "PDKIM >> Header data for hash, canonicalized, in sequence >>>>>>>>>>>>>>\n"); /* SIGNING ---------------------------------------------------------------- */ /* When signing, walk through our header list and add them to the hash. As we @@ -1471,7 +1549,7 @@ while (sig) DEBUG(D_acl) { - debug_printf("PDKIM [%s] hh computed: ", sig->domain); + debug_printf("PDKIM [%s] Header hash computed: ", sig->domain); pdkim_hexprint(hhash.data, hhash.len); } @@ -1483,12 +1561,11 @@ while (sig) if (ctx->flags & PDKIM_MODE_SIGN) { es_ctx sctx; - const uschar * errstr; /* Import private key */ - if ((errstr = exim_rsa_signing_init(US sig->rsa_privkey, &sctx))) + if ((*err = exim_rsa_signing_init(US sig->rsa_privkey, &sctx))) { - DEBUG(D_acl) debug_printf("signing_init: %s\n", errstr); + DEBUG(D_acl) debug_printf("signing_init: %s\n", *err); return PDKIM_ERR_RSA_PRIVKEY; } @@ -1500,16 +1577,16 @@ while (sig) hdata = hhash; #endif - if ((errstr = exim_rsa_sign(&sctx, is_sha1, &hdata, &sig->sigdata))) + if ((*err = exim_rsa_sign(&sctx, is_sha1, &hdata, &sig->sighash))) { - DEBUG(D_acl) debug_printf("signing: %s\n", errstr); + DEBUG(D_acl) debug_printf("signing: %s\n", *err); 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); @@ -1519,16 +1596,13 @@ while (sig) else { ev_ctx vctx; - 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->sighash.data && sig->algo > -1 && sig->version ) ) @@ -1554,64 +1628,20 @@ while (sig) goto NEXT_VERIFY; } - /* Fetch public key for signing domain, from DNS */ - - dns_txt_name = string_sprintf("%s._domainkey.%s.", - sig->selector, sig->domain); - - dns_txt_reply = store_get(PDKIM_DNS_TXT_MAX_RECLEN); - memset(dns_txt_reply, 0, PDKIM_DNS_TXT_MAX_RECLEN); - - if ( ctx->dns_txt_callback(CS dns_txt_name, CS dns_txt_reply) != PDKIM_OK - || dns_txt_reply[0] == '\0') - { - sig->verify_status = PDKIM_VERIFY_INVALID; - sig->verify_ext_status = PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE; - goto NEXT_VERIFY; - } - - DEBUG(D_acl) - { - debug_printf( - "PDKIM >> Parsing public key record >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" - " Raw record: "); - pdkim_quoteprint(CUS dns_txt_reply, Ustrlen(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; - - DEBUG(D_acl) debug_printf( - " Error while parsing public key record\n" - "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); - goto NEXT_VERIFY; - } - - DEBUG(D_acl) debug_printf( - "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); - - /* 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_IMPORT; + if (!(sig->pubkey = pdkim_key_from_dns(ctx, sig, &vctx, err))) goto NEXT_VERIFY; - } /* Check the signature */ - if ((errstr = exim_rsa_verify(&vctx, is_sha1, &hhash, &sig->sigdata))) + if ((*err = exim_rsa_verify(&vctx, is_sha1, &hhash, &sig->sighash))) { - DEBUG(D_acl) debug_printf("headers verify: %s\n", errstr); + DEBUG(D_acl) debug_printf("headers verify: %s\n", *err); 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) */ + /* We have a winner! (if bodyhash was correct earlier) */ if (sig->verify_status == PDKIM_VERIFY_NONE) sig->verify_status = PDKIM_VERIFY_PASS; @@ -1661,22 +1691,25 @@ return ctx; /* -------------------------------------------------------------------------- */ DLLEXPORT pdkim_ctx * -pdkim_init_sign(char *domain, char *selector, char *rsa_privkey, int algo, - BOOL dot_stuffed) +pdkim_init_sign(char * domain, char * selector, char * rsa_privkey, int algo, + BOOL dot_stuffed, int(*dns_txt_callback)(char *, char *), + const uschar ** errstr) { -pdkim_ctx *ctx; -pdkim_signature *sig; +pdkim_ctx * ctx; +pdkim_signature * sig; if (!domain || !selector || !rsa_privkey) return NULL; -ctx = store_get(sizeof(pdkim_ctx)); +ctx = store_get(sizeof(pdkim_ctx) + PDKIM_MAX_BODY_LINE_LEN + sizeof(pdkim_signature)); 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); +ctx->linebuf = CS (ctx+1); -sig = store_get(sizeof(pdkim_signature)); +DEBUG(D_acl) ctx->dns_txt_callback = dns_txt_callback; + +sig = (pdkim_signature *)(ctx->linebuf + PDKIM_MAX_BODY_LINE_LEN); memset(sig, 0, sizeof(pdkim_signature)); sig->bodylength = -1; @@ -1687,7 +1720,23 @@ 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 ? HASH_SHA1 : HASH_SHA256); +if (!exim_sha_init(&sig->body_hash_ctx, + algo == PDKIM_ALGO_RSA_SHA1 ? HASH_SHA1 : HASH_SHA256)) + { + DEBUG(D_acl) debug_printf("PDKIM: hash setup internal error\n"); + return NULL; + } + +DEBUG(D_acl) + { + pdkim_signature s = *sig; + ev_ctx vctx; + + debug_printf("PDKIM (checking verify key)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + if (!pdkim_key_from_dns(ctx, &s, &vctx, errstr)) + debug_printf("WARNING: bad dkim key in dns\n"); + debug_printf("PDKIM (finished checking verify key)<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); + } return ctx; }