aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2020-07-10 11:49:36 +0300
committerSergey Poznyakoff <gray@gnu.org>2020-07-10 11:49:36 +0300
commit816bf513094698ae2338de87112ac0a2579b7896 (patch)
tree7885cad22cdc647c10bc95f3447ccf29751f3f8e /src
parent838f7a81cce4c48dcbb1b825e6b25e291ffab43a (diff)
downloadmailfromd-816bf513094698ae2338de87112ac0a2579b7896.tar.gz
mailfromd-816bf513094698ae2338de87112ac0a2579b7896.tar.bz2
Rewrite DKIM parser and formatter
Diffstat (limited to 'src')
-rw-r--r--src/dkim.c509
1 files changed, 346 insertions, 163 deletions
diff --git a/src/dkim.c b/src/dkim.c
index 4f8e6dac..4d7d08b7 100644
--- a/src/dkim.c
+++ b/src/dkim.c
@@ -588,6 +588,8 @@ dkim_format_wrap(struct dkim_format_buf *fb, size_t len)
static void
dkim_format_tag(struct dkim_format_buf *fb, char const *tag, char const *val)
{
+ if (!val)
+ return;
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);
@@ -598,31 +600,6 @@ dkim_format_tag(struct dkim_format_buf *fb, char const *tag, char const *val)
}
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)
{
@@ -644,88 +621,6 @@ dkim_format_tag_base64(struct dkim_format_buf *fb, char const *tag,
}
mu_stream_write(fb->str, ";", 1, NULL);
}
-
-int
-dkim_signature_format(struct dkim_signature *sig, int crlf, char **result)
-{
- struct dkim_format_buf fb;
- int rc;
- static char header_field[] = DKIM_SIGNATURE_HEADER ": ";
- mu_off_t off;
- size_t size;
- char *text;
-
- /* 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_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])) {
- 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);
- }
- 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->l != DKIM_LENGTH_ALL) {
- char tbuf[80];
- snprintf(tbuf, sizeof(tbuf), "%zu", sig->l);
- dkim_format_tag(&fb, "l", tbuf);
- }
- if (sig->t) {
- char tbuf[80];
- snprintf(tbuf, sizeof(tbuf), "%lu", (long unsigned) sig->t);
- dkim_format_tag(&fb, "t", tbuf);
- }
- if (sig->x) {
- char tbuf[80];
- snprintf(tbuf, sizeof(tbuf), "%lu", (long unsigned) sig->x);
- dkim_format_tag(&fb, "x", tbuf);
- }
- 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
dkim_signature_free(struct dkim_signature *sig)
@@ -740,20 +635,298 @@ dkim_signature_free(struct dkim_signature *sig)
free(sig->q);
free(sig->v);
}
+
+/*
+ * Auxiliary functions for parsing and formatting dkim_signature fields.
+ *
+ * Each parser is declared as
+ * int P(char const *value, void *data)
+ * Its arguments are:
+ * value - actual tag value obtained from the header.
+ * data - pointer to the member of struct dkim_signature.
+ * The parser returns 0 on success and non-zero on error. It is not supposed
+ * to emit any diagnostic messages.
+ *
+ * Each formatter is declared as
+ * int F(struct dkim_format_buf *fb, char const *tag, void *data)
+ * Its arguments are:
+ * fb - formatting buffer,
+ * tag - the tag name,
+ * value - pointer to the member of struct dkim_signature.
+ */
+
+/* General purpose parser for char* (or uint8_t*) fields */
+static int
+dkim_tag_char_parser(char const *value, void *data)
+{
+ char **cptr = data;
+ *cptr = mu_strdup(value);
+ return 0;
+}
+
+/* General purpose formatter for char* (or uint8_t*) fields */
+static void
+dkim_tag_char_formatter(struct dkim_format_buf *fb,
+ char const *tag,
+ void *data)
+{
+ char **cptr = data;
+ dkim_format_tag(fb, tag, *cptr);
+}
+
+/* Formatter for the a= tag */
+static void
+dkim_tag_a_formatter(struct dkim_format_buf *fb,
+ char const *tag,
+ void *data)
+{
+ char **cptr = data;
+ dkim_format_tag(fb, tag, *cptr ? *cptr : DKIM_ALGORITHM);
+}
+
+/* Formatter for the q= tag */
+static void
+dkim_tag_q_formatter(struct dkim_format_buf *fb,
+ char const *tag,
+ void *data)
+{
+ char **cptr = data;
+ dkim_format_tag(fb, tag, *cptr ? *cptr : DKIM_QUERY_METHOD);
+}
+
+/* Special formatter for the h= tag, that ensures proper wrapping. */
+static void
+dkim_tag_h_formatter(struct dkim_format_buf *fb,
+ char const *tag,
+ void *data)
+{
+ char **cptr = data;
+ void *save;
+ char const *hval;
+
+ if (!dkim_format_wrap(fb, 2))
+ dkim_format_wrap(fb, strlen(*cptr) + 3);
+
+ mu_stream_write(fb->str, "h=", 2, NULL);
+
+ if ((hval = dkim_header_list_first(*cptr, &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);
+}
+
+/* Parser and formatter for the c= tag. */
+static int
+dkim_tag_c_parser(char const *value, void *data)
+{
+ int *canon = data;
+ char *s;
+
+ if ((canon[0] = dkim_str_to_canon_type(value, &s)) == DKIM_CANON_ERR)
+ return -1;
+ if (*s == 0)
+ canon[1] = canon[0];
+ else if (*s != '/' ||
+ (canon[1] = dkim_str_to_canon_type(s + 1, NULL)) == DKIM_CANON_ERR)
+ return -1;
+ return 0;
+}
+
+static void
+dkim_tag_c_formatter(struct dkim_format_buf *fb,
+ char const *tag,
+ void *data)
+{
+ int *canon = data;
+ char v[(DKIM_CANON_STRING_MAX+1)*2];
+ strcpy(v, dkim_canon_string[canon[0]]);
+ strcat(v, "/");
+ strcat(v, dkim_canon_string[canon[1]]);
+ dkim_format_tag(fb, tag, v);
+}
+/* General-purpose parser and formatter for time_t members (t and x) */
static int
-str_to_time(char const *str, time_t *pt)
+dkim_tag_time_parser(char const *value, void *data)
{
+ time_t *tptr = data;
unsigned long n;
char *p;
errno = 0;
- n = strtoul(str, &p, 10);
+ n = strtoul(value, &p, 10);
if (errno || *p)
return -1;
- *pt = n;
+ *tptr = n;
return 0;
}
+static void
+dkim_tag_time_formatter(struct dkim_format_buf *fb,
+ char const *tag,
+ void *data)
+{
+ time_t *tptr = data;
+
+ if (*tptr) {
+ char tbuf[80];
+ snprintf(tbuf, sizeof(tbuf), "%lu", (long unsigned) *tptr);
+ dkim_format_tag(fb, tag, tbuf);
+ }
+}
+
+/* Parser and formatter for the l= tag */
+static int
+dkim_tag_l_parser(char const *value, void *data)
+{
+ size_t *sptr = data;
+ unsigned long n;
+ char *endp;
+ errno = 0;
+ n = strtoul(value, &endp, 10);
+ if (errno || *endp)
+ return -1;
+ *sptr = n;
+ return 0;
+}
+
+static void
+dkim_tag_l_formatter(struct dkim_format_buf *fb,
+ char const *tag,
+ void *data)
+{
+ size_t *sptr = data;
+ char tbuf[80];
+ if (*sptr != DKIM_LENGTH_ALL) {
+ snprintf(tbuf, sizeof(tbuf), "%zu", *sptr);
+ dkim_format_tag(fb, tag, tbuf);
+ }
+}
+
+/* Formatter for the bh= tag. */
+static void
+dkim_tag_bh_formatter(struct dkim_format_buf *fb,
+ char const *tag,
+ void *data)
+{
+ char **sptr = data;
+ dkim_format_tag_base64(fb, tag, *sptr);
+}
+
+/* Formatter for the b= tag. */
+static void
+dkim_tag_b_formatter(struct dkim_format_buf *fb,
+ char const *tag,
+ void *data)
+{
+ char **sptr = data;
+ dkim_format_nl(fb);
+ dkim_format_tag_base64(fb, tag, *sptr ? *sptr : "");
+}
+
+/* Tag definition structure */
+struct dkim_tag_descr {
+ char *tag; /* Tag name. */
+ size_t off; /* Field offset in struct dkim_signature. */
+ int (*parser)(char const *, void *);
+ /* Parser function. */
+ void (*formatter)(struct dkim_format_buf *, char const *, void *);
+ /* Formatter function. */
+};
+
+/* Order of entries in this array defines the order in which tags are
+ formatted.
+*/
+static struct dkim_tag_descr tag_descr[] = {
+ {
+ "v",
+ offsetof(struct dkim_signature, v),
+ dkim_tag_char_parser,
+ dkim_tag_char_formatter,
+ },
+ {
+ "a",
+ offsetof(struct dkim_signature, a),
+ dkim_tag_char_parser,
+ dkim_tag_a_formatter
+ },
+ {
+ "d",
+ offsetof(struct dkim_signature, d),
+ dkim_tag_char_parser,
+ dkim_tag_char_formatter,
+ },
+ {
+ "s",
+ offsetof(struct dkim_signature, s),
+ dkim_tag_char_parser,
+ dkim_tag_char_formatter,
+ },
+ {
+ "c",
+ offsetof(struct dkim_signature, canon),
+ dkim_tag_c_parser,
+ dkim_tag_c_formatter
+ },
+ {
+ "q",
+ offsetof(struct dkim_signature, q),
+ dkim_tag_char_parser,
+ dkim_tag_q_formatter
+ },
+ {
+ "h",
+ offsetof(struct dkim_signature, h),
+ dkim_tag_char_parser,
+ dkim_tag_h_formatter,
+ },
+ {
+ "i",
+ offsetof(struct dkim_signature, i),
+ dkim_tag_char_parser,
+ dkim_tag_char_formatter,
+ },
+ {
+ "l",
+ offsetof(struct dkim_signature, l),
+ dkim_tag_l_parser,
+ dkim_tag_l_formatter
+ },
+ {
+ "t",
+ offsetof(struct dkim_signature, t),
+ dkim_tag_time_parser,
+ dkim_tag_time_formatter
+ },
+ {
+ "x",
+ offsetof(struct dkim_signature, x),
+ dkim_tag_time_parser,
+ dkim_tag_time_formatter
+ },
+ {
+ "bh",
+ offsetof(struct dkim_signature, bh),
+ dkim_tag_char_parser,
+ dkim_tag_bh_formatter
+ },
+ {
+ "b",
+ offsetof(struct dkim_signature, b),
+ dkim_tag_char_parser,
+ dkim_tag_b_formatter
+ },
+ { NULL }
+};
+
+/* Table-driven DKIM-Signature parser. */
int
dkim_signature_parse(char *str, struct dkim_signature *ret_sig)
{
@@ -786,6 +959,7 @@ dkim_signature_parse(char *str, struct dkim_signature *ret_sig)
sig.canon[0] = sig.canon[1] = DKIM_CANON_SIMPLE;
sig.l = DKIM_LENGTH_ALL;
for (i = 0; i < ws.ws_wordc; i++) {
+ struct dkim_tag_descr *tg;
char *k = ws.ws_wordv[i];
char *p = strchr(k, '=');
if (!p) {
@@ -797,60 +971,13 @@ dkim_signature_parse(char *str, struct dkim_signature *ret_sig)
mu_rtrim_class(k, MU_CTYPE_BLANK);
mu_ltrim_class(p, MU_CTYPE_BLANK);
/* Handle known tags */
- if (strcmp(k, "a") == 0) {
- sig.a = (char*) mu_strdup(p);
- } 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;
-
- if ((sig.canon[0] = dkim_str_to_canon_type(p, &s))
- == DKIM_CANON_ERR) {
- rc = -1;
- goto end;
- }
-
- 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) {
- sig.s = mu_strdup(p);
- } else if (strcmp(k, "h") == 0) {
- sig.h = mu_strdup(p);
- } else if (strcmp(k, "i") == 0) {
- sig.i = mu_strdup(p);
- } else if (strcmp(k, "l") == 0) {
- unsigned long n;
- char *endp;
- errno = 0;
- n = strtoul(p, &endp, 10);
- if (errno || *endp) {
- rc = -1;
- goto end;
- }
- sig.l = n;
- } else if (strcmp(k, "t") == 0) {
- if (str_to_time(p, &sig.t)) {
- rc = -1;
- goto end;
- }
- } else if (strcmp(k, "x") == 0) {
- if (str_to_time(p, &sig.x)) {
- rc = -1;
- goto end;
+ for (tg = tag_descr; tg->tag; tg++) {
+ if (strcmp(k, tg->tag) == 0) {
+ rc = tg->parser(p, (char*)&sig + tg->off);
+ if (rc)
+ goto end;
+ break;
}
- } else if (strcmp(k, "v") == 0) {
- sig.v = mu_strdup(p);
}
}
end:
@@ -861,6 +988,62 @@ end:
*ret_sig = sig;
return rc;
}
+
+/*
+ * Table-driven DKIM-Signature formatter.
+ * The CRLF parameter defines what delimiter to use for wrapping.
+ */
+int
+dkim_signature_format(struct dkim_signature *sig, int crlf, char **result)
+{
+ struct dkim_format_buf fb;
+ int rc;
+ static char header_field[] = DKIM_SIGNATURE_HEADER ": ";
+ mu_off_t off;
+ size_t size;
+ char *text;
+ struct dkim_tag_descr *tg;
+
+ /* 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_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);
+ for (tg = tag_descr; tg->tag; tg++) {
+ tg->formatter(&fb, tg->tag, ((char*)sig + tg->off));
+ }
+
+ 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;
+}
/* Canonicalize the message into a stream.
* Arguments:

Return to:

Send suggestions and report system problems to the System administrator.