Blob


1 /*
2 * Copyright (c) 2022 Martijn van Duren <martijn@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16 #include <sys/types.h>
17 #include <sys/socket.h>
19 #include <openssl/evp.h>
20 #include <openssl/pem.h>
21 #include <openssl/sha.h>
22 #include <openssl/err.h>
24 #include <arpa/nameser.h>
26 #include <assert.h>
27 #include <ctype.h>
28 #include <errno.h>
29 #include <event.h>
30 #include <limits.h>
31 #include <netdb.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <time.h>
37 #include <unistd.h>
38 #include <asr.h>
40 #include "opensmtpd.h"
41 #include "unpack_dns.h"
42 #include "mheader.h"
44 /*
45 * Use RFC8601 (Authentication-Results) codes instead of RFC6376 codes,
46 * since they're more expressive.
47 */
48 enum state {
49 DKIM_UNKNOWN,
50 DKIM_PASS,
51 DKIM_FAIL,
52 DKIM_POLICY,
53 DKIM_NEUTRAL,
54 DKIM_TEMPERROR,
55 DKIM_PERMERROR
56 };
58 struct signature {
59 struct header *header;
60 enum state state;
61 const char *state_reason;
62 int v;
63 char *a;
64 size_t asz;
65 int ak;
66 const EVP_MD *ah;
67 char *b;
68 size_t bsz;
69 char *bheader;
70 /* Make sure padding bits for base64 decoding fit */
71 char bh[EVP_MAX_MD_SIZE + (3 - (EVP_MAX_MD_SIZE % 3))];
72 size_t bhsz;
73 EVP_MD_CTX *bhctx;
74 int c;
75 #define CANON_HEADER_SIMPLE 0
76 #define CANON_HEADER_RELAXED 1
77 #define CANON_HEADER 1
78 #define CANON_BODY_SIMPLE 0
79 #define CANON_BODY_RELAXED 1 << 1
80 #define CANON_BODY 1 << 1
81 #define CANON_DONE 1 << 2
82 char d[HOST_NAME_MAX + 1];
83 char **h;
84 char *i;
85 size_t isz;
86 ssize_t l;
87 int q;
88 char s[HOST_NAME_MAX + 1];
89 time_t t; /* Signature t=/timestamp */
90 #define KT_Y 1
91 #define KT_S 1 << 1
92 int kt; /* Key t=/Flags */
93 time_t x;
94 int z;
95 struct event_asr *query;
96 EVP_PKEY *p;
97 };
99 struct header {
100 struct message *msg;
101 uint8_t readdone;
102 uint8_t parsed;
103 char *buf;
104 size_t buflen;
105 struct signature *sig;
106 };
108 #define AUTHENTICATION_RESULTS_LINELEN 78
109 #define MIN(a, b) ((a) < (b) ? (a) : (b))
111 struct message {
112 struct osmtpd_ctx *ctx;
113 FILE *origf;
114 int parsing_headers;
115 size_t body_whitelines;
116 int has_body;
117 struct header *header;
118 size_t nheaders;
119 int err;
120 int readdone;
121 };
123 void usage(void);
124 void dkim_err(struct message *, char *);
125 void dkim_errx(struct message *, char *);
126 void dkim_conf(const char *, const char *);
127 void dkim_dataline(struct osmtpd_ctx *, const char *);
128 void dkim_commit(struct osmtpd_ctx *);
129 void *dkim_message_new(struct osmtpd_ctx *);
130 void dkim_message_free(struct osmtpd_ctx *, void *);
131 void dkim_header_add(struct osmtpd_ctx *, const char *);
132 void dkim_signature_parse(struct header *);
133 void dkim_signature_parse_v(struct signature *, char *, char *);
134 void dkim_signature_parse_a(struct signature *, char *, char *);
135 void dkim_signature_parse_b(struct signature *, char *, char *);
136 void dkim_signature_parse_bh(struct signature *, char *, char *);
137 void dkim_signature_parse_c(struct signature *, char *, char *);
138 void dkim_signature_parse_d(struct signature *, char *, char *);
139 void dkim_signature_parse_h(struct signature *, char *, char *);
140 void dkim_signature_parse_i(struct signature *, char *, char *);
141 void dkim_signature_parse_l(struct signature *, char *, char *);
142 void dkim_signature_parse_q(struct signature *, char *, char *);
143 void dkim_signature_parse_s(struct signature *, char *, char *);
144 void dkim_signature_parse_t(struct signature *, char *, char *);
145 void dkim_signature_parse_x(struct signature *, char *, char *);
146 void dkim_signature_parse_z(struct signature *, char *, char *);
147 void dkim_signature_verify(struct signature *);
148 void dkim_signature_header(EVP_MD_CTX *, struct signature *, struct header *);
149 void dkim_signature_state(struct signature *, enum state, const char *);
150 const char *dkim_state2str(enum state);
151 void dkim_header_cat(struct osmtpd_ctx *, const char *);
152 void dkim_body_parse(struct message *, const char *);
153 void dkim_body_verify(struct signature *);
154 void dkim_rr_resolve(struct asr_result *, void *);
155 void dkim_message_verify(struct message *);
156 ssize_t dkim_ar_cat(char **ar, size_t *n, size_t aroff, const char *fmt, ...)
157 __attribute__((__format__ (printf, 4, 5)));
158 void dkim_ar_print(struct osmtpd_ctx *, char *);
159 int dkim_key_text_parse(struct signature *, char *);
161 char *authservid;
162 EVP_ENCODE_CTX *ectx = NULL;
164 int
165 main(int argc, char *argv[])
167 if (argc != 1)
168 osmtpd_errx(1, "Invalid argument count");
170 OpenSSL_add_all_digests();
172 if (pledge("tmppath stdio dns", NULL) == -1)
173 osmtpd_err(1, "pledge");
175 if ((ectx = EVP_ENCODE_CTX_new()) == NULL)
176 osmtpd_err(1, "EVP_ENCODE_CTX_new");
178 osmtpd_register_conf(dkim_conf);
179 osmtpd_register_filter_dataline(dkim_dataline);
180 osmtpd_register_filter_commit(dkim_commit);
181 osmtpd_local_message(dkim_message_new, dkim_message_free);
182 osmtpd_run();
184 return 0;
187 void
188 dkim_conf(const char *key, const char *value)
190 char *end;
192 if (key == NULL) {
193 if (authservid == NULL)
194 osmtpd_errx(1, "Didn't receive admd config option");
195 return;
197 if (strcmp(key, "admd") == 0 && authservid == NULL) {
198 if ((authservid = strdup(value)) == NULL)
199 osmtpd_err(1, "malloc");
200 end = osmtpd_mheader_skip_value(authservid, 0);
201 if (authservid + strlen(authservid) != end)
202 osmtpd_errx(1, "Invalid authservid");
203 osmtpd_mheader_quoted_string_normalize(authservid);
207 void
208 dkim_dataline(struct osmtpd_ctx *ctx, const char *line)
210 struct message *msg = ctx->local_message;
211 size_t i;
213 if (msg->err) {
214 if (line[0] == '.' && line[1] =='\0') {
215 msg->readdone = 1;
216 osmtpd_filter_dataline(ctx, ".");
218 return;
221 if (fprintf(msg->origf, "%s\n", line) < 0) {
222 dkim_err(msg, "Couldn't write to tempfile");
223 return;
225 if (line[0] == '.') {
226 line++;
227 if (line[0] == '\0') {
228 msg->readdone = 1;
229 for (i = 0; i < msg->nheaders; i++) {
230 if (msg->header[i].sig == NULL)
231 continue;
232 dkim_body_verify(msg->header[i].sig);
234 dkim_message_verify(msg);
235 return;
238 if (msg->parsing_headers) {
239 dkim_header_add(ctx, line);
240 if (line[0] == '\0') {
241 msg->parsing_headers = 0;
242 for (i = 0; i < msg->nheaders; i++) {
243 if (msg->header[i].sig == NULL)
244 continue;
245 if (msg->header[i].sig->query == NULL)
246 dkim_signature_verify(
247 msg->header[i].sig);
250 return;
251 } else {
252 dkim_body_parse(msg, line);
256 void
257 dkim_commit(struct osmtpd_ctx *ctx)
259 struct message *msg = ctx->local_message;
261 if (msg->err)
262 osmtpd_filter_disconnect(ctx, "Internal server error");
263 else
264 osmtpd_filter_proceed(ctx);
267 void *
268 dkim_message_new(struct osmtpd_ctx *ctx)
270 struct message *msg;
272 if ((msg = malloc(sizeof(*msg))) == NULL)
273 osmtpd_err(1, NULL);
275 if ((msg->origf = tmpfile()) == NULL) {
276 dkim_err(msg, "Can't open tempfile");
277 return NULL;
279 msg->ctx = ctx;
280 msg->parsing_headers = 1;
281 msg->body_whitelines = 0;
282 msg->has_body = 0;
283 msg->header = NULL;
284 msg->nheaders = 0;
285 msg->err = 0;
286 msg->readdone = 0;
288 return msg;
291 void
292 dkim_message_free(struct osmtpd_ctx *ctx, void *data)
294 struct message *msg = data;
295 size_t i, j;
297 fclose(msg->origf);
298 for (i = 0; i < msg->nheaders; i++) {
299 if (msg->header[i].sig != NULL) {
300 free(msg->header[i].sig->b);
301 EVP_MD_CTX_free(msg->header[i].sig->bhctx);
302 for (j = 0; msg->header[i].sig->h != NULL &&
303 msg->header[i].sig->h[j] != NULL; j++)
304 free(msg->header[i].sig->h[j]);
305 free(msg->header[i].sig->h);
307 free(msg->header[i].sig);
309 free(msg->header);
310 free(msg);
313 void
314 dkim_header_add(struct osmtpd_ctx *ctx, const char *line)
316 struct message *msg = ctx->local_message;
317 char *start, *end, *verify;
318 struct header *headers;
319 size_t i;
321 if (msg->nheaders > 0 &&
322 msg->header[msg->nheaders - 1].readdone == 0) {
323 if (line[0] != ' ' && line[0] != '\t') {
324 msg->header[msg->nheaders - 1].readdone = 1;
325 start = msg->header[msg->nheaders - 1].buf;
326 end = osmtpd_mheader_skip_fieldname(start, 0);
327 /* In case someone uses an obs-optional */
328 verify = osmtpd_mheader_skip_wsp(end, 1);
329 if (strncasecmp(
330 start, "DKIM-Signature", end - start) == 0 &&
331 verify[0] == ':')
332 dkim_signature_parse(
333 &msg->header[msg->nheaders - 1]);
334 if (line[0] == '\0')
335 return;
336 } else {
337 dkim_header_cat(ctx, line);
338 return;
341 if (msg->nheaders % 10 == 0) {
342 if ((headers = recallocarray(msg->header, msg->nheaders,
343 msg->nheaders + 10, sizeof(*msg->header))) == NULL) {
344 dkim_err(msg, "malloc");
345 return;
347 msg->header = headers;
348 for (i = 0; i < msg->nheaders; i++) {
349 if (msg->header[i].sig == NULL)
350 continue;
351 msg->header[i].sig->header = &msg->header[i];
354 msg->header[msg->nheaders].msg = msg;
355 msg->nheaders++;
356 dkim_header_cat(ctx, line);
359 void
360 dkim_header_cat(struct osmtpd_ctx *ctx, const char *line)
362 struct message *msg = ctx->local_message;
363 struct header *header = &msg->header[msg->nheaders - 1];
364 char *buf;
366 size_t needed = header->buflen + strlen(line) + 2;
368 if (needed > (header->buflen / 1024) + 1) {
369 buf = reallocarray(header->buf, (needed / 1024) + 1, 1024);
370 if (buf == NULL) {
371 dkim_err(msg, "malloc");
372 return;
374 header->buf = buf;
376 header->buflen += snprintf(header->buf + header->buflen,
377 (((needed / 1024) + 1) * 1024) - header->buflen, "%s%s",
378 header->buflen == 0 ? "" : "\r\n", line);
381 void
382 dkim_signature_parse(struct header *header)
384 char *buf;
385 char *end, tagname[3];
386 char subdomain[HOST_NAME_MAX + 1];
387 struct signature *sig;
388 struct asr_query *query;
390 /* Format checked by dkim_header_add */
391 buf = osmtpd_mheader_skip_fieldname(header->buf, 0);
392 buf = osmtpd_mheader_skip_wsp(buf, 1) + 1;
394 if ((header->sig = calloc(1, sizeof(*header->sig))) == NULL) {
395 dkim_err(header->msg, "malloc");
396 return;
398 sig = header->sig;
399 sig->header = header;
400 sig->l = -1;
401 sig->t = -1;
402 sig->x = -1;
404 end = osmtpd_mheader_skip_dkimsig_taglist(buf, 0);
405 if (end == NULL || end[0] != '\0') {
406 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid taglist");
407 return;
410 while (buf[0] != '\0') {
411 buf = osmtpd_mheader_skip_fws(buf, 1);
412 end = osmtpd_mheader_skip_dkimsig_tagname(buf, 0);
414 /* Unknown tag-name */
415 if ((size_t)(end - buf) >= sizeof(tagname))
416 tagname[0] = '\0';
417 else
418 strlcpy(tagname, buf, (end - buf) + 1);
419 buf = osmtpd_mheader_skip_fws(end, 1);
420 /* '=' */
421 buf = osmtpd_mheader_skip_fws(buf + 1, 1);
422 end = osmtpd_mheader_skip_dkimsig_tagvalue(buf, 1);
423 if (strcmp(tagname, "v") == 0)
424 dkim_signature_parse_v(sig, buf, end);
425 else if (strcmp(tagname, "a") == 0)
426 dkim_signature_parse_a(sig, buf, end);
427 else if (strcmp(tagname, "b") == 0)
428 dkim_signature_parse_b(sig, buf, end);
429 else if (strcmp(tagname, "bh") == 0)
430 dkim_signature_parse_bh(sig, buf, end);
431 else if (strcmp(tagname, "c") == 0)
432 dkim_signature_parse_c(sig, buf, end);
433 else if (strcmp(tagname, "d") == 0)
434 dkim_signature_parse_d(sig, buf, end);
435 else if (strcmp(tagname, "h") == 0)
436 dkim_signature_parse_h(sig, buf, end);
437 else if (strcmp(tagname, "i") == 0)
438 dkim_signature_parse_i(sig, buf, end);
439 else if (strcmp(tagname, "l") == 0)
440 dkim_signature_parse_l(sig, buf, end);
441 else if (strcmp(tagname, "q") == 0)
442 dkim_signature_parse_q(sig, buf, end);
443 else if (strcmp(tagname, "s") == 0)
444 dkim_signature_parse_s(sig, buf, end);
445 else if (strcmp(tagname, "t") == 0)
446 dkim_signature_parse_t(sig, buf, end);
447 else if (strcmp(tagname, "x") == 0)
448 dkim_signature_parse_x(sig, buf, end);
449 else if (strcmp(tagname, "z") == 0)
450 dkim_signature_parse_z(sig, buf, end);
452 buf = osmtpd_mheader_skip_fws(end, 1);
453 if (buf[0] == ';')
454 buf++;
455 else if (buf[0] != '\0') {
456 dkim_signature_state(sig, DKIM_PERMERROR,
457 "Invalid taglist");
458 return;
461 if (sig->state != DKIM_UNKNOWN)
462 return;
464 if (sig->v != 1)
465 dkim_signature_state(sig, DKIM_PERMERROR, "Missing v tag");
466 else if (sig->ah == NULL)
467 dkim_signature_state(sig, DKIM_PERMERROR, "Missing a tag");
468 else if (sig->b == NULL)
469 dkim_signature_state(sig, DKIM_PERMERROR, "Missing b tag");
470 else if (sig->bhsz == 0)
471 dkim_signature_state(sig, DKIM_PERMERROR, "Missing bh tag");
472 else if (sig->d[0] == '\0')
473 dkim_signature_state(sig, DKIM_PERMERROR, "Missing d tag");
474 else if (sig->h == NULL)
475 dkim_signature_state(sig, DKIM_PERMERROR, "Missing h tag");
476 else if (sig->s[0] == '\0')
477 dkim_signature_state(sig, DKIM_PERMERROR, "Missing s tag");
479 if ((size_t)snprintf(subdomain, sizeof(subdomain), "%s._domainkey.%s",
480 sig->s, sig->d) >= sizeof(subdomain)) {
481 dkim_signature_state(sig, DKIM_PERMERROR,
482 "dns/txt query too long");
483 return;
486 if ((query = res_query_async(subdomain, C_IN, T_TXT, NULL)) == NULL) {
487 dkim_err(header->msg, "res_query_async");
488 return;
490 if ((sig->query = event_asr_run(query, dkim_rr_resolve, sig)) == NULL) {
491 dkim_err(header->msg, "event_asr_run");
492 asr_abort(query);
493 return;
497 void
498 dkim_signature_parse_v(struct signature *sig, char *start, char *end)
500 if (sig->v != 0) { /* Duplicate tag */
501 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate v tag");
502 return;
504 /* Unsupported version */
505 if (start[0] != '1' || start + 1 != end)
506 dkim_signature_state(sig, DKIM_NEUTRAL, "Unsupported v tag");
507 else
508 sig->v = 1;
511 void
512 dkim_signature_parse_a(struct signature *sig, char *start, char *end)
514 char ah[sizeof("sha256")];
516 if (sig->ah != NULL) {
517 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate a tag");
518 return;
521 if (osmtpd_mheader_skip_dkimsig_sigatagalg(start, 0) != end) {
522 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid a tag");
523 return;
525 sig->a = start;
526 sig->asz = (size_t)(end - start);
527 if (strncmp(start, "rsa-", 4) == 0) {
528 start += 4;
529 sig->ak = EVP_PKEY_RSA;
530 } else {
531 dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag k");
532 return;
534 if ((size_t)(end - start) >= sizeof(ah)) {
535 dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag h");
536 return;
538 strlcpy(ah, start, sizeof(ah));
539 ah[end - start] = '\0';
540 if ((sig->ah = EVP_get_digestbyname(ah)) == NULL) {
541 dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag h");
542 return;
544 if ((sig->bhctx = EVP_MD_CTX_new()) == NULL) {
545 dkim_err(sig->header->msg, "EVP_MD_CTX_new");
546 return;
548 if (EVP_DigestInit_ex(sig->bhctx, sig->ah, NULL) <= 0) {
549 dkim_err(sig->header->msg, "EVP_DigestInit_ex");
550 return;
554 void
555 dkim_signature_parse_b(struct signature *sig, char *start, char *end)
557 int decodesz;
559 if (sig->b != NULL) {
560 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate b tag");
561 return;
563 sig->bheader = start;
564 if ((sig->b = malloc((((end - start) / 4) + 1) * 3)) == NULL) {
565 dkim_err(sig->header->msg, "malloc");
566 return;
568 /* EVP_DecodeBlock doesn't handle internal whitespace */
569 EVP_DecodeInit(ectx);
570 if (EVP_DecodeUpdate(ectx, sig->b, &decodesz, start,
571 (int)(end - start)) == -1) {
572 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid b tag");
573 return;
575 sig->bsz = decodesz;
576 if (EVP_DecodeFinal(ectx, sig->b + sig->bsz,
577 &decodesz) == -1) {
578 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid b tag");
579 return;
581 sig->bsz += decodesz;
584 void
585 dkim_signature_parse_bh(struct signature *sig, char *start, char *end)
587 char *b64;
588 size_t n;
589 int decodesz;
591 if (sig->bhsz != 0) {
592 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate bh tag");
593 return;
595 /*
596 * EVP_Decode* expects sig->bh to be large enough,
597 * so count the actual b64 characters.
598 */
599 b64 = start;
600 n = 0;
601 while (1) {
602 b64 = osmtpd_mheader_skip_fws(b64, 1);
603 if (osmtpd_mheader_skip_alphadigitps(b64, 0) == NULL)
604 break;
605 n++;
606 b64++;
608 if (b64[0] == '=') {
609 n++;
610 b64 = osmtpd_mheader_skip_fws(b64 + 1, 1);
611 if (b64[0] == '=') {
612 n++;
613 b64++;
616 /* Invalid tag value */
617 if (b64 != end || n % 4 != 0 || (n / 4) * 3 > sizeof(sig->bh)) {
618 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
619 return;
621 /* EVP_DecodeBlock doesn't handle internal whitespace */
622 EVP_DecodeInit(ectx);
623 if (EVP_DecodeUpdate(ectx, sig->bh, &decodesz, start,
624 (int)(end - start)) == -1) {
625 /* Paranoia check */
626 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
627 return;
629 sig->bhsz = decodesz;
630 if (EVP_DecodeFinal(ectx, sig->bh + sig->bhsz, &decodesz) == -1) {
631 /* Paranoia check */
632 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
633 return;
635 sig->bhsz += decodesz;
638 void
639 dkim_signature_parse_c(struct signature *sig, char *start, char *end)
641 if (sig->c != 0) {
642 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate c tag");
643 return;
645 if (strncmp(start, "simple", 6) == 0) {
646 sig->c = CANON_HEADER_SIMPLE;
647 start += 6;
648 } else if (strncmp(start, "relaxed", 7) == 0) {
649 sig->c = CANON_HEADER_RELAXED;
650 start += 7;
651 } else {
652 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid c tag");
653 return;
655 if (start[0] == '/') {
656 start++;
657 if (strncmp(start, "simple", 6) == 0) {
658 sig->c |= CANON_BODY_SIMPLE;
659 start += 6;
660 } else if (strncmp(start, "relaxed", 7) == 0) {
661 sig->c |= CANON_BODY_RELAXED;
662 start += 7;
663 } else {
664 dkim_signature_state(sig, DKIM_PERMERROR,
665 "Invalid c tag");
666 return;
670 if (start != end) {
671 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid c tag");
672 return;
674 sig->c |= CANON_DONE;
677 void
678 dkim_signature_parse_d(struct signature *sig, char *start, char *end)
680 if (sig->d[0] != '\0') {
681 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate d tag");
682 return;
684 if (osmtpd_mheader_skip_domain(start, 0) != end ||
685 (size_t)(end - start) >= sizeof(sig->d)) {
686 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid d tag");
687 return;
689 strlcpy(sig->d, start, end - start + 1);
692 void
693 dkim_signature_parse_h(struct signature *sig, char *start, char *end)
695 char *h;
696 size_t n = 0;
698 if (sig->h != NULL) {
699 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate h tag");
700 return;
702 h = start;
703 while (1) {
704 if ((h = osmtpd_mheader_skip_hdrname(h, 0)) == NULL) {
705 dkim_signature_state(sig, DKIM_PERMERROR,
706 "Invalid h tag");
707 return;
709 n++;
710 /* ';' is part of hdr-name */
711 if (h > end) {
712 h = end;
713 break;
715 h = osmtpd_mheader_skip_fws(h, 1);
716 if (h[0] != ':')
717 break;
718 h = osmtpd_mheader_skip_fws(h + 1, 1);
720 if (h != end) {
721 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid h tag");
722 return;
724 if ((sig->h = calloc(n + 1, sizeof(*sig->h))) == NULL) {
725 dkim_err(sig->header->msg, "malloc");
726 return;
728 n = 0;
729 h = start;
730 while (1) {
731 h = osmtpd_mheader_skip_hdrname(start, 0);
732 /* ';' is part of hdr-name */
733 if (h > end) {
734 sig->h[n] = strndup(start, end - start);
735 break;
737 if ((sig->h[n++] = strndup(start, h - start)) == NULL) {
738 dkim_err(sig->header->msg, "malloc");
739 return;
741 start = osmtpd_mheader_skip_fws(h, 1);
742 if (start[0] != ':')
743 break;
744 start = osmtpd_mheader_skip_fws(start + 1, 1);
748 void
749 dkim_signature_parse_i(struct signature *sig, char *start, char *end)
751 char *i;
753 if (sig->i != NULL) {
754 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate i tag");
755 return;
757 i = osmtpd_mheader_skip_local_part(start, 1);
758 if (i[0] != '@') {
759 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid i tag");
760 return;
762 if (osmtpd_mheader_skip_domain(i + 1, 0) != end) {
763 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid i tag");
764 return;
766 sig->i = start;
767 sig->isz = (size_t)(end - start);
770 void
771 dkim_signature_parse_l(struct signature *sig, char *start, char *end)
773 long long l;
774 char *lend;
776 if (sig->l != -1) { /* Duplicate tag */
777 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate l tag");
778 return;
780 errno = 0;
781 l = strtoll(start, &lend, 10);
782 /* > 76 digits in stroll is an overflow */
783 if (osmtpd_mheader_skip_digit(start, 0) == NULL ||
784 lend != end || errno != 0) {
785 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid l tag");
786 return;
788 if (l > SSIZE_MAX) {
789 dkim_signature_state(sig, DKIM_PERMERROR, "l tag too large");
790 return;
792 sig->l = (ssize_t)l;
795 void
796 dkim_signature_parse_q(struct signature *sig, char *start, char *end)
798 char *qend;
800 if (sig->q != 0) {
801 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate q tag");
802 return;
805 while (1) {
806 start = osmtpd_mheader_skip_fws(start, 1);
807 qend = osmtpd_mheader_skip_dkimsig_sigqtagmethod(start, 0);
808 if (qend == NULL) {
809 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid q tag");
810 return;
812 if (strncmp(start, "dns/txt", qend - start) == 0)
813 sig->q = 1;
814 start = osmtpd_mheader_skip_fws(qend, 1);
815 if (start[0] != ':')
816 break;
818 if (start != end) {
819 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid q tag");
820 return;
822 if (sig->q != 1) {
823 sig->q = 1;
824 dkim_signature_state(sig, DKIM_NEUTRAL, "No useable q found");
825 return;
829 void
830 dkim_signature_parse_s(struct signature *sig, char *start, char *end)
832 if (sig->s[0] != '\0') {
833 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate s tag");
834 return;
836 if (osmtpd_mheader_skip_dkimsig_selector(start, 0) != end) {
837 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid s tag");
838 return;
840 strlcpy(sig->s, start, end - start + 1);
843 void
844 dkim_signature_parse_t(struct signature *sig, char *start, char *end)
846 char *tend;
848 if (sig->t != -1) {
849 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate t tag");
850 return;
852 errno = 0;
853 sig->t = strtoll(start, &tend, 10);
854 if (osmtpd_mheader_skip_digit(start, 0) == NULL || tend != end ||
855 tend - start > 12 || errno != 0) {
856 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid t tag");
857 return;
861 void
862 dkim_signature_parse_x(struct signature *sig, char *start, char *end)
864 char *xend;
866 if (sig->x != -1) {
867 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate x tag");
868 return;
870 errno = 0;
871 sig->x = strtoll(start, &xend, 10);
872 if (osmtpd_mheader_skip_digit(start, 0) == NULL || xend != end ||
873 xend - start > 12 || errno != 0) {
874 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid x tag");
875 return;
879 void
880 dkim_signature_parse_z(struct signature *sig, char *start, char *end)
882 if (sig->z != 0) {
883 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate z tag");
884 return;
887 sig->z = 1;
888 if (osmtpd_mheader_skip_dkimsig_sigztagvalue(start, 0) != end) {
889 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid z tag");
890 return;
894 void
895 dkim_signature_verify(struct signature *sig)
897 struct message *msg = sig->header->msg;
898 static EVP_MD_CTX *bctx = NULL;
899 char *end;
900 size_t i, header;
902 if (sig->state != DKIM_UNKNOWN)
903 return;
905 if (bctx == NULL) {
906 if ((bctx = EVP_MD_CTX_new()) == NULL) {
907 dkim_errx(msg, "EVP_MD_CTX_new");
908 return;
911 if (EVP_DigestVerifyInit(bctx, NULL, sig->ah, NULL, sig->p) != 1) {
912 dkim_errx(msg, "EVP_DigestVerifyInit");
913 return;
916 for (i = 0; i < msg->nheaders; i++)
917 msg->header[i].parsed = 0;
919 for (header = 0; sig->h[header] != NULL; header++) {
920 for (i = msg->nheaders; i > 0; ) {
921 i--;
922 if (msg->header[i].parsed ||
923 strncasecmp(msg->header[i].buf, sig->h[header],
924 strlen(sig->h[header])) != 0 ||
925 msg->header[i].sig == sig)
926 continue;
927 end = osmtpd_mheader_skip_fws(
928 msg->header[i].buf + strlen(sig->h[header]), 1);
929 if (end[0] != ':')
930 continue;
931 dkim_signature_header(bctx, sig, &(msg->header[i]));
932 msg->header[i].parsed = 1;
935 dkim_signature_header(bctx, sig, sig->header);
936 if (EVP_DigestVerifyFinal(bctx, sig->b, sig->bsz) != 1)
937 dkim_signature_state(sig, DKIM_FAIL, "b mismatch");
940 void
941 dkim_signature_header(EVP_MD_CTX *bctx, struct signature *sig,
942 struct header *header)
944 char c, *ptr = header->buf, *end;
945 int inhdrname = 1;
946 int canon = sig->c & CANON_HEADER;
948 for (ptr = header->buf; ptr[0] != '\0'; ptr++) {
949 if (inhdrname) {
950 if (canon == CANON_HEADER_RELAXED) {
951 ptr = osmtpd_mheader_skip_fws(ptr, 1);
952 c = tolower(ptr[0]);
953 } else
954 c = ptr[0];
955 if (c == ':') {
956 inhdrname = 0;
957 if (canon == CANON_HEADER_RELAXED)
958 ptr = osmtpd_mheader_skip_fws(
959 ptr + 1, 1) - 1;
961 if (EVP_DigestVerifyUpdate(bctx, &c, 1) == 0) {
962 dkim_errx(sig->header->msg,
963 "EVP_DigestVerifyUpdate");
964 return;
966 continue;
968 end = osmtpd_mheader_skip_fws(ptr, 1);
969 if (end == ptr) {
970 if (sig->header == header && ptr == sig->bheader) {
971 ptr = osmtpd_mheader_skip_dkimsig_tagvalue(
972 ptr, 0) - 1;
973 continue;
975 if (EVP_DigestVerifyUpdate(bctx, ptr, 1) == 0) {
976 dkim_errx(sig->header->msg,
977 "EVP_DigestVerifyUpdate");
978 return;
980 } else {
981 if (canon == CANON_HEADER_RELAXED) {
982 if (end[0] == '\0')
983 continue;
984 if (EVP_DigestVerifyUpdate(bctx, " ", 1) == 0) {
985 dkim_errx(sig->header->msg,
986 "EVP_DigestVerifyUpdate");
987 return;
989 } else {
990 if (EVP_DigestVerifyUpdate(bctx, ptr,
991 end - ptr) == 0) {
992 dkim_errx(sig->header->msg,
993 "EVP_DigestVerifyUpdate");
994 return;
997 ptr = end - 1;
1001 if (sig->header != header) {
1002 if (EVP_DigestVerifyUpdate(bctx, "\r\n", 2) == 0) {
1003 dkim_errx(sig->header->msg, "EVP_DigestVerifyUpdate");
1004 return;
1009 void
1010 dkim_signature_state(struct signature *sig, enum state state,
1011 const char *reason)
1013 if (sig->query != NULL) {
1014 event_asr_abort(sig->query);
1015 sig->query = NULL;
1017 switch (sig->state) {
1018 case DKIM_UNKNOWN:
1019 break;
1020 case DKIM_PASS:
1021 case DKIM_FAIL:
1022 osmtpd_errx(1, "Unexpected transition");
1023 case DKIM_POLICY:
1024 if (state == DKIM_PASS)
1025 return;
1026 break;
1027 case DKIM_NEUTRAL:
1028 if (state == DKIM_PASS)
1029 return;
1030 if (state == DKIM_TEMPERROR || state == DKIM_PERMERROR)
1031 break;
1032 osmtpd_errx(1, "Unexpected transition");
1033 case DKIM_TEMPERROR:
1034 if (state == DKIM_PERMERROR)
1035 break;
1036 return;
1037 case DKIM_PERMERROR:
1038 return;
1040 sig->state = state;
1041 sig->state_reason = reason;
1044 const char *
1045 dkim_state2str(enum state state)
1047 switch (state)
1049 case DKIM_UNKNOWN:
1050 return "unknown";
1051 case DKIM_PASS:
1052 return "pass";
1053 case DKIM_FAIL:
1054 return "fail";
1055 case DKIM_POLICY:
1056 return "policy";
1057 case DKIM_NEUTRAL:
1058 return "neutral";
1059 case DKIM_TEMPERROR:
1060 return "temperror";
1061 case DKIM_PERMERROR:
1062 return "permerror";
1066 void
1067 dkim_rr_resolve(struct asr_result *ar, void *arg)
1069 struct signature *sig = arg;
1070 char key[UINT16_MAX + 1];
1071 const char *rr_txt;
1072 size_t keylen, cstrlen;
1073 struct unpack pack;
1074 struct dns_header h;
1075 struct dns_query q;
1076 struct dns_rr rr;
1078 sig->query = NULL;
1080 if (ar->ar_h_errno == TRY_AGAIN || ar->ar_h_errno == NO_RECOVERY) {
1081 dkim_signature_state(sig, DKIM_TEMPERROR,
1082 hstrerror(ar->ar_h_errno));
1083 return;
1085 if (ar->ar_h_errno != NETDB_SUCCESS) {
1086 dkim_signature_state(sig, DKIM_PERMERROR,
1087 hstrerror(ar->ar_h_errno));
1088 return;
1091 unpack_init(&pack, ar->ar_data, ar->ar_datalen);
1092 if (unpack_header(&pack, &h) != 0 ||
1093 unpack_query(&pack, &q) != 0) {
1094 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid dns/txt");
1095 return;
1097 for (; h.ancount > 0; h.ancount--) {
1098 unpack_rr(&pack, &rr);
1099 if (rr.rr_type != T_TXT)
1100 continue;
1102 keylen = 0;
1103 rr_txt = rr.rr.other.rdata;
1104 while (rr.rr.other.rdlen > 0) {
1105 cstrlen = ((const unsigned char *)rr_txt)[0];
1106 if (cstrlen >= rr.rr.other.rdlen ||
1107 keylen + cstrlen >= sizeof(key))
1108 break;
1110 * RFC 6376 Section 3.6.2.2
1111 * Strings in a TXT RR MUST be concatenated together
1112 * before use with no intervening whitespace.
1114 strlcpy(key + keylen, rr_txt + 1, cstrlen + 1);
1115 rr.rr.other.rdlen -= (cstrlen + 1);
1116 rr_txt += (cstrlen + 1);
1117 keylen += cstrlen;
1119 if (rr.rr.other.rdlen > 0) /* Invalid TXT RDATA */
1120 continue;
1122 if (dkim_key_text_parse(sig, key))
1123 break;
1126 if (h.ancount == 0) {
1127 dkim_signature_state(sig, DKIM_PERMERROR,
1128 "No matching key found");
1129 } else {
1130 /* Only verify if all headers have been read */
1131 if (!sig->header->msg->parsing_headers)
1132 dkim_signature_verify(sig);
1134 dkim_message_verify(sig->header->msg);
1137 int
1138 dkim_key_text_parse(struct signature *sig, char *key)
1140 char *end, *tagvend, tagname;
1141 char tmp, pkraw[UINT16_MAX] = "", pkimp[UINT16_MAX];
1142 size_t pkoff, linelen;
1143 int h = 0, k = 0, n = 0, p = 0, s = 0, t = 0;
1144 BIO *bio;
1146 key = osmtpd_mheader_skip_fws(key, 1);
1147 /* Validate syntax early */
1148 if ((end = osmtpd_mheader_skip_dkimsig_taglist(key, 0)) == NULL)
1149 return 0;
1152 * RFC 6376 section 3.6.1, v=:
1153 * RECOMMENDED...This tag MUST be the first tag in the record.
1155 if (osmtpd_mheader_skip_dkimsig_tagname(key, 0) - key == 1 &&
1156 key[0] == 'v') {
1157 key = osmtpd_mheader_skip_dkimsig_tagname(key, 0);
1158 key = osmtpd_mheader_skip_fws(key, 1);
1159 key++; /* = */
1160 key = osmtpd_mheader_skip_fws(key, 1);
1161 end = osmtpd_mheader_skip_dkimsig_tagvalue(key, 0);
1162 if (end - key != 5 || strncmp(key, "DKIM1", 5) != 0)
1163 return 0;
1164 key += 5;
1165 key = osmtpd_mheader_skip_fws(key, 1);
1166 if (key[0] == ';')
1167 key++;
1169 while (key[0] != '\0') {
1170 key = osmtpd_mheader_skip_fws(key, 1);
1171 end = osmtpd_mheader_skip_dkimsig_tagname(key, 0);
1173 if ((size_t)(end - key) != 1) {
1174 key = osmtpd_mheader_skip_fws(end, 1);
1175 /* '=' */
1176 key++;
1177 key = osmtpd_mheader_skip_fws(key, 1);
1178 key = osmtpd_mheader_skip_dkimsig_tagvalue(key, 0);
1179 key = osmtpd_mheader_skip_fws(key, 1);
1180 if (key[0] == ';')
1181 key++;
1182 continue;
1184 tagname = key[0];
1185 key = osmtpd_mheader_skip_fws(end, 1);
1186 /* '=' */
1187 key++;
1188 key = osmtpd_mheader_skip_fws(key, 1);
1189 end = osmtpd_mheader_skip_dkimsig_tagvalue(key, 0);
1190 switch (tagname) {
1191 case 'v':
1192 /* tagname in wrong position */
1193 return 0;
1194 case 'h':
1195 if (h != 0) /* Duplicate tag */
1196 return 0;
1197 /* Invalid tag value */
1198 if (osmtpd_mheader_skip_dkimsig_keyhtagvalue(
1199 key, 0) != end)
1200 return 0;
1201 while (1) {
1202 if ((tagvend =
1203 osmtpd_mheader_skip_dkimsig_keyhtagvalue(
1204 key, 0)) == NULL)
1205 break;
1206 tmp = tagvend[0];
1207 tagvend[0] = '\0';
1208 if (EVP_get_digestbyname(key) == sig->ah) {
1209 tagvend[0] = tmp;
1210 h = 1;
1211 break;
1213 tagvend[0] = tmp;
1214 key = osmtpd_mheader_skip_fws(tagvend, 1);
1215 if (key[0] != ':')
1216 break;
1217 key = osmtpd_mheader_skip_fws(key + 1, 1);
1219 if (h != 1)
1220 return 0;
1221 key = end;
1222 break;
1223 case 'k':
1224 if (k != 0) /* Duplicate tag */
1225 return 0;
1226 k = 1;
1227 if (strncmp(key, "rsa", end - key) != 0)
1228 return 0;
1229 key = end;
1230 break;
1231 case 'n':
1232 if (n != 0) /* Duplicate tag */
1233 return 0;
1234 n = 1;
1235 key = end;
1236 break;
1237 case 'p':
1238 if (p != 0) /* Duplicate tag */
1239 return 0;
1240 p = 1;
1241 tagvend = osmtpd_mheader_skip_base64string(key, 1);
1242 /* Invalid tag value */
1243 if (tagvend != end ||
1244 (size_t)(end - key) >= sizeof(pkraw))
1245 return 0;
1246 strlcpy(pkraw, key, tagvend - key + 1);
1247 key = end;
1248 break;
1249 case 's':
1250 if (s != 0) /* Duplicate tag */
1251 return 0;
1252 /* Invalid tag value */
1253 if (osmtpd_mheader_skip_dkimsig_keystagvalue(key, 0) !=
1254 end)
1255 return 0;
1256 while (1) {
1257 if ((tagvend =
1258 osmtpd_mheader_skip_dkimsig_keystagtype(
1259 key, 0)) != NULL)
1260 break;
1261 if (strncmp(key, "*", tagvend - key) == 0 ||
1262 strncmp(key, "email", tagvend - key) == 0) {
1263 s = 1;
1264 break;
1266 key = osmtpd_mheader_skip_fws(tagvend, 1);
1267 if (key[0] != ':')
1268 break;
1269 key = osmtpd_mheader_skip_fws(key + 1, 1);
1271 if (s != 1)
1272 return 0;
1273 key = end;
1274 break;
1275 case 't':
1276 if (t != 0) /* Duplicate tag */
1277 return 0;
1278 t = 1;
1279 if (osmtpd_mheader_skip_dkimsig_keystagtype(key, 0) !=
1280 end)
1281 return 0;
1282 while (1) {
1283 tagvend =
1284 osmtpd_mheader_skip_dkimsig_keyttagflag(
1285 key, 0);
1286 if (strncmp(key, "y", tagvend - key) == 0)
1287 sig->kt |= KT_Y;
1288 else if (strncmp(key, "s", tagvend - key) == 0)
1289 sig->kt |= KT_S;
1290 key = osmtpd_mheader_skip_fws(tagvend, 1);
1291 if (key[0] != ':')
1292 break;
1293 key = osmtpd_mheader_skip_fws(key + 1, 1);
1295 break;
1296 default:
1297 key = end;
1298 break;
1301 key = osmtpd_mheader_skip_fws(key, 1);
1302 if (key[0] == ';')
1303 key++;
1304 else if (key[0] != '\0')
1305 return 0;
1308 if (p == 0) /* Missing tag */
1309 return 0;
1310 if (k == 0 && sig->ak != EVP_PKEY_RSA) /* Default to RSA */
1311 return 0;
1313 if (pkraw[0] == '\0') {
1314 dkim_signature_state(sig, DKIM_PERMERROR, "Key is revoked");
1315 return 1;
1318 switch (sig->ak) {
1319 case EVP_PKEY_RSA:
1320 pkoff = strlcpy(pkimp, "-----BEGIN PUBLIC KEY-----\n",
1321 sizeof(pkimp));
1322 linelen = 0;
1323 for (key = pkraw; key[0] != '\0';) {
1324 if (pkoff + 2 >= sizeof(pkimp))
1325 return 0;
1326 pkimp[pkoff++] = key[0];
1327 if (++linelen == 64) {
1328 pkimp[pkoff++] = '\n';
1329 linelen = 0;
1331 key = osmtpd_mheader_skip_fws(key + 1, 1);
1333 /* Leverage pkoff check in loop */
1334 if (linelen != 0)
1335 pkimp[pkoff++] = '\n';
1336 /* PEM_read_bio_PUBKEY will catch truncated keys */
1337 pkoff += strlcpy(pkimp + pkoff, "-----END PUBLIC KEY-----\n",
1338 sizeof(pkimp) - pkoff);
1339 if ((bio = BIO_new_mem_buf(pkimp, pkoff)) == NULL) {
1340 dkim_err(sig->header->msg, "BIO_new_mem_buf");
1341 return 1;
1343 sig->p = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
1344 BIO_free(bio);
1345 if (sig->p == NULL) {
1347 * XXX No clue how to differentiate between invalid key
1348 * and temporary failure like *alloc.
1349 * Assume invalid key, because it's more likely.
1351 return 0;
1353 break;
1355 return 1;
1358 void
1359 dkim_body_parse(struct message *msg, const char *line)
1361 char buf[999]; /* Line limit + 1 */
1362 struct signature *sig;
1363 size_t r, w;
1364 ssize_t linelen;
1365 size_t hashn, hashlf;
1366 size_t i;
1367 int canon = CANON_BODY_SIMPLE;
1369 linelen = (ssize_t)strlcpy(buf, line, sizeof(buf));
1370 if (linelen >= (ssize_t)sizeof(buf)) {
1371 dkim_errx(msg, "Line too long");
1372 return;
1374 if (buf[0] == '\0') {
1375 msg->body_whitelines++;
1376 return;
1379 while (msg->body_whitelines-- > 0) {
1380 for (i = 0; i < msg->nheaders; i++) {
1381 if ((sig = msg->header[i].sig) == NULL)
1382 continue;
1383 hashlf = sig->l == -1 ? 2 : MIN(2, sig->l);
1384 sig->l -= sig->l == -1 ? 0 : hashlf;
1385 if (EVP_DigestUpdate(sig->bhctx, "\r\n", hashlf) == 0) {
1386 dkim_errx(msg, "Can't update hash context");
1387 return;
1391 msg->body_whitelines = 0;
1392 msg->has_body = 1;
1394 hash:
1395 for (i = 0; i < msg->nheaders; i++) {
1396 sig = msg->header[i].sig;
1397 if (sig == NULL || ((sig->c & CANON_BODY) != canon) ||
1398 sig->state != DKIM_UNKNOWN)
1399 continue;
1400 hashn = sig->l == -1 ? linelen : MIN(linelen, sig->l);
1401 sig->l -= sig->l == -1 ? 0 : hashn;
1402 hashlf = sig->l == -1 ? 2 : MIN(2, sig->l);
1403 sig->l -= sig->l == -1 ? 0 : hashlf;
1404 if (EVP_DigestUpdate(sig->bhctx, buf, hashn) == 0 ||
1405 EVP_DigestUpdate(sig->bhctx, "\r\n", hashlf) == 0) {
1406 dkim_errx(msg, "Can't update hash context");
1407 return;
1410 if (canon == CANON_BODY_RELAXED)
1411 return;
1412 canon = CANON_BODY_RELAXED;
1413 for (r = w = 0; buf[r] != '\0'; r++) {
1414 if (buf[r] == ' ' || buf[r] == '\t') {
1415 if (r != 0 && buf[w - 1] == ' ')
1416 continue;
1417 else
1418 buf[w++] = ' ';
1419 } else
1420 buf[w++] = buf[r];
1422 linelen = (w != 0 && buf[w - 1] == ' ') ? w - 1 : w;
1423 buf[linelen] = '\0';
1425 goto hash;
1428 void
1429 dkim_body_verify(struct signature *sig)
1431 unsigned char digest[EVP_MAX_MD_SIZE];
1432 unsigned int digestsz;
1434 if (sig->state != DKIM_UNKNOWN)
1435 return;
1437 if ((sig->c & CANON_BODY) == CANON_BODY_SIMPLE &&
1438 !sig->header->msg->has_body) {
1439 if (EVP_DigestUpdate(sig->bhctx, "\r\n",
1440 sig->l == -1 ? 2 : MIN(2, sig->l)) <= 0) {
1441 dkim_errx(sig->header->msg,
1442 "Can't update hash context");
1443 return;
1447 if (EVP_DigestFinal_ex(sig->bhctx, digest, &digestsz) == 0) {
1448 dkim_errx(sig->header->msg, "Can't finalize hash context");
1449 return;
1452 if (digestsz != sig->bhsz || memcmp(digest, sig->bh, digestsz) != 0)
1453 dkim_signature_state(sig, DKIM_FAIL, "bh mismatch");
1456 void
1457 dkim_message_verify(struct message *msg)
1459 struct signature *sig;
1460 size_t i;
1461 ssize_t n, aroff = 0;
1462 int found = 0;
1463 char *line = NULL;
1464 size_t linelen = 0;
1466 if (!msg->readdone)
1467 return;
1469 for (i = 0; i < msg->nheaders; i++) {
1470 if (msg->header[i].sig == NULL)
1471 continue;
1472 if (msg->header[i].sig->query != NULL)
1473 return;
1474 if (msg->header[i].sig->state != DKIM_UNKNOWN)
1475 continue;
1476 dkim_signature_state(msg->header[i].sig, DKIM_PASS, NULL);
1479 if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1480 "Authentication-Results: %s", authservid)) == -1) {
1481 dkim_err(msg, "malloc");
1482 goto fail;
1484 for (i = 0; i < msg->nheaders; i++) {
1485 sig = msg->header[i].sig;
1486 if (sig == NULL)
1487 continue;
1488 found = 1;
1489 if ((aroff = dkim_ar_cat(&line, &linelen, aroff, "; dkim=%s",
1490 dkim_state2str(sig->state))) == -1) {
1491 dkim_err(msg, "malloc");
1492 goto fail;
1494 if (sig->state_reason != NULL) {
1495 if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1496 " reason=\"%s\"", sig->state_reason)) == -1) {
1497 dkim_err(msg, "malloc");
1498 goto fail;
1501 if (sig->s[0] != '\0') {
1502 if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1503 " header.s=%s", sig->s)) == -1) {
1504 dkim_err(msg, "malloc");
1505 goto fail;
1508 if (sig->d[0] != '\0') {
1509 if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1510 " header.d=%s", sig->d)) == -1) {
1511 dkim_err(msg, "malloc");
1512 goto fail;
1516 * Don't print i-tag, since localpart can be a quoted-string,
1517 * which can contain FWS and CFWS.
1519 if (sig->a != NULL) {
1520 if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1521 " header.a=%.*s", (int)sig->asz, sig->a)) == -1) {
1522 dkim_err(msg, "malloc");
1523 goto fail;
1527 if (!found) {
1528 aroff = dkim_ar_cat(&line, &linelen, aroff, "; dkim=none");
1529 if (aroff == -1) {
1530 dkim_err(msg, "malloc");
1531 goto fail;
1534 dkim_ar_print(msg->ctx, line);
1536 rewind(msg->origf);
1537 while ((n = getline(&line, &linelen, msg->origf)) != -1) {
1538 line[n - 1] = '\0';
1539 osmtpd_filter_dataline(msg->ctx, "%s", line);
1541 if (ferror(msg->origf))
1542 dkim_err(msg, "getline");
1543 fail:
1544 free(line);
1545 return;
1548 void
1549 dkim_ar_print(struct osmtpd_ctx *ctx, char *start)
1551 char *scan, *checkpoint, *ncheckpoint;
1552 size_t arlen = 0;
1553 int first = 1, arid = 1;
1555 checkpoint = start;
1556 ncheckpoint = osmtpd_mheader_skip_hdrname(start, 0) + 1;
1557 for (scan = start; scan[0] != '\0'; scan++) {
1558 if (scan[0] == '\t')
1559 arlen = (arlen + 8) & ~7;
1560 else
1561 arlen++;
1562 if (arlen >= AUTHENTICATION_RESULTS_LINELEN) {
1563 osmtpd_filter_dataline(ctx, "%s%.*s", first ? "" : "\t",
1564 (int)((checkpoint == start ?
1565 ncheckpoint : checkpoint) - start), start);
1566 start = osmtpd_mheader_skip_cfws(checkpoint, 1);
1567 scan = start;
1568 arlen = 8;
1569 first = 0;
1571 if (scan == ncheckpoint) {
1572 checkpoint = ncheckpoint;
1573 ncheckpoint = osmtpd_mheader_skip_cfws(ncheckpoint, 1);
1574 /* authserv-id */
1575 if (arid) {
1576 ncheckpoint = osmtpd_mheader_skip_value(
1577 ncheckpoint, 0);
1578 arid = 0;
1579 /* methodspec */
1580 } else if (strncmp(ncheckpoint, "dkim",
1581 sizeof("dkim") - 1) == 0) {
1582 ncheckpoint = osmtpd_mheader_skip_keyword(
1583 ncheckpoint + sizeof("dkim"), 0);
1584 /* reasonspec */
1585 } else if (strncmp(ncheckpoint, "reason",
1586 sizeof("reason") - 1) == 0) {
1587 ncheckpoint = osmtpd_mheader_skip_value(
1588 ncheckpoint + sizeof("reason"), 0);
1589 /* propspec */
1590 } else {
1591 ncheckpoint += sizeof("header.x=") - 1;
1592 ncheckpoint = osmtpd_mheader_skip_ar_pvalue(
1593 ncheckpoint, 0);
1594 if (ncheckpoint[0] == ';')
1595 ncheckpoint++;
1599 osmtpd_filter_dataline(ctx, "%s%s", first ? "" : "\t", start);
1602 ssize_t
1603 dkim_ar_cat(char **ar, size_t *n, size_t aroff, const char *fmt, ...)
1605 va_list ap;
1606 char *artmp;
1607 int size;
1608 size_t nn;
1610 assert(*n >= aroff);
1611 va_start(ap, fmt);
1612 size = vsnprintf(*ar + aroff, *n - aroff, fmt, ap);
1613 va_end(ap);
1614 if (size + aroff <= *n)
1615 return (ssize_t)size + aroff;
1616 nn = (((aroff + size) / 256) + 1) * 256;
1617 artmp = realloc(*ar, nn);
1618 if (artmp == NULL)
1619 return -1;
1620 *ar = artmp;
1621 *n = nn;
1622 va_start(ap, fmt);
1623 size = vsnprintf(*ar + aroff, *n - aroff, fmt, ap);
1624 va_end(ap);
1625 return (ssize_t)size + aroff;
1628 void
1629 dkim_err(struct message *msg, char *text)
1631 msg->err = 1;
1632 fprintf(stderr, "%s: %s\n", text, strerror(errno));
1635 void
1636 dkim_errx(struct message *msg, char *text)
1638 msg->err = 1;
1639 fprintf(stderr, "%s\n", text);
1642 __dead void
1643 usage(void)
1645 fprintf(stderr, "usage: filter-dkimverify\n");
1646 exit(1);