aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2020-07-09 18:54:08 +0300
committerSergey Poznyakoff <gray@gnu.org>2020-07-09 18:54:08 +0300
commitba541da248eb75ae85f9dc4535a8b097d7aaaf59 (patch)
treea11024e1f96374c811fd45342f19a3651fcf7f3e /src
parente87b7139c79c84d7af859c086002c1ead0ca21c9 (diff)
downloadmailfromd-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.bi30
-rw-r--r--src/dkim.c255
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;
diff --git a/src/dkim.c b/src/dkim.c
index 37038774..65df649b 100644
--- a/src/dkim.c
+++ b/src/dkim.c
@@ -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. */

Return to:

Send suggestions and report system problems to the System administrator.