diff options
author | Sergey Poznyakoff <gray@gnu.org> | 2020-07-09 18:54:08 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org> | 2020-07-09 18:54:08 +0300 |
commit | ba541da248eb75ae85f9dc4535a8b097d7aaaf59 (patch) | |
tree | a11024e1f96374c811fd45342f19a3651fcf7f3e /src | |
parent | e87b7139c79c84d7af859c086002c1ead0ca21c9 (diff) | |
download | mailfromd-ba541da248eb75ae85f9dc4535a8b097d7aaaf59.tar.gz mailfromd-ba541da248eb75ae85f9dc4535a8b097d7aaaf59.tar.bz2 |
Implement DKIM-Signature wrapping. Fix DKIM interaction with header_replace
* src/builtin/dkim.bi (do_msgmod): When processing header_replace,
first check if the requested header is already present. If it is,
proceed with header insert. Otherwise do mu_header_append. This
mimics the Sendmail behaviour.
* src/dkim.c (dkim_header_list_end, dkim_header_list_next)
(dkim_header_list_first): New functions.
(dkim_signature_format): Rewrite. Take additional argument crlf.
When set, insert CRLF instead of LF when wrapping.
Diffstat (limited to 'src')
-rw-r--r-- | src/builtin/dkim.bi | 30 | ||||
-rw-r--r-- | src/dkim.c | 255 |
2 files changed, 223 insertions, 62 deletions
diff --git a/src/builtin/dkim.bi b/src/builtin/dkim.bi index a85c2b51..663ea009 100644 --- a/src/builtin/dkim.bi +++ b/src/builtin/dkim.bi @@ -71,24 +71,32 @@ do_msgmod(void *item, void *data) switch (msgmod->opcode) { case header_replace: if (!dkim_header_list_match(md->h_list, msgmod->name)) - break; - rc = mu_header_insert(hdr, msgmod->name, msgmod->value, - NULL, - msgmod->idx, MU_HEADER_REPLACE); - if (rc == MU_ERR_NOENT) { + break; + if (mu_header_sget_value(hdr, msgmod->name, NULL) == 0) { + rc = mu_header_insert(hdr, msgmod->name, msgmod->value, + NULL, + msgmod->idx, MU_HEADER_REPLACE); + if (rc == MU_ERR_NOENT) { + rc = mu_header_append(hdr, msgmod->name, msgmod->value); + if (rc) + mu_diag_funcall(MU_DIAG_ERROR, + "mu_header_insert", + msgmod->name, + rc); + } else if (rc) { + mu_diag_funcall(MU_DIAG_ERROR, + "mu_header_insert", + msgmod->name, + rc); + } + } else { rc = mu_header_append(hdr, msgmod->name, msgmod->value); if (rc) mu_diag_funcall(MU_DIAG_ERROR, "mu_header_append", msgmod->name, rc); - } else if (rc) { - mu_diag_funcall(MU_DIAG_ERROR, - "mu_header_insert", - msgmod->name, - rc); } - if (rc) { md->e_msgmod = msgmod; return rc; @@ -301,6 +301,56 @@ pubkey_from_base64(struct rsa_public_key *pub, const char *str) return result; } +struct h_list_buf { + char const *ptr; + char *base; + size_t size; +}; +void +dkim_header_list_end(void *save) +{ + struct h_list_buf *hbuf = save; + free(hbuf->base); + free(hbuf); +} + +char * +dkim_header_list_next(void *save) +{ + struct h_list_buf *hbuf = save; + char const *hp = hbuf->ptr; + + while (*hp && (*hp == ' ' || *hp == '\t' || *hp == ':')) + hp++; + if (*hp) { + size_t len = strcspn(hp, " \t:"); + if (len + 1 > hbuf->size) { + hbuf->base = mu_realloc(hbuf->base, len + 1); + hbuf->size = len + 1; + } + memcpy(hbuf->base, hp, len); + hbuf->base[len] = 0; + hbuf->ptr = hp + len; + } else { + hbuf->ptr = hp; + return NULL; + } + return hbuf->base; +} + +char * +dkim_header_list_first(char const *h_list, void *save) +{ + struct h_list_buf *hbuf, **hbuf_ptr = save; + + hbuf = mu_alloc(sizeof(hbuf[0])); + hbuf->ptr = h_list; + hbuf->base = NULL; + hbuf->size = 0; + *hbuf_ptr = hbuf; + return dkim_header_list_next(hbuf); +} + int dkim_header_list_match(char const *h_list, char const *h) { @@ -345,6 +395,7 @@ hash_stream(mu_stream_t str, struct sha256_ctx *ctx) while ((rc = mu_stream_read(str, buffer, sizeof(buffer), &count)) == 0 && count > 0) { +// mu_error("READ %*.*s", (int)count, (int)count, buffer); sha256_update(ctx, count, buffer); } @@ -367,6 +418,7 @@ hash_stream_segment(mu_stream_t str, size_t len, struct sha256_ctx *ctx) if (n > len) n = len; rc = mu_stream_read(str, buffer, n, &count); + //printf("HASH %*.*s\n",(int)count,(int)count,buffer); if (rc) { mu_diag_funcall(MU_DIAG_ERROR, "mu_stream_read", NULL, rc); @@ -457,7 +509,7 @@ dkim_rsa_sha256_sign(struct rsa_private_key *priv, struct sha256_ctx *ctx, * re. "DKIM-Signature Canonicalization Header" IANA registry. */ static char const *dkim_canon_string[] = { "simple", "relaxed", NULL }; - +#define DKIM_CANON_STRING_MAX (sizeof(dkim_canon_string[DKIM_CANON_RELAXED])-1) /* * 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. @@ -482,70 +534,171 @@ dkim_str_to_canon_type(char const *str, char **endp) } /* Format a struct dkim_signature as a DKIM-Signature header. */ + +#define MAX_LINE_LEN 78 + +struct dkim_format_buf { + mu_stream_t str; + mu_stream_stat_buffer stat; + int crlf; +}; + +static inline void +dkim_format_nl(struct dkim_format_buf *fb) +{ + if (fb->crlf) + mu_stream_write(fb->str, "\r\n ", 3, NULL); + else + mu_stream_write(fb->str, "\n ", 2, NULL); + fb->stat[MU_STREAM_STAT_OUT] = 0; +} + +static inline int +dkim_format_wrap(struct dkim_format_buf *fb, size_t len) +{ + if (fb->stat[MU_STREAM_STAT_OUT] + && fb->stat[MU_STREAM_STAT_OUT] + len > MAX_LINE_LEN) { + dkim_format_nl(fb); + return 1; + } + return 0; +} + +static void +dkim_format_tag(struct dkim_format_buf *fb, char const *tag, char const *val) +{ + if (!dkim_format_wrap(fb, strlen(tag) + strlen(val) + 2)) + dkim_format_wrap(fb, strlen(tag) + 1); + mu_stream_write(fb->str, tag, strlen(tag), NULL); + mu_stream_write(fb->str, "=", 1, NULL); + dkim_format_wrap(fb, strlen(val) + 1); + mu_stream_write(fb->str, val, strlen(val), NULL); + mu_stream_write(fb->str, ";", 1, NULL); +} + +static void +dkim_format_tag_h(struct dkim_format_buf *fb, char const *val) +{ + void *save; + char const *hval; + + if (!dkim_format_wrap(fb, 2)) + dkim_format_wrap(fb, strlen(val) + 3); + + mu_stream_write(fb->str, "h=", 2, NULL); + + if ((hval = dkim_header_list_first(val, &save)) != NULL) { + dkim_format_wrap(fb, strlen(hval)); + mu_stream_write(fb->str, hval, strlen(hval), NULL); + while ((hval = dkim_header_list_next(save)) != NULL) { + dkim_format_wrap(fb, strlen(hval) + 1); + mu_stream_write(fb->str, ":", 1, NULL); + mu_stream_write(fb->str, hval, strlen(hval), NULL); + } + } + dkim_header_list_end(save); + dkim_format_wrap(fb, 1); + mu_stream_write(fb->str, ";", 1, NULL); +} + +static void +dkim_format_tag_base64(struct dkim_format_buf *fb, char const *tag, + char const *val) +{ + size_t len = strlen(val); + + if (!dkim_format_wrap(fb, strlen(tag) + strlen(val) + 2)) + dkim_format_wrap(fb, strlen(tag) + 1); + mu_stream_write(fb->str, tag, strlen(tag), NULL); + mu_stream_write(fb->str, "=", 1, NULL); + + while (len > 0) { + size_t n = MAX_LINE_LEN - fb->stat[MU_STREAM_STAT_OUT]; + if (len < n) + n = len; + mu_stream_write(fb->str, val, n, NULL); + dkim_format_wrap(fb, 1); + val += n; + len -= n; + } + mu_stream_write(fb->str, ";", 1, NULL); +} + int -dkim_signature_format(struct dkim_signature *sig, char **result) +dkim_signature_format(struct dkim_signature *sig, int crlf, char **result) { - mu_opool_t op; + struct dkim_format_buf fb; int rc; + static char header_field[] = DKIM_SIGNATURE_HEADER ": "; + mu_off_t off; + size_t size; + char *text; - rc = mu_opool_create(&op, MU_OPOOL_DEFAULT); - if (rc) + /* Initialize format buffer */ + fb.crlf = crlf; + rc = mu_memory_stream_create(&fb.str, MU_STREAM_RDWR); + if (rc) { + mu_diag_funcall(MU_DIAG_ERROR, + "mu_memory_stream_create", + NULL, rc); return rc; - mu_opool_appendz(op, DKIM_SIGNATURE_HEADER ": "); - mu_opool_appendz(op, "v="); - mu_opool_appendz(op, sig->v); - mu_opool_appendz(op, "; a="); - mu_opool_appendz(op, sig->a ? sig->a : DKIM_ALGORITHM); - mu_opool_appendz(op, "; d="); - mu_opool_appendz(op, sig->d); - mu_opool_appendz(op, "; s="); - mu_opool_appendz(op, sig->s); - mu_opool_appendz(op, "; "); + } + mu_stream_set_stat(fb.str, + MU_STREAM_STAT_MASK (MU_STREAM_STAT_OUT) | + MU_STREAM_STAT_MASK (MU_STREAM_STAT_OUTLN), + fb.stat); + mu_stream_write(fb.str, header_field, strlen(header_field), NULL); + dkim_format_tag(&fb, "v", sig->v); + dkim_format_tag(&fb, "a", sig->a ? sig->a : DKIM_ALGORITHM); + dkim_format_tag(&fb, "d", sig->d); + dkim_format_tag(&fb, "s", sig->s); if (!(sig->canon[0] == DKIM_CANON_SIMPLE && sig->canon[0] == sig->canon[1])) { - mu_opool_appendz(op, "c="); - mu_opool_appendz(op, dkim_canon_string[sig->canon[0]]); - mu_opool_append(op, "/", 1); - mu_opool_appendz(op, dkim_canon_string[sig->canon[1]]); - mu_opool_appendz(op, "; "); - } - mu_opool_appendz(op, "q="); - mu_opool_appendz(op, sig->q ? sig->q : DKIM_QUERY_METHOD); - mu_opool_appendz(op, "; "); - if (sig->i) { - mu_opool_appendz(op, "i="); - mu_opool_appendz(op, sig->i); - mu_opool_appendz(op, "; "); + char v[(DKIM_CANON_STRING_MAX+1)*2]; + strcpy(v, dkim_canon_string[sig->canon[0]]); + strcat(v, "/"); + strcat(v, dkim_canon_string[sig->canon[1]]); + dkim_format_tag(&fb, "c", v); } - mu_opool_appendz(op, "h="); - mu_opool_appendz(op, sig->h); - mu_opool_appendz(op, "; "); + dkim_format_tag(&fb, "q", sig->q ? sig->q : DKIM_QUERY_METHOD); + if (sig->i) + dkim_format_tag(&fb, "i", sig->i); + dkim_format_tag_h(&fb, sig->h); if (sig->t) { char tbuf[80]; snprintf(tbuf, sizeof(tbuf), "%lu", (long unsigned) sig->t); - mu_opool_appendz(op, "t="); - mu_opool_appendz(op, tbuf); - mu_opool_appendz(op, "; "); + dkim_format_tag(&fb, "t", tbuf); } if (sig->x) { char tbuf[80]; snprintf(tbuf, sizeof(tbuf), "%lu", (long unsigned) sig->x); - mu_opool_appendz(op, "x="); - mu_opool_appendz(op, tbuf); - mu_opool_appendz(op, "; "); + dkim_format_tag(&fb, "x", tbuf); } - mu_opool_appendz(op, "bh="); - mu_opool_appendz(op, (char*)sig->bh); - mu_opool_appendz(op, "; "); - mu_opool_appendz(op, "b="); - if (sig->b) - mu_opool_appendz(op, (char*)sig->b); - mu_opool_append_char(op, 0); - - *result = mu_opool_detach(op, NULL); - //FIXME: Add line wrapping - mu_opool_destroy(&op); - return 0; + dkim_format_tag_base64(&fb, "bh", (char*)sig->bh); + dkim_format_nl(&fb); + dkim_format_tag_base64(&fb, "b", sig->b ? (char*)sig->b : ""); + + if (mu_stream_err(fb.str)) { + rc = mu_stream_last_error(fb.str); + mu_diag_funcall(MU_DIAG_ERROR, + "dkim_signature_format", NULL, rc); + } else { + rc = 0; + mu_stream_seek(fb.str, 0, MU_SEEK_CUR, &off); + + //FIXME: assert(size < (size_t)~0) + size = off; + text = mu_alloc(size + 1); + + mu_stream_seek(fb.str, 0, MU_SEEK_SET, NULL); + + mu_stream_read(fb.str, text, size, NULL); + text[size] = 0; + *result = text; + } + mu_stream_destroy(&fb.str); + + return rc; } void @@ -1061,7 +1214,7 @@ end: memcpy(vp + n, "b=", 2); strcpy(vp + n + 2, sigstr + n + 2 + blen); } else { - dkim_signature_format(sig, &sig_str_buf); + dkim_signature_format(sig, 0, &sig_str_buf); } mu_fixed_memory_stream_create(&str, sig_str_buf, strlen(sig_str_buf), MU_STREAM_RDWR|MU_STREAM_SEEK); @@ -1144,7 +1297,7 @@ mfd_dkim_sign(mu_message_t msg, struct dkim_signature *sig, dkim_rsa_sha256_sign(&priv, &ctx, &sig->b); /* Create the header */ - dkim_signature_format(sig, ret_sighdr); + dkim_signature_format(sig, 1, ret_sighdr); result = 0; } /* Reclaim the allocated memory. */ |