Blob


1 /*
2 * Copyright (c) 2024 Kirill A. Korinsky <kirill@korins.ky>
3 * Copyright (c) 2022 Martijn van Duren <martijn@openbsd.org>
4 * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
5 * Copyright (c) 2017 Gilles Chehade <gilles@poolp.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19 #include <sys/types.h>
20 #include <sys/socket.h>
22 #include <openssl/evp.h>
23 #include <openssl/pem.h>
24 #include <openssl/sha.h>
25 #include <openssl/err.h>
27 #include <arpa/nameser.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <event.h>
32 #include <limits.h>
33 #include <netdb.h>
34 #include <opensmtpd.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdarg.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <time.h>
41 #include <unistd.h>
42 #include <asr.h>
44 #include "openbsd-compat.h"
45 #include "unpack_dns.h"
46 #include "ltok.h"
48 /*
49 * Use RFC8601 (Authentication-Results) codes instead of RFC6376 codes,
50 * since they're more expressive with additional NONE and SOFTFAIL for
51 * RFC7208 (Sender Policy Framework (SPF)).
52 */
53 enum ar_state {
54 AR_UNKNOWN,
55 AR_NONE,
56 AR_PASS,
57 AR_FAIL,
58 AR_SOFTFAIL,
59 AR_POLICY,
60 AR_NEUTRAL,
61 AR_TEMPERROR,
62 AR_PERMERROR
63 };
65 struct ar_signature {
66 struct header *header;
67 enum ar_state state;
68 const char *state_reason;
69 int dkim;
70 int seal;
71 int v;
72 const char *a;
73 size_t asz;
74 int ak;
75 int sephash;
76 const EVP_MD *ah;
77 char *b;
78 size_t bsz;
79 const char *bheader;
80 size_t bheadersz;
81 #define HEADER_B_MAX_LEN 8
82 char bheaderclean[HEADER_B_MAX_LEN + 1];
83 /* Make sure padding bits for base64 decoding fit */
84 char bh[EVP_MAX_MD_SIZE + (3 - (EVP_MAX_MD_SIZE % 3))];
85 size_t bhsz;
86 EVP_MD_CTX *bhctx;
87 int c;
88 enum ar_state cv;
89 #define CANON_HEADER_SIMPLE 0
90 #define CANON_HEADER_RELAXED 1
91 #define CANON_HEADER 1
92 #define CANON_BODY_SIMPLE 0
93 #define CANON_BODY_RELAXED 1 << 1
94 #define CANON_BODY 1 << 1
95 #define CANON_DONE 1 << 2
96 char d[HOST_NAME_MAX + 1];
97 char **h;
98 int arc_i;
99 const char *i;
100 size_t isz;
101 ssize_t l;
102 int q;
103 char s[HOST_NAME_MAX + 1];
104 time_t t; /* Signature t=/timestamp */
105 #define KT_Y 1
106 #define KT_S 1 << 1
107 int kt; /* Key t=/Flags */
108 time_t x;
109 int z;
110 struct event_asr *query;
111 EVP_PKEY *p;
112 /* RFC 6376 doesn't care about CNAME, use simalr with SPF limit */
113 #define AR_LOOKUP_LOOKUP_LIMIT 11
114 int nqueries;
115 };
117 /*
118 * RFC 5321 doesn't limit record size, enforce some resanable limit
119 */
120 #define SPF_RECORD_MAX 4096
122 struct spf_query {
123 struct spf_record *spf;
124 struct event_asr *eva;
125 int type;
126 enum ar_state q;
127 int include;
128 int exists;
129 char *domain;
130 char *txt;
131 int pos;
132 };
134 struct spf_record {
135 struct osmtpd_ctx *ctx;
136 enum ar_state state;
137 const char *state_reason;
138 char *sender_local;
139 char *sender_domain;
140 int nqueries;
141 int running;
142 int done;
143 /* RFC 7208 Section 4.6.4 limits to 10 DNS lookup,
144 * and one is reserved for the first query.
145 * To prevent of infinity loop I count each CNAME
146 * as dedicated lookup, same as A and AAAA.
147 * So, I use 41 as the limit. */
148 #define SPF_DNS_LOOKUP_LIMIT 41
149 struct spf_query queries[SPF_DNS_LOOKUP_LIMIT];
150 };
152 struct header {
153 struct message *msg;
154 uint8_t readdone;
155 uint8_t parsed;
156 char *buf;
157 size_t buflen;
158 struct ar_signature *sig;
159 };
161 #define AUTHENTICATION_RESULTS_LINELEN 78
162 #ifndef MIN
163 #define MIN(a, b) ((a) < (b) ? (a) : (b))
164 #endif
166 #ifndef s6_addr32
167 #define s6_addr32 __u6_addr.__u6_addr32
168 #endif
170 /* RFC 8617 Section 4.2.1 */
171 #define ARC_MIN_I 1
172 #define ARC_MAX_I 50
174 struct message {
175 struct osmtpd_ctx *ctx;
176 FILE *origf;
177 int parsing_headers;
178 size_t body_whitelines;
179 int has_body;
180 struct header *header;
181 size_t nheaders;
182 int readdone;
183 int nqueries;
184 struct ar_signature *last_arc_seal;
185 struct ar_signature *last_arc_sign;
186 struct ar_signature **arc_seals;
187 struct ar_signature **arc_signs;
188 };
190 struct session {
191 struct osmtpd_ctx *ctx;
192 enum ar_state iprev;
193 struct spf_record *spf_helo;
194 struct spf_record *spf_mailfrom;
195 struct sockaddr_storage src;
196 char *identity;
197 char *rdns;
198 };
200 void usage(void);
201 void auth_conf(const char *, const char *);
202 void auth_connect(struct osmtpd_ctx *, const char *, enum osmtpd_status, struct sockaddr_storage *, struct sockaddr_storage *);
203 void spf_identity(struct osmtpd_ctx *, const char *);
204 void spf_mailfrom(struct osmtpd_ctx *, const char *);
205 void auth_dataline(struct osmtpd_ctx *, const char *);
206 void *spf_record_new(struct osmtpd_ctx *, const char *);
207 void spf_record_free(struct spf_record *);
208 void *auth_session_new(struct osmtpd_ctx *);
209 void auth_session_free(struct osmtpd_ctx *, void *);
210 void *auth_message_new(struct osmtpd_ctx *);
211 void auth_message_free(struct osmtpd_ctx *, void *);
212 void ar_header_add(struct osmtpd_ctx *, const char *);
213 void ar_signature_parse(struct header *, int, int);
214 void ar_signature_parse_v(struct ar_signature *, const char *, const char *);
215 void ar_signature_parse_a(struct ar_signature *, const char *, const char *);
216 void ar_signature_parse_b(struct ar_signature *, const char *, const char *);
217 void ar_signature_parse_bh(struct ar_signature *, const char *, const char *);
218 void ar_signature_parse_c(struct ar_signature *, const char *, const char *);
219 void arc_signature_parse_cv(struct ar_signature *, const char *, const char *);
220 void ar_signature_parse_d(struct ar_signature *, const char *, const char *);
221 void ar_signature_parse_h(struct ar_signature *, const char *, const char *);
222 void dkim_signature_parse_i(struct ar_signature *, const char *, const char *);
223 void arc_signature_parse_i(struct ar_signature *, const char *, const char *);
224 void ar_signature_parse_l(struct ar_signature *, const char *, const char *);
225 void ar_signature_parse_q(struct ar_signature *, const char *, const char *);
226 void ar_signature_parse_s(struct ar_signature *, const char *, const char *);
227 void ar_signature_parse_t(struct ar_signature *, const char *, const char *);
228 void ar_signature_parse_x(struct ar_signature *, const char *, const char *);
229 void ar_signature_parse_z(struct ar_signature *, const char *, const char *);
230 void ar_lookup_record(struct ar_signature *sig, const char *);
231 void ar_signature_verify(struct ar_signature *);
232 void ar_signature_header(EVP_MD_CTX *, struct ar_signature *, struct header *);
233 void ar_signature_state(struct ar_signature *, enum ar_state, const char *);
234 const char *ar_state2str(enum ar_state);
235 void ar_header_cat(struct osmtpd_ctx *, const char *);
236 void ar_body_parse(struct message *, const char *);
237 void ar_body_verify(struct ar_signature *);
238 void ar_rr_resolve(struct asr_result *, void *);
239 char *spf_evaluate_domain(struct spf_record *, const char *);
240 void spf_lookup_record(struct spf_record *, const char *, int,
241 enum ar_state, int, int);
242 void spf_done(struct spf_record *, enum ar_state, const char *);
243 void spf_resolve(struct asr_result *, void *);
244 void spf_resolve_txt(struct dns_rr *, struct spf_query *);
245 void spf_resolve_mx(struct dns_rr *, struct spf_query *);
246 void spf_resolve_a(struct dns_rr *, struct spf_query *);
247 void spf_resolve_aaaa(struct dns_rr *, struct spf_query *);
248 void spf_resolve_cname(struct dns_rr *, struct spf_query *);
249 char* spf_parse_txt(const char *, size_t);
250 int spf_check_cidr(struct spf_record *, struct in_addr *, int );
251 int spf_check_cidr6(struct spf_record *, struct in6_addr *, int );
252 int spf_execute_txt(struct spf_query *);
253 int spf_ar_cat(const char *, struct spf_record *, char **, size_t *, ssize_t *);
254 void auth_message_verify(struct message *);
255 void auth_ar_create(struct osmtpd_ctx *);
256 int ar_signature_ar_cat(const char *, struct ar_signature *, char **, size_t *, ssize_t *);
257 ssize_t auth_ar_cat(char **ar, size_t *n, size_t aroff, const char *fmt, ...)
258 __attribute__((__format__ (printf, 4, 5)));
259 int auth_ar_print(struct osmtpd_ctx *, const char *);
260 int ar_key_text_parse(struct ar_signature *, const char *);
263 /* RFC8617 Section 5.1.1 */
264 static char *arc_seal_headers[] = {
265 "ARC-Authentication-Results",
266 "ARC-Message-Signature",
267 "ARC-Seal"
268 };
270 char *authservid = NULL;
271 int arc = 0;
272 EVP_ENCODE_CTX *ectx = NULL;
274 int
275 main(int argc, char *argv[])
277 int ch;
279 OpenSSL_add_all_digests();
281 if (pledge("tmppath stdio dns", NULL) == -1)
282 osmtpd_err(1, "pledge");
284 while ((ch = getopt(argc, argv, "A")) != -1) {
285 switch (ch) {
286 case 'A':
287 arc = 1;
288 break;
289 default:
290 usage();
294 argc -= optind;
295 argv += optind;
296 if (argc > 1)
297 osmtpd_errx(1, "invalid authservid count");
298 if (argc == 1)
299 authservid = argv[0];
301 if ((ectx = EVP_ENCODE_CTX_new()) == NULL)
302 osmtpd_err(1, "EVP_ENCODE_CTX_new");
304 osmtpd_need(OSMTPD_NEED_SRC|OSMTPD_NEED_FCRDNS|OSMTPD_NEED_IDENTITY|OSMTPD_NEED_GREETING);
305 osmtpd_register_conf(auth_conf);
306 osmtpd_register_filter_dataline(auth_dataline);
307 osmtpd_register_report_connect(1, auth_connect);
308 osmtpd_register_filter_helo(spf_identity);
309 osmtpd_register_filter_ehlo(spf_identity);
310 osmtpd_register_filter_mailfrom(spf_mailfrom);
311 osmtpd_local_session(auth_session_new, auth_session_free);
312 osmtpd_local_message(auth_message_new, auth_message_free);
313 osmtpd_run();
315 return 0;
318 void
319 auth_conf(const char *key, const char *value)
321 const char *end;
323 if (key == NULL) {
324 if (authservid == NULL)
325 osmtpd_errx(1, "Didn't receive admd config option");
326 return;
328 if (strcmp(key, "admd") == 0 && authservid == NULL) {
329 if ((authservid = strdup(value)) == NULL)
330 osmtpd_err(1, "%s: malloc", __func__);
331 end = osmtpd_ltok_skip_value(authservid, 0);
332 if (authservid + strlen(authservid) != end)
333 osmtpd_errx(1, "Invalid authservid");
337 void
338 auth_connect(struct osmtpd_ctx *ctx, const char *rdns, enum osmtpd_status fcrdns,
339 struct sockaddr_storage *src, struct sockaddr_storage *dst)
341 struct session *ses = ctx->local_session;
343 if (fcrdns == OSMTPD_STATUS_OK)
344 ses->iprev = AR_PASS;
345 else
346 ses->iprev = AR_FAIL;
348 memcpy(&ses->src, src, sizeof(struct sockaddr_storage));
350 if (rdns != NULL) {
351 if ((ses->rdns = strdup(rdns)) == NULL)
352 osmtpd_err(1, "%s: malloc", __func__);
356 void
357 spf_identity(struct osmtpd_ctx *ctx, const char *identity)
359 char from[HOST_NAME_MAX + 12];
361 struct session *ses = ctx->local_session;
363 if (identity == NULL) {
364 osmtpd_filter_proceed(ctx);
365 return;
368 if ((ses->identity = strdup(identity)) == NULL)
369 osmtpd_err(1, "%s: strdup", __func__);
371 if (strlen(identity) == 0) {
372 osmtpd_filter_proceed(ctx);
373 return;
376 snprintf(from, sizeof(from), "postmaster@%s", identity);
378 if ((ses->spf_helo = spf_record_new(ctx, from)) == NULL)
379 osmtpd_filter_proceed(ctx);
382 void
383 spf_mailfrom(struct osmtpd_ctx *ctx, const char *from)
385 struct session *ses = ctx->local_session;
387 if (from == NULL || !strlen(from)) {
388 osmtpd_filter_proceed(ctx);
389 return;
392 if (ses->spf_mailfrom)
393 spf_record_free(ses->spf_mailfrom);
395 if ((ses->spf_mailfrom = spf_record_new(ctx, from)) == NULL)
396 osmtpd_filter_proceed(ctx);
399 void
400 auth_dataline(struct osmtpd_ctx *ctx, const char *line)
402 struct message *msg = ctx->local_message;
403 size_t i;
405 if (fprintf(msg->origf, "%s\n", line) < 0)
406 osmtpd_err(1, "Couldn't write to tempfile");
408 if (line[0] == '.') {
409 line++;
410 if (line[0] == '\0') {
411 msg->readdone = 1;
412 for (i = 0; i < msg->nheaders; i++) {
413 if (msg->header[i].sig == NULL)
414 continue;
415 ar_body_verify(msg->header[i].sig);
417 auth_message_verify(msg);
418 return;
421 if (msg->parsing_headers) {
422 ar_header_add(ctx, line);
423 if (line[0] == '\0') {
424 msg->parsing_headers = 0;
425 for (i = 0; i < msg->nheaders; i++) {
426 if (msg->header[i].sig == NULL)
427 continue;
428 if (msg->header[i].sig->query == NULL)
429 ar_signature_verify(
430 msg->header[i].sig);
433 return;
434 } else {
435 ar_body_parse(msg, line);
439 void *
440 spf_record_new(struct osmtpd_ctx *ctx, const char *from)
442 int i;
443 const char *at;
444 struct spf_record *spf;
446 if ((spf = malloc(sizeof(*spf))) == NULL)
447 osmtpd_err(1, "%s: malloc", __func__);
449 spf->ctx = ctx;
450 spf->state = AR_NONE;
451 spf->state_reason = NULL;
452 spf->nqueries = 0;
453 spf->running = 0;
454 spf->done = 0;
456 for (i = 0; i < SPF_DNS_LOOKUP_LIMIT; i++) {
457 spf->queries[i].domain = NULL;
458 spf->queries[i].txt = NULL;
459 spf->queries[i].eva = NULL;
462 from = osmtpd_ltok_skip_cfws(from, 1);
464 if ((at = osmtpd_ltok_skip_local_part(from, 0)) == NULL)
465 goto fail;
467 if ((spf->sender_local = strndup(from, at - from)) == NULL)
468 osmtpd_err(1, "%s: malloc", __func__);
470 if (*at != '@')
471 goto fail_local;
472 at++;
474 if ((from = osmtpd_ltok_skip_domain(at, 0)) == NULL)
475 goto fail_local;
478 if ((spf->sender_domain = strndup(at, from - at)) == NULL)
479 osmtpd_err(1, "%s: malloc", __func__);
481 spf_lookup_record(
482 spf, spf->sender_domain, T_TXT, AR_PASS, 0, 0);
484 return spf;
486 fail_local:
487 free(spf->sender_local);
489 fail:
490 free(spf);
491 return NULL;
494 void
495 spf_record_free(struct spf_record *spf)
497 int i;
499 for (i = 0; i < SPF_DNS_LOOKUP_LIMIT; i++) {
500 if (spf->queries[i].domain)
501 free(spf->queries[i].domain);
502 if (spf->queries[i].txt)
503 free(spf->queries[i].txt);
504 if (spf->queries[i].eva)
505 event_asr_abort(spf->queries[i].eva);
508 free(spf->sender_local);
509 free(spf->sender_domain);
511 free(spf);
514 void *
515 auth_session_new(struct osmtpd_ctx *ctx)
517 struct session *ses;
519 if ((ses = malloc(sizeof(*ses))) == NULL)
520 osmtpd_err(1, "%s: malloc", __func__);
522 ses->ctx = ctx;
523 ses->iprev = AR_NONE;
525 ses->spf_helo = NULL;
526 ses->spf_mailfrom = NULL;
528 ses->identity = NULL;
529 ses->rdns = NULL;
531 return ses;
534 void
535 auth_session_free(struct osmtpd_ctx *ctx, void *data)
537 struct session *ses = data;
539 if (ses->spf_helo)
540 spf_record_free(ses->spf_helo);
541 if (ses->spf_mailfrom)
542 spf_record_free(ses->spf_mailfrom);
543 if (ses->identity)
544 free(ses->identity);
545 if (ses->rdns)
546 free(ses->rdns);
548 free(ses);
551 void *
552 auth_message_new(struct osmtpd_ctx *ctx)
554 struct message *msg;
556 if ((msg = malloc(sizeof(*msg))) == NULL)
557 osmtpd_err(1, "%s: malloc", __func__);
559 if ((msg->origf = tmpfile()) == NULL) {
560 osmtpd_warn(NULL, "Can't open tempfile");
561 free(msg);
562 return NULL;
564 msg->ctx = ctx;
565 msg->parsing_headers = 1;
566 msg->body_whitelines = 0;
567 msg->has_body = 0;
568 msg->header = NULL;
569 msg->nheaders = 0;
570 msg->readdone = 0;
571 msg->nqueries = 0;
572 msg->last_arc_seal = NULL;
573 msg->last_arc_sign = NULL;
574 if ((msg->arc_seals =
575 calloc(ARC_MAX_I + 1, sizeof(*msg->arc_seals))) == NULL)
576 osmtpd_err(1, "%s: malloc", __func__);
577 if ((msg->arc_signs =
578 calloc(ARC_MAX_I + 1, sizeof(*msg->arc_signs))) == NULL)
579 osmtpd_err(1, "%s: malloc", __func__);
581 return msg;
584 void
585 auth_message_free(struct osmtpd_ctx *ctx, void *data)
587 struct message *msg = data;
588 size_t i, j;
590 fclose(msg->origf);
591 for (i = 0; i < msg->nheaders; i++) {
592 if (msg->header[i].sig != NULL) {
593 free(msg->header[i].sig->b);
594 EVP_MD_CTX_free(msg->header[i].sig->bhctx);
595 if (msg->header[i].sig->h != arc_seal_headers) {
596 for (j = 0; msg->header[i].sig->h != NULL &&
597 msg->header[i].sig->h[j] != NULL; j++)
598 free(msg->header[i].sig->h[j]);
599 free(msg->header[i].sig->h);
601 EVP_PKEY_free(msg->header[i].sig->p);
602 if (msg->header[i].sig->query)
603 event_asr_abort(msg->header[i].sig->query);
605 free(msg->header[i].buf);
606 free(msg->header[i].sig);
608 free(msg->header);
609 free(msg->arc_seals);
610 free(msg->arc_signs);
611 free(msg);
614 void
615 ar_header_add(struct osmtpd_ctx *ctx, const char *line)
617 struct message *msg = ctx->local_message;
618 const char *start, *end, *verify;
619 struct header *headers;
620 size_t i;
622 if (msg->nheaders > 0 &&
623 msg->header[msg->nheaders - 1].readdone == 0) {
624 if (line[0] != ' ' && line[0] != '\t') {
625 msg->header[msg->nheaders - 1].readdone = 1;
626 start = msg->header[msg->nheaders - 1].buf;
627 end = osmtpd_ltok_skip_field_name(start, 0);
628 /* In case someone uses an obs-optional */
629 if (end != NULL)
630 verify = osmtpd_ltok_skip_wsp(end, 1);
631 if (end != NULL &&
632 strncasecmp(
633 start, "DKIM-Signature", end - start) == 0 &&
634 verify[0] == ':')
635 ar_signature_parse(
636 &msg->header[msg->nheaders - 1], 1, 0);
637 else if (end != NULL &&
638 strncasecmp(
639 start, "ARC-Message-Signature", end - start) == 0 &&
640 verify[0] == ':')
641 ar_signature_parse(
642 &msg->header[msg->nheaders - 1], 0, 0);
643 else if (end != NULL &&
644 strncasecmp(
645 start, "ARC-Seal", end - start) == 0 &&
646 verify[0] == ':')
647 ar_signature_parse(
648 &msg->header[msg->nheaders - 1], 0, 1);
650 if (line[0] == '\0')
651 return;
652 } else {
653 ar_header_cat(ctx, line);
654 return;
657 if (msg->nheaders % 10 == 0) {
658 if ((headers = recallocarray(msg->header, msg->nheaders,
659 msg->nheaders + 10, sizeof(*msg->header))) == NULL)
660 osmtpd_err(1, "%s: malloc", __func__);
661 msg->header = headers;
662 for (i = 0; i < msg->nheaders; i++) {
663 if (msg->header[i].sig == NULL)
664 continue;
665 msg->header[i].sig->header = &msg->header[i];
668 msg->header[msg->nheaders].msg = msg;
669 msg->nheaders++;
670 ar_header_cat(ctx, line);
673 void
674 ar_header_cat(struct osmtpd_ctx *ctx, const char *line)
676 struct message *msg = ctx->local_message;
677 struct header *header = &msg->header[msg->nheaders - 1];
678 char *buf;
680 size_t needed = header->buflen + strlen(line) + 2;
682 if (needed > (header->buflen / 1024) + 1) {
683 buf = reallocarray(header->buf, (needed / 1024) + 1, 1024);
684 if (buf == NULL)
685 osmtpd_err(1, "%s: malloc", __func__);
686 header->buf = buf;
688 header->buflen += snprintf(header->buf + header->buflen,
689 (((needed / 1024) + 1) * 1024) - header->buflen, "%s%s",
690 header->buflen == 0 ? "" : "\r\n", line);
693 void
694 ar_signature_parse(struct header *header, int dkim, int seal)
696 struct ar_signature *sig, *last;
697 const char *buf, *i, *end;
698 char tagname[3];
699 char subdomain[HOST_NAME_MAX + 1];
700 size_t ilen, dlen;
702 /* Format checked by ar_header_add */
703 buf = osmtpd_ltok_skip_field_name(header->buf, 0);
704 buf = osmtpd_ltok_skip_wsp(buf, 1) + 1;
706 if ((header->sig = calloc(1, sizeof(*header->sig))) == NULL)
707 osmtpd_err(1, "%s: malloc", __func__);
708 sig = header->sig;
709 sig->header = header;
710 sig->dkim = dkim;
711 sig->seal = seal;
712 sig->l = -1;
713 sig->t = -1;
714 sig->x = -1;
716 end = osmtpd_ltok_skip_tag_list(buf, 0);
717 if (end == NULL || end[0] != '\0') {
718 ar_signature_state(sig, AR_PERMERROR, "Invalid tag-list");
719 return;
722 while (buf[0] != '\0') {
723 buf = osmtpd_ltok_skip_fws(buf, 1);
724 end = osmtpd_ltok_skip_tag_name(buf, 0);
726 /* Unknown tag-name */
727 if ((size_t)(end - buf) >= sizeof(tagname))
728 tagname[0] = '\0';
729 else
730 strlcpy(tagname, buf, (end - buf) + 1);
731 buf = osmtpd_ltok_skip_fws(end, 1);
732 /* '=' */
733 buf = osmtpd_ltok_skip_fws(buf + 1, 1);
734 end = osmtpd_ltok_skip_tag_value(buf, 1);
735 if (dkim && strcmp(tagname, "v") == 0)
736 ar_signature_parse_v(sig, buf, end);
737 else if (strcmp(tagname, "a") == 0)
738 ar_signature_parse_a(sig, buf, end);
739 else if (strcmp(tagname, "b") == 0)
740 ar_signature_parse_b(sig, buf, end);
741 else if (!seal && strcmp(tagname, "bh") == 0)
742 ar_signature_parse_bh(sig, buf, end);
743 else if (!seal && strcmp(tagname, "c") == 0)
744 ar_signature_parse_c(sig, buf, end);
745 else if (seal && strcmp(tagname, "cv") == 0)
746 arc_signature_parse_cv(sig, buf, end);
747 else if (strcmp(tagname, "d") == 0)
748 ar_signature_parse_d(sig, buf, end);
749 else if (!seal && strcmp(tagname, "h") == 0)
750 ar_signature_parse_h(sig, buf, end);
751 else if (dkim && strcmp(tagname, "i") == 0)
752 dkim_signature_parse_i(sig, buf, end);
753 else if (!dkim && strcmp(tagname, "i") == 0)
754 arc_signature_parse_i(sig, buf, end);
755 else if (!seal && strcmp(tagname, "l") == 0)
756 ar_signature_parse_l(sig, buf, end);
757 else if (!seal && strcmp(tagname, "q") == 0)
758 ar_signature_parse_q(sig, buf, end);
759 else if (strcmp(tagname, "s") == 0)
760 ar_signature_parse_s(sig, buf, end);
761 else if (strcmp(tagname, "t") == 0)
762 ar_signature_parse_t(sig, buf, end);
763 else if (!seal && strcmp(tagname, "x") == 0)
764 ar_signature_parse_x(sig, buf, end);
765 else if (!seal && strcmp(tagname, "z") == 0)
766 ar_signature_parse_z(sig, buf, end);
768 buf = osmtpd_ltok_skip_fws(end, 1);
769 if (buf[0] == ';')
770 buf++;
771 else if (buf[0] != '\0') {
772 ar_signature_state(sig, AR_PERMERROR,
773 "Invalid tag-list");
774 return;
777 if (sig->state != AR_UNKNOWN)
778 return;
780 if (dkim && sig->v != 1)
781 ar_signature_state(sig, AR_PERMERROR, "Missing v tag");
782 else if (sig->ah == NULL)
783 ar_signature_state(sig, AR_PERMERROR, "Missing a tag");
784 else if (sig->b == NULL)
785 ar_signature_state(sig, AR_PERMERROR, "Missing b tag");
786 else if (!seal && sig->bhsz == 0)
787 ar_signature_state(sig, AR_PERMERROR, "Missing bh tag");
788 else if (seal && sig->cv == AR_UNKNOWN)
789 ar_signature_state(sig, AR_PERMERROR, "Missing cv tag");
790 else if (sig->d[0] == '\0')
791 ar_signature_state(sig, AR_PERMERROR, "Missing d tag");
792 else if (!dkim && sig->arc_i == 0)
793 ar_signature_state(sig, AR_PERMERROR, "Missing i tag");
794 else if (!seal && sig->h == NULL)
795 ar_signature_state(sig, AR_PERMERROR, "Missing h tag");
796 else if (sig->s[0] == '\0')
797 ar_signature_state(sig, AR_PERMERROR, "Missing s tag");
798 if (sig->state != AR_UNKNOWN)
799 return;
801 if (seal) {
802 sig->c = CANON_HEADER_RELAXED;
803 sig->h = arc_seal_headers;
806 if (sig->i != NULL) {
807 i = osmtpd_ltok_skip_local_part(sig->i, 1) + 1;
808 ilen = sig->isz - (size_t)(i - sig->i);
809 dlen = strlen(sig->d);
810 if (ilen < dlen) {
811 ar_signature_state(sig, AR_PERMERROR,
812 "i tag not subdomain of d");
813 return;
815 i += ilen - dlen;
816 if ((i[-1] != '.' && i[-1] != '@') ||
817 strncasecmp(i, sig->d, dlen) != 0) {
818 ar_signature_state(sig, AR_PERMERROR,
819 "i tag not subdomain of d");
820 return;
823 if (sig->t != -1 && sig->x != -1 && sig->t > sig->x) {
824 ar_signature_state(sig, AR_PERMERROR, "t tag after x tag");
825 return;
828 if (!dkim) {
829 if (seal) {
830 last = header->msg->last_arc_seal;
831 header->msg->last_arc_seal = sig;
832 if (header->msg->arc_seals[sig->arc_i] == NULL)
833 header->msg->arc_seals[sig->arc_i] = sig;
834 } else {
835 last = header->msg->last_arc_sign;
836 header->msg->last_arc_sign = sig;
837 if (header->msg->arc_signs[sig->arc_i] == NULL)
838 header->msg->arc_signs[sig->arc_i] = sig;
841 if (last != NULL) {
842 if ((last->arc_i - 1) != sig->arc_i) {
843 ar_signature_state(
844 last, AR_PERMERROR, "Invalind i-chain");
845 return;
848 switch (last->cv) {
849 case AR_UNKNOWN:
850 case AR_FAIL:
851 break;
852 case AR_PASS:
853 if (sig->cv == AR_PASS)
854 break;
855 default:
856 ar_signature_state(
857 last, AR_PERMERROR, "Invalind cv-chain");
858 return;
863 if ((size_t)snprintf(subdomain, sizeof(subdomain), "%s._domainkey.%s",
864 sig->s, sig->d) >= sizeof(subdomain)) {
865 ar_signature_state(sig, AR_PERMERROR,
866 "dns/txt query too long");
867 return;
870 ar_lookup_record(sig, subdomain);
873 void
874 ar_lookup_record(struct ar_signature *sig, const char *domain)
876 struct asr_query *query;
878 if (sig->state != AR_UNKNOWN)
879 return;
881 sig->nqueries++;
883 if (sig->query != NULL) {
884 event_asr_abort(sig->query);
885 sig->query = NULL;
886 sig->header->msg->nqueries--;
889 if ((query = res_query_async(domain, C_IN, T_TXT, NULL)) == NULL)
890 osmtpd_err(1, "res_query_async");
892 if ((sig->query = event_asr_run(query, ar_rr_resolve, sig)) == NULL)
893 osmtpd_err(1, "res_query_async");
895 sig->header->msg->nqueries++;
898 void
899 ar_signature_parse_v(struct ar_signature *sig, const char *start, const char *end)
901 if (sig->v != 0) { /* Duplicate tag */
902 ar_signature_state(sig, AR_PERMERROR, "Duplicate v tag");
903 return;
905 /* Unsupported version */
906 if (start[0] != '1' || start + 1 != end)
907 ar_signature_state(sig, AR_NEUTRAL, "Unsupported v tag");
908 else
909 sig->v = 1;
912 void
913 ar_signature_parse_a(struct ar_signature *sig, const char *start, const char *end)
915 char ah[sizeof("sha256")];
917 if (sig->ah != NULL) {
918 ar_signature_state(sig, AR_PERMERROR, "Duplicate a tag");
919 return;
922 if (osmtpd_ltok_skip_sig_a_tag_alg(start, 0) != end) {
923 ar_signature_state(sig, AR_PERMERROR, "Invalid a tag");
924 return;
926 sig->a = start;
927 sig->asz = (size_t)(end - start);
928 if (strncmp(start, "rsa-", 4) == 0) {
929 start += 4;
930 sig->ak = EVP_PKEY_RSA;
931 sig->sephash = 0;
932 #if HAVE_ED25519
933 } else if (strncmp(start, "ed25519-", 8) == 0) {
934 start += 8;
935 sig->ak = EVP_PKEY_ED25519;
936 sig->sephash = 1;
937 #endif
938 } else {
939 ar_signature_state(sig, AR_NEUTRAL, "Unsuppored a tag k");
940 return;
942 if ((size_t)(end - start) >= sizeof(ah)) {
943 ar_signature_state(sig, AR_NEUTRAL, "Unsuppored a tag h");
944 return;
946 strlcpy(ah, start, sizeof(ah));
947 ah[end - start] = '\0';
948 if ((sig->ah = EVP_get_digestbyname(ah)) == NULL) {
949 ar_signature_state(sig, AR_NEUTRAL, "Unsuppored a tag h");
950 return;
952 if ((sig->bhctx = EVP_MD_CTX_new()) == NULL)
953 osmtpd_err(1, "EVP_MD_CTX_new");
955 if (EVP_DigestInit_ex(sig->bhctx, sig->ah, NULL) <= 0) {
956 ar_signature_state(sig, AR_FAIL, "Unsuppored a tag ah");
957 return;
961 void
962 ar_signature_parse_b(struct ar_signature *sig, const char *start, const char *end)
964 int decodesz;
965 size_t i, j;
967 if (sig->b != NULL) {
968 ar_signature_state(sig, AR_PERMERROR, "Duplicate b tag");
969 return;
971 sig->bheader = start;
972 sig->bheadersz = end - start;
973 if ((sig->b = malloc(((sig->bheadersz / 4) + 1) * 3)) == NULL)
974 osmtpd_err(1, "%s: malloc", __func__);
975 /* EVP_DecodeBlock doesn't handle internal whitespace */
976 EVP_DecodeInit(ectx);
977 if (EVP_DecodeUpdate(ectx, sig->b, &decodesz, start,
978 (int)(end - start)) == -1) {
979 ar_signature_state(sig, AR_PERMERROR, "Invalid b tag");
980 return;
982 sig->bsz = decodesz;
983 if (EVP_DecodeFinal(ectx, sig->b + sig->bsz,
984 &decodesz) == -1) {
985 ar_signature_state(sig, AR_PERMERROR, "Invalid b tag");
986 return;
988 sig->bsz += decodesz;
989 for (i = 0, j = 0;
990 i < sig->bheadersz && j < HEADER_B_MAX_LEN; i++) {
991 if (isalnum(sig->bheader[i]) || sig->bheader[i] == '/'
992 || sig->bheader[i] == '+' || sig->bheader[i] == '=')
993 sig->bheaderclean[j++] = sig->bheader[i];
995 sig->bheaderclean[j] = '\0';
998 void
999 ar_signature_parse_bh(struct ar_signature *sig, const char *start, const char *end)
1001 const char *b64;
1002 size_t n;
1003 int decodesz;
1005 if (sig->bhsz != 0) {
1006 ar_signature_state(sig, AR_PERMERROR, "Duplicate bh tag");
1007 return;
1010 * EVP_Decode* expects sig->bh to be large enough,
1011 * so count the actual b64 characters.
1013 b64 = start;
1014 n = 0;
1015 while (1) {
1016 b64 = osmtpd_ltok_skip_fws(b64, 1);
1017 if (osmtpd_ltok_skip_alphadigitps(b64, 0) == NULL)
1018 break;
1019 n++;
1020 b64++;
1022 if (b64[0] == '=') {
1023 n++;
1024 b64 = osmtpd_ltok_skip_fws(b64 + 1, 1);
1025 if (b64[0] == '=') {
1026 n++;
1027 b64++;
1030 /* Invalid tag value */
1031 if (b64 != end || n % 4 != 0 || (n / 4) * 3 > sizeof(sig->bh)) {
1032 ar_signature_state(sig, AR_PERMERROR, "Invalid bh tag");
1033 return;
1035 /* EVP_DecodeBlock doesn't handle internal whitespace */
1036 EVP_DecodeInit(ectx);
1037 if (EVP_DecodeUpdate(ectx, sig->bh, &decodesz, start,
1038 (int)(end - start)) == -1) {
1039 /* Paranoia check */
1040 ar_signature_state(sig, AR_PERMERROR, "Invalid bh tag");
1041 return;
1043 sig->bhsz = decodesz;
1044 if (EVP_DecodeFinal(ectx, sig->bh + sig->bhsz, &decodesz) == -1) {
1045 /* Paranoia check */
1046 ar_signature_state(sig, AR_PERMERROR, "Invalid bh tag");
1047 return;
1049 sig->bhsz += decodesz;
1052 void
1053 ar_signature_parse_c(struct ar_signature *sig, const char *start, const char *end)
1055 if (sig->c != 0) {
1056 ar_signature_state(sig, AR_PERMERROR, "Duplicate c tag");
1057 return;
1059 if (strncmp(start, "simple", 6) == 0) {
1060 sig->c = CANON_HEADER_SIMPLE;
1061 start += 6;
1062 } else if (strncmp(start, "relaxed", 7) == 0) {
1063 sig->c = CANON_HEADER_RELAXED;
1064 start += 7;
1065 } else {
1066 ar_signature_state(sig, AR_PERMERROR, "Invalid c tag");
1067 return;
1069 if (start[0] == '/') {
1070 start++;
1071 if (strncmp(start, "simple", 6) == 0) {
1072 sig->c |= CANON_BODY_SIMPLE;
1073 start += 6;
1074 } else if (strncmp(start, "relaxed", 7) == 0) {
1075 sig->c |= CANON_BODY_RELAXED;
1076 start += 7;
1077 } else {
1078 ar_signature_state(sig, AR_PERMERROR,
1079 "Invalid c tag");
1080 return;
1084 if (start != end) {
1085 ar_signature_state(sig, AR_PERMERROR, "Invalid c tag");
1086 return;
1088 sig->c |= CANON_DONE;
1091 void
1092 arc_signature_parse_cv(struct ar_signature *sig, const char *start, const char *end)
1094 if (sig->cv != AR_UNKNOWN) {
1095 ar_signature_state(sig, AR_PERMERROR, "Duplicate cv tag");
1096 return;
1099 if (strncmp(start, "pass", 4) == 0) {
1100 sig->cv = AR_PASS;
1101 } else if (strncmp(start, "fail", 4) == 0) {
1102 sig->cv = AR_FAIL;
1103 } else if (strncmp(start, "none", 4) == 0) {
1104 sig->cv = AR_NONE;
1105 } else {
1106 ar_signature_state(sig, AR_PERMERROR, "Invalid cv tag");
1107 return;
1111 void
1112 ar_signature_parse_d(struct ar_signature *sig, const char *start, const char *end)
1114 if (sig->d[0] != '\0') {
1115 ar_signature_state(sig, AR_PERMERROR, "Duplicate d tag");
1116 return;
1118 if (osmtpd_ltok_skip_sig_d_tag_value(start, 0) != end ||
1119 (size_t)(end - start) >= sizeof(sig->d)) {
1120 ar_signature_state(sig, AR_PERMERROR, "Invalid d tag");
1121 return;
1123 strlcpy(sig->d, start, end - start + 1);
1126 void
1127 ar_signature_parse_h(struct ar_signature *sig, const char *start, const char *end)
1129 const char *h;
1130 size_t n = 0;
1132 if (sig->h != NULL) {
1133 ar_signature_state(sig, AR_PERMERROR, "Duplicate h tag");
1134 return;
1136 if (osmtpd_ltok_skip_sig_h_tag_value(start, 0) < end) {
1137 ar_signature_state(sig, AR_PERMERROR, "Invalid h tag");
1138 return;
1140 h = start;
1141 while (1) {
1142 if ((h = osmtpd_ltok_skip_hdr_name(h, 0)) == NULL) {
1143 ar_signature_state(sig, AR_PERMERROR,
1144 "Invalid h tag");
1145 return;
1147 n++;
1148 /* ';' is part of hdr-name */
1149 if (h > end) {
1150 h = end;
1151 break;
1153 h = osmtpd_ltok_skip_fws(h, 1);
1154 if (h[0] != ':')
1155 break;
1156 h = osmtpd_ltok_skip_fws(h + 1, 1);
1158 if ((sig->h = calloc(n + 1, sizeof(*sig->h))) == NULL)
1159 osmtpd_err(1, "%s: malloc", __func__);
1160 n = 0;
1161 h = start;
1162 while (1) {
1163 h = osmtpd_ltok_skip_hdr_name(start, 0);
1164 /* ';' is part of hdr-name */
1165 if (h > end) {
1166 sig->h[n] = strndup(start, end - start);
1167 break;
1169 if ((sig->h[n++] = strndup(start, h - start)) == NULL)
1170 osmtpd_err(1, "%s: malloc", __func__);
1171 start = osmtpd_ltok_skip_fws(h, 1);
1172 if (start[0] != ':')
1173 break;
1174 start = osmtpd_ltok_skip_fws(start + 1, 1);
1178 void
1179 dkim_signature_parse_i(struct ar_signature *sig, const char *start, const char *end)
1181 if (sig->i != NULL) {
1182 ar_signature_state(sig, AR_PERMERROR, "Duplicate i tag");
1183 return;
1185 if (osmtpd_ltok_skip_sig_i_tag_value(start, 0) != end) {
1186 ar_signature_state(sig, AR_PERMERROR, "Invalid i tag");
1187 return;
1189 sig->i = start;
1190 sig->isz = (size_t)(end - start);
1193 void
1194 arc_signature_parse_i(struct ar_signature *sig, const char *start, const char *end)
1196 char *ep;
1197 long i;
1198 if (sig->arc_i != 0) {
1199 ar_signature_state(sig, AR_PERMERROR, "Duplicate i tag");
1200 return;
1202 if (osmtpd_ltok_skip_digit(start, 0) != end) {
1203 ar_signature_state(sig, AR_PERMERROR, "Invalid i tag");
1204 return;
1206 i = strtol(start, &ep, 10);
1207 if (i < ARC_MIN_I || i > ARC_MAX_I || ep != end) {
1208 ar_signature_state(sig, AR_PERMERROR, "Invalid i tag");
1209 return;
1211 sig->arc_i = (int) i;
1214 void
1215 ar_signature_parse_l(struct ar_signature *sig, const char *start, const char *end)
1217 long long l;
1218 char *lend;
1220 if (sig->l != -1) { /* Duplicate tag */
1221 ar_signature_state(sig, AR_PERMERROR, "Duplicate l tag");
1222 return;
1224 errno = 0;
1225 l = strtoll(start, &lend, 10);
1226 /* > 76 digits in stroll is an overflow */
1227 if (osmtpd_ltok_skip_digit(start, 0) == NULL ||
1228 lend != end || errno != 0) {
1229 ar_signature_state(sig, AR_PERMERROR, "Invalid l tag");
1230 return;
1232 if (l > SSIZE_MAX) {
1233 ar_signature_state(sig, AR_PERMERROR, "l tag too large");
1234 return;
1236 sig->l = (ssize_t)l;
1239 void
1240 ar_signature_parse_q(struct ar_signature *sig, const char *start, const char *end)
1242 const char *qend;
1244 if (sig->q != 0) {
1245 ar_signature_state(sig, AR_PERMERROR, "Duplicate q tag");
1246 return;
1249 while (1) {
1250 start = osmtpd_ltok_skip_fws(start, 1);
1251 qend = osmtpd_ltok_skip_sig_q_tag_method(start, 0);
1252 if (qend == NULL) {
1253 ar_signature_state(sig, AR_PERMERROR, "Invalid q tag");
1254 return;
1256 if (strncmp(start, "dns/txt", qend - start) == 0)
1257 sig->q = 1;
1258 start = osmtpd_ltok_skip_fws(qend, 1);
1259 if (start[0] != ':')
1260 break;
1262 if (start != end) {
1263 ar_signature_state(sig, AR_PERMERROR, "Invalid q tag");
1264 return;
1266 if (sig->q != 1) {
1267 sig->q = 1;
1268 ar_signature_state(sig, AR_NEUTRAL, "No useable q found");
1269 return;
1273 void
1274 ar_signature_parse_s(struct ar_signature *sig, const char *start, const char *end)
1276 if (sig->s[0] != '\0') {
1277 ar_signature_state(sig, AR_PERMERROR, "Duplicate s tag");
1278 return;
1280 if (osmtpd_ltok_skip_selector(start, 0) != end) {
1281 ar_signature_state(sig, AR_PERMERROR, "Invalid s tag");
1282 return;
1284 strlcpy(sig->s, start, end - start + 1);
1287 void
1288 ar_signature_parse_t(struct ar_signature *sig, const char *start, const char *end)
1290 char *tend;
1292 if (sig->t != -1) {
1293 ar_signature_state(sig, AR_PERMERROR, "Duplicate t tag");
1294 return;
1296 errno = 0;
1297 sig->t = strtoll(start, &tend, 10);
1298 if (osmtpd_ltok_skip_digit(start, 0) == NULL || tend != end ||
1299 tend - start > 12 || errno != 0) {
1300 ar_signature_state(sig, AR_PERMERROR, "Invalid t tag");
1301 return;
1305 void
1306 ar_signature_parse_x(struct ar_signature *sig, const char *start, const char *end)
1308 char *xend;
1310 if (sig->x != -1) {
1311 ar_signature_state(sig, AR_PERMERROR, "Duplicate x tag");
1312 return;
1314 errno = 0;
1315 sig->x = strtoll(start, &xend, 10);
1316 if (osmtpd_ltok_skip_digit(start, 0) == NULL || xend != end ||
1317 xend - start > 12 || errno != 0) {
1318 ar_signature_state(sig, AR_PERMERROR, "Invalid x tag");
1319 return;
1323 void
1324 ar_signature_parse_z(struct ar_signature *sig, const char *start, const char *end)
1326 if (sig->z != 0) {
1327 ar_signature_state(sig, AR_PERMERROR, "Duplicate z tag");
1328 return;
1331 sig->z = 1;
1332 if (osmtpd_ltok_skip_sig_z_tag_value(start, 0) != end) {
1333 ar_signature_state(sig, AR_PERMERROR, "Invalid z tag");
1334 return;
1338 void
1339 ar_signature_verify(struct ar_signature *sig)
1341 struct message *msg = sig->header->msg;
1342 static EVP_MD_CTX *bctx = NULL;
1343 char digest[EVP_MAX_MD_SIZE];
1344 unsigned int digestsz;
1345 const char *end;
1346 size_t i, header;
1348 if (sig->state != AR_UNKNOWN)
1349 return;
1351 if (sig->cv == AR_FAIL ||
1352 (sig->cv == AR_PASS && sig->arc_i == 1) ||
1353 (sig->cv == AR_NONE && sig->arc_i > 1)) {
1354 ar_signature_state(sig, AR_FAIL, "cv tag");
1355 return;
1358 if (bctx == NULL) {
1359 if ((bctx = EVP_MD_CTX_new()) == NULL)
1360 osmtpd_err(1, "EVP_MD_CTX_new");
1362 EVP_MD_CTX_reset(bctx);
1363 if (!sig->sephash) {
1364 if (EVP_DigestVerifyInit(bctx, NULL, sig->ah, NULL,
1365 sig->p) != 1) {
1366 ar_signature_state(sig, AR_FAIL, "ah tag");
1367 return;
1369 } else {
1370 if (EVP_DigestInit_ex(bctx, sig->ah, NULL) != 1) {
1371 ar_signature_state(sig, AR_FAIL, "ah tag");
1372 return;
1376 for (i = 0; i < msg->nheaders; i++)
1377 msg->header[i].parsed = 0;
1379 if (sig->h != NULL) {
1380 for (header = 0; sig->h[header] != NULL; header++) {
1381 for (i = msg->nheaders; i > 0; ) {
1382 i--;
1383 if (msg->header[i].parsed ||
1384 strncasecmp(msg->header[i].buf, sig->h[header],
1385 strlen(sig->h[header])) != 0 ||
1386 msg->header[i].sig == sig)
1387 continue;
1388 end = osmtpd_ltok_skip_fws(
1389 msg->header[i].buf + strlen(sig->h[header]), 1);
1390 if (end[0] != ':')
1391 continue;
1392 ar_signature_header(bctx, sig, &(msg->header[i]));
1393 msg->header[i].parsed = 1;
1394 break;
1399 ar_signature_header(bctx, sig, sig->header);
1400 if (!sig->sephash) {
1401 if (EVP_DigestVerifyFinal(bctx, sig->b, sig->bsz) != 1) {
1402 ar_signature_state(sig, AR_FAIL, "b mismatch");
1403 return;
1405 } else {
1406 if (EVP_DigestFinal_ex(bctx, digest, &digestsz) == 0)
1407 osmtpd_err(1, "EVP_DigestFinal_ex");
1409 if (EVP_DigestVerifyInit(bctx, NULL, NULL, NULL, sig->p) != 1)
1410 osmtpd_err(1, "EVP_DigestVerifyInit");
1412 switch (EVP_DigestVerify(bctx, sig->b, sig->bsz, digest,
1413 digestsz)) {
1414 case 1:
1415 break;
1416 case 0:
1417 ar_signature_state(sig, AR_FAIL, "b mismatch");
1418 return;
1419 default:
1420 osmtpd_err(1, "EVP_DigestVerify");
1424 if (sig->arc_i > 0) {
1425 if (msg->arc_seals[sig->arc_i] == NULL ||
1426 msg->arc_signs[sig->arc_i] == NULL) {
1427 ar_signature_state(sig, AR_PERMERROR, "missed ARC header");
1428 return;
1431 if (msg->arc_seals[sig->arc_i]->state == AR_UNKNOWN ||
1432 msg->arc_signs[sig->arc_i]->state == AR_UNKNOWN)
1433 return;
1435 if (msg->arc_seals[sig->arc_i]->state !=
1436 msg->arc_signs[sig->arc_i]->state) {
1437 ar_signature_state(
1438 msg->arc_signs[sig->arc_i], AR_FAIL, NULL);
1439 return;
1444 /* EVP_DigestVerifyUpdate is a macro, so we can't alias this on a variable */
1445 #define ar_b_digest_update(a, b, c) \
1446 (sig->sephash ? EVP_DigestUpdate((a), (b), (c)) :\
1447 EVP_DigestVerifyUpdate((a), (b), (c)))
1449 void
1450 ar_signature_header(EVP_MD_CTX *bctx, struct ar_signature *sig,
1451 struct header *header)
1453 char c;
1454 const char *ptr = header->buf, *end;
1455 int inhdrname = 1;
1456 int canon = sig->c & CANON_HEADER;
1458 for (ptr = header->buf; ptr[0] != '\0'; ptr++) {
1459 if (inhdrname) {
1460 if (canon == CANON_HEADER_RELAXED) {
1461 ptr = osmtpd_ltok_skip_fws(ptr, 1);
1462 c = tolower(ptr[0]);
1463 } else
1464 c = ptr[0];
1465 if (c == ':') {
1466 inhdrname = 0;
1467 if (canon == CANON_HEADER_RELAXED)
1468 ptr = osmtpd_ltok_skip_fws(
1469 ptr + 1, 1) - 1;
1471 if (ar_b_digest_update(bctx, &c, 1) == 0)
1472 osmtpd_errx(1, "ar_b_digest_update");
1473 continue;
1475 end = osmtpd_ltok_skip_fws(ptr, 1);
1476 if (end == ptr) {
1477 if (sig->header == header && ptr == sig->bheader) {
1478 ptr = osmtpd_ltok_skip_tag_value(
1479 ptr, 0) - 1;
1480 continue;
1482 if (ar_b_digest_update(bctx, ptr, 1) == 0)
1483 osmtpd_errx(1, "ar_b_digest_update");
1484 } else {
1485 if (canon == CANON_HEADER_RELAXED) {
1486 if (end[0] == '\0')
1487 continue;
1488 if (ar_b_digest_update(bctx, " ", 1) == 0)
1489 osmtpd_errx(1, "ar_b_digest_update");
1490 } else {
1491 if (ar_b_digest_update(bctx, ptr,
1492 end - ptr) == 0)
1493 osmtpd_errx(1, "ar_b_digest_update");
1495 ptr = end - 1;
1499 if (sig->header != header) {
1500 if (ar_b_digest_update(bctx, "\r\n", 2) == 0)
1501 osmtpd_errx(1, "ar_b_digest_update");
1505 void
1506 ar_signature_state(struct ar_signature *sig, enum ar_state state,
1507 const char *reason)
1509 if (sig->query != NULL) {
1510 event_asr_abort(sig->query);
1511 sig->query = NULL;
1512 sig->header->msg->nqueries--;
1514 switch (sig->state) {
1515 case AR_UNKNOWN:
1516 case AR_NONE:
1517 break;
1518 case AR_FAIL:
1519 if (state == AR_PERMERROR)
1520 break;
1521 case AR_PASS:
1522 case AR_SOFTFAIL:
1523 osmtpd_errx(1, "Unexpected transition: %s -> %",
1524 ar_state2str(sig->state), ar_state2str(state));
1525 case AR_POLICY:
1526 if (state == AR_PASS)
1527 return;
1528 break;
1529 case AR_NEUTRAL:
1530 if (state == AR_PASS)
1531 return;
1532 if (state == AR_TEMPERROR || state == AR_PERMERROR)
1533 break;
1534 osmtpd_errx(1, "Unexpected transition: %s -> %",
1535 ar_state2str(sig->state), ar_state2str(state));
1536 case AR_TEMPERROR:
1537 if (state == AR_PERMERROR)
1538 break;
1539 return;
1540 case AR_PERMERROR:
1541 return;
1543 sig->state = state;
1544 sig->state_reason = reason;
1547 const char *
1548 ar_state2str(enum ar_state state)
1550 switch (state)
1552 case AR_UNKNOWN:
1553 return "unknown";
1554 case AR_NONE:
1555 return "none";
1556 case AR_PASS:
1557 return "pass";
1558 case AR_FAIL:
1559 return "fail";
1560 case AR_SOFTFAIL:
1561 return "softfail";
1562 case AR_POLICY:
1563 return "policy";
1564 case AR_NEUTRAL:
1565 return "neutral";
1566 case AR_TEMPERROR:
1567 return "temperror";
1568 case AR_PERMERROR:
1569 return "permerror";
1573 void
1574 ar_rr_resolve(struct asr_result *ar, void *arg)
1576 struct ar_signature *sig = arg;
1577 char key[UINT16_MAX + 1];
1578 const char *rr_txt;
1579 size_t keylen, cstrlen;
1580 struct unpack pack;
1581 struct dns_header h;
1582 struct dns_query q;
1583 struct dns_rr rr;
1584 char buf[HOST_NAME_MAX + 1];
1586 sig->query = NULL;
1587 sig->header->msg->nqueries--;
1589 if (sig->state != AR_UNKNOWN)
1590 goto verify;
1592 if (ar->ar_h_errno == TRY_AGAIN || ar->ar_h_errno == NO_RECOVERY) {
1593 ar_signature_state(sig, AR_TEMPERROR,
1594 hstrerror(ar->ar_h_errno));
1595 goto verify;
1597 if (ar->ar_h_errno == HOST_NOT_FOUND) {
1598 ar_signature_state(sig, AR_PERMERROR,
1599 hstrerror(ar->ar_h_errno));
1600 goto verify;
1603 unpack_init(&pack, ar->ar_data, ar->ar_datalen);
1604 if (unpack_header(&pack, &h) != 0 ||
1605 unpack_query(&pack, &q) != 0) {
1606 osmtpd_warn(sig->header->msg->ctx,
1607 "Mallformed DKIM DNS response for domain %s: %s",
1608 print_dname(q.q_dname, buf, sizeof(buf)),
1609 pack.err);
1610 ar_signature_state(sig, AR_PERMERROR, pack.err);
1611 goto verify;
1614 for (; h.ancount > 0; h.ancount--) {
1615 if (unpack_rr(&pack, &rr) != 0) {
1616 osmtpd_warn(sig->header->msg->ctx,
1617 "Mallformed DKIM DNS record for domain %s: %s",
1618 print_dname(q.q_dname, buf, sizeof(buf)),
1619 pack.err);
1620 continue;
1623 /* If we below limit, follow CNAME*/
1624 if (rr.rr_type == T_CNAME &&
1625 sig->nqueries < AR_LOOKUP_LOOKUP_LIMIT ) {
1626 print_dname(rr.rr.cname.cname, buf, sizeof(buf));
1627 ar_lookup_record(sig, buf);
1628 free(ar->ar_data);
1629 return;
1632 if (rr.rr_type != T_TXT) {
1633 osmtpd_warn(sig->header->msg->ctx,
1634 "Unexpected DKIM DNS record: %d for domain %s",
1635 rr.rr_type,
1636 print_dname(q.q_dname, buf, sizeof(buf)));
1637 continue;
1640 keylen = 0;
1641 rr_txt = rr.rr.other.rdata;
1642 while (rr.rr.other.rdlen > 0) {
1643 cstrlen = ((const unsigned char *)rr_txt)[0];
1644 if (cstrlen >= rr.rr.other.rdlen ||
1645 keylen + cstrlen >= sizeof(key))
1646 break;
1648 * RFC 6376 Section 3.6.2.2
1649 * Strings in a TXT RR MUST be concatenated together
1650 * before use with no intervening whitespace.
1652 strlcpy(key + keylen, rr_txt + 1, cstrlen + 1);
1653 rr.rr.other.rdlen -= (cstrlen + 1);
1654 rr_txt += (cstrlen + 1);
1655 keylen += cstrlen;
1657 if (rr.rr.other.rdlen > 0) /* Invalid TXT RDATA */
1658 continue;
1660 if (ar_key_text_parse(sig, key))
1661 break;
1664 if (h.ancount == 0) {
1665 ar_signature_state(sig, AR_PERMERROR,
1666 "No matching key found");
1667 } else {
1668 /* Only verify if all headers have been read */
1669 if (!sig->header->msg->parsing_headers)
1670 ar_signature_verify(sig);
1672 verify:
1673 free(ar->ar_data);
1674 auth_message_verify(sig->header->msg);
1677 int
1678 ar_key_text_parse(struct ar_signature *sig, const char *key)
1680 char tagname, *hashname;
1681 const char *end, *tagvend;
1682 char pkraw[UINT16_MAX] = "", pkimp[UINT16_MAX];
1683 size_t pkrawlen = 0, pkoff, linelen;
1684 int h = 0, k = 0, n = 0, p = 0, s = 0, t = 0, first = 1;
1685 BIO *bio;
1686 #ifdef HAVE_ED25519
1687 size_t pklen;
1688 int tmp;
1689 #endif
1691 key = osmtpd_ltok_skip_fws(key, 1);
1692 /* Validate syntax early */
1693 if ((end = osmtpd_ltok_skip_tag_list(key, 0)) == NULL)
1694 return 0;
1696 while (key[0] != '\0') {
1697 key = osmtpd_ltok_skip_fws(key, 1);
1698 if ((end = osmtpd_ltok_skip_tag_name(key, 0)) == NULL)
1699 return 0;
1701 if ((size_t)(end - key) != 1)
1702 tagname = '\0';
1703 else
1704 tagname = key[0];
1705 key = osmtpd_ltok_skip_fws(end, 1);
1706 /* '=' */
1707 if (key[0] != '=')
1708 return 0;
1709 key = osmtpd_ltok_skip_fws(key + 1, 1);
1710 if ((end = osmtpd_ltok_skip_tag_value(key, 0)) == NULL)
1711 return 0;
1712 switch (tagname) {
1713 case 'v':
1715 * RFC 6376 section 3.6.1, v=:
1716 * RECOMMENDED...This tag MUST be the first tag in the
1717 * record.
1719 if (!first ||
1720 osmtpd_ltok_skip_key_v_tag_value(key, 0) != end)
1721 return 0;
1722 key = end;
1723 break;
1724 case 'h':
1725 if (h != 0) /* Duplicate tag */
1726 return 0;
1727 /* Invalid tag value */
1728 if (osmtpd_ltok_skip_key_h_tag_value(key, 0) != end)
1729 return 0;
1730 while (1) {
1731 if ((tagvend = osmtpd_ltok_skip_key_h_tag_alg(
1732 key, 0)) == NULL)
1733 break;
1734 hashname = strndup(key, tagvend - key);
1735 if (hashname == NULL)
1736 osmtpd_err(1, "strndup");
1737 if (EVP_get_digestbyname(hashname) == sig->ah) {
1738 free(hashname);
1739 h = 1;
1740 break;
1742 free(hashname);
1743 key = osmtpd_ltok_skip_fws(tagvend, 1);
1744 if (key[0] != ':')
1745 break;
1746 key = osmtpd_ltok_skip_fws(key + 1, 1);
1748 if (h != 1)
1749 return 0;
1750 key = end;
1751 break;
1752 case 'k':
1753 if (k != 0) /* Duplicate tag */
1754 return 0;
1755 k = 1;
1756 if (strncmp(key, "rsa", end - key) == 0) {
1757 if (sig->ak != EVP_PKEY_RSA)
1758 return 0;
1759 #if HAVE_ED25519
1760 } else if (strncmp(key, "ed25519", end - key) == 0) {
1761 if (sig->ak != EVP_PKEY_ED25519)
1762 return 0;
1763 #endif
1764 } else
1765 return 0;
1766 key = end;
1767 break;
1768 case 'n':
1769 if (n != 0) /* Duplicate tag */
1770 return 0;
1771 n = 1;
1772 /* semicolon is part of safe-char */
1773 if (osmtpd_ltok_skip_key_n_tag_value(key, 0) < end)
1774 return 0;
1775 key = end;
1776 break;
1777 case 'p':
1778 if (p != 0) /* Duplicate tag */
1779 return 0;
1780 p = 1;
1781 while (1) {
1782 key = osmtpd_ltok_skip_fws(key, 1);
1783 if (osmtpd_ltok_skip_alphadigitps(
1784 key, 0) == NULL)
1785 break;
1786 pkraw[pkrawlen++] = key++[0];
1787 if (pkrawlen >= sizeof(pkraw))
1788 return 0;
1790 if (key[0] == '=') {
1791 pkraw[pkrawlen++] = '=';
1792 key = osmtpd_ltok_skip_fws(key + 1, 1);
1793 if (pkrawlen >= sizeof(pkraw))
1794 return 0;
1795 if (key[0] == '=') {
1796 pkraw[pkrawlen++] = '=';
1797 key++;
1798 if (pkrawlen >= sizeof(pkraw))
1799 return 0;
1802 /* Invalid tag value */
1803 if (pkrawlen % 4 != 0 || key != end)
1804 return 0;
1805 break;
1806 case 's':
1807 if (s != 0) /* Duplicate tag */
1808 return 0;
1809 /* Invalid tag value */
1810 if (osmtpd_ltok_skip_key_s_tag_value(key, 0) != end)
1811 return 0;
1812 while (1) {
1813 if ((tagvend =
1814 osmtpd_ltok_skip_key_s_tag_type(
1815 key, 0)) == NULL)
1816 break;
1817 if (strncmp(key, "*", tagvend - key) == 0 ||
1818 strncmp(key, "email", tagvend - key) == 0) {
1819 s = 1;
1820 break;
1822 key = osmtpd_ltok_skip_fws(tagvend, 1);
1823 if (key[0] != ':')
1824 break;
1825 key = osmtpd_ltok_skip_fws(key + 1, 1);
1827 if (s != 1)
1828 return 0;
1829 key = end;
1830 break;
1831 case 't':
1832 if (t != 0) /* Duplicate tag */
1833 return 0;
1834 t = 1;
1835 if (osmtpd_ltok_skip_key_t_tag_value(key, 0) != end)
1836 return 0;
1837 while (1) {
1838 tagvend = osmtpd_ltok_skip_key_t_tag_flag(
1839 key, 0);
1840 if (strncmp(key, "y", tagvend - key) == 0)
1841 sig->kt |= KT_Y;
1842 else if (strncmp(key, "s", tagvend - key) == 0)
1843 sig->kt |= KT_S;
1844 key = osmtpd_ltok_skip_fws(tagvend, 1);
1845 if (key[0] != ':')
1846 break;
1847 key = osmtpd_ltok_skip_fws(key + 1, 1);
1849 break;
1850 default:
1851 key = end;
1852 break;
1855 first = 0;
1856 key = osmtpd_ltok_skip_fws(key, 1);
1857 if (key[0] == ';')
1858 key++;
1859 else if (key[0] != '\0')
1860 return 0;
1863 if (!p) /* Missing tag */
1864 return 0;
1865 if (k == 0 && sig->ak != EVP_PKEY_RSA) /* Default to RSA */
1866 return 0;
1868 if (pkraw[0] == '\0') {
1869 ar_signature_state(sig, AR_PERMERROR, "Key is revoked");
1870 return 1;
1873 switch (sig->ak) {
1874 case EVP_PKEY_RSA:
1875 pkoff = strlcpy(pkimp, "-----BEGIN PUBLIC KEY-----\n",
1876 sizeof(pkimp));
1877 linelen = 0;
1878 for (key = pkraw; key[0] != '\0';) {
1879 if (pkoff + 2 >= sizeof(pkimp))
1880 return 0;
1881 pkimp[pkoff++] = key++[0];
1882 if (++linelen == 64) {
1883 pkimp[pkoff++] = '\n';
1884 linelen = 0;
1887 /* Leverage pkoff check in loop */
1888 if (linelen != 0)
1889 pkimp[pkoff++] = '\n';
1890 /* PEM_read_bio_PUBKEY will catch truncated keys */
1891 pkoff += strlcpy(pkimp + pkoff, "-----END PUBLIC KEY-----\n",
1892 sizeof(pkimp) - pkoff);
1893 if ((bio = BIO_new_mem_buf(pkimp, pkoff)) == NULL)
1894 osmtpd_err(1, "BIO_new_mem_buf");
1895 sig->p = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
1896 BIO_free(bio);
1897 break;
1898 #if HAVE_ED25519
1899 case EVP_PKEY_ED25519:
1900 if ((pkrawlen / 4) * 3 >= sizeof(pkimp))
1901 return 0;
1902 EVP_DecodeInit(ectx);
1903 if (EVP_DecodeUpdate(ectx, pkimp, &tmp, pkraw, pkrawlen) == -1)
1904 return 0;
1905 pklen = tmp;
1906 if (EVP_DecodeFinal(ectx, pkimp, &tmp) == -1)
1907 return 0;
1908 pklen += tmp;
1909 sig->p = EVP_PKEY_new_raw_public_key(sig->ak, NULL, pkimp,
1910 pklen);
1911 break;
1912 #endif
1914 if (sig->p == NULL) {
1916 * XXX No clue how to differentiate between invalid key and
1917 * temporary failure like *alloc.
1918 * Assume invalid key, because it's more likely.
1920 return 0;
1922 return 1;
1925 void
1926 ar_body_parse(struct message *msg, const char *line)
1928 struct ar_signature *sig;
1929 const char *end = line, *hash, *prev;
1930 size_t hashn, len, i;
1931 int wsp, ret;
1933 if (line[0] == '\0') {
1934 msg->body_whitelines++;
1935 return;
1938 while (msg->body_whitelines-- > 0) {
1939 for (i = 0; i < msg->nheaders; i++) {
1940 if ((sig = msg->header[i].sig) == NULL ||
1941 sig->state != AR_UNKNOWN)
1942 continue;
1943 hashn = sig->l == -1 ? 2 : MIN(2, sig->l);
1944 sig->l -= sig->l == -1 ? 0 : hashn;
1945 if (EVP_DigestUpdate(sig->bhctx, "\r\n", hashn) == 0)
1946 osmtpd_errx(1, "EVP_DigestUpdate");
1949 msg->body_whitelines = 0;
1950 msg->has_body = 1;
1952 while (line[0] != '\0') {
1953 while (1) {
1954 prev = end;
1955 if ((end = osmtpd_ltok_skip_wsp(end, 0)) == NULL)
1956 break;
1958 end = prev;
1959 wsp = end != line;
1960 if (!wsp) {
1961 while (osmtpd_ltok_skip_wsp(end, 0) == NULL &&
1962 end[0] != '\0')
1963 end++;
1965 for (i = 0; i < msg->nheaders; i++) {
1966 sig = msg->header[i].sig;
1967 if (sig == NULL || sig->state != AR_UNKNOWN)
1968 continue;
1969 if (wsp &&
1970 (sig->c & CANON_BODY) == CANON_BODY_RELAXED) {
1971 hash = " ";
1972 len = end[0] == '\0' ? 0 : 1;
1973 } else {
1974 hash = line;
1975 len = (size_t)(end - line);
1977 hashn = sig->l == -1 ? len : MIN(len, (size_t)sig->l);
1978 sig->l -= sig->l == -1 ? 0 : hashn;
1979 ret = EVP_DigestUpdate(sig->bhctx, hash, hashn);
1980 if (ret == 0)
1981 osmtpd_err(1, "EVP_DigestUpdate");
1983 line = end;
1985 for (i = 0; i < msg->nheaders; i++) {
1986 sig = msg->header[i].sig;
1987 if (sig == NULL || sig->state != AR_UNKNOWN)
1988 continue;
1989 hashn = sig->l == -1 ? 2 : MIN(2, sig->l);
1990 sig->l -= sig->l == -1 ? 0 : hashn;
1991 ret = EVP_DigestUpdate(sig->bhctx, "\r\n", hashn);
1992 if (ret == 0)
1993 osmtpd_err(1, "EVP_DigestUpdate");
1997 void
1998 ar_body_verify(struct ar_signature *sig)
2000 unsigned char digest[EVP_MAX_MD_SIZE];
2001 unsigned int digestsz;
2003 if (sig->state != AR_UNKNOWN)
2004 return;
2006 if (sig->seal)
2007 return;
2009 if ((sig->c & CANON_BODY) == CANON_BODY_SIMPLE &&
2010 !sig->header->msg->has_body) {
2011 if (EVP_DigestUpdate(sig->bhctx, "\r\n",
2012 sig->l == -1 ? 2 : MIN(2, sig->l)) <= 0)
2013 osmtpd_errx(1, "EVP_DigestUpdate");
2015 if (sig->l > 0) {
2016 ar_signature_state(sig, AR_PERMERROR,
2017 "l tag larger than body");
2018 return;
2021 if (EVP_DigestFinal_ex(sig->bhctx, digest, &digestsz) == 0)
2022 osmtpd_err(1, "EVP_DigestFinal_ex");
2024 if (digestsz != sig->bhsz || memcmp(digest, sig->bh, digestsz) != 0)
2025 ar_signature_state(sig, AR_FAIL, "bh mismatch");
2028 char *
2029 spf_evaluate_domain(struct spf_record *spf, const char *domain)
2031 struct session *ses = spf->ctx->local_session;
2033 char spec[HOST_NAME_MAX + 1];
2034 char macro[HOST_NAME_MAX + 1], smacro[sizeof(macro)];
2035 char delimiters[sizeof(".-+,/_=")];
2036 char *endptr, *tmp;
2037 const u_char *addr;
2038 size_t i, mlen;
2039 long digits;
2040 int reverse;
2042 if (domain == NULL || domain[0] == '\0') {
2043 spf_done(spf, AR_PERMERROR, "Empty domain");
2044 return NULL;
2047 for (i = 0;
2048 domain[0] != ' ' && domain[0] != '\0' && i < sizeof(spec);
2049 domain++) {
2051 if (domain[0] < 0x21 || domain[0] > 0x7e) {
2052 spf_done(
2053 spf, AR_PERMERROR, "Invalid character in domain-spec");
2054 return NULL;
2057 if (domain[0] != '%') {
2058 spec[i++] = domain[0];
2059 continue;
2061 domain++;
2063 switch (domain[0]) {
2064 case '%':
2065 spec[i++] = '%';
2066 break;
2067 case '_':
2068 spec[i++] = ' ';
2069 break;
2070 case '-':
2071 if (i + 3 >= sizeof(spec)) {
2072 spf_done(
2073 spf, AR_PERMERROR, "domain-spec too large");
2074 return NULL;
2077 spec[i++] = '%';
2078 spec[i++] = '2';
2079 spec[i++] = '0';
2080 break;
2081 case '{':
2082 domain++;
2083 digits = -1;
2084 reverse = 0;
2085 delimiters[0] = '\0';
2087 switch (domain[0]) {
2088 case 'S':
2089 case 's':
2090 mlen = (size_t) snprintf(macro, sizeof(macro),
2091 "%s@%s", spf->sender_local,
2092 spf->sender_domain);
2093 break;
2094 case '{':
2095 domain++;
2096 digits = -1;
2097 reverse = 0;
2098 delimiters[0] = '\0';
2100 switch (domain[0]) {
2101 case 'S':
2102 case 's':
2103 mlen = (size_t) snprintf(macro, sizeof(macro),
2104 "%s@%s", spf->sender_local,
2105 spf->sender_domain);
2106 break;
2107 case 'L':
2108 case 'l':
2109 mlen = strlcpy(macro,
2110 spf->sender_local, sizeof(macro));
2111 break;
2112 case 'O':
2113 case 'o':
2114 mlen = strlcpy(macro,
2115 spf->sender_domain,
2116 sizeof(macro));
2117 break;
2118 case 'D':
2119 case 'd':
2120 if (spf->nqueries < 1) {
2121 spf_done(spf, AR_PERMERROR,
2122 "no domain for d macro");
2123 return NULL;
2125 mlen = strlcpy(macro,
2126 spf->queries[spf->nqueries - 1].domain,
2127 sizeof(macro));
2128 break;
2129 case 'I':
2130 case 'i':
2131 if (ses->src.ss_family == AF_INET) {
2132 addr = (u_char *)(&((struct sockaddr_in *)
2133 &(ses->src))->sin_addr);
2134 mlen = snprintf(macro, sizeof(macro),
2135 "%u.%u.%u.%u",
2136 (addr[0] & 0xff), (addr[1] & 0xff),
2137 (addr[2] & 0xff), (addr[3] & 0xff));
2138 } else if (ses->src.ss_family == AF_INET6) {
2139 addr = (u_char *)(&((struct sockaddr_in6 *)
2140 &(ses->src))->sin6_addr);
2141 mlen = snprintf(macro, sizeof(macro),
2142 "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
2143 "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
2144 "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
2145 "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx",
2146 (u_char) ((addr[0] >> 4) & 0x0f), (u_char) (addr[0] & 0x0f),
2147 (u_char) ((addr[1] >> 4) & 0x0f), (u_char) (addr[1] & 0x0f),
2148 (u_char) ((addr[2] >> 4) & 0x0f), (u_char) (addr[2] & 0x0f),
2149 (u_char) ((addr[3] >> 4) & 0x0f), (u_char) (addr[3] & 0x0f),
2150 (u_char) ((addr[4] >> 4) & 0x0f), (u_char) (addr[4] & 0x0f),
2151 (u_char) ((addr[5] >> 4) & 0x0f), (u_char) (addr[5] & 0x0f),
2152 (u_char) ((addr[6] >> 4) & 0x0f), (u_char) (addr[6] & 0x0f),
2153 (u_char) ((addr[7] >> 4) & 0x0f), (u_char) (addr[7] & 0x0f),
2154 (u_char) ((addr[8] >> 4) & 0x0f), (u_char) (addr[8] & 0x0f),
2155 (u_char) ((addr[9] >> 4) & 0x0f), (u_char) (addr[9] & 0x0f),
2156 (u_char) ((addr[10] >> 4) & 0x0f), (u_char) (addr[10] & 0x0f),
2157 (u_char) ((addr[11] >> 4) & 0x0f), (u_char) (addr[11] & 0x0f),
2158 (u_char) ((addr[12] >> 4) & 0x0f), (u_char) (addr[12] & 0x0f),
2159 (u_char) ((addr[13] >> 4) & 0x0f), (u_char) (addr[13] & 0x0f),
2160 (u_char) ((addr[14] >> 4) & 0x0f), (u_char) (addr[14] & 0x0f),
2161 (u_char) ((addr[15] >> 4) & 0x0f), (u_char) (addr[15] & 0x0f));
2162 } else {
2163 spf_done(spf, AR_PERMERROR,
2164 "unsupported type of address");
2165 return NULL;
2167 break;
2168 case 'P':
2169 case 'p':
2170 mlen = strlcpy(macro, ses->rdns, sizeof(macro));
2171 break;
2172 case 'V':
2173 case 'v':
2174 if (ses->src.ss_family == AF_INET)
2175 mlen = strlcpy(macro, "in-addr",
2176 sizeof(macro));
2177 else if (ses->src.ss_family == AF_INET6)
2178 mlen = strlcpy(macro, "ip6",
2179 sizeof(macro));
2180 else {
2181 spf_done(spf, AR_PERMERROR,
2182 "unsupported type of address");
2183 return NULL;
2185 break;
2186 case 'H':
2187 case 'h':
2188 mlen = strlcpy(macro, ses->identity,
2189 sizeof(macro));
2190 break;
2191 default:
2192 spf_done(spf, AR_PERMERROR,
2193 "Unexpected macro in domain-spec");
2194 return NULL;
2197 if (mlen >= sizeof(macro)) {
2198 spf_done(spf, AR_PERMERROR,
2199 "Macro expansions too large");
2200 return NULL;
2203 domain++;
2204 if (isdigit(domain[0])) {
2205 digits = strtol(domain, &endptr, 10);
2206 if (digits < 1) {
2207 spf_done(spf, AR_PERMERROR,
2208 "digits in macro can't be 0");
2209 return NULL;
2211 domain = endptr;
2214 if (domain[0] == 'r') {
2215 domain++;
2216 reverse = 1;
2219 for (; strchr(".-+,/_=", domain[0]) != NULL; domain++) {
2220 if (strchr(delimiters, domain[0]) == NULL) {
2221 delimiters[strlen(delimiters) + 1] = '\0';
2222 delimiters[strlen(delimiters)] = domain[0];
2226 if (delimiters[0] == '\0') {
2227 delimiters[0] = '.';
2228 delimiters[1] = '\0';
2231 if (domain[0] != '}') {
2232 spf_done(spf, AR_PERMERROR,
2233 "Mallformed macro, expected end");
2234 return NULL;
2237 if (reverse) {
2238 smacro[0] = '\0';
2239 tmp = macro + strlen(macro) - 1;
2241 * DIGIT rightmost elements after reversal is DIGIT
2242 * lefmost elements before reversal
2244 while (1) {
2245 while (tmp > macro &&
2246 strchr(delimiters, tmp[0]) == NULL)
2247 tmp--;
2248 if (tmp == macro)
2249 break;
2250 if (digits == 0)
2251 break;
2252 if (digits > 0)
2253 digits--;
2255 tmp[0] = '\0';
2256 if (smacro[0] != '\0')
2257 strlcat(smacro, ".", sizeof(smacro));
2258 strlcat(smacro, tmp + 1, sizeof(smacro));
2259 tmp--;
2261 if (digits != 0) {
2262 if (smacro[0] != '\0')
2263 strlcat(smacro, ".", sizeof(smacro));
2264 strlcat(smacro, macro, sizeof(smacro));
2266 } else {
2267 if (digits != -1) {
2268 tmp = macro;
2269 endptr = macro + strlen(macro);
2270 while (digits > 0) {
2271 while (tmp < endptr &&
2272 strchr(delimiters, tmp[0]) == NULL)
2273 tmp++;
2274 if (tmp == endptr)
2275 break;
2276 if (digits == 1) {
2277 tmp[0] = '\0';
2278 break;
2280 digits--;
2281 tmp++;
2284 strlcpy(smacro, macro, sizeof(smacro));
2287 spec[i] = '\0';
2288 i = strlcat(spec, smacro, sizeof(spec));
2289 if (i >= sizeof(spec)) {
2290 spf_done(
2291 spf, AR_PERMERROR, "domain-spec too large");
2292 return NULL;
2294 break;
2295 case 'P':
2296 case 'p':
2297 mlen = strlcpy(macro, ses->rdns, sizeof(macro));
2298 break;
2299 case 'V':
2300 case 'v':
2301 if (ses->src.ss_family == AF_INET)
2302 mlen = strlcpy(macro, "in-addr",
2303 sizeof(macro));
2304 else if (ses->src.ss_family == AF_INET6)
2305 mlen = strlcpy(macro, "ip6",
2306 sizeof(macro));
2307 else {
2308 spf_done(spf, AR_PERMERROR,
2309 "unsupported type of address");
2310 return NULL;
2312 break;
2313 case 'H':
2314 case 'h':
2315 mlen = strlcpy(macro, ses->identity,
2316 sizeof(macro));
2317 break;
2318 default:
2319 spf_done(spf, AR_PERMERROR,
2320 "Mallformed macro, unexpected character after %");
2321 return NULL;
2324 if (mlen >= sizeof(macro)) {
2325 spf_done(spf, AR_PERMERROR,
2326 "Macro expansions too large");
2327 return NULL;
2330 domain++;
2331 if (isdigit(domain[0])) {
2332 digits = strtol(domain, &endptr, 10);
2333 if (digits < 1) {
2334 spf_done(spf, AR_PERMERROR,
2335 "digits in macro can't be 0");
2336 return NULL;
2338 domain = endptr;
2341 if (domain[0] == 'r') {
2342 domain++;
2343 reverse = 1;
2346 for (; strchr(".-+,/_=", domain[0]) != NULL; domain++) {
2347 if (strchr(delimiters, domain[0]) == NULL) {
2348 delimiters[strlen(delimiters) + 1] = '\0';
2349 delimiters[strlen(delimiters)] = domain[0];
2353 if (delimiters[0] == '\0') {
2354 delimiters[0] = '.';
2355 delimiters[1] = '\0';
2358 if (domain[0] != '}') {
2359 spf_done(spf, AR_PERMERROR,
2360 "Mallformed macro, expected end");
2361 return NULL;
2364 if (reverse) {
2365 smacro[0] = '\0';
2366 tmp = macro + strlen(macro) - 1;
2368 * DIGIT rightmost elements after reversal is DIGIT
2369 * lefmost elements before reversal
2371 while (1) {
2372 while (tmp > macro &&
2373 strchr(delimiters, tmp[0]) == NULL)
2374 tmp--;
2375 if (tmp == macro)
2376 break;
2377 if (digits == 0)
2378 break;
2379 if (digits > 0)
2380 digits--;
2382 tmp[0] = '\0';
2383 if (smacro[0] != '\0')
2384 strlcat(smacro, ".", sizeof(smacro));
2385 strlcat(smacro, tmp + 1, sizeof(smacro));
2386 tmp--;
2388 if (digits != 0) {
2389 if (smacro[0] != '\0')
2390 strlcat(smacro, ".", sizeof(smacro));
2391 strlcat(smacro, macro, sizeof(smacro));
2393 } else {
2394 if (digits != -1) {
2395 tmp = macro;
2396 endptr = macro + strlen(macro);
2397 while (digits > 0) {
2398 while (tmp < endptr &&
2399 strchr(delimiters, tmp[0]) == NULL)
2400 tmp++;
2401 if (tmp == endptr)
2402 break;
2403 if (digits == 1) {
2404 tmp[0] = '\0';
2405 break;
2407 digits--;
2408 tmp++;
2411 strlcpy(smacro, macro, sizeof(smacro));
2414 spec[i] = '\0';
2415 i = strlcat(spec, smacro, sizeof(spec));
2416 if (i >= sizeof(spec)) {
2417 spf_done(
2418 spf, AR_PERMERROR, "domain-spec too large");
2419 return NULL;
2421 break;
2423 default:
2424 spf_done(spf, AR_PERMERROR,
2425 "Mallformed macro, unexpected character after %");
2426 return NULL;
2430 if ((tmp = strndup(spec, i)) == NULL)
2431 osmtpd_err(1, "%s: strndup", __func__);
2433 return tmp;
2436 void
2437 spf_lookup_record(struct spf_record *spf, const char *domain, int type,
2438 enum ar_state qualifier, int include, int exists)
2440 struct asr_query *aq;
2441 struct spf_query *query;
2443 if (spf->done)
2444 return;
2446 if (spf->nqueries >= SPF_DNS_LOOKUP_LIMIT) {
2447 spf_done(spf, AR_PERMERROR, "Too many DNS queries");
2448 return;
2451 query = &spf->queries[spf->nqueries];
2452 query->spf = spf;
2453 query->type = type;
2454 query->q = qualifier;
2455 query->include = include;
2456 query->exists = exists;
2457 query->txt = NULL;
2458 query->eva = NULL;
2460 if ((query->domain = spf_evaluate_domain(spf, domain)) == NULL)
2461 return;
2463 if (domain == NULL || !strlen(domain)) {
2464 spf_done(spf, AR_PERMERROR, "Empty domain");
2465 return;
2468 if ((aq = res_query_async(query->domain, C_IN, type, NULL)) == NULL)
2469 osmtpd_err(1, "res_query_async");
2471 if ((query->eva = event_asr_run(aq, spf_resolve, query)) == NULL)
2472 osmtpd_err(1, "event_asr_run");
2474 spf->running++;
2475 spf->nqueries++;
2478 void
2479 spf_resolve(struct asr_result *ar, void *arg)
2481 int i;
2483 struct spf_query *query = arg;
2484 struct spf_record *spf = query->spf;
2485 struct unpack pack;
2486 struct dns_header h;
2487 struct dns_query q;
2488 struct dns_rr rr;
2489 char buf[HOST_NAME_MAX + 1];
2491 query->eva = NULL;
2492 query->spf->running--;
2494 if (ar->ar_h_errno == TRY_AGAIN
2495 || ar->ar_h_errno == NO_RECOVERY) {
2496 spf_done(query->spf, AR_TEMPERROR, hstrerror(ar->ar_h_errno));
2497 goto end;
2500 if (ar->ar_h_errno == HOST_NOT_FOUND) {
2501 if (query->include && !query->exists)
2502 spf_done(query->spf,
2503 AR_PERMERROR, hstrerror(ar->ar_h_errno));
2504 goto consume;
2507 unpack_init(&pack, ar->ar_data, ar->ar_datalen);
2508 if (unpack_header(&pack, &h) != 0 ||
2509 unpack_query(&pack, &q) != 0) {
2510 osmtpd_warn(query->spf->ctx,
2511 "Mallformed SPF DNS response for domain %s: %s",
2512 print_dname(q.q_dname, buf, sizeof(buf)),
2513 pack.err);
2514 spf_done(query->spf, AR_TEMPERROR, pack.err);
2515 goto end;
2518 for (; h.ancount; h.ancount--) {
2519 if (unpack_rr(&pack, &rr) != 0) {
2520 osmtpd_warn(query->spf->ctx,
2521 "Mallformed SPF DNS record for domain %s: %s",
2522 print_dname(q.q_dname, buf, sizeof(buf)),
2523 pack.err);
2524 continue;
2527 switch (rr.rr_type)
2529 case T_TXT:
2530 spf_resolve_txt(&rr, query);
2531 break;
2533 case T_MX:
2534 spf_resolve_mx(&rr, query);
2535 break;
2537 case T_A:
2538 spf_resolve_a(&rr, query);
2539 break;
2541 case T_AAAA:
2542 spf_resolve_aaaa(&rr, query);
2543 break;
2545 case T_CNAME:
2546 spf_resolve_cname(&rr, query);
2547 break;
2549 default:
2550 osmtpd_warn(spf->ctx,
2551 "Unexpected SPF DNS record: %d for domain %s",
2552 rr.rr_type, query->domain);
2553 spf_done(query->spf, AR_TEMPERROR, "Unexpected record");
2554 break;
2557 if (spf->done)
2558 goto end;
2561 consume:
2562 if (spf->running > 0)
2563 goto end;
2565 for (i = spf->nqueries - 1; i >= 0; i--) {
2566 if (spf->queries[i].txt != NULL) {
2567 if (spf_execute_txt(&spf->queries[i]) != 0)
2568 break;
2572 end:
2573 free(ar->ar_data);
2574 if (!spf->done && spf->running == 0)
2575 spf_done(spf, AR_NONE, NULL);
2578 void
2579 spf_resolve_txt(struct dns_rr *rr, struct spf_query *query)
2581 char *txt;
2582 txt = spf_parse_txt(rr->rr.other.rdata, rr->rr.other.rdlen);
2583 if (txt == NULL) {
2584 osmtpd_warn(NULL, "spf_parse_txt");
2585 return;
2588 if (strncasecmp("v=spf1 ", txt, 7)) {
2589 free(txt);
2590 return;
2593 if (query->txt != NULL) {
2594 free(txt);
2595 spf_done(query->spf, AR_PERMERROR, "Duplicated SPF record");
2596 return;
2599 query->txt = txt;
2600 query->pos = 0;
2601 spf_execute_txt(query);
2604 void
2605 spf_resolve_mx(struct dns_rr *rr, struct spf_query *query)
2607 char buf[HOST_NAME_MAX + 1];
2609 char *domain = print_dname(rr->rr.mx.exchange, buf, sizeof(buf));
2611 spf_lookup_record(query->spf, domain, T_A,
2612 query->q, query->include, 0);
2613 spf_lookup_record(query->spf, domain, T_AAAA,
2614 query->q, query->include, 0);
2617 void
2618 spf_resolve_a(struct dns_rr *rr, struct spf_query *query)
2620 if (query->exists ||
2621 spf_check_cidr(query->spf, &rr->rr.in_a.addr, 32) == 0) {
2622 spf_done(query->spf, query->q, NULL);
2626 void
2627 spf_resolve_aaaa(struct dns_rr *rr, struct spf_query *query)
2629 if (spf_check_cidr6(query->spf, &rr->rr.in_aaaa.addr6, 128) == 0) {
2630 spf_done(query->spf, query->q, NULL);
2634 void
2635 spf_resolve_cname(struct dns_rr *rr, struct spf_query *query)
2637 char buf[HOST_NAME_MAX + 1];
2639 char *domain = print_dname(rr->rr.cname.cname, buf, sizeof(buf));
2641 spf_lookup_record(query->spf, domain, query->type,
2642 query->q, query->include, 0);
2645 char *
2646 spf_parse_txt(const char *rdata, size_t rdatalen)
2648 size_t len, dstsz = SPF_RECORD_MAX - 1;
2649 ssize_t r = 0;
2650 char *dst, *odst;
2652 if (rdatalen >= dstsz) {
2653 errno = EOVERFLOW;
2654 return NULL;
2657 odst = dst = malloc(dstsz);
2658 if (dst == NULL)
2659 osmtpd_err(1, "%s: malloc", __func__);
2661 while (rdatalen) {
2662 len = *(const unsigned char *)rdata;
2663 if (len >= rdatalen) {
2664 errno = EINVAL;
2665 return NULL;
2668 rdata++;
2669 rdatalen--;
2671 if (len == 0)
2672 continue;
2674 if (len >= dstsz) {
2675 errno = EOVERFLOW;
2676 return NULL;
2678 memmove(dst, rdata, len);
2679 dst += len;
2680 dstsz -= len;
2682 rdata += len;
2683 rdatalen -= len;
2684 r += len;
2687 odst[r] = '\0';
2689 return odst;
2692 int
2693 spf_check_cidr(struct spf_record *spf, struct in_addr *net, int bits)
2695 struct in_addr *addr;
2696 struct session *ses = spf->ctx->local_session;
2698 if (ses->src.ss_family != AF_INET)
2699 return -1;
2701 if (bits == 0)
2702 return 0;
2704 addr = &(((struct sockaddr_in *)(&ses->src))->sin_addr);
2706 return ((addr->s_addr ^ net->s_addr) & htonl(0xFFFFFFFFu << (32 - bits)));
2709 int
2710 spf_check_cidr6(struct spf_record *spf, struct in6_addr *net, int bits)
2712 int rc;
2713 uint32_t *a, *n, whole, incomplete;
2714 struct in6_addr *addr;
2715 struct session *ses = spf->ctx->local_session;
2717 if (ses->src.ss_family != AF_INET6)
2718 return -1;
2720 if (bits == 0)
2721 return 0;
2723 addr = &(((struct sockaddr_in6 *)(&ses->src))->sin6_addr);
2725 a = addr->s6_addr32;
2726 n = net->s6_addr32;
2728 whole = bits >> 5;
2729 incomplete = bits & 0x1f;
2730 if (whole) {
2731 rc = memcmp(a, n, whole << 2);
2732 if (rc)
2733 return rc;
2735 if (incomplete)
2736 return (a[whole] ^ n[whole]) & htonl((0xffffffffu) << (32 - incomplete));
2738 return 0;
2741 int
2742 spf_execute_txt(struct spf_query *query)
2744 struct in_addr ina;
2745 struct in6_addr in6a;
2746 char *ap = NULL;
2747 char *in = query->txt + query->pos;
2748 char *end;
2749 int bits;
2751 enum ar_state q = query->q;
2753 while ((ap = strsep(&in, " ")) != NULL) {
2754 if (strcasecmp(ap, "v=spf1") == 0)
2755 continue;
2757 end = ap + strlen(ap)-1;
2758 if (*end == '.')
2759 *end = '\0';
2761 if (*ap == '+') {
2762 q = AR_PASS;
2763 ap++;
2764 } else if (*ap == '-') {
2765 q = AR_FAIL;
2766 ap++;
2767 } else if (*ap == '~') {
2768 q = AR_SOFTFAIL;
2769 ap++;
2770 } else if (*ap == '?') {
2771 q = AR_NEUTRAL;
2772 ap++;
2775 if (q != AR_PASS && query->include)
2776 continue;
2778 if (strncasecmp("all", ap, 3) == 0) {
2779 spf_done(query->spf, q, NULL);
2780 return 0;
2782 if (strncasecmp("ip4:", ap, 4) == 0) {
2783 if ((bits = inet_net_pton(AF_INET, ap + 4, &ina, sizeof(ina))) == -1)
2784 continue;
2786 if (spf_check_cidr(query->spf, &ina, bits) == 0) {
2787 spf_done(query->spf, q, NULL);
2788 return 0;
2790 continue;
2792 if (strncasecmp("ip6:", ap, 4) == 0) {
2793 if ((bits = inet_net_pton(AF_INET6, ap + 4, &in6a, sizeof(in6a))) == -1)
2794 continue;
2796 if (spf_check_cidr6(query->spf, &in6a, bits) == 0) {
2797 spf_done(query->spf, q, NULL);
2798 return 0;
2800 continue;
2802 if (strcasecmp("a", ap) == 0) {
2803 spf_lookup_record(query->spf, query->domain, T_A,
2804 q, query->include, 0);
2805 spf_lookup_record(query->spf, query->domain, T_AAAA,
2806 q, query->include, 0);
2807 break;
2809 if (strncasecmp("a:", ap, 2) == 0) {
2810 spf_lookup_record(query->spf, ap + 2, T_A,
2811 q, query->include, 0);
2812 spf_lookup_record(query->spf, ap + 2, T_AAAA,
2813 q, query->include, 0);
2814 break;
2816 if (strncasecmp("exists:", ap, 7) == 0) {
2817 spf_lookup_record(query->spf, ap + 7, T_A,
2818 q, query->include, 1);
2819 break;
2821 if (strncasecmp("include:", ap, 8) == 0) {
2822 spf_lookup_record(query->spf, ap + 8, T_TXT, q, 1, 0);
2823 break;
2825 if (strncasecmp("redirect=", ap, 9) == 0) {
2826 if (in != NULL)
2827 continue;
2828 spf_lookup_record(query->spf, ap + 9, T_TXT,
2829 q, query->include, 0);
2830 return 0;
2832 if (strcasecmp("mx", ap) == 0) {
2833 spf_lookup_record(query->spf, query->domain, T_MX,
2834 q, query->include, 0);
2835 break;
2837 if (strncasecmp("mx:", ap, 3) == 0) {
2838 spf_lookup_record(query->spf, ap + 3, T_MX,
2839 q, query->include, 0);
2840 break;
2844 if (in == NULL)
2845 return 0;
2847 query->pos = in - query->txt;
2849 return query->pos;
2852 void
2853 spf_done(struct spf_record *spf, enum ar_state state, const char *reason)
2855 int i;
2857 if (spf->done)
2858 return;
2860 for (i = 0; i < spf->nqueries; i++) {
2861 if (spf->queries[i].eva) {
2862 event_asr_abort(spf->queries[i].eva);
2863 spf->queries[i].eva = NULL;
2867 spf->nqueries = 0;
2868 spf->running = 0;
2869 spf->state = state;
2870 spf->state_reason = reason;
2871 spf->done = 1;
2873 osmtpd_filter_proceed(spf->ctx);
2876 int
2877 spf_ar_cat(const char *type, struct spf_record *spf, char **line, size_t *linelen, ssize_t *aroff)
2879 if (spf == NULL) {
2880 if ((*aroff =
2881 auth_ar_cat(line, linelen, *aroff,
2882 "; spf=none %s=none", type)
2883 ) == -1) {
2884 return -1;
2886 return 0;
2889 if ((*aroff =
2890 auth_ar_cat(line, linelen, *aroff,
2891 "; spf=%s", ar_state2str(spf->state))
2892 ) == -1) {
2893 return -1;
2896 if ((*aroff =
2897 auth_ar_cat(line, linelen, *aroff,
2898 " %s=%s@%s",
2899 type,
2900 spf->sender_local,
2901 spf->sender_domain)
2902 ) == -1) {
2903 return -1;
2906 if (spf->state_reason != NULL) {
2907 if ((*aroff =
2908 auth_ar_cat(line, linelen, *aroff,
2909 " reason=\"%s\"", spf->state_reason)
2910 ) == -1) {
2911 return -1;
2915 return 0;
2918 void
2919 auth_message_verify(struct message *msg)
2921 size_t i;
2923 if (!msg->readdone || msg->nqueries > 0)
2924 return;
2926 for (i = 0; i < msg->nheaders; i++) {
2927 if (msg->header[i].sig == NULL)
2928 continue;
2929 if (msg->header[i].sig->query != NULL)
2930 return;
2931 if (msg->header[i].sig->state != AR_UNKNOWN)
2932 continue;
2933 ar_signature_state(msg->header[i].sig, AR_PASS, NULL);
2936 auth_ar_create(msg->ctx);
2939 int
2940 ar_signature_ar_cat(const char *type, struct ar_signature *sig, char **line, size_t *linelen, ssize_t *aroff)
2942 if ((*aroff =
2943 auth_ar_cat(line, linelen, *aroff,
2944 "; %s=%s", type, ar_state2str(sig->state))
2945 ) == -1)
2946 return -1;
2948 if (sig->state_reason != NULL) {
2949 if ((*aroff =
2950 auth_ar_cat(line, linelen, *aroff,
2951 " reason=\"%s\"", sig->state_reason)
2952 ) == -1)
2953 return -1;
2956 if (sig->s[0] != '\0') {
2957 if ((*aroff =
2958 auth_ar_cat(line, linelen, *aroff,
2959 " header.s=%s", sig->s)
2960 ) == -1)
2961 return -1;
2964 if (sig->d[0] != '\0') {
2965 if ((*aroff =
2966 auth_ar_cat(line, linelen, *aroff,
2967 " header.d=%s", sig->d)
2968 ) == -1)
2969 return -1;
2973 * Don't print i-tag for DKIM, since localpart can be a
2974 * quoted-string, which can contain FWS and CFWS. But
2975 * ARC is different story and it should be printed out.
2977 if (sig->arc_i != 0) {
2978 if ((*aroff =
2979 auth_ar_cat(line, linelen, *aroff,
2980 " header.i=%d", sig->arc_i)
2981 ) == -1)
2982 return -1;
2985 if (sig->a != NULL) {
2986 if ((*aroff =
2987 auth_ar_cat(line, linelen, *aroff,
2988 " header.a=%.*s", (int)sig->asz, sig->a)
2989 ) == -1)
2990 return -1;
2993 if (sig->bheaderclean[0] != '\0') {
2994 if ((*aroff =
2995 auth_ar_cat(line, linelen, *aroff,
2996 " header.b=%s", sig->bheaderclean)
2997 ) == -1)
2998 return -1;
3001 return 0;
3004 void
3005 auth_ar_create(struct osmtpd_ctx *ctx)
3007 struct ar_signature *sig;
3008 size_t i;
3009 ssize_t n, aroff = 0;
3010 int found = 0;
3011 char *line = NULL;
3012 size_t linelen = 0;
3013 struct session *ses = ctx->local_session;
3014 struct message *msg = ctx->local_message;
3016 if (!arc && (aroff = auth_ar_cat(&line, &linelen, aroff,
3017 "Authentication-Results: %s", authservid)) == -1)
3018 osmtpd_err(1, "%s: malloc", __func__);
3020 if (arc) {
3021 for (i = ARC_MAX_I; i >= ARC_MIN_I; i--) {
3022 if (msg->arc_signs[i] != NULL)
3023 break;
3025 i += 1;
3027 if (i <= ARC_MAX_I && (aroff = auth_ar_cat(
3028 &line, &linelen, aroff,
3029 "ARC-Authentication-Results: i=%zu; %s",
3030 i, authservid)) == -1)
3031 osmtpd_err(1, "%s: malloc", __func__);
3034 for (i = 0; i < msg->nheaders; i++) {
3035 sig = msg->header[i].sig;
3036 if (sig == NULL || !sig->dkim)
3037 continue;
3039 found = 1;
3041 if (ar_signature_ar_cat(
3042 "dkim", sig, &line, &linelen, &aroff) != 0)
3043 osmtpd_err(1, "%s: malloc", __func__);
3046 if (!found) {
3047 aroff = auth_ar_cat(&line, &linelen, aroff, "; dkim=none");
3048 if (aroff == -1)
3049 osmtpd_err(1, "%s: malloc", __func__);
3052 found = 0;
3054 for (i = ARC_MAX_I; i > 0; i--) {
3055 sig = msg->arc_signs[i];
3056 if (sig == NULL)
3057 continue;
3059 found = 1;
3061 if (ar_signature_ar_cat(
3062 "arc", sig, &line, &linelen, &aroff) != 0)
3063 osmtpd_err(1, "%s: malloc", __func__);
3065 break;
3068 if (!found) {
3069 aroff = auth_ar_cat(&line, &linelen, aroff, "; arc=none");
3070 if (aroff == -1)
3071 osmtpd_err(1, "%s: malloc", __func__);
3074 if ((aroff = auth_ar_cat(&line, &linelen, aroff,
3075 "; iprev=%s", ar_state2str(ses->iprev))) == -1)
3076 osmtpd_err(1, "%s: malloc", __func__);
3078 if (spf_ar_cat("smtp.helo", ses->spf_helo,
3079 &line, &linelen, &aroff) != 0)
3080 osmtpd_err(1, "%s: malloc", __func__);
3082 if (ses->spf_mailfrom != NULL) {
3083 if (spf_ar_cat("smtp.mailfrom", ses->spf_mailfrom,
3084 &line, &linelen, &aroff) != 0)
3085 osmtpd_err(1, "%s: malloc", __func__);
3086 } else {
3087 if (spf_ar_cat("smtp.mailfrom", ses->spf_helo,
3088 &line, &linelen, &aroff) != 0)
3089 osmtpd_err(1, "%s: malloc", __func__);
3092 if (aroff == -1)
3093 osmtpd_err(1, "%s: malloc", __func__);
3095 if (auth_ar_print(msg->ctx, line) != 0)
3096 osmtpd_warn(msg->ctx, "Invalid AR line: %s", line);
3098 rewind(msg->origf);
3099 while ((n = getline(&line, &linelen, msg->origf)) != -1) {
3100 line[n - 1] = '\0';
3101 osmtpd_filter_dataline(msg->ctx, "%s", line);
3103 if (ferror(msg->origf))
3104 osmtpd_err(1, "%s: ferror", __func__);
3105 free(line);
3106 return;
3109 int
3110 auth_ar_print(struct osmtpd_ctx *ctx, const char *start)
3112 const char *scan, *checkpoint, *ncheckpoint;
3113 int arlen = 0, first = 1, arid = 1;
3115 checkpoint = start;
3116 ncheckpoint = osmtpd_ltok_skip_hdr_name(start, 0) + 1;
3117 for (scan = start; scan[0] != '\0'; scan++) {
3118 if (scan[0] == '\t')
3119 arlen = (arlen + 8) & ~7;
3120 else
3121 arlen++;
3122 if (arlen >= AUTHENTICATION_RESULTS_LINELEN) {
3123 arlen = (int)(checkpoint - start);
3124 if (arlen <= 0) {
3125 arlen = (int)(ncheckpoint - start);
3126 checkpoint = ncheckpoint;
3128 osmtpd_filter_dataline(ctx, "%s%.*s", first ? "" : "\t",
3129 arlen, start);
3130 start = osmtpd_ltok_skip_cfws(checkpoint, 1);
3131 if (*start == '\0')
3132 return 0;
3133 ncheckpoint = start;
3134 scan = start;
3135 arlen = 8;
3136 first = 0;
3138 if (scan == ncheckpoint) {
3139 checkpoint = ncheckpoint;
3140 ncheckpoint = osmtpd_ltok_skip_cfws(ncheckpoint, 1);
3141 /* ARC-AR starts with i= */
3142 if (strncmp(ncheckpoint, "i=",
3143 sizeof("i=") - 1) == 0) {
3144 ncheckpoint = osmtpd_ltok_skip_digit(
3145 ncheckpoint + sizeof("i=") - 1, 0);
3146 /* authserv-id */
3147 } else if (arid) {
3148 ncheckpoint = osmtpd_ltok_skip_value(
3149 ncheckpoint, 0);
3150 arid = 0;
3151 /* methodspec */
3152 } else if (strncmp(ncheckpoint, "arc=",
3153 sizeof("arc=") - 1) == 0) {
3154 ncheckpoint = osmtpd_ltok_skip_keyword(
3155 ncheckpoint + sizeof("arc=") - 1, 0);
3156 } else if (strncmp(ncheckpoint, "dkim=",
3157 sizeof("dkim=") - 1) == 0) {
3158 ncheckpoint = osmtpd_ltok_skip_keyword(
3159 ncheckpoint + sizeof("dkim=") - 1, 0);
3160 } else if (strncmp(ncheckpoint, "iprev=",
3161 sizeof("iprev=") - 1) == 0) {
3162 ncheckpoint = osmtpd_ltok_skip_keyword(
3163 ncheckpoint + sizeof("iprev=") - 1, 0);
3164 } else if (strncmp(ncheckpoint, "spf=",
3165 sizeof("spf=") - 1) == 0) {
3166 ncheckpoint = osmtpd_ltok_skip_keyword(
3167 ncheckpoint + sizeof("spf=") - 1, 0);
3168 /* reasonspec */
3169 } else if (strncmp(ncheckpoint, "reason=",
3170 sizeof("reason=") - 1) == 0) {
3171 ncheckpoint = osmtpd_ltok_skip_ar_reasonspec(
3172 ncheckpoint, 0);
3173 /* propspec */
3174 } else {
3175 ncheckpoint = osmtpd_ltok_skip_ar_propspec(
3176 ncheckpoint, 0);
3179 if (ncheckpoint == NULL)
3180 return -1;
3182 if (*ncheckpoint == ';')
3183 ncheckpoint++;
3186 osmtpd_filter_dataline(ctx, "%s%s", first ? "" : "\t", start);
3187 return 0;
3190 ssize_t
3191 auth_ar_cat(char **ar, size_t *n, size_t aroff, const char *fmt, ...)
3193 va_list ap;
3194 char *artmp;
3195 int size;
3196 size_t nn;
3198 va_start(ap, fmt);
3199 size = vsnprintf(*ar + aroff, *n - aroff, fmt, ap);
3200 va_end(ap);
3201 if (size + aroff < *n)
3202 return (ssize_t)size + aroff;
3203 nn = (((aroff + size) / 256) + 1) * 256;
3204 artmp = realloc(*ar, nn);
3205 if (artmp == NULL)
3206 return -1;
3207 *ar = artmp;
3208 *n = nn;
3209 va_start(ap, fmt);
3210 size = vsnprintf(*ar + aroff, *n - aroff, fmt, ap);
3211 va_end(ap);
3212 return (ssize_t)size + aroff;
3215 __dead void
3216 usage(void)
3218 fprintf(stderr, "usage: filter-auth [-A] [authserv-id]\n");
3219 exit(1);