X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fpdkim%2Fpdkim.c;h=f468d232be298523cdd5f0c6cc56cb6df179efa5;hb=f7302073a0de0db3750659a0f27b869ea45a0e4e;hp=f3959cdc7339c8e64993f4d68044f844a6ff4410;hpb=c2f669a4994192344613569e198c7b503d46d45e;p=exim.git diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index f3959cdc7..f468d232b 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -45,7 +45,7 @@ #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 @@ -62,51 +62,51 @@ /* -------------------------------------------------------------------------- */ 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 } }; @@ -115,26 +115,47 @@ pdkim_combined_canon_entry pdkim_combined_canons[] = { 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)"; } } @@ -224,7 +245,7 @@ pdkim_free_ctx(pdkim_ctx *ctx) /*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; @@ -313,10 +334,10 @@ return relaxed; /* -------------------------------------------------------------------------- */ #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++; @@ -338,7 +359,7 @@ return initial_pos; /* -------------------------------------------------------------------------- */ -static char * +static uschar * pdkim_decode_qp(uschar * str) { int nchar = 0; @@ -407,6 +428,10 @@ sig = store_get(sizeof(pdkim_signature)); 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++) @@ -476,8 +501,8 @@ for (p = raw_hdr; ; p++) 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++) @@ -542,10 +567,6 @@ 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')) @@ -555,14 +576,14 @@ DEBUG(D_acl) { 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; } @@ -570,10 +591,10 @@ 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; @@ -642,8 +663,8 @@ for (p = raw_record; ; p++) 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"); @@ -665,7 +686,7 @@ 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 */ @@ -738,7 +759,7 @@ while (sig) 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); } @@ -921,9 +942,9 @@ if (ctx->mode == PDKIM_MODE_SIGN) /* DKIM-Signature: headers are added to the verification list */ if (ctx->mode == PDKIM_MODE_VERIFY) { - 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; @@ -1019,7 +1040,7 @@ for (p = 0; pcur_header_len < PDKIM_MAX_HEADER_LEN) ctx->cur_header = string_catn(ctx->cur_header, &ctx->cur_header_size, - &ctx->cur_header_len, &data[p], 1); + &ctx->cur_header_len, CUS &data[p], 1); } } return PDKIM_OK; @@ -1175,7 +1196,7 @@ canon_all = string_cat (canon_all, &can_size, &can_len, canon_all[can_len] = '\0'; hdr = string_cat(NULL, &hdr_size, &hdr_len, - "DKIM-Signature: v="PDKIM_SIGNATURE_VERSION); + US"DKIM-Signature: v="PDKIM_SIGNATURE_VERSION); col = hdr_len; /* Required and static bits */ @@ -1192,7 +1213,7 @@ hdr = pdkim_headcat(&col, hdr, &hdr_size, &hdr_len, US";", US"s=", /* 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";"; @@ -1225,25 +1246,25 @@ if (sig->identity) 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); } @@ -1296,7 +1317,7 @@ while (sig) 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"); @@ -1329,7 +1350,7 @@ while (sig) : 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); @@ -1387,7 +1408,7 @@ while (sig) : string_copy(CUS hdrs->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)); DEBUG(D_acl) pdkim_quoteprint(rh, Ustrlen(rh)); hdrs->tag = 1; @@ -1412,13 +1433,13 @@ while (sig) { 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) @@ -1475,6 +1496,37 @@ while (sig) 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.", @@ -1499,7 +1551,7 @@ while (sig) 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; @@ -1603,12 +1655,12 @@ 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; } @@ -1628,7 +1680,7 @@ pdkim_set_optional(pdkim_ctx *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);