X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Fpdkim%2Fpdkim.c;h=9ebcfc1b6676d8f724522d49e56e0950b302eb29;hb=87abcb247b4444bab5fd0bcb212ddb26d5fd9191;hp=e291d9dd31de83fc42ae65f6d4e1c0d8381ab421;hpb=cb78c1a805d1e86dad86d8eb031eb0517a62ec20;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index e291d9dd3..9ebcfc1b6 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -26,8 +26,8 @@ #ifndef DISABLE_DKIM /* entire file */ -#ifndef SUPPORT_TLS -# error Need SUPPORT_TLS for DKIM +#ifdef DISABLE_TLS +# error Must not DISABLE_TLS, for DKIM #endif #include "crypt_ver.h" @@ -80,7 +80,7 @@ const pdkim_hashtype pdkim_hashes[] = { const uschar * pdkim_keytypes[] = { [KEYTYPE_RSA] = US"rsa", #ifdef SIGN_HAVE_ED25519 - [KEYTYPE_ED25519] = US"ed25519", /* Works for 3.6.0 GnuTLS */ + [KEYTYPE_ED25519] = US"ed25519", /* Works for 3.6.0 GnuTLS, OpenSSL 1.1.1 */ #endif #ifdef notyet_EC_dkim_extensions /* https://tools.ietf.org/html/draft-srose-dkim-ecc-00 */ @@ -124,9 +124,8 @@ return string_sprintf("%s-%s", int pdkim_hashname_to_hashtype(const uschar * s, unsigned len) { -int i; if (!len) len = Ustrlen(s); -for (i = 0; i < nelem(pdkim_hashes); i++) +for (int i = 0; i < nelem(pdkim_hashes); i++) if (Ustrncmp(s, pdkim_hashes[i].dkim_hashname, len) == 0) return i; return -1; @@ -136,9 +135,8 @@ void pdkim_cstring_to_canons(const uschar * s, unsigned len, int * canon_head, int * canon_body) { -int i; if (!len) len = Ustrlen(s); -for (i = 0; pdkim_combined_canons[i].str; i++) +for (int i = 0; pdkim_combined_canons[i].str; i++) if ( Ustrncmp(s, pdkim_combined_canons[i].str, len) == 0 && len == Ustrlen(pdkim_combined_canons[i].str)) { @@ -192,6 +190,7 @@ switch(status) case PDKIM_ERR_RSA_SIGNING: return US"SIGNING"; case PDKIM_ERR_LONG_LINE: return US"LONG_LINE"; case PDKIM_ERR_BUFFER_TOO_SMALL: return US"BUFFER_TOO_SMALL"; + case PDKIM_ERR_EXCESS_SIGS: return US"EXCESS_SIGS"; case PDKIM_SIGN_PRIVKEY_WRAP: return US"PRIVKEY_WRAP"; case PDKIM_SIGN_PRIVKEY_B64D: return US"PRIVKEY_B64D"; default: return US"(unknown)"; @@ -204,8 +203,7 @@ switch(status) void pdkim_quoteprint(const uschar *data, int len) { -int i; -for (i = 0; i < len; i++) +for (int i = 0; i < len; i++) { const int c = data[i]; switch (c) @@ -230,8 +228,7 @@ debug_printf("\n"); void pdkim_hexprint(const uschar *data, int len) { -int i; -if (data) for (i = 0 ; i < len; i++) debug_printf("%02x", data[i]); +if (data) for (int i = 0 ; i < len; i++) debug_printf("%02x", data[i]); else debug_printf(""); debug_printf("\n"); } @@ -331,11 +328,10 @@ pdkim_relax_header_n(const uschar * header, int len, BOOL append_crlf) { BOOL past_field_name = FALSE; BOOL seen_wsp = FALSE; -const uschar * p; uschar * relaxed = store_get(len+3); uschar * q = relaxed; -for (p = header; p - header < len; p++) +for (const uschar * p = header; p - header < len; p++) { uschar c = *p; @@ -449,7 +445,7 @@ b->len = dlen; uschar * pdkim_encode_base64(blob * b) { -return b64encode(b->data, b->len); +return b64encode(CUS b->data, b->len); } @@ -462,13 +458,12 @@ static pdkim_signature * pdkim_parse_sig_header(pdkim_ctx * ctx, uschar * raw_hdr) { pdkim_signature * sig; -uschar *p, *q; +uschar *q; gstring * cur_tag = NULL; gstring * cur_val = NULL; BOOL past_hname = FALSE; BOOL in_b_val = FALSE; int where = PDKIM_HDR_LIMBO; -int i; sig = store_get(sizeof(pdkim_signature)); memset(sig, 0, sizeof(pdkim_signature)); @@ -481,7 +476,7 @@ sig->hashtype = -1; q = sig->rawsig_no_b_val = store_get(Ustrlen(raw_hdr)+1); -for (p = raw_hdr; ; p++) +for (uschar * p = raw_hdr; ; p++) { char c = *p; @@ -561,18 +556,18 @@ for (p = raw_hdr; ; p++) break; case 'a': /* algorithm */ { - uschar * s = Ustrchr(cur_val->s, '-'); - - for(i = 0; i < nelem(pdkim_keytypes); i++) - if (Ustrncmp(cur_val->s, pdkim_keytypes[i], s - cur_val->s) == 0) - { sig->keytype = i; break; } - if (sig->keytype < 0) - log_write(0, LOG_MAIN, - "DKIM: ignoring signature due to nonhandled keytype in a=%s", - cur_val->s); - - sig->hashtype = pdkim_hashname_to_hashtype(++s, 0); - break; + const uschar * list = cur_val->s; + int sep = '-'; + uschar * elem; + + if ((elem = string_nextinlist(&list, &sep, NULL, 0))) + for (int i = 0; i < nelem(pdkim_keytypes); i++) + if (Ustrcmp(elem, pdkim_keytypes[i]) == 0) + { sig->keytype = i; break; } + if ((elem = string_nextinlist(&list, &sep, NULL, 0))) + for (int i = 0; i < nelem(pdkim_hashes); i++) + if (Ustrcmp(elem, pdkim_hashes[i].dkim_hashname) == 0) + { sig->hashtype = i; break; } } case 'c': /* canonicalization */ @@ -580,7 +575,7 @@ for (p = raw_hdr; ; p++) &sig->canon_headers, &sig->canon_body); break; case 'q': /* Query method (for pubkey)*/ - for (i = 0; pdkim_querymethods[i]; i++) + for (int i = 0; pdkim_querymethods[i]; i++) if (Ustrcmp(cur_val->s, pdkim_querymethods[i]) == 0) { sig->querymethod = i; /* we never actually use this */ @@ -729,7 +724,6 @@ if (b->canon_method == PDKIM_CANON_RELAXED) if (!relaxed_data) { BOOL seen_wsp = FALSE; - const uschar * p, * r; int q = 0; /* We want to be able to free this else we allocate @@ -740,7 +734,7 @@ if (b->canon_method == PDKIM_CANON_RELAXED) relaxed_data = store_malloc(sizeof(blob) + orig_data->len+1); relaxed_data->data = US (relaxed_data+1); - for (p = orig_data->data, r = p + orig_data->len; p < r; p++) + for (const uschar * p = orig_data->data, * r = p + orig_data->len; p < r; p++) { char c = *p; if (c == '\r') @@ -787,20 +781,17 @@ return relaxed_data; static void pdkim_finish_bodyhash(pdkim_ctx * ctx) { -pdkim_bodyhash * b; -pdkim_signature * sig; - -for (b = ctx->bodyhash; b; b = b->next) /* Finish hashes */ +for (pdkim_bodyhash * b = ctx->bodyhash; b; b = b->next) /* Finish hashes */ { - DEBUG(D_acl) debug_printf("PDKIM: finish bodyhash %d/%d/%d len %ld\n", + DEBUG(D_acl) debug_printf("PDKIM: finish bodyhash %d/%d/%ld len %ld\n", b->hashtype, b->canon_method, b->bodylength, b->signed_body_bytes); exim_sha_finish(&b->body_hash_ctx, &b->bh); } /* Traverse all signatures */ -for (sig = ctx->sig; sig; sig = sig->next) +for (pdkim_signature * sig = ctx->sig; sig; sig = sig->next) { - b = sig->calc_body_hash; + pdkim_bodyhash * b = sig->calc_body_hash; DEBUG(D_acl) { @@ -848,15 +839,13 @@ for (sig = ctx->sig; sig; sig = sig->next) static void pdkim_body_complete(pdkim_ctx * ctx) { -pdkim_bodyhash * b; - /* 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 */ -for (b = ctx->bodyhash; b; b = b->next) +for (pdkim_bodyhash * b = ctx->bodyhash; b; b = b->next) if ( b->canon_method == PDKIM_CANON_SIMPLE && b->signed_body_bytes == 0 && b->num_buffered_blanklines > 0 @@ -877,7 +866,6 @@ static void pdkim_bodyline_complete(pdkim_ctx * ctx) { blob line = {.data = ctx->linebuf, .len = ctx->linebuf_offset}; -pdkim_bodyhash * b; blob * rnl = NULL; blob * rline = NULL; @@ -901,12 +889,13 @@ if (ctx->flags & PDKIM_DOT_TERM) /* Empty lines need to be buffered until we find a non-empty line */ if (memcmp(line.data, "\r\n", 2) == 0) { - for (b = ctx->bodyhash; b; b = b->next) b->num_buffered_blanklines++; + for (pdkim_bodyhash * b = ctx->bodyhash; b; b = b->next) + b->num_buffered_blanklines++; goto all_skip; } /* Process line for each bodyhash separately */ -for (b = ctx->bodyhash; b; b = b->next) +for (pdkim_bodyhash * b = ctx->bodyhash; b; b = b->next) { if (b->canon_method == PDKIM_CANON_RELAXED) { @@ -955,9 +944,6 @@ return; static int 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->ptr > 1) && (ctx->cur_header->s[ctx->cur_header->ptr-1] == '\r') ) --ctx->cur_header->ptr; @@ -972,7 +958,7 @@ if (++ctx->num_headers > PDKIM_MAX_HEADERS) goto BAIL; /* SIGNING -------------------------------------------------------------- */ if (ctx->flags & PDKIM_MODE_SIGN) - for (sig = ctx->sig; sig; sig = sig->next) /* Traverse all signatures */ + for (pdkim_signature * 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->s); @@ -992,6 +978,7 @@ else DKIM_SIGNATURE_HEADERNAME, Ustrlen(DKIM_SIGNATURE_HEADERNAME)) == 0) { + pdkim_signature * 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. */ @@ -1008,6 +995,13 @@ else while (last_sig->next) last_sig = last_sig->next; last_sig->next = sig; } + + if (--dkim_collect_input == 0) + { + ctx->headers = pdkim_prepend_stringlist(ctx->headers, ctx->cur_header->s); + ctx->cur_header->s[ctx->cur_header->ptr = 0] = '\0'; + return PDKIM_ERR_EXCESS_SIGS; + } } /* all headers are stored for signature verification */ @@ -1027,15 +1021,14 @@ return PDKIM_OK; DLLEXPORT int pdkim_feed(pdkim_ctx * ctx, uschar * data, int len) { -int p, rc; - /* Alternate EOD signal, used in non-dotstuffing mode */ if (!data) pdkim_body_complete(ctx); -else for (p = 0; pflags & PDKIM_PAST_HDRS) { @@ -1336,6 +1329,28 @@ return string_from_gstring(hdr); /* -------------------------------------------------------------------------- */ +/* According to draft-ietf-dcrup-dkim-crypto-07 "keys are 256 bits" (referring +to DNS, hence the pubkey). Check for more than 32 bytes; if so assume the +alternate possible representation (still) being discussed: a +SubjectPublickeyInfo wrapped key - and drop all but the trailing 32-bytes (it +should be a DER, with exactly 12 leading bytes - but we could accept a BER also, +which could be any size). We still rely on the crypto library for checking for +undersize. + +When the RFC is published this should be re-addressed. */ + +static void +check_bare_ed25519_pubkey(pdkim_pubkey * p) +{ +int excess = p->key.len - 32; +if (excess > 0) + { + DEBUG(D_acl) debug_printf("PDKIM: unexpected pubkey len %lu\n", p->key.len); + p->key.data += excess; p->key.len = 32; + } +} + + static pdkim_pubkey * pdkim_key_from_dns(pdkim_ctx * ctx, pdkim_signature * sig, ev_ctx * vctx, const uschar ** errstr) @@ -1397,8 +1412,7 @@ instead. Assume writing on the sig is ok in that case. */ if (sig->keytype < 0) { - int i; - for(i = 0; i < nelem(pdkim_keytypes); i++) + for(int i = 0; i < nelem(pdkim_keytypes); i++) if (Ustrcmp(p->keytype, pdkim_keytypes[i]) == 0) { sig->keytype = i; goto k_ok; } DEBUG(D_acl) debug_printf("verify_init: unhandled keytype %s\n", p->keytype); @@ -1408,6 +1422,9 @@ if (sig->keytype < 0) } k_ok: +if (sig->keytype == KEYTYPE_ED25519) + check_bare_ed25519_pubkey(p); + if ((*errstr = exim_dkim_verify_init(&p->key, sig->keytype == KEYTYPE_ED25519 ? KEYFMT_ED25519_BARE : KEYFMT_DER, vctx))) @@ -1429,8 +1446,6 @@ DLLEXPORT int pdkim_feed_finish(pdkim_ctx * ctx, pdkim_signature ** return_signatures, const uschar ** err) { -pdkim_bodyhash * b; -pdkim_signature * sig; BOOL verify_pass = FALSE; /* Check if we must still flush a (partial) header. If that is the @@ -1444,7 +1459,7 @@ if (ctx->cur_header && ctx->cur_header->ptr > 0) if ((rc = pdkim_header_complete(ctx)) != PDKIM_OK) return rc; - for (b = ctx->bodyhash; b; b = b->next) + for (pdkim_bodyhash * b = ctx->bodyhash; b; b = b->next) rnl = pdkim_update_ctx_bodyhash(b, &lineending, rnl); if (rnl) store_free(rnl); } @@ -1464,7 +1479,7 @@ if (!ctx->sig) return PDKIM_OK; } -for (sig = ctx->sig; sig; sig = sig->next) +for (pdkim_signature * sig = ctx->sig; sig; sig = sig->next) { hctx hhash_ctx; uschar * sig_hdr = US""; @@ -1528,7 +1543,6 @@ for (sig = ctx->sig; sig; sig = sig->next) if (ctx->flags & PDKIM_MODE_SIGN) { gstring * g = NULL; - pdkim_stringlist *p; const uschar * l; uschar * s; int sep = 0; @@ -1544,8 +1558,8 @@ for (sig = ctx->sig; sig; sig = sig->next) } sig->keytype = sctx.keytype; - for (sig->headernames = NULL, /* Collected signed header names */ - p = sig->headers; p; p = p->next) + sig->headernames = NULL; /* Collected signed header names */ + for (pdkim_stringlist * p = sig->headers; p; p = p->next) { uschar * rh = p->value; @@ -1592,12 +1606,11 @@ for (sig = ctx->sig; sig; sig = sig->next) { uschar * p = sig->headernames; uschar * q; - pdkim_stringlist * hdrs; if (p) { /* clear tags */ - for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next) + for (pdkim_stringlist * hdrs = ctx->headers; hdrs; hdrs = hdrs->next) hdrs->tag = 0; p = string_copy(p); @@ -1607,7 +1620,7 @@ for (sig = ctx->sig; sig; sig = sig->next) *q = '\0'; /*XXX walk the list of headers in same order as received. */ - for (hdrs = ctx->headers; hdrs; hdrs = hdrs->next) + for (pdkim_stringlist * hdrs = ctx->headers; hdrs; hdrs = hdrs->next) if ( hdrs->tag == 0 && strncasecmp(CCS hdrs->value, CCS p, Ustrlen(p)) == 0 && (hdrs->value)[Ustrlen(p)] == ':' @@ -1734,11 +1747,19 @@ for (sig = ctx->sig; sig; sig = sig->next) 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"); + " Error in DKIM-Signature header: tags missing or invalid (%s)\n" + "PDKIM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", + !(sig->domain && *sig->domain) ? "d=" + : !(sig->selector && *sig->selector) ? "s=" + : !(sig->headernames && *sig->headernames) ? "h=" + : !sig->bodyhash.data ? "bh=" + : !sig->sighash.data ? "b=" + : sig->keytype < 0 || sig->hashtype < 0 ? "a=" + : "v=" + ); goto NEXT_VERIFY; } - + /* Make sure sig uses supported DKIM version (only v1) */ if (sig->version != 1) {