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 <ctype.h>
27 #include <errno.h>
28 #include <event.h>
29 #include <limits.h>
30 #include <netdb.h>
31 #include <opensmtpd.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 "unpack_dns.h"
41 #include "ltok.h"
43 /*
44 * Use RFC8601 (Authentication-Results) codes instead of RFC6376 codes,
45 * since they're more expressive.
46 */
47 enum state {
48 DKIM_UNKNOWN,
49 DKIM_PASS,
50 DKIM_FAIL,
51 DKIM_POLICY,
52 DKIM_NEUTRAL,
53 DKIM_TEMPERROR,
54 DKIM_PERMERROR
55 };
57 struct signature {
58 struct header *header;
59 enum state state;
60 const char *state_reason;
61 int v;
62 const char *a;
63 size_t asz;
64 int ak;
65 int sephash;
66 const EVP_MD *ah;
67 char *b;
68 size_t bsz;
69 const char *bheader;
70 size_t bheadersz;
71 #define HEADER_B_MAX_LEN 8
72 char bheaderclean[HEADER_B_MAX_LEN + 1];
73 /* Make sure padding bits for base64 decoding fit */
74 char bh[EVP_MAX_MD_SIZE + (3 - (EVP_MAX_MD_SIZE % 3))];
75 size_t bhsz;
76 EVP_MD_CTX *bhctx;
77 int c;
78 #define CANON_HEADER_SIMPLE 0
79 #define CANON_HEADER_RELAXED 1
80 #define CANON_HEADER 1
81 #define CANON_BODY_SIMPLE 0
82 #define CANON_BODY_RELAXED 1 << 1
83 #define CANON_BODY 1 << 1
84 #define CANON_DONE 1 << 2
85 char d[HOST_NAME_MAX + 1];
86 char **h;
87 const char *i;
88 size_t isz;
89 ssize_t l;
90 int q;
91 char s[HOST_NAME_MAX + 1];
92 time_t t; /* Signature t=/timestamp */
93 #define KT_Y 1
94 #define KT_S 1 << 1
95 int kt; /* Key t=/Flags */
96 time_t x;
97 int z;
98 struct event_asr *query;
99 EVP_PKEY *p;
100 /* RFC 6376 doesn't care about CNAME, use simalr with SPF limit */
101 #define DKIM_LOOKUP_LOOKUP_LIMIT 11
102 int nqueries;
103 };
105 struct header {
106 struct message *msg;
107 uint8_t readdone;
108 uint8_t parsed;
109 char *buf;
110 size_t buflen;
111 struct signature *sig;
112 };
114 #define AUTHENTICATION_RESULTS_LINELEN 78
115 #define MIN(a, b) ((a) < (b) ? (a) : (b))
117 struct message {
118 struct osmtpd_ctx *ctx;
119 FILE *origf;
120 int parsing_headers;
121 size_t body_whitelines;
122 int has_body;
123 struct header *header;
124 size_t nheaders;
125 int readdone;
126 };
128 void usage(void);
129 void dkim_conf(const char *, const char *);
130 void dkim_dataline(struct osmtpd_ctx *, const char *);
131 void *dkim_message_new(struct osmtpd_ctx *);
132 void dkim_message_free(struct osmtpd_ctx *, void *);
133 void dkim_header_add(struct osmtpd_ctx *, const char *);
134 void dkim_signature_parse(struct header *);
135 void dkim_signature_parse_v(struct signature *, const char *, const char *);
136 void dkim_signature_parse_a(struct signature *, const char *, const char *);
137 void dkim_signature_parse_b(struct signature *, const char *, const char *);
138 void dkim_signature_parse_bh(struct signature *, const char *, const char *);
139 void dkim_signature_parse_c(struct signature *, const char *, const char *);
140 void dkim_signature_parse_d(struct signature *, const char *, const char *);
141 void dkim_signature_parse_h(struct signature *, const char *, const char *);
142 void dkim_signature_parse_i(struct signature *, const char *, const char *);
143 void dkim_signature_parse_l(struct signature *, const char *, const char *);
144 void dkim_signature_parse_q(struct signature *, const char *, const char *);
145 void dkim_signature_parse_s(struct signature *, const char *, const char *);
146 void dkim_signature_parse_t(struct signature *, const char *, const char *);
147 void dkim_signature_parse_x(struct signature *, const char *, const char *);
148 void dkim_signature_parse_z(struct signature *, const char *, const char *);
149 void dkim_lookup_record(struct signature *sig, const char *domain);
150 void dkim_signature_verify(struct signature *);
151 void dkim_signature_header(EVP_MD_CTX *, struct signature *, struct header *);
152 void dkim_signature_state(struct signature *, enum state, const char *);
153 const char *dkim_state2str(enum state);
154 void dkim_header_cat(struct osmtpd_ctx *, const char *);
155 void dkim_body_parse(struct message *, const char *);
156 void dkim_body_verify(struct signature *);
157 void dkim_rr_resolve(struct asr_result *, void *);
158 void dkim_message_verify(struct message *);
159 ssize_t dkim_ar_cat(char **ar, size_t *n, size_t aroff, const char *fmt, ...)
160 __attribute__((__format__ (printf, 4, 5)));
161 int dkim_ar_print(struct osmtpd_ctx *, const char *);
162 int dkim_key_text_parse(struct signature *, const char *);
164 char *authservid;
165 EVP_ENCODE_CTX *ectx = NULL;
167 int
168 main(int argc, char *argv[])
170 if (argc != 1)
171 osmtpd_errx(1, "Invalid argument count");
173 OpenSSL_add_all_digests();
175 if (pledge("tmppath stdio dns", NULL) == -1)
176 osmtpd_err(1, "pledge");
178 if ((ectx = EVP_ENCODE_CTX_new()) == NULL)
179 osmtpd_err(1, "EVP_ENCODE_CTX_new");
181 osmtpd_register_conf(dkim_conf);
182 osmtpd_register_filter_dataline(dkim_dataline);
183 osmtpd_local_message(dkim_message_new, dkim_message_free);
184 osmtpd_run();
186 return 0;
189 void
190 dkim_conf(const char *key, const char *value)
192 const char *end;
194 if (key == NULL) {
195 if (authservid == NULL)
196 osmtpd_errx(1, "Didn't receive admd config option");
197 return;
199 if (strcmp(key, "admd") == 0 && authservid == NULL) {
200 if ((authservid = strdup(value)) == NULL)
201 osmtpd_err(1, "%s: malloc", __func__);
202 end = osmtpd_ltok_skip_value(authservid, 0);
203 if (authservid + strlen(authservid) != end)
204 osmtpd_errx(1, "Invalid authservid");
208 void
209 dkim_dataline(struct osmtpd_ctx *ctx, const char *line)
211 struct message *msg = ctx->local_message;
212 size_t i;
214 if (fprintf(msg->origf, "%s\n", line) < 0)
215 osmtpd_err(1, "Couldn't write to tempfile");
217 if (line[0] == '.') {
218 line++;
219 if (line[0] == '\0') {
220 msg->readdone = 1;
221 for (i = 0; i < msg->nheaders; i++) {
222 if (msg->header[i].sig == NULL)
223 continue;
224 dkim_body_verify(msg->header[i].sig);
226 dkim_message_verify(msg);
227 return;
230 if (msg->parsing_headers) {
231 dkim_header_add(ctx, line);
232 if (line[0] == '\0') {
233 msg->parsing_headers = 0;
234 for (i = 0; i < msg->nheaders; i++) {
235 if (msg->header[i].sig == NULL)
236 continue;
237 if (msg->header[i].sig->query == NULL)
238 dkim_signature_verify(
239 msg->header[i].sig);
242 return;
243 } else {
244 dkim_body_parse(msg, line);
248 void *
249 dkim_message_new(struct osmtpd_ctx *ctx)
251 struct message *msg;
253 if ((msg = malloc(sizeof(*msg))) == NULL)
254 osmtpd_err(1, "%s: malloc", __func__);
256 if ((msg->origf = tmpfile()) == NULL) {
257 osmtpd_warn(NULL, "Can't open tempfile");
258 free(msg);
259 return NULL;
261 msg->ctx = ctx;
262 msg->parsing_headers = 1;
263 msg->body_whitelines = 0;
264 msg->has_body = 0;
265 msg->header = NULL;
266 msg->nheaders = 0;
267 msg->readdone = 0;
269 return msg;
272 void
273 dkim_message_free(struct osmtpd_ctx *ctx, void *data)
275 struct message *msg = data;
276 size_t i, j;
278 fclose(msg->origf);
279 for (i = 0; i < msg->nheaders; i++) {
280 if (msg->header[i].sig != NULL) {
281 free(msg->header[i].sig->b);
282 EVP_MD_CTX_free(msg->header[i].sig->bhctx);
283 for (j = 0; msg->header[i].sig->h != NULL &&
284 msg->header[i].sig->h[j] != NULL; j++)
285 free(msg->header[i].sig->h[j]);
286 free(msg->header[i].sig->h);
287 EVP_PKEY_free(msg->header[i].sig->p);
288 if (msg->header[i].sig->query)
289 event_asr_abort(msg->header[i].sig->query);
291 free(msg->header[i].buf);
292 free(msg->header[i].sig);
294 free(msg->header);
295 free(msg);
298 void
299 dkim_header_add(struct osmtpd_ctx *ctx, const char *line)
301 struct message *msg = ctx->local_message;
302 const char *start, *end, *verify;
303 struct header *headers;
304 size_t i;
306 if (msg->nheaders > 0 &&
307 msg->header[msg->nheaders - 1].readdone == 0) {
308 if (line[0] != ' ' && line[0] != '\t') {
309 msg->header[msg->nheaders - 1].readdone = 1;
310 start = msg->header[msg->nheaders - 1].buf;
311 end = osmtpd_ltok_skip_field_name(start, 0);
312 /* In case someone uses an obs-optional */
313 if (end != NULL)
314 verify = osmtpd_ltok_skip_wsp(end, 1);
315 if (end != NULL &&
316 strncasecmp(
317 start, "DKIM-Signature", end - start) == 0 &&
318 verify[0] == ':')
319 dkim_signature_parse(
320 &msg->header[msg->nheaders - 1]);
321 if (line[0] == '\0')
322 return;
323 } else {
324 dkim_header_cat(ctx, line);
325 return;
328 if (msg->nheaders % 10 == 0) {
329 if ((headers = recallocarray(msg->header, msg->nheaders,
330 msg->nheaders + 10, sizeof(*msg->header))) == NULL)
331 osmtpd_err(1, "%s: malloc", __func__);
332 msg->header = headers;
333 for (i = 0; i < msg->nheaders; i++) {
334 if (msg->header[i].sig == NULL)
335 continue;
336 msg->header[i].sig->header = &msg->header[i];
339 msg->header[msg->nheaders].msg = msg;
340 msg->nheaders++;
341 dkim_header_cat(ctx, line);
344 void
345 dkim_header_cat(struct osmtpd_ctx *ctx, const char *line)
347 struct message *msg = ctx->local_message;
348 struct header *header = &msg->header[msg->nheaders - 1];
349 char *buf;
351 size_t needed = header->buflen + strlen(line) + 2;
353 if (needed > (header->buflen / 1024) + 1) {
354 buf = reallocarray(header->buf, (needed / 1024) + 1, 1024);
355 if (buf == NULL)
356 osmtpd_err(1, "%s: malloc", __func__);
357 header->buf = buf;
359 header->buflen += snprintf(header->buf + header->buflen,
360 (((needed / 1024) + 1) * 1024) - header->buflen, "%s%s",
361 header->buflen == 0 ? "" : "\r\n", line);
364 void
365 dkim_signature_parse(struct header *header)
367 struct signature *sig;
368 const char *buf, *i, *end;
369 char tagname[3];
370 char subdomain[HOST_NAME_MAX + 1];
371 size_t ilen, dlen;
373 /* Format checked by dkim_header_add */
374 buf = osmtpd_ltok_skip_field_name(header->buf, 0);
375 buf = osmtpd_ltok_skip_wsp(buf, 1) + 1;
377 if ((header->sig = calloc(1, sizeof(*header->sig))) == NULL)
378 osmtpd_err(1, "%s: malloc", __func__);
379 sig = header->sig;
380 sig->header = header;
381 sig->l = -1;
382 sig->t = -1;
383 sig->x = -1;
385 end = osmtpd_ltok_skip_tag_list(buf, 0);
386 if (end == NULL || end[0] != '\0') {
387 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid tag-list");
388 return;
391 while (buf[0] != '\0') {
392 buf = osmtpd_ltok_skip_fws(buf, 1);
393 end = osmtpd_ltok_skip_tag_name(buf, 0);
395 /* Unknown tag-name */
396 if ((size_t)(end - buf) >= sizeof(tagname))
397 tagname[0] = '\0';
398 else
399 strlcpy(tagname, buf, (end - buf) + 1);
400 buf = osmtpd_ltok_skip_fws(end, 1);
401 /* '=' */
402 buf = osmtpd_ltok_skip_fws(buf + 1, 1);
403 end = osmtpd_ltok_skip_tag_value(buf, 1);
404 if (strcmp(tagname, "v") == 0)
405 dkim_signature_parse_v(sig, buf, end);
406 else if (strcmp(tagname, "a") == 0)
407 dkim_signature_parse_a(sig, buf, end);
408 else if (strcmp(tagname, "b") == 0)
409 dkim_signature_parse_b(sig, buf, end);
410 else if (strcmp(tagname, "bh") == 0)
411 dkim_signature_parse_bh(sig, buf, end);
412 else if (strcmp(tagname, "c") == 0)
413 dkim_signature_parse_c(sig, buf, end);
414 else if (strcmp(tagname, "d") == 0)
415 dkim_signature_parse_d(sig, buf, end);
416 else if (strcmp(tagname, "h") == 0)
417 dkim_signature_parse_h(sig, buf, end);
418 else if (strcmp(tagname, "i") == 0)
419 dkim_signature_parse_i(sig, buf, end);
420 else if (strcmp(tagname, "l") == 0)
421 dkim_signature_parse_l(sig, buf, end);
422 else if (strcmp(tagname, "q") == 0)
423 dkim_signature_parse_q(sig, buf, end);
424 else if (strcmp(tagname, "s") == 0)
425 dkim_signature_parse_s(sig, buf, end);
426 else if (strcmp(tagname, "t") == 0)
427 dkim_signature_parse_t(sig, buf, end);
428 else if (strcmp(tagname, "x") == 0)
429 dkim_signature_parse_x(sig, buf, end);
430 else if (strcmp(tagname, "z") == 0)
431 dkim_signature_parse_z(sig, buf, end);
433 buf = osmtpd_ltok_skip_fws(end, 1);
434 if (buf[0] == ';')
435 buf++;
436 else if (buf[0] != '\0') {
437 dkim_signature_state(sig, DKIM_PERMERROR,
438 "Invalid tag-list");
439 return;
442 if (sig->state != DKIM_UNKNOWN)
443 return;
445 if (sig->v != 1)
446 dkim_signature_state(sig, DKIM_PERMERROR, "Missing v tag");
447 else if (sig->ah == NULL)
448 dkim_signature_state(sig, DKIM_PERMERROR, "Missing a tag");
449 else if (sig->b == NULL)
450 dkim_signature_state(sig, DKIM_PERMERROR, "Missing b tag");
451 else if (sig->bhsz == 0)
452 dkim_signature_state(sig, DKIM_PERMERROR, "Missing bh tag");
453 else if (sig->d[0] == '\0')
454 dkim_signature_state(sig, DKIM_PERMERROR, "Missing d tag");
455 else if (sig->h == NULL)
456 dkim_signature_state(sig, DKIM_PERMERROR, "Missing h tag");
457 else if (sig->s[0] == '\0')
458 dkim_signature_state(sig, DKIM_PERMERROR, "Missing s tag");
459 if (sig->state != DKIM_UNKNOWN)
460 return;
462 if (sig->i != NULL) {
463 i = osmtpd_ltok_skip_local_part(sig->i, 1) + 1;
464 ilen = sig->isz - (size_t)(i - sig->i);
465 dlen = strlen(sig->d);
466 if (ilen < dlen) {
467 dkim_signature_state(sig, DKIM_PERMERROR,
468 "i tag not subdomain of d");
469 return;
471 i += ilen - dlen;
472 if ((i[-1] != '.' && i[-1] != '@') ||
473 strncasecmp(i, sig->d, dlen) != 0) {
474 dkim_signature_state(sig, DKIM_PERMERROR,
475 "i tag not subdomain of d");
476 return;
479 if (sig->t != -1 && sig->x != -1 && sig->t > sig->x) {
480 dkim_signature_state(sig, DKIM_PERMERROR, "t tag after x tag");
481 return;
484 if ((size_t)snprintf(subdomain, sizeof(subdomain), "%s._domainkey.%s",
485 sig->s, sig->d) >= sizeof(subdomain)) {
486 dkim_signature_state(sig, DKIM_PERMERROR,
487 "dns/txt query too long");
488 return;
491 dkim_lookup_record(sig, subdomain);
494 void
495 dkim_lookup_record(struct signature *sig, const char *domain)
497 struct asr_query *query;
499 sig->nqueries++;
501 if (sig->query != NULL) {
502 event_asr_abort(sig->query);
503 sig->query = NULL;
505 if ((query = res_query_async(domain, C_IN, T_TXT, NULL)) == NULL)
506 osmtpd_err(1, "res_query_async");
508 if ((sig->query = event_asr_run(query, dkim_rr_resolve, sig)) == NULL)
509 osmtpd_err(1, "res_query_async");
512 void
513 dkim_signature_parse_v(struct signature *sig, const char *start, const char *end)
515 if (sig->v != 0) { /* Duplicate tag */
516 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate v tag");
517 return;
519 /* Unsupported version */
520 if (start[0] != '1' || start + 1 != end)
521 dkim_signature_state(sig, DKIM_NEUTRAL, "Unsupported v tag");
522 else
523 sig->v = 1;
526 void
527 dkim_signature_parse_a(struct signature *sig, const char *start, const char *end)
529 char ah[sizeof("sha256")];
531 if (sig->ah != NULL) {
532 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate a tag");
533 return;
536 if (osmtpd_ltok_skip_sig_a_tag_alg(start, 0) != end) {
537 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid a tag");
538 return;
540 sig->a = start;
541 sig->asz = (size_t)(end - start);
542 if (strncmp(start, "rsa-", 4) == 0) {
543 start += 4;
544 sig->ak = EVP_PKEY_RSA;
545 sig->sephash = 0;
546 #if HAVE_ED25519
547 } else if (strncmp(start, "ed25519-", 8) == 0) {
548 start += 8;
549 sig->ak = EVP_PKEY_ED25519;
550 sig->sephash = 1;
551 #endif
552 } else {
553 dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag k");
554 return;
556 if ((size_t)(end - start) >= sizeof(ah)) {
557 dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag h");
558 return;
560 strlcpy(ah, start, sizeof(ah));
561 ah[end - start] = '\0';
562 if ((sig->ah = EVP_get_digestbyname(ah)) == NULL) {
563 dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag h");
564 return;
566 if ((sig->bhctx = EVP_MD_CTX_new()) == NULL)
567 osmtpd_err(1, "EVP_MD_CTX_new");
569 if (EVP_DigestInit_ex(sig->bhctx, sig->ah, NULL) <= 0) {
570 dkim_signature_state(sig, DKIM_FAIL, "Unsuppored a tag ah");
571 return;
575 void
576 dkim_signature_parse_b(struct signature *sig, const char *start, const char *end)
578 int decodesz;
579 size_t i, j;
581 if (sig->b != NULL) {
582 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate b tag");
583 return;
585 sig->bheader = start;
586 sig->bheadersz = end - start;
587 if ((sig->b = malloc(((sig->bheadersz / 4) + 1) * 3)) == NULL)
588 osmtpd_err(1, "%s: malloc", __func__);
589 /* EVP_DecodeBlock doesn't handle internal whitespace */
590 EVP_DecodeInit(ectx);
591 if (EVP_DecodeUpdate(ectx, sig->b, &decodesz, sig->bheader,
592 (int) sig->bheadersz) == -1) {
593 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid b tag");
594 return;
596 sig->bsz = decodesz;
597 if (EVP_DecodeFinal(ectx, sig->b + sig->bsz,
598 &decodesz) == -1) {
599 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid b tag");
600 return;
602 sig->bsz += decodesz;
603 for (i = 0, j = 0;
604 i < sig->bheadersz && j < HEADER_B_MAX_LEN; i++) {
605 if (isalnum(sig->bheader[i]) || sig->bheader[i] == '/'
606 || sig->bheader[i] == '+' || sig->bheader[i] == '=')
607 sig->bheaderclean[j++] = sig->bheader[i];
609 sig->bheaderclean[j] = '\0';
612 void
613 dkim_signature_parse_bh(struct signature *sig, const char *start, const char *end)
615 const char *b64;
616 size_t n;
617 int decodesz;
619 if (sig->bhsz != 0) {
620 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate bh tag");
621 return;
623 /*
624 * EVP_Decode* expects sig->bh to be large enough,
625 * so count the actual b64 characters.
626 */
627 b64 = start;
628 n = 0;
629 while (1) {
630 b64 = osmtpd_ltok_skip_fws(b64, 1);
631 if (osmtpd_ltok_skip_alphadigitps(b64, 0) == NULL)
632 break;
633 n++;
634 b64++;
636 if (b64[0] == '=') {
637 n++;
638 b64 = osmtpd_ltok_skip_fws(b64 + 1, 1);
639 if (b64[0] == '=') {
640 n++;
641 b64++;
644 /* Invalid tag value */
645 if (b64 != end || n % 4 != 0 || (n / 4) * 3 > sizeof(sig->bh)) {
646 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
647 return;
649 /* EVP_DecodeBlock doesn't handle internal whitespace */
650 EVP_DecodeInit(ectx);
651 if (EVP_DecodeUpdate(ectx, sig->bh, &decodesz, start,
652 (int)(end - start)) == -1) {
653 /* Paranoia check */
654 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
655 return;
657 sig->bhsz = decodesz;
658 if (EVP_DecodeFinal(ectx, sig->bh + sig->bhsz, &decodesz) == -1) {
659 /* Paranoia check */
660 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
661 return;
663 sig->bhsz += decodesz;
666 void
667 dkim_signature_parse_c(struct signature *sig, const char *start, const char *end)
669 if (sig->c != 0) {
670 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate c tag");
671 return;
673 if (strncmp(start, "simple", 6) == 0) {
674 sig->c = CANON_HEADER_SIMPLE;
675 start += 6;
676 } else if (strncmp(start, "relaxed", 7) == 0) {
677 sig->c = CANON_HEADER_RELAXED;
678 start += 7;
679 } else {
680 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid c tag");
681 return;
683 if (start[0] == '/') {
684 start++;
685 if (strncmp(start, "simple", 6) == 0) {
686 sig->c |= CANON_BODY_SIMPLE;
687 start += 6;
688 } else if (strncmp(start, "relaxed", 7) == 0) {
689 sig->c |= CANON_BODY_RELAXED;
690 start += 7;
691 } else {
692 dkim_signature_state(sig, DKIM_PERMERROR,
693 "Invalid c tag");
694 return;
698 if (start != end) {
699 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid c tag");
700 return;
702 sig->c |= CANON_DONE;
705 void
706 dkim_signature_parse_d(struct signature *sig, const char *start, const char *end)
708 if (sig->d[0] != '\0') {
709 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate d tag");
710 return;
712 if (osmtpd_ltok_skip_sig_d_tag_value(start, 0) != end ||
713 (size_t)(end - start) >= sizeof(sig->d)) {
714 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid d tag");
715 return;
717 strlcpy(sig->d, start, end - start + 1);
720 void
721 dkim_signature_parse_h(struct signature *sig, const char *start, const char *end)
723 const char *h;
724 size_t n = 0;
726 if (sig->h != NULL) {
727 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate h tag");
728 return;
730 if (osmtpd_ltok_skip_sig_h_tag_value(start, 0) < end) {
731 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid h tag");
732 return;
734 h = start;
735 while (1) {
736 if ((h = osmtpd_ltok_skip_hdr_name(h, 0)) == NULL) {
737 dkim_signature_state(sig, DKIM_PERMERROR,
738 "Invalid h tag");
739 return;
741 n++;
742 /* ';' is part of hdr-name */
743 if (h > end) {
744 h = end;
745 break;
747 h = osmtpd_ltok_skip_fws(h, 1);
748 if (h[0] != ':')
749 break;
750 h = osmtpd_ltok_skip_fws(h + 1, 1);
752 if ((sig->h = calloc(n + 1, sizeof(*sig->h))) == NULL)
753 osmtpd_err(1, "%s: malloc", __func__);
754 n = 0;
755 h = start;
756 while (1) {
757 h = osmtpd_ltok_skip_hdr_name(start, 0);
758 /* ';' is part of hdr-name */
759 if (h > end) {
760 sig->h[n] = strndup(start, end - start);
761 break;
763 if ((sig->h[n++] = strndup(start, h - start)) == NULL)
764 osmtpd_err(1, "%s: malloc", __func__);
765 start = osmtpd_ltok_skip_fws(h, 1);
766 if (start[0] != ':')
767 break;
768 start = osmtpd_ltok_skip_fws(start + 1, 1);
772 void
773 dkim_signature_parse_i(struct signature *sig, const char *start, const char *end)
775 if (sig->i != NULL) {
776 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate i tag");
777 return;
779 if (osmtpd_ltok_skip_sig_i_tag_value(start, 0) != end) {
780 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid i tag");
781 return;
783 sig->i = start;
784 sig->isz = (size_t)(end - start);
787 void
788 dkim_signature_parse_l(struct signature *sig, const char *start, const char *end)
790 long long l;
791 char *lend;
793 if (sig->l != -1) { /* Duplicate tag */
794 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate l tag");
795 return;
797 errno = 0;
798 l = strtoll(start, &lend, 10);
799 /* > 76 digits in stroll is an overflow */
800 if (osmtpd_ltok_skip_digit(start, 0) == NULL ||
801 lend != end || errno != 0) {
802 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid l tag");
803 return;
805 if (l > SSIZE_MAX) {
806 dkim_signature_state(sig, DKIM_PERMERROR, "l tag too large");
807 return;
809 sig->l = (ssize_t)l;
812 void
813 dkim_signature_parse_q(struct signature *sig, const char *start, const char *end)
815 const char *qend;
817 if (sig->q != 0) {
818 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate q tag");
819 return;
822 while (1) {
823 start = osmtpd_ltok_skip_fws(start, 1);
824 qend = osmtpd_ltok_skip_sig_q_tag_method(start, 0);
825 if (qend == NULL) {
826 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid q tag");
827 return;
829 if (strncmp(start, "dns/txt", qend - start) == 0)
830 sig->q = 1;
831 start = osmtpd_ltok_skip_fws(qend, 1);
832 if (start[0] != ':')
833 break;
835 if (start != end) {
836 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid q tag");
837 return;
839 if (sig->q != 1) {
840 sig->q = 1;
841 dkim_signature_state(sig, DKIM_NEUTRAL, "No useable q found");
842 return;
846 void
847 dkim_signature_parse_s(struct signature *sig, const char *start, const char *end)
849 if (sig->s[0] != '\0') {
850 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate s tag");
851 return;
853 if (osmtpd_ltok_skip_selector(start, 0) != end) {
854 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid s tag");
855 return;
857 strlcpy(sig->s, start, end - start + 1);
860 void
861 dkim_signature_parse_t(struct signature *sig, const char *start, const char *end)
863 char *tend;
865 if (sig->t != -1) {
866 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate t tag");
867 return;
869 errno = 0;
870 sig->t = strtoll(start, &tend, 10);
871 if (osmtpd_ltok_skip_digit(start, 0) == NULL || tend != end ||
872 tend - start > 12 || errno != 0) {
873 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid t tag");
874 return;
878 void
879 dkim_signature_parse_x(struct signature *sig, const char *start, const char *end)
881 char *xend;
883 if (sig->x != -1) {
884 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate x tag");
885 return;
887 errno = 0;
888 sig->x = strtoll(start, &xend, 10);
889 if (osmtpd_ltok_skip_digit(start, 0) == NULL || xend != end ||
890 xend - start > 12 || errno != 0) {
891 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid x tag");
892 return;
896 void
897 dkim_signature_parse_z(struct signature *sig, const char *start, const char *end)
899 if (sig->z != 0) {
900 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate z tag");
901 return;
904 sig->z = 1;
905 if (osmtpd_ltok_skip_sig_z_tag_value(start, 0) != end) {
906 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid z tag");
907 return;
911 void
912 dkim_signature_verify(struct signature *sig)
914 struct message *msg = sig->header->msg;
915 static EVP_MD_CTX *bctx = NULL;
916 char digest[EVP_MAX_MD_SIZE];
917 unsigned int digestsz;
918 const char *end;
919 size_t i, header;
921 if (sig->state != DKIM_UNKNOWN)
922 return;
924 if (bctx == NULL) {
925 if ((bctx = EVP_MD_CTX_new()) == NULL)
926 osmtpd_err(1, "EVP_MD_CTX_new");
928 EVP_MD_CTX_reset(bctx);
929 if (!sig->sephash) {
930 if (EVP_DigestVerifyInit(bctx, NULL, sig->ah, NULL,
931 sig->p) != 1) {
932 dkim_signature_state(sig, DKIM_FAIL, "ah tag");
933 return;
935 } else {
936 if (EVP_DigestInit_ex(bctx, sig->ah, NULL) != 1) {
937 dkim_signature_state(sig, DKIM_FAIL, "ah tag");
938 return;
942 for (i = 0; i < msg->nheaders; i++)
943 msg->header[i].parsed = 0;
945 for (header = 0; sig->h[header] != NULL; header++) {
946 for (i = msg->nheaders; i > 0; ) {
947 i--;
948 if (msg->header[i].parsed ||
949 strncasecmp(msg->header[i].buf, sig->h[header],
950 strlen(sig->h[header])) != 0 ||
951 msg->header[i].sig == sig)
952 continue;
953 end = osmtpd_ltok_skip_fws(
954 msg->header[i].buf + strlen(sig->h[header]), 1);
955 if (end[0] != ':')
956 continue;
957 dkim_signature_header(bctx, sig, &(msg->header[i]));
958 msg->header[i].parsed = 1;
959 break;
962 dkim_signature_header(bctx, sig, sig->header);
963 if (!sig->sephash) {
964 if (EVP_DigestVerifyFinal(bctx, sig->b, sig->bsz) != 1)
965 dkim_signature_state(sig, DKIM_FAIL, "b mismatch");
966 } else {
967 if (EVP_DigestFinal_ex(bctx, digest, &digestsz) == 0)
968 osmtpd_err(1, "EVP_DigestFinal_ex");
970 if (EVP_DigestVerifyInit(bctx, NULL, NULL, NULL, sig->p) != 1)
971 osmtpd_err(1, "EVP_DigestVerifyInit");
973 switch (EVP_DigestVerify(bctx, sig->b, sig->bsz, digest,
974 digestsz)) {
975 case 1:
976 break;
977 case 0:
978 dkim_signature_state(sig, DKIM_FAIL, "b mismatch");
979 break;
980 default:
981 osmtpd_err(1, "EVP_DigestVerify");
986 /* EVP_DigestVerifyUpdate is a macro, so we can't alias this on a variable */
987 #define dkim_b_digest_update(a, b, c) \
988 (sig->sephash ? EVP_DigestUpdate((a), (b), (c)) :\
989 EVP_DigestVerifyUpdate((a), (b), (c)))
991 void
992 dkim_signature_header(EVP_MD_CTX *bctx, struct signature *sig,
993 struct header *header)
995 char c;
996 const char *ptr = header->buf, *end;
997 int inhdrname = 1;
998 int canon = sig->c & CANON_HEADER;
1000 for (ptr = header->buf; ptr[0] != '\0'; ptr++) {
1001 if (inhdrname) {
1002 if (canon == CANON_HEADER_RELAXED) {
1003 ptr = osmtpd_ltok_skip_fws(ptr, 1);
1004 c = tolower(ptr[0]);
1005 } else
1006 c = ptr[0];
1007 if (c == ':') {
1008 inhdrname = 0;
1009 if (canon == CANON_HEADER_RELAXED)
1010 ptr = osmtpd_ltok_skip_fws(
1011 ptr + 1, 1) - 1;
1013 if (dkim_b_digest_update(bctx, &c, 1) == 0)
1014 osmtpd_errx(1, "dkim_b_digest_update");
1015 continue;
1017 end = osmtpd_ltok_skip_fws(ptr, 1);
1018 if (end == ptr) {
1019 if (sig->header == header && ptr == sig->bheader) {
1020 ptr = osmtpd_ltok_skip_tag_value(
1021 ptr, 0) - 1;
1022 continue;
1024 if (dkim_b_digest_update(bctx, ptr, 1) == 0)
1025 osmtpd_errx(1, "dkim_b_digest_update");
1026 } else {
1027 if (canon == CANON_HEADER_RELAXED) {
1028 if (end[0] == '\0')
1029 continue;
1030 if (dkim_b_digest_update(bctx, " ", 1) == 0)
1031 osmtpd_errx(1, "dkim_b_digest_update");
1032 } else {
1033 if (dkim_b_digest_update(bctx, ptr,
1034 end - ptr) == 0)
1035 osmtpd_errx(1, "dkim_b_digest_update");
1037 ptr = end - 1;
1041 if (sig->header != header) {
1042 if (dkim_b_digest_update(bctx, "\r\n", 2) == 0)
1043 osmtpd_errx(1, "dkim_b_digest_update");
1047 void
1048 dkim_signature_state(struct signature *sig, enum state state,
1049 const char *reason)
1051 if (sig->query != NULL) {
1052 event_asr_abort(sig->query);
1053 sig->query = NULL;
1055 switch (sig->state) {
1056 case DKIM_UNKNOWN:
1057 break;
1058 case DKIM_PASS:
1059 case DKIM_FAIL:
1060 osmtpd_errx(1, "Unexpected transition");
1061 case DKIM_POLICY:
1062 if (state == DKIM_PASS)
1063 return;
1064 break;
1065 case DKIM_NEUTRAL:
1066 if (state == DKIM_PASS)
1067 return;
1068 if (state == DKIM_TEMPERROR || state == DKIM_PERMERROR)
1069 break;
1070 osmtpd_errx(1, "Unexpected transition");
1071 case DKIM_TEMPERROR:
1072 if (state == DKIM_PERMERROR)
1073 break;
1074 return;
1075 case DKIM_PERMERROR:
1076 return;
1078 sig->state = state;
1079 sig->state_reason = reason;
1082 const char *
1083 dkim_state2str(enum state state)
1085 switch (state)
1087 case DKIM_UNKNOWN:
1088 return "unknown";
1089 case DKIM_PASS:
1090 return "pass";
1091 case DKIM_FAIL:
1092 return "fail";
1093 case DKIM_POLICY:
1094 return "policy";
1095 case DKIM_NEUTRAL:
1096 return "neutral";
1097 case DKIM_TEMPERROR:
1098 return "temperror";
1099 case DKIM_PERMERROR:
1100 return "permerror";
1104 void
1105 dkim_rr_resolve(struct asr_result *ar, void *arg)
1107 struct signature *sig = arg;
1108 char key[UINT16_MAX + 1];
1109 const char *rr_txt;
1110 size_t keylen, cstrlen;
1111 struct unpack pack;
1112 struct dns_header h;
1113 struct dns_query q;
1114 struct dns_rr rr;
1115 char buf[HOST_NAME_MAX + 1];
1117 sig->query = NULL;
1119 if (ar->ar_h_errno == TRY_AGAIN || ar->ar_h_errno == NO_RECOVERY) {
1120 dkim_signature_state(sig, DKIM_TEMPERROR,
1121 hstrerror(ar->ar_h_errno));
1122 goto verify;
1124 if (ar->ar_h_errno != NETDB_SUCCESS) {
1125 dkim_signature_state(sig, DKIM_PERMERROR,
1126 hstrerror(ar->ar_h_errno));
1127 goto verify;
1130 unpack_init(&pack, ar->ar_data, ar->ar_datalen);
1131 if (unpack_header(&pack, &h) != 0 ||
1132 unpack_query(&pack, &q) != 0) {
1133 osmtpd_warn(sig->header->msg->ctx,
1134 "Mallformed DKIM DNS response for domain %s: %s",
1135 print_dname(q.q_dname, buf, sizeof(buf)),
1136 pack.err);
1137 dkim_signature_state(sig, DKIM_PERMERROR, pack.err);
1138 goto verify;
1141 for (; h.ancount > 0; h.ancount--) {
1142 if (unpack_rr(&pack, &rr) != 0) {
1143 osmtpd_warn(sig->header->msg->ctx,
1144 "Mallformed DKIM DNS record for domain %s: %s",
1145 print_dname(q.q_dname, buf, sizeof(buf)),
1146 pack.err);
1147 continue;
1150 /* If we below limit, follow CNAME*/
1151 if (rr.rr_type == T_CNAME &&
1152 sig->nqueries < DKIM_LOOKUP_LOOKUP_LIMIT ) {
1153 print_dname(rr.rr.cname.cname, buf, sizeof(buf));
1154 dkim_lookup_record(sig, buf);
1155 free(ar->ar_data);
1156 return;
1159 if (rr.rr_type != T_TXT) {
1160 osmtpd_warn(sig->header->msg->ctx,
1161 "Unexpected DKIM DNS record: %d for domain %s",
1162 rr.rr_type,
1163 print_dname(q.q_dname, buf, sizeof(buf)));
1164 continue;
1167 keylen = 0;
1168 rr_txt = rr.rr.other.rdata;
1169 while (rr.rr.other.rdlen > 0) {
1170 cstrlen = ((const unsigned char *)rr_txt)[0];
1171 if (cstrlen >= rr.rr.other.rdlen ||
1172 keylen + cstrlen >= sizeof(key))
1173 break;
1175 * RFC 6376 Section 3.6.2.2
1176 * Strings in a TXT RR MUST be concatenated together
1177 * before use with no intervening whitespace.
1179 strlcpy(key + keylen, rr_txt + 1, cstrlen + 1);
1180 rr.rr.other.rdlen -= (cstrlen + 1);
1181 rr_txt += (cstrlen + 1);
1182 keylen += cstrlen;
1184 if (rr.rr.other.rdlen > 0) /* Invalid TXT RDATA */
1185 continue;
1187 if (dkim_key_text_parse(sig, key))
1188 break;
1191 if (h.ancount == 0) {
1192 dkim_signature_state(sig, DKIM_PERMERROR,
1193 "No matching key found");
1194 } else {
1195 /* Only verify if all headers have been read */
1196 if (!sig->header->msg->parsing_headers)
1197 dkim_signature_verify(sig);
1199 verify:
1200 free(ar->ar_data);
1201 dkim_message_verify(sig->header->msg);
1204 int
1205 dkim_key_text_parse(struct signature *sig, const char *key)
1207 char tagname, *hashname;
1208 const char *end, *tagvend;
1209 char pkraw[UINT16_MAX] = "", pkimp[UINT16_MAX];
1210 size_t pkrawlen = 0, pkoff, linelen;
1211 int h = 0, k = 0, n = 0, p = 0, s = 0, t = 0, first = 1;
1212 BIO *bio;
1213 #ifdef HAVE_ED25519
1214 size_t pklen;
1215 int tmp;
1216 #endif
1218 key = osmtpd_ltok_skip_fws(key, 1);
1219 /* Validate syntax early */
1220 if ((end = osmtpd_ltok_skip_tag_list(key, 0)) == NULL)
1221 return 0;
1223 while (key[0] != '\0') {
1224 key = osmtpd_ltok_skip_fws(key, 1);
1225 if ((end = osmtpd_ltok_skip_tag_name(key, 0)) == NULL)
1226 return 0;
1228 if ((size_t)(end - key) != 1)
1229 tagname = '\0';
1230 else
1231 tagname = key[0];
1232 key = osmtpd_ltok_skip_fws(end, 1);
1233 /* '=' */
1234 if (key[0] != '=')
1235 return 0;
1236 key = osmtpd_ltok_skip_fws(key + 1, 1);
1237 if ((end = osmtpd_ltok_skip_tag_value(key, 0)) == NULL)
1238 return 0;
1239 switch (tagname) {
1240 case 'v':
1242 * RFC 6376 section 3.6.1, v=:
1243 * RECOMMENDED...This tag MUST be the first tag in the
1244 * record.
1246 if (!first ||
1247 osmtpd_ltok_skip_key_v_tag_value(key, 0) != end)
1248 return 0;
1249 key = end;
1250 break;
1251 case 'h':
1252 if (h != 0) /* Duplicate tag */
1253 return 0;
1254 /* Invalid tag value */
1255 if (osmtpd_ltok_skip_key_h_tag_value(key, 0) != end)
1256 return 0;
1257 while (1) {
1258 if ((tagvend = osmtpd_ltok_skip_key_h_tag_alg(
1259 key, 0)) == NULL)
1260 break;
1261 hashname = strndup(key, tagvend - key);
1262 if (hashname == NULL)
1263 osmtpd_err(1, "strndup");
1264 if (EVP_get_digestbyname(hashname) == sig->ah) {
1265 free(hashname);
1266 h = 1;
1267 break;
1269 free(hashname);
1270 key = osmtpd_ltok_skip_fws(tagvend, 1);
1271 if (key[0] != ':')
1272 break;
1273 key = osmtpd_ltok_skip_fws(key + 1, 1);
1275 if (h != 1)
1276 return 0;
1277 key = end;
1278 break;
1279 case 'k':
1280 if (k != 0) /* Duplicate tag */
1281 return 0;
1282 k = 1;
1283 if (strncmp(key, "rsa", end - key) == 0) {
1284 if (sig->ak != EVP_PKEY_RSA)
1285 return 0;
1286 #if HAVE_ED25519
1287 } else if (strncmp(key, "ed25519", end - key) == 0) {
1288 if (sig->ak != EVP_PKEY_ED25519)
1289 return 0;
1290 #endif
1291 } else
1292 return 0;
1293 key = end;
1294 break;
1295 case 'n':
1296 if (n != 0) /* Duplicate tag */
1297 return 0;
1298 n = 1;
1299 /* semicolon is part of safe-char */
1300 if (osmtpd_ltok_skip_key_n_tag_value(key, 0) < end)
1301 return 0;
1302 key = end;
1303 break;
1304 case 'p':
1305 if (p != 0) /* Duplicate tag */
1306 return 0;
1307 p = 1;
1308 while (1) {
1309 key = osmtpd_ltok_skip_fws(key, 1);
1310 if (osmtpd_ltok_skip_alphadigitps(
1311 key, 0) == NULL)
1312 break;
1313 pkraw[pkrawlen++] = key++[0];
1314 if (pkrawlen >= sizeof(pkraw))
1315 return 0;
1317 if (key[0] == '=') {
1318 pkraw[pkrawlen++] = '=';
1319 key = osmtpd_ltok_skip_fws(key + 1, 1);
1320 if (pkrawlen >= sizeof(pkraw))
1321 return 0;
1322 if (key[0] == '=') {
1323 pkraw[pkrawlen++] = '=';
1324 key++;
1325 if (pkrawlen >= sizeof(pkraw))
1326 return 0;
1329 /* Invalid tag value */
1330 if (pkrawlen % 4 != 0 || key != end)
1331 return 0;
1332 break;
1333 case 's':
1334 if (s != 0) /* Duplicate tag */
1335 return 0;
1336 /* Invalid tag value */
1337 if (osmtpd_ltok_skip_key_s_tag_value(key, 0) != end)
1338 return 0;
1339 while (1) {
1340 if ((tagvend =
1341 osmtpd_ltok_skip_key_s_tag_type(
1342 key, 0)) == NULL)
1343 break;
1344 if (strncmp(key, "*", tagvend - key) == 0 ||
1345 strncmp(key, "email", tagvend - key) == 0) {
1346 s = 1;
1347 break;
1349 key = osmtpd_ltok_skip_fws(tagvend, 1);
1350 if (key[0] != ':')
1351 break;
1352 key = osmtpd_ltok_skip_fws(key + 1, 1);
1354 if (s != 1)
1355 return 0;
1356 key = end;
1357 break;
1358 case 't':
1359 if (t != 0) /* Duplicate tag */
1360 return 0;
1361 t = 1;
1362 if (osmtpd_ltok_skip_key_t_tag_value(key, 0) != end)
1363 return 0;
1364 while (1) {
1365 tagvend = osmtpd_ltok_skip_key_t_tag_flag(
1366 key, 0);
1367 if (strncmp(key, "y", tagvend - key) == 0)
1368 sig->kt |= KT_Y;
1369 else if (strncmp(key, "s", tagvend - key) == 0)
1370 sig->kt |= KT_S;
1371 key = osmtpd_ltok_skip_fws(tagvend, 1);
1372 if (key[0] != ':')
1373 break;
1374 key = osmtpd_ltok_skip_fws(key + 1, 1);
1376 break;
1377 default:
1378 key = end;
1379 break;
1382 first = 0;
1383 key = osmtpd_ltok_skip_fws(key, 1);
1384 if (key[0] == ';')
1385 key++;
1386 else if (key[0] != '\0')
1387 return 0;
1390 if (!p) /* Missing tag */
1391 return 0;
1392 if (k == 0 && sig->ak != EVP_PKEY_RSA) /* Default to RSA */
1393 return 0;
1395 if (pkraw[0] == '\0') {
1396 dkim_signature_state(sig, DKIM_PERMERROR, "Key is revoked");
1397 return 1;
1400 switch (sig->ak) {
1401 case EVP_PKEY_RSA:
1402 pkoff = strlcpy(pkimp, "-----BEGIN PUBLIC KEY-----\n",
1403 sizeof(pkimp));
1404 linelen = 0;
1405 for (key = pkraw; key[0] != '\0';) {
1406 if (pkoff + 2 >= sizeof(pkimp))
1407 return 0;
1408 pkimp[pkoff++] = key++[0];
1409 if (++linelen == 64) {
1410 pkimp[pkoff++] = '\n';
1411 linelen = 0;
1414 /* Leverage pkoff check in loop */
1415 if (linelen != 0)
1416 pkimp[pkoff++] = '\n';
1417 /* PEM_read_bio_PUBKEY will catch truncated keys */
1418 pkoff += strlcpy(pkimp + pkoff, "-----END PUBLIC KEY-----\n",
1419 sizeof(pkimp) - pkoff);
1420 if ((bio = BIO_new_mem_buf(pkimp, pkoff)) == NULL)
1421 osmtpd_err(1, "BIO_new_mem_buf");
1422 sig->p = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
1423 BIO_free(bio);
1424 break;
1425 #if HAVE_ED25519
1426 case EVP_PKEY_ED25519:
1427 if ((pkrawlen / 4) * 3 >= sizeof(pkimp))
1428 return 0;
1429 EVP_DecodeInit(ectx);
1430 if (EVP_DecodeUpdate(ectx, pkimp, &tmp, pkraw, pkrawlen) == -1)
1431 return 0;
1432 pklen = tmp;
1433 if (EVP_DecodeFinal(ectx, pkimp, &tmp) == -1)
1434 return 0;
1435 pklen += tmp;
1436 sig->p = EVP_PKEY_new_raw_public_key(sig->ak, NULL, pkimp,
1437 pklen);
1438 break;
1439 #endif
1441 if (sig->p == NULL) {
1443 * XXX No clue how to differentiate between invalid key and
1444 * temporary failure like *alloc.
1445 * Assume invalid key, because it's more likely.
1447 return 0;
1449 return 1;
1452 void
1453 dkim_body_parse(struct message *msg, const char *line)
1455 struct signature *sig;
1456 const char *end = line, *hash, *prev;
1457 size_t hashn, len, i;
1458 int wsp, ret;
1460 if (line[0] == '\0') {
1461 msg->body_whitelines++;
1462 return;
1465 while (msg->body_whitelines-- > 0) {
1466 for (i = 0; i < msg->nheaders; i++) {
1467 if ((sig = msg->header[i].sig) == NULL ||
1468 sig->state != DKIM_UNKNOWN)
1469 continue;
1470 hashn = sig->l == -1 ? 2 : MIN(2, sig->l);
1471 sig->l -= sig->l == -1 ? 0 : hashn;
1472 if (EVP_DigestUpdate(sig->bhctx, "\r\n", hashn) == 0)
1473 osmtpd_errx(1, "EVP_DigestUpdate");
1476 msg->body_whitelines = 0;
1477 msg->has_body = 1;
1479 while (line[0] != '\0') {
1480 while (1) {
1481 prev = end;
1482 if ((end = osmtpd_ltok_skip_wsp(end, 0)) == NULL)
1483 break;
1485 end = prev;
1486 wsp = end != line;
1487 if (!wsp) {
1488 while (osmtpd_ltok_skip_wsp(end, 0) == NULL &&
1489 end[0] != '\0')
1490 end++;
1492 for (i = 0; i < msg->nheaders; i++) {
1493 sig = msg->header[i].sig;
1494 if (sig == NULL || sig->state != DKIM_UNKNOWN)
1495 continue;
1496 if (wsp &&
1497 (sig->c & CANON_BODY) == CANON_BODY_RELAXED) {
1498 hash = " ";
1499 len = end[0] == '\0' ? 0 : 1;
1500 } else {
1501 hash = line;
1502 len = (size_t)(end - line);
1504 hashn = sig->l == -1 ? len : MIN(len, (size_t)sig->l);
1505 sig->l -= sig->l == -1 ? 0 : hashn;
1506 ret = EVP_DigestUpdate(sig->bhctx, hash, hashn);
1507 if (ret == 0)
1508 osmtpd_err(1, "EVP_DigestUpdate");
1510 line = end;
1512 for (i = 0; i < msg->nheaders; i++) {
1513 sig = msg->header[i].sig;
1514 if (sig == NULL || sig->state != DKIM_UNKNOWN)
1515 continue;
1516 hashn = sig->l == -1 ? 2 : MIN(2, sig->l);
1517 sig->l -= sig->l == -1 ? 0 : hashn;
1518 ret = EVP_DigestUpdate(sig->bhctx, "\r\n", hashn);
1519 if (ret == 0)
1520 osmtpd_err(1, "EVP_DigestUpdate");
1524 void
1525 dkim_body_verify(struct signature *sig)
1527 unsigned char digest[EVP_MAX_MD_SIZE];
1528 unsigned int digestsz;
1530 if (sig->state != DKIM_UNKNOWN)
1531 return;
1533 if ((sig->c & CANON_BODY) == CANON_BODY_SIMPLE &&
1534 !sig->header->msg->has_body) {
1535 if (EVP_DigestUpdate(sig->bhctx, "\r\n",
1536 sig->l == -1 ? 2 : MIN(2, sig->l)) <= 0)
1537 osmtpd_errx(1, "EVP_DigestUpdate");
1539 if (sig->l > 0) {
1540 dkim_signature_state(sig, DKIM_PERMERROR,
1541 "l tag larger than body");
1542 return;
1545 if (EVP_DigestFinal_ex(sig->bhctx, digest, &digestsz) == 0)
1546 osmtpd_err(1, "EVP_DigestFinal_ex");
1548 if (digestsz != sig->bhsz || memcmp(digest, sig->bh, digestsz) != 0)
1549 dkim_signature_state(sig, DKIM_FAIL, "bh mismatch");
1552 void
1553 dkim_message_verify(struct message *msg)
1555 struct signature *sig;
1556 size_t i;
1557 ssize_t n, aroff = 0;
1558 int found = 0;
1559 char *line = NULL;
1560 size_t linelen = 0;
1562 if (!msg->readdone)
1563 return;
1565 for (i = 0; i < msg->nheaders; i++) {
1566 if (msg->header[i].sig == NULL)
1567 continue;
1568 if (msg->header[i].sig->query != NULL)
1569 return;
1570 if (msg->header[i].sig->state != DKIM_UNKNOWN)
1571 continue;
1572 dkim_signature_state(msg->header[i].sig, DKIM_PASS, NULL);
1575 if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1576 "Authentication-Results: %s", authservid)) == -1)
1577 osmtpd_err(1, "%s: malloc", __func__);
1578 for (i = 0; i < msg->nheaders; i++) {
1579 sig = msg->header[i].sig;
1580 if (sig == NULL)
1581 continue;
1582 found = 1;
1583 if ((aroff = dkim_ar_cat(&line, &linelen, aroff, "; dkim=%s",
1584 dkim_state2str(sig->state))) == -1)
1585 osmtpd_err(1, "%s: malloc", __func__);
1586 if (sig->state_reason != NULL) {
1587 if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1588 " reason=\"%s\"", sig->state_reason)) == -1)
1589 osmtpd_err(1, "%s: malloc", __func__);
1591 if (sig->s[0] != '\0') {
1592 if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1593 " header.s=%s", sig->s)) == -1)
1594 osmtpd_err(1, "%s: malloc", __func__);
1596 if (sig->d[0] != '\0') {
1597 if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1598 " header.d=%s", sig->d)) == -1)
1599 osmtpd_err(1, "%s: malloc", __func__);
1602 * Don't print i-tag, since localpart can be a quoted-string,
1603 * which can contain FWS and CFWS.
1605 if (sig->a != NULL) {
1606 if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1607 " header.a=%.*s", (int)sig->asz, sig->a)) == -1)
1608 osmtpd_err(1, "%s: malloc", __func__);
1610 if (sig->bheaderclean[0] != '\0') {
1611 if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1612 " header.b=%s", sig->bheaderclean)) == -1)
1613 osmtpd_err(1, "%s: malloc", __func__);
1616 if (!found) {
1617 aroff = dkim_ar_cat(&line, &linelen, aroff, "; dkim=none");
1618 if (aroff == -1)
1619 osmtpd_err(1, "%s: malloc", __func__);
1621 if (dkim_ar_print(msg->ctx, line) != 0) {
1622 osmtpd_warnx(msg->ctx, "Mallformed AR header");
1623 goto fail;
1626 rewind(msg->origf);
1627 while ((n = getline(&line, &linelen, msg->origf)) != -1) {
1628 line[n - 1] = '\0';
1629 osmtpd_filter_dataline(msg->ctx, "%s", line);
1631 if (ferror(msg->origf))
1632 osmtpd_err(1, "%s: ferror", __func__);
1633 fail:
1634 free(line);
1635 return;
1638 int
1639 dkim_ar_print(struct osmtpd_ctx *ctx, const char *start)
1641 const char *scan, *checkpoint, *ncheckpoint;
1642 int arlen = 0, first = 1, arid = 1;
1644 checkpoint = start;
1645 ncheckpoint = osmtpd_ltok_skip_hdr_name(start, 0) + 1;
1646 for (scan = start; scan[0] != '\0'; scan++) {
1647 if (scan[0] == '\t')
1648 arlen = (arlen + 8) & ~7;
1649 else
1650 arlen++;
1651 if (arlen >= AUTHENTICATION_RESULTS_LINELEN) {
1652 arlen = (int)(checkpoint - start);
1653 if (arlen <= 0) {
1654 arlen = (int)(ncheckpoint - start);
1655 checkpoint = ncheckpoint;
1657 osmtpd_filter_dataline(ctx, "%s%.*s", first ? "" : "\t",
1658 arlen, start);
1659 start = osmtpd_ltok_skip_cfws(checkpoint, 1);
1660 if (*start == '\0')
1661 return 0;
1662 ncheckpoint = start;
1663 scan = start;
1664 arlen = 8;
1665 first = 0;
1667 if (scan == ncheckpoint) {
1668 checkpoint = ncheckpoint;
1669 ncheckpoint = osmtpd_ltok_skip_cfws(ncheckpoint, 1);
1670 /* authserv-id */
1671 if (arid) {
1672 ncheckpoint = osmtpd_ltok_skip_value(
1673 ncheckpoint, 0);
1674 arid = 0;
1675 /* methodspec */
1676 } else if (strncmp(ncheckpoint, "dkim=",
1677 sizeof("dkim=") - 1) == 0) {
1678 ncheckpoint = osmtpd_ltok_skip_keyword(
1679 ncheckpoint + sizeof("dkim=") - 1, 0);
1680 /* reasonspec */
1681 } else if (strncmp(ncheckpoint, "reason=",
1682 sizeof("reason=") - 1) == 0) {
1683 ncheckpoint = osmtpd_ltok_skip_ar_reasonspec(
1684 ncheckpoint, 0);
1685 /* propspec */
1686 } else {
1687 ncheckpoint = osmtpd_ltok_skip_ar_propspec(
1688 ncheckpoint, 0);
1691 if (ncheckpoint == NULL)
1692 return -1;
1694 if (*ncheckpoint == ';')
1695 ncheckpoint++;
1698 osmtpd_filter_dataline(ctx, "%s%s", first ? "" : "\t", start);
1699 return 0;
1702 ssize_t
1703 dkim_ar_cat(char **ar, size_t *n, size_t aroff, const char *fmt, ...)
1705 va_list ap;
1706 char *artmp;
1707 int size;
1708 size_t nn;
1710 va_start(ap, fmt);
1711 size = vsnprintf(*ar + aroff, *n - aroff, fmt, ap);
1712 va_end(ap);
1713 if (size + aroff < *n)
1714 return (ssize_t)size + aroff;
1715 nn = (((aroff + size) / 256) + 1) * 256;
1716 artmp = realloc(*ar, nn);
1717 if (artmp == NULL)
1718 return -1;
1719 *ar = artmp;
1720 *n = nn;
1721 va_start(ap, fmt);
1722 size = vsnprintf(*ar + aroff, *n - aroff, fmt, ap);
1723 va_end(ap);
1724 return (ssize_t)size + aroff;
1727 __dead void
1728 usage(void)
1730 fprintf(stderr, "usage: filter-dkimverify\n");
1731 exit(1);