diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2020-07-07 15:51:35 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2020-07-07 16:49:46 +0300 |
commit | c0165798d33e476ea8d8dc7f854b0b5edbe7278b (patch) | |
tree | 4ee00e8b5bf3798fffff349cefe3e51fa337366f /src | |
parent | e112614a2abc999b4f63a5e8dc9349e8d07ace9d (diff) | |
download | mailfromd-c0165798d33e476ea8d8dc7f854b0b5edbe7278b.tar.gz mailfromd-c0165798d33e476ea8d8dc7f854b0b5edbe7278b.tar.bz2 |
Fixes in DKIM code
* lib/dns.c (dkim_lookup): The v= tag is optional.
* src/builtin/dkim.bi (dkim_sign): Insert the created header at
the beginning.
(dkim_verify): Mark with MF_DSEXP.
* src/dkim-canonicalize.c (dkim_canonicalizer): Fix relaxed body
canonicalization: ignore whitespace at the end of the line (before
CRLF).
* src/dkim.c (dkim_str_to_canon_type): Take two arguments. Return
the pointer to the first character after the spec in *endp.
All uses changed.
(dkim_signature_parse): Fix typo (missing else).
Fix c= tag parsing.
(dkim_sig_validate): The q= tag is optional.
Fix verification of the i= tag.
(pubkey_validate): Validate v= tag, if supplied.
Fix validation of the key type and algorithm.
* src/dkim.h (DKIM_KEYRECORD_VERSION): New constant.
Diffstat (limited to 'src')
-rw-r--r-- | src/builtin/dkim.bi | 12 | ||||
-rw-r--r-- | src/dkim-canonicalize.c | 18 | ||||
-rw-r--r-- | src/dkim.c | 66 | ||||
-rw-r--r-- | src/dkim.h | 3 |
4 files changed, 68 insertions, 31 deletions
diff --git a/src/builtin/dkim.bi b/src/builtin/dkim.bi index 52ac4e3c..a85c2b51 100644 --- a/src/builtin/dkim.bi +++ b/src/builtin/dkim.bi @@ -198,13 +198,13 @@ STRING canon_h, STRING canon_b, STRING headers) env_get_locus(env, &locus); if (MF_DEFINED(canon_h)) { - sig.canon[0] = dkim_str_to_canon_type(canon_h); + sig.canon[0] = dkim_str_to_canon_type(canon_h, NULL); if (sig.canon[0] == DKIM_CANON_ERR) MF_THROW(mfe_failure, _("bad canonicalization type: %s"), canon_h); } if (MF_DEFINED(canon_b)) { - sig.canon[1] = dkim_str_to_canon_type(canon_b); + sig.canon[1] = dkim_str_to_canon_type(canon_b, NULL); if (sig.canon[1] == DKIM_CANON_ERR) MF_THROW(mfe_failure, _("bad canonicalization type: %s"), canon_b); @@ -260,12 +260,13 @@ STRING canon_h, STRING canon_b, STRING headers) while (mu_isblank(*p)) p++; - trace("%s%s:%u: %s \"%s: %s\"", + trace("%s%s:%u: %s %d \"%s: %s\"", mailfromd_msgid(env_get_context(env)), locus.beg.mu_file, locus.beg.mu_line, - msgmod_opcode_str(header_add), + msgmod_opcode_str(header_insert), + 1, sighdr, p); - env_msgmod_append(env, header_add, sighdr, p, 1); + env_msgmod_append(env, header_insert, sighdr, p, 1); free(sighdr); } END @@ -273,6 +274,7 @@ END MF_VAR(dkim_explanation_code, NUMBER); MF_VAR(dkim_explanation, STRING); +MF_DSEXP MF_DEFUN(dkim_verify, NUMBER, NUMBER nmsg) { mu_message_t msg = bi_message_from_descr(env, nmsg); diff --git a/src/dkim-canonicalize.c b/src/dkim-canonicalize.c index 42e01413..ff221b7d 100644 --- a/src/dkim-canonicalize.c +++ b/src/dkim-canonicalize.c @@ -128,7 +128,13 @@ dkim_canonicalizer(void *xd, } else if (*iptr == ':') { *optr++ = *iptr++; encoder->state = HR_COLON; - } else if (mu_isheadr(*iptr)) { + } else if (mu_isheadr(*iptr) || + /* + * Work around the bug in mailutils 3.9: + * the MU_CTYPE_HEADR class did not include + * underscore. + */ + *iptr == '_') { *optr++ = mu_tolower(*iptr++); } else { iobuf->errcode = MU_ERR_USER0; @@ -237,14 +243,14 @@ dkim_canonicalizer(void *xd, break; case BR_WS: - if (!mu_isblank(*iptr)) { + if (*iptr == '\n') { + iptr++; + encoder->nlcount++; + encoder->state = BR_NL; + } else if (!mu_isblank(*iptr)) { *optr++ = ' '; encoder->state = BR_INIT; } else { - if (*iptr == '\n') { - encoder->nlcount++; - encoder->state = BR_NL; - } iptr++; } break; @@ -458,14 +458,26 @@ dkim_rsa_sha256_sign(struct rsa_private_key *priv, struct sha256_ctx *ctx, */ static char const *dkim_canon_string[] = { "simple", "relaxed", NULL }; +/* + * Convert STR to a DKIM_CANON_* constant (return DKIM_CANON_ERR on error). + * If ENDP is NULL, STR must be one of the strings from dkim_canon_string. + * Otherwise, STR may be followed by '\0' or '/'. The pointer to that + * character will be returned in the memory location pointed to by ENDP. + */ int -dkim_str_to_canon_type(char const *str) +dkim_str_to_canon_type(char const *str, char **endp) { int i; - - for (i = 0; dkim_canon_string[i]; i++) - if (strcmp(str, dkim_canon_string[i]) == 0) - return i; + size_t len = strcspn(str, "/"); + if (endp || str[len] == 0) { + for (i = 0; dkim_canon_string[i]; i++) + if (len == strlen(dkim_canon_string[i]) && + memcmp(str, dkim_canon_string[i], len) == 0) { + if (endp) + *endp = (char*)(str + len); + return i; + } + } return DKIM_CANON_ERR; } @@ -595,24 +607,28 @@ dkim_signature_parse(char *str, struct dkim_signature *ret_sig) *p++ = 0; if (strcmp(k, "a") == 0) { sig.a = (char*) mu_strdup(p); - } if (strcmp(k, "b") == 0) { + } else if (strcmp(k, "b") == 0) { sig.b = (uint8_t*) mu_strdup(p); } else if (strcmp(k, "bh") == 0) { sig.bh = (uint8_t*) mu_strdup(p); } else if (strcmp(k, "q") == 0) { sig.q = mu_strdup(p); } else if (strcmp(k, "c") == 0) { - char *s = strchr(p, '/'); - if (!s) { + char *s; + + if ((sig.canon[0] = dkim_str_to_canon_type(p, &s)) + == DKIM_CANON_ERR) { rc = -1; goto end; } - *s++ = 0; - if ((sig.canon[0] = dkim_str_to_canon_type(p)) == DKIM_CANON_ERR || - (sig.canon[1] = dkim_str_to_canon_type(s)) == DKIM_CANON_ERR) { + + if (*s == 0) + sig.canon[1] = sig.canon[0]; + else if (*s != '/' || + (sig.canon[1] = dkim_str_to_canon_type(s + 1, NULL)) == DKIM_CANON_ERR) { rc = -1; goto end; - } + } } else if (strcmp(k, "d") == 0) { sig.d = mu_strdup(p); } else if (strcmp(k, "s") == 0) { @@ -1159,7 +1175,7 @@ int dkim_result_trans[] = { }; static int -dkim_sig_validate(struct dkim_signature const *sig) +dkim_sig_validate(struct dkim_signature *sig) { if (!sig->a || !sig->b @@ -1167,18 +1183,19 @@ dkim_sig_validate(struct dkim_signature const *sig) || !sig->d || !sig->h || !sig->s - || !sig->q || !sig->v) { return DKIM_EXPL_SIG_MISS; } - + if (strcmp(sig->v, DKIM_VERSION)) return DKIM_EXPL_BAD_VERSION; if (strcmp(sig->a, DKIM_ALGORITHM)) return DKIM_EXPL_BAD_ALGORITHM; - if (strcmp(sig->q, DKIM_QUERY_METHOD)) + if (!sig->q) + sig->q = mu_strdup(DKIM_QUERY_METHOD); + else if (strcmp(sig->q, DKIM_QUERY_METHOD)) return DKIM_EXPL_BAD_QUERY; if (sig->i) { @@ -1191,7 +1208,7 @@ dkim_sig_validate(struct dkim_signature const *sig) dlen = strlen(sig->d); if (!(dlen <= ilen && mu_c_strcasecmp(sig->d, p + ilen - dlen) == 0 && - sig->d[ilen - dlen - 1] == '.')) + (p[ilen - dlen - 1] == '.' || p[ilen - dlen - 1] == '@'))) return DKIM_EXPL_DOMAIN_MISMATCH; } @@ -1261,14 +1278,25 @@ static int pubkey_validate(mu_assoc_t a, struct dkim_signature const *sig) { char *s; + size_t n; + + if ((s = mu_assoc_get(a, "v")) != NULL && + strcmp(s, DKIM_KEYRECORD_VERSION)) + return DKIM_EXPL_KEY_SYNTAX; + if ((s = mu_assoc_get(a, "p")) == NULL) return DKIM_EXPL_KEY_SYNTAX; if (s[0] == 0) return DKIM_EXPL_KEY_REVOKED; - + + n = strcspn(sig->a, "-"); + if ((s = mu_assoc_get(a, "k")) != NULL && + !(n == strlen(s) && memcmp(s, sig->a, n) == 0)) + return DKIM_EXPL_BAD_ALGORITHM; + if ((s = mu_assoc_get(a, "h")) != NULL && - !dkim_header_list_match(s, sig->a)) + !dkim_header_list_match(s, sig->a + n + 1)) return DKIM_EXPL_BAD_ALGORITHM; return DKIM_EXPL_OK; } @@ -27,6 +27,7 @@ enum { }; #define DKIM_VERSION "1" +#define DKIM_KEYRECORD_VERSION "DKIM1" #define DKIM_SIGNATURE_HEADER "DKIM-Signature" #define DKIM_QUERY_METHOD "dns/txt" #define DKIM_ALGORITHM "rsa-sha256" @@ -52,7 +53,7 @@ struct dkim_signature { }; /* Convert canonicalization type to a DKIM_CANON_ constant. */ -int dkim_str_to_canon_type(char const *str); +int dkim_str_to_canon_type(char const *str, char **endp); /* * Create canonicalizer filter for STREAM. Use canonicalization types |