commit 6609fda3c91445d79dbb58f5a57a620176caff76 from: Martijn van Duren date: Thu Apr 16 11:51:32 2026 UTC Move dkimverify to the new libopensmtpd. There's a few edgecases to implement, which now lead to crashes to make them visible commit - 7258013668ef07ebc2c942108e7b4851c327adaa commit + 6609fda3c91445d79dbb58f5a57a620176caff76 blob - 8f324e536ea02ba338e434de491b4f778535a399 blob + 3ea6e58668009bcadf70739c6903f841027c7cda --- main.c +++ main.c @@ -116,43 +116,39 @@ struct message { int has_body; struct header *header; size_t nheaders; - int err; int readdone; }; void usage(void); -void dkim_err(struct message *, char *); -void dkim_errx(struct message *, char *); void dkim_conf(const char *, const char *); -void dkim_dataline(struct osmtpd_ctx *, const char *); -void dkim_commit(struct osmtpd_ctx *); +int dkim_dataline(struct osmtpd_ctx *, const char *); void *dkim_message_new(struct osmtpd_ctx *); void dkim_message_free(struct osmtpd_ctx *, void *); -void dkim_header_add(struct osmtpd_ctx *, const char *); -void dkim_signature_parse(struct header *); -void dkim_signature_parse_v(struct signature *, const char *, const char *); -void dkim_signature_parse_a(struct signature *, const char *, const char *); -void dkim_signature_parse_b(struct signature *, const char *, const char *); -void dkim_signature_parse_bh(struct signature *, const char *, const char *); -void dkim_signature_parse_c(struct signature *, const char *, const char *); -void dkim_signature_parse_d(struct signature *, const char *, const char *); -void dkim_signature_parse_h(struct signature *, const char *, const char *); -void dkim_signature_parse_i(struct signature *, const char *, const char *); -void dkim_signature_parse_l(struct signature *, const char *, const char *); -void dkim_signature_parse_q(struct signature *, const char *, const char *); -void dkim_signature_parse_s(struct signature *, const char *, const char *); -void dkim_signature_parse_t(struct signature *, const char *, const char *); -void dkim_signature_parse_x(struct signature *, const char *, const char *); -void dkim_signature_parse_z(struct signature *, const char *, const char *); -void dkim_signature_verify(struct signature *); -void dkim_signature_header(EVP_MD_CTX *, struct signature *, struct header *); +int dkim_header_add(struct osmtpd_ctx *, const char *); +int dkim_signature_parse(struct header *); +int dkim_signature_parse_v(struct signature *, const char *, const char *); +int dkim_signature_parse_a(struct signature *, const char *, const char *); +int dkim_signature_parse_b(struct signature *, const char *, const char *); +int dkim_signature_parse_bh(struct signature *, const char *, const char *); +int dkim_signature_parse_c(struct signature *, const char *, const char *); +int dkim_signature_parse_d(struct signature *, const char *, const char *); +int dkim_signature_parse_h(struct signature *, const char *, const char *); +int dkim_signature_parse_i(struct signature *, const char *, const char *); +int dkim_signature_parse_l(struct signature *, const char *, const char *); +int dkim_signature_parse_q(struct signature *, const char *, const char *); +int dkim_signature_parse_s(struct signature *, const char *, const char *); +int dkim_signature_parse_t(struct signature *, const char *, const char *); +int dkim_signature_parse_x(struct signature *, const char *, const char *); +int dkim_signature_parse_z(struct signature *, const char *, const char *); +int dkim_signature_verify(struct signature *); +int dkim_signature_header(EVP_MD_CTX *, struct signature *, struct header *); void dkim_signature_state(struct signature *, enum state, const char *); const char *dkim_state2str(enum state); -void dkim_header_cat(struct osmtpd_ctx *, const char *); -void dkim_body_parse(struct message *, const char *); -void dkim_body_verify(struct signature *); +int dkim_header_cat(struct osmtpd_ctx *, const char *); +int dkim_body_parse(struct message *, const char *); +int dkim_body_verify(struct signature *); void dkim_rr_resolve(struct asr_result *, void *); -void dkim_message_verify(struct message *); +int dkim_message_verify(struct message *); ssize_t dkim_ar_cat(char **ar, size_t *n, size_t aroff, const char *fmt, ...) __attribute__((__format__ (printf, 4, 5))); void dkim_ar_print(struct osmtpd_ctx *, const char *); @@ -177,7 +173,6 @@ main(int argc, char *argv[]) osmtpd_register_conf(dkim_conf); osmtpd_register_filter_dataline(dkim_dataline); - osmtpd_register_filter_commit(dkim_commit); osmtpd_local_message(dkim_message_new, dkim_message_free); osmtpd_run(); @@ -203,23 +198,15 @@ dkim_conf(const char *key, const char *value) } } -void +int dkim_dataline(struct osmtpd_ctx *ctx, const char *line) { struct message *msg = ctx->local_message; size_t i; - if (msg->err) { - if (line[0] == '.' && line[1] =='\0') { - msg->readdone = 1; - osmtpd_filter_dataline(ctx, "."); - } - return; - } - if (fprintf(msg->origf, "%s\n", line) < 0) { - dkim_err(msg, "Couldn't write to tempfile"); - return; + osmtpd_warnx(ctx, "Couldn't write to tempfile"); + return -1; } if (line[0] == '.') { line++; @@ -228,39 +215,30 @@ dkim_dataline(struct osmtpd_ctx *ctx, const char *line for (i = 0; i < msg->nheaders; i++) { if (msg->header[i].sig == NULL) continue; - dkim_body_verify(msg->header[i].sig); + if (dkim_body_verify(msg->header[i].sig) == -1) + return -1; } - dkim_message_verify(msg); - return; + return dkim_message_verify(msg); } } if (msg->parsing_headers) { - dkim_header_add(ctx, line); + if (dkim_header_add(ctx, line) == -1) + return -1; if (line[0] == '\0') { msg->parsing_headers = 0; for (i = 0; i < msg->nheaders; i++) { if (msg->header[i].sig == NULL) continue; if (msg->header[i].sig->query == NULL) - dkim_signature_verify( - msg->header[i].sig); + if (dkim_signature_verify( + msg->header[i].sig) == -1) + return -1; } } - return; - } else { - dkim_body_parse(msg, line); - } -} + } else + return dkim_body_parse(msg, line); -void -dkim_commit(struct osmtpd_ctx *ctx) -{ - struct message *msg = ctx->local_message; - - if (msg->err) - osmtpd_filter_disconnect(ctx, "Internal server error"); - else - osmtpd_filter_proceed(ctx); + return 0; } void * @@ -272,7 +250,8 @@ dkim_message_new(struct osmtpd_ctx *ctx) osmtpd_err(1, NULL); if ((msg->origf = tmpfile()) == NULL) { - dkim_err(msg, "Can't open tempfile"); + osmtpd_warnx(ctx, "Can't open tempfile"); + free(msg); return NULL; } msg->ctx = ctx; @@ -281,7 +260,6 @@ dkim_message_new(struct osmtpd_ctx *ctx) msg->has_body = 0; msg->header = NULL; msg->nheaders = 0; - msg->err = 0; msg->readdone = 0; return msg; @@ -311,7 +289,7 @@ dkim_message_free(struct osmtpd_ctx *ctx, void *data) free(msg); } -void +int dkim_header_add(struct osmtpd_ctx *ctx, const char *line) { struct message *msg = ctx->local_message; @@ -331,21 +309,22 @@ dkim_header_add(struct osmtpd_ctx *ctx, const char *li if (end != NULL && strncasecmp( start, "DKIM-Signature", end - start) == 0 && - verify[0] == ':') - dkim_signature_parse( - &msg->header[msg->nheaders - 1]); + verify[0] == ':') { + if (dkim_signature_parse( + &msg->header[msg->nheaders - 1]) == -1) + return -1; + } if (line[0] == '\0') - return; + return 0; } else { - dkim_header_cat(ctx, line); - return; + return dkim_header_cat(ctx, line); } } if (msg->nheaders % 10 == 0) { if ((headers = recallocarray(msg->header, msg->nheaders, msg->nheaders + 10, sizeof(*msg->header))) == NULL) { - dkim_err(msg, "malloc"); - return; + osmtpd_warn(ctx, "malloc"); + return -1; } msg->header = headers; for (i = 0; i < msg->nheaders; i++) { @@ -356,10 +335,10 @@ dkim_header_add(struct osmtpd_ctx *ctx, const char *li } msg->header[msg->nheaders].msg = msg; msg->nheaders++; - dkim_header_cat(ctx, line); + return dkim_header_cat(ctx, line); } -void +int dkim_header_cat(struct osmtpd_ctx *ctx, const char *line) { struct message *msg = ctx->local_message; @@ -371,17 +350,18 @@ dkim_header_cat(struct osmtpd_ctx *ctx, const char *li if (needed > (header->buflen / 1024) + 1) { buf = reallocarray(header->buf, (needed / 1024) + 1, 1024); if (buf == NULL) { - dkim_err(msg, "malloc"); - return; + osmtpd_warn(ctx, "malloc"); + return -1; } header->buf = buf; } header->buflen += snprintf(header->buf + header->buflen, (((needed / 1024) + 1) * 1024) - header->buflen, "%s%s", header->buflen == 0 ? "" : "\r\n", line); + return 0; } -void +int dkim_signature_parse(struct header *header) { struct signature *sig; @@ -390,14 +370,15 @@ dkim_signature_parse(struct header *header) char tagname[3]; char subdomain[HOST_NAME_MAX + 1]; size_t ilen, dlen; + int r; /* Format checked by dkim_header_add */ buf = osmtpd_ltok_skip_field_name(header->buf, 0); buf = osmtpd_ltok_skip_wsp(buf, 1) + 1; if ((header->sig = calloc(1, sizeof(*header->sig))) == NULL) { - dkim_err(header->msg, "malloc"); - return; + osmtpd_warn(header->msg->ctx, "malloc"); + return -1; } sig = header->sig; sig->header = header; @@ -408,7 +389,7 @@ dkim_signature_parse(struct header *header) end = osmtpd_ltok_skip_tag_list(buf, 0); if (end == NULL || end[0] != '\0') { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid tag-list"); - return; + return 0; } while (buf[0] != '\0') { @@ -419,39 +400,43 @@ dkim_signature_parse(struct header *header) if ((size_t)(end - buf) >= sizeof(tagname)) tagname[0] = '\0'; else - strlcpy(tagname, buf, (end - buf) + 1); + strncpy(tagname, buf, (end - buf)); buf = osmtpd_ltok_skip_fws(end, 1); /* '=' */ buf = osmtpd_ltok_skip_fws(buf + 1, 1); end = osmtpd_ltok_skip_tag_value(buf, 1); if (strcmp(tagname, "v") == 0) - dkim_signature_parse_v(sig, buf, end); + r = dkim_signature_parse_v(sig, buf, end); else if (strcmp(tagname, "a") == 0) - dkim_signature_parse_a(sig, buf, end); + r = dkim_signature_parse_a(sig, buf, end); else if (strcmp(tagname, "b") == 0) - dkim_signature_parse_b(sig, buf, end); + r = dkim_signature_parse_b(sig, buf, end); else if (strcmp(tagname, "bh") == 0) - dkim_signature_parse_bh(sig, buf, end); + r = dkim_signature_parse_bh(sig, buf, end); else if (strcmp(tagname, "c") == 0) - dkim_signature_parse_c(sig, buf, end); + r = dkim_signature_parse_c(sig, buf, end); else if (strcmp(tagname, "d") == 0) - dkim_signature_parse_d(sig, buf, end); + r = dkim_signature_parse_d(sig, buf, end); else if (strcmp(tagname, "h") == 0) - dkim_signature_parse_h(sig, buf, end); + r = dkim_signature_parse_h(sig, buf, end); else if (strcmp(tagname, "i") == 0) - dkim_signature_parse_i(sig, buf, end); + r = dkim_signature_parse_i(sig, buf, end); else if (strcmp(tagname, "l") == 0) - dkim_signature_parse_l(sig, buf, end); + r = dkim_signature_parse_l(sig, buf, end); else if (strcmp(tagname, "q") == 0) - dkim_signature_parse_q(sig, buf, end); + r = dkim_signature_parse_q(sig, buf, end); else if (strcmp(tagname, "s") == 0) - dkim_signature_parse_s(sig, buf, end); + r = dkim_signature_parse_s(sig, buf, end); else if (strcmp(tagname, "t") == 0) - dkim_signature_parse_t(sig, buf, end); + r = dkim_signature_parse_t(sig, buf, end); else if (strcmp(tagname, "x") == 0) - dkim_signature_parse_x(sig, buf, end); + r = dkim_signature_parse_x(sig, buf, end); else if (strcmp(tagname, "z") == 0) - dkim_signature_parse_z(sig, buf, end); + r = dkim_signature_parse_z(sig, buf, end); + else + r = 0; + if (r == -1) + return -1; buf = osmtpd_ltok_skip_fws(end, 1); if (buf[0] == ';') @@ -459,11 +444,11 @@ dkim_signature_parse(struct header *header) else if (buf[0] != '\0') { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid tag-list"); - return; + return 0; } } if (sig->state != DKIM_UNKNOWN) - return; + return 0; if (sig->v != 1) dkim_signature_state(sig, DKIM_PERMERROR, "Missing v tag"); @@ -480,7 +465,7 @@ dkim_signature_parse(struct header *header) else if (sig->s[0] == '\0') dkim_signature_state(sig, DKIM_PERMERROR, "Missing s tag"); if (sig->state != DKIM_UNKNOWN) - return; + return 0; if (sig->i != NULL) { i = osmtpd_ltok_skip_local_part(sig->i, 1) + 1; @@ -489,66 +474,70 @@ dkim_signature_parse(struct header *header) if (ilen < dlen) { dkim_signature_state(sig, DKIM_PERMERROR, "i tag not subdomain of d"); - return; + return 0; } i += ilen - dlen; if ((i[-1] != '.' && i[-1] != '@') || strncasecmp(i, sig->d, dlen) != 0) { dkim_signature_state(sig, DKIM_PERMERROR, "i tag not subdomain of d"); - return; + return 0; } } if (sig->t != -1 && sig->x != -1 && sig->t > sig->x) { dkim_signature_state(sig, DKIM_PERMERROR, "t tag after x tag"); - return; + return 0; } if ((size_t)snprintf(subdomain, sizeof(subdomain), "%s._domainkey.%s", sig->s, sig->d) >= sizeof(subdomain)) { dkim_signature_state(sig, DKIM_PERMERROR, "dns/txt query too long"); - return; + return 0; } if ((query = res_query_async(subdomain, C_IN, T_TXT, NULL)) == NULL) { - dkim_err(header->msg, "res_query_async"); - return; + osmtpd_warnx(header->msg->ctx, "res_query_async"); + return -1; } if ((sig->query = event_asr_run(query, dkim_rr_resolve, sig)) == NULL) { - dkim_err(header->msg, "event_asr_run"); + osmtpd_warnx(header->msg->ctx, "event_asr_run"); asr_abort(query); - return; + return -1; } + + return 0; } -void +int dkim_signature_parse_v(struct signature *sig, const char *start, const char *end) { if (sig->v != 0) { /* Duplicate tag */ dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate v tag"); - return; + return 0; } /* Unsupported version */ if (start[0] != '1' || start + 1 != end) dkim_signature_state(sig, DKIM_NEUTRAL, "Unsupported v tag"); else sig->v = 1; + + return -1; } -void +int dkim_signature_parse_a(struct signature *sig, const char *start, const char *end) { char ah[sizeof("sha256")]; if (sig->ah != NULL) { dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate a tag"); - return; + return 0; } if (osmtpd_ltok_skip_sig_a_tag_alg(start, 0) != end) { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid a tag"); - return; + return 0; } sig->a = start; sig->asz = (size_t)(end - start); @@ -564,59 +553,62 @@ dkim_signature_parse_a(struct signature *sig, const ch #endif } else { dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag k"); - return; + return 0; } if ((size_t)(end - start) >= sizeof(ah)) { dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag h"); - return; + return 0; } strlcpy(ah, start, sizeof(ah)); ah[end - start] = '\0'; if ((sig->ah = EVP_get_digestbyname(ah)) == NULL) { dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag h"); - return; + return 0; } if ((sig->bhctx = EVP_MD_CTX_new()) == NULL) { - dkim_err(sig->header->msg, "EVP_MD_CTX_new"); - return; + osmtpd_warnx(sig->header->msg->ctx, "EVP_MD_CTX_new"); + return -1; } if (EVP_DigestInit_ex(sig->bhctx, sig->ah, NULL) <= 0) { - dkim_err(sig->header->msg, "EVP_DigestInit_ex"); - return; + osmtpd_warnx(sig->header->msg->ctx, "EVP_DigestInit_ex"); + return -1; } + + return 0; } -void +int dkim_signature_parse_b(struct signature *sig, const char *start, const char *end) { int decodesz; if (sig->b != NULL) { dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate b tag"); - return; + return 0; } sig->bheader = start; if ((sig->b = malloc((((end - start) / 4) + 1) * 3)) == NULL) { - dkim_err(sig->header->msg, "malloc"); - return; + osmtpd_warnx(sig->header->msg->ctx, "malloc"); + return -1; } /* EVP_DecodeBlock doesn't handle internal whitespace */ EVP_DecodeInit(ectx); if (EVP_DecodeUpdate(ectx, sig->b, &decodesz, start, (int)(end - start)) == -1) { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid b tag"); - return; + return 0; } sig->bsz = decodesz; - if (EVP_DecodeFinal(ectx, sig->b + sig->bsz, - &decodesz) == -1) { + if (EVP_DecodeFinal(ectx, sig->b + sig->bsz, &decodesz) == -1) { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid b tag"); - return; + return 0; } sig->bsz += decodesz; + + return 0; } -void +int dkim_signature_parse_bh(struct signature *sig, const char *start, const char *end) { const char *b64; @@ -625,7 +617,7 @@ dkim_signature_parse_bh(struct signature *sig, const c if (sig->bhsz != 0) { dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate bh tag"); - return; + return 0; } /* * EVP_Decode* expects sig->bh to be large enough, @@ -651,7 +643,7 @@ dkim_signature_parse_bh(struct signature *sig, const c /* Invalid tag value */ if (b64 != end || n % 4 != 0 || (n / 4) * 3 > sizeof(sig->bh)) { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag"); - return; + return 0; } /* EVP_DecodeBlock doesn't handle internal whitespace */ EVP_DecodeInit(ectx); @@ -659,23 +651,25 @@ dkim_signature_parse_bh(struct signature *sig, const c (int)(end - start)) == -1) { /* Paranoia check */ dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag"); - return; + return 0; } sig->bhsz = decodesz; if (EVP_DecodeFinal(ectx, sig->bh + sig->bhsz, &decodesz) == -1) { /* Paranoia check */ dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag"); - return; + return 0; } sig->bhsz += decodesz; + + return 0; } -void +int dkim_signature_parse_c(struct signature *sig, const char *start, const char *end) { if (sig->c != 0) { dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate c tag"); - return; + return 0; } if (strncmp(start, "simple", 6) == 0) { sig->c = CANON_HEADER_SIMPLE; @@ -685,7 +679,7 @@ dkim_signature_parse_c(struct signature *sig, const ch start += 7; } else { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid c tag"); - return; + return 0; } if (start[0] == '/') { start++; @@ -698,33 +692,36 @@ dkim_signature_parse_c(struct signature *sig, const ch } else { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid c tag"); - return; + return 0; } } if (start != end) { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid c tag"); - return; + return 0; } sig->c |= CANON_DONE; + + return 0; } -void +int dkim_signature_parse_d(struct signature *sig, const char *start, const char *end) { if (sig->d[0] != '\0') { dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate d tag"); - return; + return 0; } if (osmtpd_ltok_skip_sig_d_tag_value(start, 0) != end || (size_t)(end - start) >= sizeof(sig->d)) { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid d tag"); - return; + return 0; } strlcpy(sig->d, start, end - start + 1); + return 0; } -void +int dkim_signature_parse_h(struct signature *sig, const char *start, const char *end) { const char *h; @@ -732,18 +729,18 @@ dkim_signature_parse_h(struct signature *sig, const ch if (sig->h != NULL) { dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate h tag"); - return; + return 0; } if (osmtpd_ltok_skip_sig_h_tag_value(start, 0) < end) { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid h tag"); - return; + return 0; } h = start; while (1) { if ((h = osmtpd_ltok_skip_hdr_name(h, 0)) == NULL) { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid h tag"); - return; + return 0; } n++; /* ';' is part of hdr-name */ @@ -757,8 +754,8 @@ dkim_signature_parse_h(struct signature *sig, const ch h = osmtpd_ltok_skip_fws(h + 1, 1); } if ((sig->h = calloc(n + 1, sizeof(*sig->h))) == NULL) { - dkim_err(sig->header->msg, "malloc"); - return; + osmtpd_warn(sig->header->msg->ctx, "malloc"); + return -1; } n = 0; h = start; @@ -770,32 +767,35 @@ dkim_signature_parse_h(struct signature *sig, const ch break; } if ((sig->h[n++] = strndup(start, h - start)) == NULL) { - dkim_err(sig->header->msg, "malloc"); - return; + osmtpd_warn(sig->header->msg->ctx, "malloc"); + return -1; } start = osmtpd_ltok_skip_fws(h, 1); if (start[0] != ':') break; start = osmtpd_ltok_skip_fws(start + 1, 1); } + return 0; } -void +int dkim_signature_parse_i(struct signature *sig, const char *start, const char *end) { if (sig->i != NULL) { dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate i tag"); - return; + return 0; } if (osmtpd_ltok_skip_sig_i_tag_value(start, 0) != end) { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid i tag"); - return; + return 0; } sig->i = start; sig->isz = (size_t)(end - start); + + return 0; } -void +int dkim_signature_parse_l(struct signature *sig, const char *start, const char *end) { long long l; @@ -803,7 +803,7 @@ dkim_signature_parse_l(struct signature *sig, const ch if (sig->l != -1) { /* Duplicate tag */ dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate l tag"); - return; + return 0; } errno = 0; l = strtoll(start, &lend, 10); @@ -811,23 +811,25 @@ dkim_signature_parse_l(struct signature *sig, const ch if (osmtpd_ltok_skip_digit(start, 0) == NULL || lend != end || errno != 0) { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid l tag"); - return; + return 0; } if (l > SSIZE_MAX) { dkim_signature_state(sig, DKIM_PERMERROR, "l tag too large"); - return; + return 0; } sig->l = (ssize_t)l; + + return 0; } -void +int dkim_signature_parse_q(struct signature *sig, const char *start, const char *end) { const char *qend; if (sig->q != 0) { dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate q tag"); - return; + return 0; } while (1) { @@ -835,7 +837,7 @@ dkim_signature_parse_q(struct signature *sig, const ch qend = osmtpd_ltok_skip_sig_q_tag_method(start, 0); if (qend == NULL) { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid q tag"); - return; + return 0; } if (strncmp(start, "dns/txt", qend - start) == 0) sig->q = 1; @@ -845,81 +847,95 @@ dkim_signature_parse_q(struct signature *sig, const ch } if (start != end) { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid q tag"); - return; + return 0; } if (sig->q != 1) { sig->q = 1; dkim_signature_state(sig, DKIM_NEUTRAL, "No useable q found"); - return; + return 0; } + + return 0; } -void +int dkim_signature_parse_s(struct signature *sig, const char *start, const char *end) { if (sig->s[0] != '\0') { dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate s tag"); - return; + return 0; } if (osmtpd_ltok_skip_selector(start, 0) != end) { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid s tag"); - return; + return 0; } - strlcpy(sig->s, start, end - start + 1); + if (sizeof(sig->s) >= (size_t)(end - start)) { + dkim_signature_state(sig, DKIM_PERMERROR, "s tag too long"); + return 0; + } + strncpy(sig->s, start, end - start); + + return 0; } -void +int dkim_signature_parse_t(struct signature *sig, const char *start, const char *end) { char *tend; if (sig->t != -1) { dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate t tag"); - return; + return 0; } errno = 0; sig->t = strtoll(start, &tend, 10); if (osmtpd_ltok_skip_digit(start, 0) == NULL || tend != end || tend - start > 12 || errno != 0) { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid t tag"); - return; + return 0; } + + return 0; } -void +int dkim_signature_parse_x(struct signature *sig, const char *start, const char *end) { char *xend; if (sig->x != -1) { dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate x tag"); - return; + return 0; } errno = 0; sig->x = strtoll(start, &xend, 10); if (osmtpd_ltok_skip_digit(start, 0) == NULL || xend != end || xend - start > 12 || errno != 0) { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid x tag"); - return; + return 0; } + + return 0; } -void +int dkim_signature_parse_z(struct signature *sig, const char *start, const char *end) { if (sig->z != 0) { dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate z tag"); - return; + return 0; } sig->z = 1; if (osmtpd_ltok_skip_sig_z_tag_value(start, 0) != end) { dkim_signature_state(sig, DKIM_PERMERROR, "Invalid z tag"); - return; + return 0; } + + return 0; } -void +int dkim_signature_verify(struct signature *sig) { struct message *msg = sig->header->msg; @@ -930,25 +946,25 @@ dkim_signature_verify(struct signature *sig) size_t i, header; if (sig->state != DKIM_UNKNOWN) - return; + return 0; if (bctx == NULL) { if ((bctx = EVP_MD_CTX_new()) == NULL) { - dkim_errx(msg, "EVP_MD_CTX_new"); - return; + osmtpd_warnx(msg->ctx, "EVP_MD_CTX_new"); + return -1; } } EVP_MD_CTX_reset(bctx); if (!sig->sephash) { if (EVP_DigestVerifyInit(bctx, NULL, sig->ah, NULL, sig->p) != 1) { - dkim_errx(msg, "EVP_DigestVerifyInit"); - return; + osmtpd_warnx(msg->ctx, "EVP_DigestVerifyInit"); + return -1; } } else { if (EVP_DigestInit_ex(bctx, sig->ah, NULL) != 1) { - dkim_errx(msg, "EVP_DigestInit_ex"); - return; + osmtpd_warnx(msg->ctx, "EVP_DigestInit_ex"); + return -1; } } @@ -967,22 +983,25 @@ dkim_signature_verify(struct signature *sig) msg->header[i].buf + strlen(sig->h[header]), 1); if (end[0] != ':') continue; - dkim_signature_header(bctx, sig, &(msg->header[i])); + if (dkim_signature_header(bctx, sig, + &(msg->header[i])) == -1) + return -1; msg->header[i].parsed = 1; } } - dkim_signature_header(bctx, sig, sig->header); + if (dkim_signature_header(bctx, sig, sig->header) == -1) + return -1; if (!sig->sephash) { if (EVP_DigestVerifyFinal(bctx, sig->b, sig->bsz) != 1) dkim_signature_state(sig, DKIM_FAIL, "b mismatch"); } else { if (EVP_DigestFinal_ex(bctx, digest, &digestsz) == 0) { - dkim_errx(msg, "EVP_DigestFinal_ex"); - return; + osmtpd_warnx(msg->ctx, "EVP_DigestFinal_ex"); + return -1; } if (EVP_DigestVerifyInit(bctx, NULL, NULL, NULL, sig->p) != 1) { - dkim_errx(msg, "EVP_DigestVerifyInit"); - return; + osmtpd_warnx(msg->ctx, "EVP_DigestVerifyInit"); + return -1; } switch (EVP_DigestVerify(bctx, sig->b, sig->bsz, digest, digestsz)) { @@ -992,10 +1011,11 @@ dkim_signature_verify(struct signature *sig) dkim_signature_state(sig, DKIM_FAIL, "b mismatch"); break; default: - dkim_errx(msg, "EVP_DigestVerify"); - return; + osmtpd_warnx(msg->ctx, "EVP_DigestVerify"); + return -1; } } + return 0; } /* EVP_DigestVerifyUpdate is a macro, so we can't alias this on a variable */ @@ -1003,7 +1023,7 @@ dkim_signature_verify(struct signature *sig) (sig->sephash ? EVP_DigestUpdate((a), (b), (c)) :\ EVP_DigestVerifyUpdate((a), (b), (c))) -void +int dkim_signature_header(EVP_MD_CTX *bctx, struct signature *sig, struct header *header) { @@ -1026,9 +1046,9 @@ dkim_signature_header(EVP_MD_CTX *bctx, struct signatu ptr + 1, 1) - 1; } if (dkim_b_digest_update(bctx, &c, 1) == 0) { - dkim_errx(sig->header->msg, + osmtpd_warnx(sig->header->msg->ctx, "dkim_b_digest_update"); - return; + return -1; } continue; } @@ -1040,25 +1060,25 @@ dkim_signature_header(EVP_MD_CTX *bctx, struct signatu continue; } if (dkim_b_digest_update(bctx, ptr, 1) == 0) { - dkim_errx(sig->header->msg, + osmtpd_warnx(sig->header->msg->ctx, "dkim_b_digest_update"); - return; + return -1; } } else { if (canon == CANON_HEADER_RELAXED) { if (end[0] == '\0') continue; if (dkim_b_digest_update(bctx, " ", 1) == 0) { - dkim_errx(sig->header->msg, + osmtpd_warnx(sig->header->msg->ctx, "dkim_b_digest_update"); - return; + return -1; } } else { if (dkim_b_digest_update(bctx, ptr, end - ptr) == 0) { - dkim_errx(sig->header->msg, + osmtpd_warnx(sig->header->msg->ctx, "dkim_b_digest_update"); - return; + return -1; } } ptr = end - 1; @@ -1067,10 +1087,13 @@ dkim_signature_header(EVP_MD_CTX *bctx, struct signatu } if (sig->header != header) { if (dkim_b_digest_update(bctx, "\r\n", 2) == 0) { - dkim_errx(sig->header->msg, "dkim_b_digest_update"); - return; + osmtpd_warnx(sig->header->msg->ctx, + "dkim_b_digest_update"); + return -1; } } + + return 0; } void @@ -1194,19 +1217,23 @@ dkim_rr_resolve(struct asr_result *ar, void *arg) dkim_signature_state(sig, DKIM_PERMERROR, "No matching key found"); } else { + if (sig->p == NULL) + osmtpd_errx(1, "%s:%d TODO", __func__, __LINE__); /* Only verify if all headers have been read */ if (!sig->header->msg->parsing_headers) - dkim_signature_verify(sig); + if (dkim_signature_verify(sig) == -1) + osmtpd_errx(1, "%s:%d TODO", __func__, __LINE__); } verify: free(ar->ar_data); - dkim_message_verify(sig->header->msg); + if (dkim_message_verify(sig->header->msg) == -1) + osmtpd_errx(1, "%s:%d TODO", __func__, __LINE__); } int dkim_key_text_parse(struct signature *sig, const char *key) { - char tagname, *hashname; + char tagname, hashname[100]; const char *end, *tagvend; char pkraw[UINT16_MAX] = "", pkimp[UINT16_MAX]; size_t pkrawlen = 0, pkoff, linelen; @@ -1260,17 +1287,13 @@ dkim_key_text_parse(struct signature *sig, const char if ((tagvend = osmtpd_ltok_skip_key_h_tag_alg( key, 0)) == NULL) break; - hashname = strndup(key, tagvend - key); - if (hashname == NULL) { - dkim_err(sig->header->msg, "malloc"); + if (sizeof(hashname) <= (size_t)(tagvend - key)) return 0; - } + strncpy(hashname, key, tagvend - key); if (EVP_get_digestbyname(hashname) == sig->ah) { - free(hashname); h = 1; break; } - free(hashname); key = osmtpd_ltok_skip_fws(tagvend, 1); if (key[0] != ':') break; @@ -1422,7 +1445,7 @@ dkim_key_text_parse(struct signature *sig, const char pkoff += strlcpy(pkimp + pkoff, "-----END PUBLIC KEY-----\n", sizeof(pkimp) - pkoff); if ((bio = BIO_new_mem_buf(pkimp, pkoff)) == NULL) { - dkim_err(sig->header->msg, "BIO_new_mem_buf"); + osmtpd_warnx(sig->header->msg->ctx, "BIO_new_mem_buf"); return 1; } sig->p = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); @@ -1455,7 +1478,7 @@ dkim_key_text_parse(struct signature *sig, const char return 1; } -void +int dkim_body_parse(struct message *msg, const char *line) { struct signature *sig; @@ -1465,7 +1488,7 @@ dkim_body_parse(struct message *msg, const char *line) if (line[0] == '\0') { msg->body_whitelines++; - return; + return 0; } while (msg->body_whitelines-- > 0) { @@ -1476,8 +1499,8 @@ dkim_body_parse(struct message *msg, const char *line) hashn = sig->l == -1 ? 2 : MIN(2, sig->l); sig->l -= sig->l == -1 ? 0 : hashn; if (EVP_DigestUpdate(sig->bhctx, "\r\n", hashn) == 0) { - dkim_errx(msg, "EVP_DigestUpdate"); - return; + osmtpd_warnx(msg->ctx, "EVP_DigestUpdate"); + return -1; } } } @@ -1513,8 +1536,8 @@ dkim_body_parse(struct message *msg, const char *line) sig->l -= sig->l == -1 ? 0 : hashn; ret = EVP_DigestUpdate(sig->bhctx, hash, hashn); if (ret == 0) { - dkim_errx(msg, "EVP_DigestUpdate"); - return; + osmtpd_warnx(msg->ctx, "EVP_DigestUpdate"); + return -1; } } line = end; @@ -1527,46 +1550,50 @@ dkim_body_parse(struct message *msg, const char *line) sig->l -= sig->l == -1 ? 0 : hashn; ret = EVP_DigestUpdate(sig->bhctx, "\r\n", hashn); if (ret == 0) { - dkim_errx(msg, "EVP_DigestUpdate"); - return; + osmtpd_warnx(msg->ctx, "EVP_DigestUpdate"); + return -1; } } + + return 0; } -void +int dkim_body_verify(struct signature *sig) { unsigned char digest[EVP_MAX_MD_SIZE]; unsigned int digestsz; if (sig->state != DKIM_UNKNOWN) - return; + return 0; if ((sig->c & CANON_BODY) == CANON_BODY_SIMPLE && !sig->header->msg->has_body) { if (EVP_DigestUpdate(sig->bhctx, "\r\n", sig->l == -1 ? 2 : MIN(2, sig->l)) <= 0) { - dkim_errx(sig->header->msg, + osmtpd_warnx(sig->header->msg->ctx, "Can't update hash context"); - return; + return -1; } } if (sig->l > 0) { dkim_signature_state(sig, DKIM_PERMERROR, "l tag larger than body"); - return; + return 0; } if (EVP_DigestFinal_ex(sig->bhctx, digest, &digestsz) == 0) { - dkim_errx(sig->header->msg, "EVP_DigestFinal_ex"); - return; + osmtpd_warnx(sig->header->msg->ctx, "EVP_DigestFinal_ex"); + return -1; } if (digestsz != sig->bhsz || memcmp(digest, sig->bh, digestsz) != 0) dkim_signature_state(sig, DKIM_FAIL, "bh mismatch"); + + return 0; } -void +int dkim_message_verify(struct message *msg) { struct signature *sig; @@ -1575,15 +1602,16 @@ dkim_message_verify(struct message *msg) int found = 0; char *line = NULL; size_t linelen = 0; + int r = -1; if (!msg->readdone) - return; + return 0; for (i = 0; i < msg->nheaders; i++) { if (msg->header[i].sig == NULL) continue; if (msg->header[i].sig->query != NULL) - return; + return 0; if (msg->header[i].sig->state != DKIM_UNKNOWN) continue; dkim_signature_state(msg->header[i].sig, DKIM_PASS, NULL); @@ -1591,7 +1619,7 @@ dkim_message_verify(struct message *msg) if ((aroff = dkim_ar_cat(&line, &linelen, aroff, "Authentication-Results: %s", authservid)) == -1) { - dkim_err(msg, "malloc"); + osmtpd_warn(msg->ctx, "malloc"); goto fail; } for (i = 0; i < msg->nheaders; i++) { @@ -1601,27 +1629,27 @@ dkim_message_verify(struct message *msg) found = 1; if ((aroff = dkim_ar_cat(&line, &linelen, aroff, "; dkim=%s", dkim_state2str(sig->state))) == -1) { - dkim_err(msg, "malloc"); + osmtpd_warn(msg->ctx, "malloc"); goto fail; } if (sig->state_reason != NULL) { if ((aroff = dkim_ar_cat(&line, &linelen, aroff, " reason=\"%s\"", sig->state_reason)) == -1) { - dkim_err(msg, "malloc"); + osmtpd_warn(msg->ctx, "malloc"); goto fail; } } if (sig->s[0] != '\0') { if ((aroff = dkim_ar_cat(&line, &linelen, aroff, " header.s=%s", sig->s)) == -1) { - dkim_err(msg, "malloc"); + osmtpd_warn(msg->ctx, "malloc"); goto fail; } } if (sig->d[0] != '\0') { if ((aroff = dkim_ar_cat(&line, &linelen, aroff, " header.d=%s", sig->d)) == -1) { - dkim_err(msg, "malloc"); + osmtpd_warn(msg->ctx, "malloc"); goto fail; } } @@ -1632,7 +1660,7 @@ dkim_message_verify(struct message *msg) if (sig->a != NULL) { if ((aroff = dkim_ar_cat(&line, &linelen, aroff, " header.a=%.*s", (int)sig->asz, sig->a)) == -1) { - dkim_err(msg, "malloc"); + osmtpd_warn(msg->ctx, "malloc"); goto fail; } } @@ -1640,7 +1668,7 @@ dkim_message_verify(struct message *msg) if (!found) { aroff = dkim_ar_cat(&line, &linelen, aroff, "; dkim=none"); if (aroff == -1) { - dkim_err(msg, "malloc"); + osmtpd_warn(msg->ctx, "malloc"); goto fail; } } @@ -1651,11 +1679,14 @@ dkim_message_verify(struct message *msg) line[n - 1] = '\0'; osmtpd_filter_dataline(msg->ctx, "%s", line); } - if (ferror(msg->origf)) - dkim_err(msg, "getline"); + if (ferror(msg->origf)) { + osmtpd_warnx(msg->ctx, "getline"); + goto fail; + } + r = 0; fail: free(line); - return; + return r; } void @@ -1737,20 +1768,6 @@ dkim_ar_cat(char **ar, size_t *n, size_t aroff, const return (ssize_t)size + aroff; } -void -dkim_err(struct message *msg, char *text) -{ - msg->err = 1; - fprintf(stderr, "%s: %s\n", text, strerror(errno)); -} - -void -dkim_errx(struct message *msg, char *text) -{ - msg->err = 1; - fprintf(stderr, "%s\n", text); -} - __dead void usage(void) {