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>
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.
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.
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>
34 #include <opensmtpd.h>
44 #include "openbsd-compat.h"
45 #include "unpack_dns.h"
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)).
66 struct header *header;
68 const char *state_reason;
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))];
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];
103 char s[HOST_NAME_MAX + 1];
104 time_t t; /* Signature t=/timestamp */
107 int kt; /* Key t=/Flags */
110 struct event_asr *query;
112 /* RFC 6376 doesn't care about CNAME, use simalr with SPF limit */
113 #define AR_LOOKUP_LOOKUP_LIMIT 11
118 * RFC 5321 doesn't limit record size, enforce some resanable limit
120 #define SPF_RECORD_MAX 4096
123 struct spf_record *spf;
124 struct event_asr *eva;
135 struct osmtpd_ctx *ctx;
137 const char *state_reason;
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];
158 struct ar_signature *sig;
161 #define AUTHENTICATION_RESULTS_LINELEN 78
163 #define MIN(a, b) ((a) < (b) ? (a) : (b))
167 #define s6_addr32 __u6_addr.__u6_addr32
170 /* RFC 8617 Section 4.2.1 */
175 struct osmtpd_ctx *ctx;
178 size_t body_whitelines;
180 struct header *header;
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;
191 struct osmtpd_ctx *ctx;
193 struct spf_record *spf_helo;
194 struct spf_record *spf_mailfrom;
195 struct sockaddr_storage src;
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",
270 char *authservid = NULL;
272 EVP_ENCODE_CTX *ectx = NULL;
275 main(int argc, char *argv[])
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) {
297 osmtpd_errx(1, "invalid authservid count");
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);
319 auth_conf(const char *key, const char *value)
324 if (authservid == NULL)
325 osmtpd_errx(1, "Didn't receive admd config option");
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");
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;
346 ses->iprev = AR_FAIL;
348 memcpy(&ses->src, src, sizeof(struct sockaddr_storage));
351 if ((ses->rdns = strdup(rdns)) == NULL)
352 osmtpd_err(1, "%s: malloc", __func__);
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);
368 if ((ses->identity = strdup(identity)) == NULL)
369 osmtpd_err(1, "%s: strdup", __func__);
371 if (strlen(identity) == 0) {
372 osmtpd_filter_proceed(ctx);
376 snprintf(from, sizeof(from), "postmaster@%s", identity);
378 if ((ses->spf_helo = spf_record_new(ctx, from)) == NULL)
379 osmtpd_filter_proceed(ctx);
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);
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);
400 auth_dataline(struct osmtpd_ctx *ctx, const char *line)
402 struct message *msg = ctx->local_message;
405 if (fprintf(msg->origf, "%s\n", line) < 0)
406 osmtpd_err(1, "Couldn't write to tempfile");
408 if (line[0] == '.') {
410 if (line[0] == '\0') {
412 for (i = 0; i < msg->nheaders; i++) {
413 if (msg->header[i].sig == NULL)
415 ar_body_verify(msg->header[i].sig);
417 auth_message_verify(msg);
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)
428 if (msg->header[i].sig->query == NULL)
435 ar_body_parse(msg, line);
440 spf_record_new(struct osmtpd_ctx *ctx, const char *from)
444 struct spf_record *spf;
446 if ((spf = malloc(sizeof(*spf))) == NULL)
447 osmtpd_err(1, "%s: malloc", __func__);
450 spf->state = AR_NONE;
451 spf->state_reason = NULL;
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)
467 if ((spf->sender_local = strndup(from, at - from)) == NULL)
468 osmtpd_err(1, "%s: malloc", __func__);
474 if ((from = osmtpd_ltok_skip_domain(at, 0)) == NULL)
478 if ((spf->sender_domain = strndup(at, from - at)) == NULL)
479 osmtpd_err(1, "%s: malloc", __func__);
482 spf, spf->sender_domain, T_TXT, AR_PASS, 0, 0);
487 free(spf->sender_local);
495 spf_record_free(struct spf_record *spf)
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);
515 auth_session_new(struct osmtpd_ctx *ctx)
519 if ((ses = malloc(sizeof(*ses))) == NULL)
520 osmtpd_err(1, "%s: malloc", __func__);
523 ses->iprev = AR_NONE;
525 ses->spf_helo = NULL;
526 ses->spf_mailfrom = NULL;
528 ses->identity = NULL;
535 auth_session_free(struct osmtpd_ctx *ctx, void *data)
537 struct session *ses = data;
540 spf_record_free(ses->spf_helo);
541 if (ses->spf_mailfrom)
542 spf_record_free(ses->spf_mailfrom);
552 auth_message_new(struct osmtpd_ctx *ctx)
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");
565 msg->parsing_headers = 1;
566 msg->body_whitelines = 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__);
585 auth_message_free(struct osmtpd_ctx *ctx, void *data)
587 struct message *msg = data;
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);
609 free(msg->arc_seals);
610 free(msg->arc_signs);
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;
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 */
630 verify = osmtpd_ltok_skip_wsp(end, 1);
633 start, "DKIM-Signature", end - start) == 0 &&
636 &msg->header[msg->nheaders - 1], 1, 0);
637 else if (end != NULL &&
639 start, "ARC-Message-Signature", end - start) == 0 &&
642 &msg->header[msg->nheaders - 1], 0, 0);
643 else if (end != NULL &&
645 start, "ARC-Seal", end - start) == 0 &&
648 &msg->header[msg->nheaders - 1], 0, 1);
653 ar_header_cat(ctx, line);
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)
665 msg->header[i].sig->header = &msg->header[i];
668 msg->header[msg->nheaders].msg = msg;
670 ar_header_cat(ctx, line);
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];
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);
685 osmtpd_err(1, "%s: malloc", __func__);
688 header->buflen += snprintf(header->buf + header->buflen,
689 (((needed / 1024) + 1) * 1024) - header->buflen, "%s%s",
690 header->buflen == 0 ? "" : "\r\n", line);
694 ar_signature_parse(struct header *header, int dkim, int seal)
696 struct ar_signature *sig, *last;
697 const char *buf, *i, *end;
699 char subdomain[HOST_NAME_MAX + 1];
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__);
709 sig->header = header;
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");
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))
730 strlcpy(tagname, buf, (end - buf) + 1);
731 buf = osmtpd_ltok_skip_fws(end, 1);
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);
771 else if (buf[0] != '\0') {
772 ar_signature_state(sig, AR_PERMERROR,
777 if (sig->state != AR_UNKNOWN)
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)
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);
811 ar_signature_state(sig, AR_PERMERROR,
812 "i tag not subdomain of d");
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");
823 if (sig->t != -1 && sig->x != -1 && sig->t > sig->x) {
824 ar_signature_state(sig, AR_PERMERROR, "t tag after x tag");
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;
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;
842 if ((last->arc_i - 1) != sig->arc_i) {
844 last, AR_PERMERROR, "Invalind i-chain");
853 if (sig->cv == AR_PASS)
857 last, AR_PERMERROR, "Invalind cv-chain");
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");
870 ar_lookup_record(sig, subdomain);
874 ar_lookup_record(struct ar_signature *sig, const char *domain)
876 struct asr_query *query;
878 if (sig->state != AR_UNKNOWN)
883 if (sig->query != NULL) {
884 event_asr_abort(sig->query);
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++;
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");
905 /* Unsupported version */
906 if (start[0] != '1' || start + 1 != end)
907 ar_signature_state(sig, AR_NEUTRAL, "Unsupported v tag");
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");
922 if (osmtpd_ltok_skip_sig_a_tag_alg(start, 0) != end) {
923 ar_signature_state(sig, AR_PERMERROR, "Invalid a tag");
927 sig->asz = (size_t)(end - start);
928 if (strncmp(start, "rsa-", 4) == 0) {
930 sig->ak = EVP_PKEY_RSA;
933 } else if (strncmp(start, "ed25519-", 8) == 0) {
935 sig->ak = EVP_PKEY_ED25519;
939 ar_signature_state(sig, AR_NEUTRAL, "Unsuppored a tag k");
942 if ((size_t)(end - start) >= sizeof(ah)) {
943 ar_signature_state(sig, AR_NEUTRAL, "Unsuppored a tag h");
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");
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");
962 ar_signature_parse_b(struct ar_signature *sig, const char *start, const char *end)
967 if (sig->b != NULL) {
968 ar_signature_state(sig, AR_PERMERROR, "Duplicate b tag");
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");
983 if (EVP_DecodeFinal(ectx, sig->b + sig->bsz,
985 ar_signature_state(sig, AR_PERMERROR, "Invalid b tag");
988 sig->bsz += decodesz;
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';
999 ar_signature_parse_bh(struct ar_signature *sig, const char *start, const char *end)
1005 if (sig->bhsz != 0) {
1006 ar_signature_state(sig, AR_PERMERROR, "Duplicate bh tag");
1010 * EVP_Decode* expects sig->bh to be large enough,
1011 * so count the actual b64 characters.
1016 b64 = osmtpd_ltok_skip_fws(b64, 1);
1017 if (osmtpd_ltok_skip_alphadigitps(b64, 0) == NULL)
1022 if (b64[0] == '=') {
1024 b64 = osmtpd_ltok_skip_fws(b64 + 1, 1);
1025 if (b64[0] == '=') {
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");
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");
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");
1049 sig->bhsz += decodesz;
1053 ar_signature_parse_c(struct ar_signature *sig, const char *start, const char *end)
1056 ar_signature_state(sig, AR_PERMERROR, "Duplicate c tag");
1059 if (strncmp(start, "simple", 6) == 0) {
1060 sig->c = CANON_HEADER_SIMPLE;
1062 } else if (strncmp(start, "relaxed", 7) == 0) {
1063 sig->c = CANON_HEADER_RELAXED;
1066 ar_signature_state(sig, AR_PERMERROR, "Invalid c tag");
1069 if (start[0] == '/') {
1071 if (strncmp(start, "simple", 6) == 0) {
1072 sig->c |= CANON_BODY_SIMPLE;
1074 } else if (strncmp(start, "relaxed", 7) == 0) {
1075 sig->c |= CANON_BODY_RELAXED;
1078 ar_signature_state(sig, AR_PERMERROR,
1085 ar_signature_state(sig, AR_PERMERROR, "Invalid c tag");
1088 sig->c |= CANON_DONE;
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");
1099 if (strncmp(start, "pass", 4) == 0) {
1101 } else if (strncmp(start, "fail", 4) == 0) {
1103 } else if (strncmp(start, "none", 4) == 0) {
1106 ar_signature_state(sig, AR_PERMERROR, "Invalid cv tag");
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");
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");
1123 strlcpy(sig->d, start, end - start + 1);
1127 ar_signature_parse_h(struct ar_signature *sig, const char *start, const char *end)
1132 if (sig->h != NULL) {
1133 ar_signature_state(sig, AR_PERMERROR, "Duplicate h tag");
1136 if (osmtpd_ltok_skip_sig_h_tag_value(start, 0) < end) {
1137 ar_signature_state(sig, AR_PERMERROR, "Invalid h tag");
1142 if ((h = osmtpd_ltok_skip_hdr_name(h, 0)) == NULL) {
1143 ar_signature_state(sig, AR_PERMERROR,
1148 /* ';' is part of hdr-name */
1153 h = osmtpd_ltok_skip_fws(h, 1);
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__);
1163 h = osmtpd_ltok_skip_hdr_name(start, 0);
1164 /* ';' is part of hdr-name */
1166 sig->h[n] = strndup(start, end - start);
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] != ':')
1174 start = osmtpd_ltok_skip_fws(start + 1, 1);
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");
1185 if (osmtpd_ltok_skip_sig_i_tag_value(start, 0) != end) {
1186 ar_signature_state(sig, AR_PERMERROR, "Invalid i tag");
1190 sig->isz = (size_t)(end - start);
1194 arc_signature_parse_i(struct ar_signature *sig, const char *start, const char *end)
1198 if (sig->arc_i != 0) {
1199 ar_signature_state(sig, AR_PERMERROR, "Duplicate i tag");
1202 if (osmtpd_ltok_skip_digit(start, 0) != end) {
1203 ar_signature_state(sig, AR_PERMERROR, "Invalid i tag");
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");
1211 sig->arc_i = (int) i;
1215 ar_signature_parse_l(struct ar_signature *sig, const char *start, const char *end)
1220 if (sig->l != -1) { /* Duplicate tag */
1221 ar_signature_state(sig, AR_PERMERROR, "Duplicate l tag");
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");
1232 if (l > SSIZE_MAX) {
1233 ar_signature_state(sig, AR_PERMERROR, "l tag too large");
1236 sig->l = (ssize_t)l;
1240 ar_signature_parse_q(struct ar_signature *sig, const char *start, const char *end)
1245 ar_signature_state(sig, AR_PERMERROR, "Duplicate q tag");
1250 start = osmtpd_ltok_skip_fws(start, 1);
1251 qend = osmtpd_ltok_skip_sig_q_tag_method(start, 0);
1253 ar_signature_state(sig, AR_PERMERROR, "Invalid q tag");
1256 if (strncmp(start, "dns/txt", qend - start) == 0)
1258 start = osmtpd_ltok_skip_fws(qend, 1);
1259 if (start[0] != ':')
1263 ar_signature_state(sig, AR_PERMERROR, "Invalid q tag");
1268 ar_signature_state(sig, AR_NEUTRAL, "No useable q found");
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");
1280 if (osmtpd_ltok_skip_selector(start, 0) != end) {
1281 ar_signature_state(sig, AR_PERMERROR, "Invalid s tag");
1284 strlcpy(sig->s, start, end - start + 1);
1288 ar_signature_parse_t(struct ar_signature *sig, const char *start, const char *end)
1293 ar_signature_state(sig, AR_PERMERROR, "Duplicate t tag");
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");
1306 ar_signature_parse_x(struct ar_signature *sig, const char *start, const char *end)
1311 ar_signature_state(sig, AR_PERMERROR, "Duplicate x tag");
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");
1324 ar_signature_parse_z(struct ar_signature *sig, const char *start, const char *end)
1327 ar_signature_state(sig, AR_PERMERROR, "Duplicate z tag");
1332 if (osmtpd_ltok_skip_sig_z_tag_value(start, 0) != end) {
1333 ar_signature_state(sig, AR_PERMERROR, "Invalid z tag");
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;
1348 if (sig->state != AR_UNKNOWN)
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");
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,
1366 ar_signature_state(sig, AR_FAIL, "ah tag");
1370 if (EVP_DigestInit_ex(bctx, sig->ah, NULL) != 1) {
1371 ar_signature_state(sig, AR_FAIL, "ah tag");
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; ) {
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)
1388 end = osmtpd_ltok_skip_fws(
1389 msg->header[i].buf + strlen(sig->h[header]), 1);
1392 ar_signature_header(bctx, sig, &(msg->header[i]));
1393 msg->header[i].parsed = 1;
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");
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,
1417 ar_signature_state(sig, AR_FAIL, "b mismatch");
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");
1431 if (msg->arc_seals[sig->arc_i]->state == AR_UNKNOWN ||
1432 msg->arc_signs[sig->arc_i]->state == AR_UNKNOWN)
1435 if (msg->arc_seals[sig->arc_i]->state !=
1436 msg->arc_signs[sig->arc_i]->state) {
1438 msg->arc_signs[sig->arc_i], AR_FAIL, NULL);
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)))
1450 ar_signature_header(EVP_MD_CTX *bctx, struct ar_signature *sig,
1451 struct header *header)
1454 const char *ptr = header->buf, *end;
1456 int canon = sig->c & CANON_HEADER;
1458 for (ptr = header->buf; ptr[0] != '\0'; ptr++) {
1460 if (canon == CANON_HEADER_RELAXED) {
1461 ptr = osmtpd_ltok_skip_fws(ptr, 1);
1462 c = tolower(ptr[0]);
1467 if (canon == CANON_HEADER_RELAXED)
1468 ptr = osmtpd_ltok_skip_fws(
1471 if (ar_b_digest_update(bctx, &c, 1) == 0)
1472 osmtpd_errx(1, "ar_b_digest_update");
1475 end = osmtpd_ltok_skip_fws(ptr, 1);
1477 if (sig->header == header && ptr == sig->bheader) {
1478 ptr = osmtpd_ltok_skip_tag_value(
1482 if (ar_b_digest_update(bctx, ptr, 1) == 0)
1483 osmtpd_errx(1, "ar_b_digest_update");
1485 if (canon == CANON_HEADER_RELAXED) {
1488 if (ar_b_digest_update(bctx, " ", 1) == 0)
1489 osmtpd_errx(1, "ar_b_digest_update");
1491 if (ar_b_digest_update(bctx, ptr,
1493 osmtpd_errx(1, "ar_b_digest_update");
1499 if (sig->header != header) {
1500 if (ar_b_digest_update(bctx, "\r\n", 2) == 0)
1501 osmtpd_errx(1, "ar_b_digest_update");
1506 ar_signature_state(struct ar_signature *sig, enum ar_state state,
1509 if (sig->query != NULL) {
1510 event_asr_abort(sig->query);
1512 sig->header->msg->nqueries--;
1514 switch (sig->state) {
1519 if (state == AR_PERMERROR)
1523 osmtpd_errx(1, "Unexpected transition: %s -> %",
1524 ar_state2str(sig->state), ar_state2str(state));
1526 if (state == AR_PASS)
1530 if (state == AR_PASS)
1532 if (state == AR_TEMPERROR || state == AR_PERMERROR)
1534 osmtpd_errx(1, "Unexpected transition: %s -> %",
1535 ar_state2str(sig->state), ar_state2str(state));
1537 if (state == AR_PERMERROR)
1544 sig->state_reason = reason;
1548 ar_state2str(enum ar_state state)
1574 ar_rr_resolve(struct asr_result *ar, void *arg)
1576 struct ar_signature *sig = arg;
1577 char key[UINT16_MAX + 1];
1579 size_t keylen, cstrlen;
1581 struct dns_header h;
1584 char buf[HOST_NAME_MAX + 1];
1587 sig->header->msg->nqueries--;
1589 if (sig->state != AR_UNKNOWN)
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));
1597 if (ar->ar_h_errno == HOST_NOT_FOUND) {
1598 ar_signature_state(sig, AR_PERMERROR,
1599 hstrerror(ar->ar_h_errno));
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)),
1610 ar_signature_state(sig, AR_PERMERROR, pack.err);
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)),
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);
1632 if (rr.rr_type != T_TXT) {
1633 osmtpd_warn(sig->header->msg->ctx,
1634 "Unexpected DKIM DNS record: %d for domain %s",
1636 print_dname(q.q_dname, buf, sizeof(buf)));
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))
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);
1657 if (rr.rr.other.rdlen > 0) /* Invalid TXT RDATA */
1660 if (ar_key_text_parse(sig, key))
1664 if (h.ancount == 0) {
1665 ar_signature_state(sig, AR_PERMERROR,
1666 "No matching key found");
1668 /* Only verify if all headers have been read */
1669 if (!sig->header->msg->parsing_headers)
1670 ar_signature_verify(sig);
1674 auth_message_verify(sig->header->msg);
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;
1691 key = osmtpd_ltok_skip_fws(key, 1);
1692 /* Validate syntax early */
1693 if ((end = osmtpd_ltok_skip_tag_list(key, 0)) == NULL)
1696 while (key[0] != '\0') {
1697 key = osmtpd_ltok_skip_fws(key, 1);
1698 if ((end = osmtpd_ltok_skip_tag_name(key, 0)) == NULL)
1701 if ((size_t)(end - key) != 1)
1705 key = osmtpd_ltok_skip_fws(end, 1);
1709 key = osmtpd_ltok_skip_fws(key + 1, 1);
1710 if ((end = osmtpd_ltok_skip_tag_value(key, 0)) == NULL)
1715 * RFC 6376 section 3.6.1, v=:
1716 * RECOMMENDED...This tag MUST be the first tag in the
1720 osmtpd_ltok_skip_key_v_tag_value(key, 0) != end)
1725 if (h != 0) /* Duplicate tag */
1727 /* Invalid tag value */
1728 if (osmtpd_ltok_skip_key_h_tag_value(key, 0) != end)
1731 if ((tagvend = osmtpd_ltok_skip_key_h_tag_alg(
1734 hashname = strndup(key, tagvend - key);
1735 if (hashname == NULL)
1736 osmtpd_err(1, "strndup");
1737 if (EVP_get_digestbyname(hashname) == sig->ah) {
1743 key = osmtpd_ltok_skip_fws(tagvend, 1);
1746 key = osmtpd_ltok_skip_fws(key + 1, 1);
1753 if (k != 0) /* Duplicate tag */
1756 if (strncmp(key, "rsa", end - key) == 0) {
1757 if (sig->ak != EVP_PKEY_RSA)
1760 } else if (strncmp(key, "ed25519", end - key) == 0) {
1761 if (sig->ak != EVP_PKEY_ED25519)
1769 if (n != 0) /* Duplicate tag */
1772 /* semicolon is part of safe-char */
1773 if (osmtpd_ltok_skip_key_n_tag_value(key, 0) < end)
1778 if (p != 0) /* Duplicate tag */
1782 key = osmtpd_ltok_skip_fws(key, 1);
1783 if (osmtpd_ltok_skip_alphadigitps(
1786 pkraw[pkrawlen++] = key++[0];
1787 if (pkrawlen >= sizeof(pkraw))
1790 if (key[0] == '=') {
1791 pkraw[pkrawlen++] = '=';
1792 key = osmtpd_ltok_skip_fws(key + 1, 1);
1793 if (pkrawlen >= sizeof(pkraw))
1795 if (key[0] == '=') {
1796 pkraw[pkrawlen++] = '=';
1798 if (pkrawlen >= sizeof(pkraw))
1802 /* Invalid tag value */
1803 if (pkrawlen % 4 != 0 || key != end)
1807 if (s != 0) /* Duplicate tag */
1809 /* Invalid tag value */
1810 if (osmtpd_ltok_skip_key_s_tag_value(key, 0) != end)
1814 osmtpd_ltok_skip_key_s_tag_type(
1817 if (strncmp(key, "*", tagvend - key) == 0 ||
1818 strncmp(key, "email", tagvend - key) == 0) {
1822 key = osmtpd_ltok_skip_fws(tagvend, 1);
1825 key = osmtpd_ltok_skip_fws(key + 1, 1);
1832 if (t != 0) /* Duplicate tag */
1835 if (osmtpd_ltok_skip_key_t_tag_value(key, 0) != end)
1838 tagvend = osmtpd_ltok_skip_key_t_tag_flag(
1840 if (strncmp(key, "y", tagvend - key) == 0)
1842 else if (strncmp(key, "s", tagvend - key) == 0)
1844 key = osmtpd_ltok_skip_fws(tagvend, 1);
1847 key = osmtpd_ltok_skip_fws(key + 1, 1);
1856 key = osmtpd_ltok_skip_fws(key, 1);
1859 else if (key[0] != '\0')
1863 if (!p) /* Missing tag */
1865 if (k == 0 && sig->ak != EVP_PKEY_RSA) /* Default to RSA */
1868 if (pkraw[0] == '\0') {
1869 ar_signature_state(sig, AR_PERMERROR, "Key is revoked");
1875 pkoff = strlcpy(pkimp, "-----BEGIN PUBLIC KEY-----\n",
1878 for (key = pkraw; key[0] != '\0';) {
1879 if (pkoff + 2 >= sizeof(pkimp))
1881 pkimp[pkoff++] = key++[0];
1882 if (++linelen == 64) {
1883 pkimp[pkoff++] = '\n';
1887 /* Leverage pkoff check in loop */
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);
1899 case EVP_PKEY_ED25519:
1900 if ((pkrawlen / 4) * 3 >= sizeof(pkimp))
1902 EVP_DecodeInit(ectx);
1903 if (EVP_DecodeUpdate(ectx, pkimp, &tmp, pkraw, pkrawlen) == -1)
1906 if (EVP_DecodeFinal(ectx, pkimp, &tmp) == -1)
1909 sig->p = EVP_PKEY_new_raw_public_key(sig->ak, NULL, pkimp,
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.
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;
1933 if (line[0] == '\0') {
1934 msg->body_whitelines++;
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)
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;
1952 while (line[0] != '\0') {
1955 if ((end = osmtpd_ltok_skip_wsp(end, 0)) == NULL)
1961 while (osmtpd_ltok_skip_wsp(end, 0) == NULL &&
1965 for (i = 0; i < msg->nheaders; i++) {
1966 sig = msg->header[i].sig;
1967 if (sig == NULL || sig->state != AR_UNKNOWN)
1970 (sig->c & CANON_BODY) == CANON_BODY_RELAXED) {
1972 len = end[0] == '\0' ? 0 : 1;
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);
1981 osmtpd_err(1, "EVP_DigestUpdate");
1985 for (i = 0; i < msg->nheaders; i++) {
1986 sig = msg->header[i].sig;
1987 if (sig == NULL || sig->state != AR_UNKNOWN)
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);
1993 osmtpd_err(1, "EVP_DigestUpdate");
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)
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");
2016 ar_signature_state(sig, AR_PERMERROR,
2017 "l tag larger than body");
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");
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(".-+,/_=")];
2042 if (domain == NULL || domain[0] == '\0') {
2043 spf_done(spf, AR_PERMERROR, "Empty domain");
2048 domain[0] != ' ' && domain[0] != '\0' && i < sizeof(spec);
2051 if (domain[0] < 0x21 || domain[0] > 0x7e) {
2053 spf, AR_PERMERROR, "Invalid character in domain-spec");
2057 if (domain[0] != '%') {
2058 spec[i++] = domain[0];
2063 switch (domain[0]) {
2071 if (i + 3 >= sizeof(spec)) {
2073 spf, AR_PERMERROR, "domain-spec too large");
2085 delimiters[0] = '\0';
2087 switch (domain[0]) {
2090 mlen = (size_t) snprintf(macro, sizeof(macro),
2091 "%s@%s", spf->sender_local,
2092 spf->sender_domain);
2098 delimiters[0] = '\0';
2100 switch (domain[0]) {
2103 mlen = (size_t) snprintf(macro, sizeof(macro),
2104 "%s@%s", spf->sender_local,
2105 spf->sender_domain);
2109 mlen = strlcpy(macro,
2110 spf->sender_local, sizeof(macro));
2114 mlen = strlcpy(macro,
2120 if (spf->nqueries < 1) {
2121 spf_done(spf, AR_PERMERROR,
2122 "no domain for d macro");
2125 mlen = strlcpy(macro,
2126 spf->queries[spf->nqueries - 1].domain,
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),
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));
2163 spf_done(spf, AR_PERMERROR,
2164 "unsupported type of address");
2170 mlen = strlcpy(macro, ses->rdns, sizeof(macro));
2174 if (ses->src.ss_family == AF_INET)
2175 mlen = strlcpy(macro, "in-addr",
2177 else if (ses->src.ss_family == AF_INET6)
2178 mlen = strlcpy(macro, "ip6",
2181 spf_done(spf, AR_PERMERROR,
2182 "unsupported type of address");
2188 mlen = strlcpy(macro, ses->identity,
2192 spf_done(spf, AR_PERMERROR,
2193 "Unexpected macro in domain-spec");
2197 if (mlen >= sizeof(macro)) {
2198 spf_done(spf, AR_PERMERROR,
2199 "Macro expansions too large");
2204 if (isdigit(domain[0])) {
2205 digits = strtol(domain, &endptr, 10);
2207 spf_done(spf, AR_PERMERROR,
2208 "digits in macro can't be 0");
2214 if (domain[0] == 'r') {
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");
2239 tmp = macro + strlen(macro) - 1;
2241 * DIGIT rightmost elements after reversal is DIGIT
2242 * lefmost elements before reversal
2245 while (tmp > macro &&
2246 strchr(delimiters, tmp[0]) == NULL)
2256 if (smacro[0] != '\0')
2257 strlcat(smacro, ".", sizeof(smacro));
2258 strlcat(smacro, tmp + 1, sizeof(smacro));
2262 if (smacro[0] != '\0')
2263 strlcat(smacro, ".", sizeof(smacro));
2264 strlcat(smacro, macro, sizeof(smacro));
2269 endptr = macro + strlen(macro);
2270 while (digits > 0) {
2271 while (tmp < endptr &&
2272 strchr(delimiters, tmp[0]) == NULL)
2284 strlcpy(smacro, macro, sizeof(smacro));
2288 i = strlcat(spec, smacro, sizeof(spec));
2289 if (i >= sizeof(spec)) {
2291 spf, AR_PERMERROR, "domain-spec too large");
2297 mlen = strlcpy(macro, ses->rdns, sizeof(macro));
2301 if (ses->src.ss_family == AF_INET)
2302 mlen = strlcpy(macro, "in-addr",
2304 else if (ses->src.ss_family == AF_INET6)
2305 mlen = strlcpy(macro, "ip6",
2308 spf_done(spf, AR_PERMERROR,
2309 "unsupported type of address");
2315 mlen = strlcpy(macro, ses->identity,
2319 spf_done(spf, AR_PERMERROR,
2320 "Mallformed macro, unexpected character after %");
2324 if (mlen >= sizeof(macro)) {
2325 spf_done(spf, AR_PERMERROR,
2326 "Macro expansions too large");
2331 if (isdigit(domain[0])) {
2332 digits = strtol(domain, &endptr, 10);
2334 spf_done(spf, AR_PERMERROR,
2335 "digits in macro can't be 0");
2341 if (domain[0] == 'r') {
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");
2366 tmp = macro + strlen(macro) - 1;
2368 * DIGIT rightmost elements after reversal is DIGIT
2369 * lefmost elements before reversal
2372 while (tmp > macro &&
2373 strchr(delimiters, tmp[0]) == NULL)
2383 if (smacro[0] != '\0')
2384 strlcat(smacro, ".", sizeof(smacro));
2385 strlcat(smacro, tmp + 1, sizeof(smacro));
2389 if (smacro[0] != '\0')
2390 strlcat(smacro, ".", sizeof(smacro));
2391 strlcat(smacro, macro, sizeof(smacro));
2396 endptr = macro + strlen(macro);
2397 while (digits > 0) {
2398 while (tmp < endptr &&
2399 strchr(delimiters, tmp[0]) == NULL)
2411 strlcpy(smacro, macro, sizeof(smacro));
2415 i = strlcat(spec, smacro, sizeof(spec));
2416 if (i >= sizeof(spec)) {
2418 spf, AR_PERMERROR, "domain-spec too large");
2424 spf_done(spf, AR_PERMERROR,
2425 "Mallformed macro, unexpected character after %");
2430 if ((tmp = strndup(spec, i)) == NULL)
2431 osmtpd_err(1, "%s: strndup", __func__);
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;
2446 if (spf->nqueries >= SPF_DNS_LOOKUP_LIMIT) {
2447 spf_done(spf, AR_PERMERROR, "Too many DNS queries");
2451 query = &spf->queries[spf->nqueries];
2454 query->q = qualifier;
2455 query->include = include;
2456 query->exists = exists;
2460 if ((query->domain = spf_evaluate_domain(spf, domain)) == NULL)
2463 if (domain == NULL || !strlen(domain)) {
2464 spf_done(spf, AR_PERMERROR, "Empty domain");
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");
2479 spf_resolve(struct asr_result *ar, void *arg)
2483 struct spf_query *query = arg;
2484 struct spf_record *spf = query->spf;
2486 struct dns_header h;
2489 char buf[HOST_NAME_MAX + 1];
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));
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));
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)),
2514 spf_done(query->spf, AR_TEMPERROR, pack.err);
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)),
2530 spf_resolve_txt(&rr, query);
2534 spf_resolve_mx(&rr, query);
2538 spf_resolve_a(&rr, query);
2542 spf_resolve_aaaa(&rr, query);
2546 spf_resolve_cname(&rr, query);
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");
2562 if (spf->running > 0)
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)
2574 if (!spf->done && spf->running == 0)
2575 spf_done(spf, AR_NONE, NULL);
2579 spf_resolve_txt(struct dns_rr *rr, struct spf_query *query)
2582 txt = spf_parse_txt(rr->rr.other.rdata, rr->rr.other.rdlen);
2584 osmtpd_warn(NULL, "spf_parse_txt");
2588 if (strncasecmp("v=spf1 ", txt, 7)) {
2593 if (query->txt != NULL) {
2595 spf_done(query->spf, AR_PERMERROR, "Duplicated SPF record");
2601 spf_execute_txt(query);
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);
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);
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);
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);
2646 spf_parse_txt(const char *rdata, size_t rdatalen)
2648 size_t len, dstsz = SPF_RECORD_MAX - 1;
2652 if (rdatalen >= dstsz) {
2657 odst = dst = malloc(dstsz);
2659 osmtpd_err(1, "%s: malloc", __func__);
2662 len = *(const unsigned char *)rdata;
2663 if (len >= rdatalen) {
2678 memmove(dst, rdata, len);
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)
2704 addr = &(((struct sockaddr_in *)(&ses->src))->sin_addr);
2706 return ((addr->s_addr ^ net->s_addr) & htonl(0xFFFFFFFFu << (32 - bits)));
2710 spf_check_cidr6(struct spf_record *spf, struct in6_addr *net, int bits)
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)
2723 addr = &(((struct sockaddr_in6 *)(&ses->src))->sin6_addr);
2725 a = addr->s6_addr32;
2729 incomplete = bits & 0x1f;
2731 rc = memcmp(a, n, whole << 2);
2736 return (a[whole] ^ n[whole]) & htonl((0xffffffffu) << (32 - incomplete));
2742 spf_execute_txt(struct spf_query *query)
2745 struct in6_addr in6a;
2747 char *in = query->txt + query->pos;
2751 enum ar_state q = query->q;
2753 while ((ap = strsep(&in, " ")) != NULL) {
2754 if (strcasecmp(ap, "v=spf1") == 0)
2757 end = ap + strlen(ap)-1;
2764 } else if (*ap == '-') {
2767 } else if (*ap == '~') {
2770 } else if (*ap == '?') {
2775 if (q != AR_PASS && query->include)
2778 if (strncasecmp("all", ap, 3) == 0) {
2779 spf_done(query->spf, q, NULL);
2782 if (strncasecmp("ip4:", ap, 4) == 0) {
2783 if ((bits = inet_net_pton(AF_INET, ap + 4, &ina, sizeof(ina))) == -1)
2786 if (spf_check_cidr(query->spf, &ina, bits) == 0) {
2787 spf_done(query->spf, q, NULL);
2792 if (strncasecmp("ip6:", ap, 4) == 0) {
2793 if ((bits = inet_net_pton(AF_INET6, ap + 4, &in6a, sizeof(in6a))) == -1)
2796 if (spf_check_cidr6(query->spf, &in6a, bits) == 0) {
2797 spf_done(query->spf, q, NULL);
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);
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);
2816 if (strncasecmp("exists:", ap, 7) == 0) {
2817 spf_lookup_record(query->spf, ap + 7, T_A,
2818 q, query->include, 1);
2821 if (strncasecmp("include:", ap, 8) == 0) {
2822 spf_lookup_record(query->spf, ap + 8, T_TXT, q, 1, 0);
2825 if (strncasecmp("redirect=", ap, 9) == 0) {
2828 spf_lookup_record(query->spf, ap + 9, T_TXT,
2829 q, query->include, 0);
2832 if (strcasecmp("mx", ap) == 0) {
2833 spf_lookup_record(query->spf, query->domain, T_MX,
2834 q, query->include, 0);
2837 if (strncasecmp("mx:", ap, 3) == 0) {
2838 spf_lookup_record(query->spf, ap + 3, T_MX,
2839 q, query->include, 0);
2847 query->pos = in - query->txt;
2853 spf_done(struct spf_record *spf, enum ar_state state, const char *reason)
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;
2870 spf->state_reason = reason;
2873 osmtpd_filter_proceed(spf->ctx);
2877 spf_ar_cat(const char *type, struct spf_record *spf, char **line, size_t *linelen, ssize_t *aroff)
2881 auth_ar_cat(line, linelen, *aroff,
2882 "; spf=none %s=none", type)
2890 auth_ar_cat(line, linelen, *aroff,
2891 "; spf=%s", ar_state2str(spf->state))
2897 auth_ar_cat(line, linelen, *aroff,
2906 if (spf->state_reason != NULL) {
2908 auth_ar_cat(line, linelen, *aroff,
2909 " reason=\"%s\"", spf->state_reason)
2919 auth_message_verify(struct message *msg)
2923 if (!msg->readdone || msg->nqueries > 0)
2926 for (i = 0; i < msg->nheaders; i++) {
2927 if (msg->header[i].sig == NULL)
2929 if (msg->header[i].sig->query != NULL)
2931 if (msg->header[i].sig->state != AR_UNKNOWN)
2933 ar_signature_state(msg->header[i].sig, AR_PASS, NULL);
2936 auth_ar_create(msg->ctx);
2940 ar_signature_ar_cat(const char *type, struct ar_signature *sig, char **line, size_t *linelen, ssize_t *aroff)
2943 auth_ar_cat(line, linelen, *aroff,
2944 "; %s=%s", type, ar_state2str(sig->state))
2948 if (sig->state_reason != NULL) {
2950 auth_ar_cat(line, linelen, *aroff,
2951 " reason=\"%s\"", sig->state_reason)
2956 if (sig->s[0] != '\0') {
2958 auth_ar_cat(line, linelen, *aroff,
2959 " header.s=%s", sig->s)
2964 if (sig->d[0] != '\0') {
2966 auth_ar_cat(line, linelen, *aroff,
2967 " header.d=%s", sig->d)
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) {
2979 auth_ar_cat(line, linelen, *aroff,
2980 " header.i=%d", sig->arc_i)
2985 if (sig->a != NULL) {
2987 auth_ar_cat(line, linelen, *aroff,
2988 " header.a=%.*s", (int)sig->asz, sig->a)
2993 if (sig->bheaderclean[0] != '\0') {
2995 auth_ar_cat(line, linelen, *aroff,
2996 " header.b=%s", sig->bheaderclean)
3005 auth_ar_create(struct osmtpd_ctx *ctx)
3007 struct ar_signature *sig;
3009 ssize_t n, aroff = 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__);
3021 for (i = ARC_MAX_I; i >= ARC_MIN_I; i--) {
3022 if (msg->arc_signs[i] != NULL)
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)
3041 if (ar_signature_ar_cat(
3042 "dkim", sig, &line, &linelen, &aroff) != 0)
3043 osmtpd_err(1, "%s: malloc", __func__);
3047 aroff = auth_ar_cat(&line, &linelen, aroff, "; dkim=none");
3049 osmtpd_err(1, "%s: malloc", __func__);
3054 for (i = ARC_MAX_I; i > 0; i--) {
3055 sig = msg->arc_signs[i];
3061 if (ar_signature_ar_cat(
3062 "arc", sig, &line, &linelen, &aroff) != 0)
3063 osmtpd_err(1, "%s: malloc", __func__);
3069 aroff = auth_ar_cat(&line, &linelen, aroff, "; arc=none");
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__);
3087 if (spf_ar_cat("smtp.mailfrom", ses->spf_helo,
3088 &line, &linelen, &aroff) != 0)
3089 osmtpd_err(1, "%s: malloc", __func__);
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);
3099 while ((n = getline(&line, &linelen, msg->origf)) != -1) {
3101 osmtpd_filter_dataline(msg->ctx, "%s", line);
3103 if (ferror(msg->origf))
3104 osmtpd_err(1, "%s: ferror", __func__);
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;
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;
3122 if (arlen >= AUTHENTICATION_RESULTS_LINELEN) {
3123 arlen = (int)(checkpoint - start);
3125 arlen = (int)(ncheckpoint - start);
3126 checkpoint = ncheckpoint;
3128 osmtpd_filter_dataline(ctx, "%s%.*s", first ? "" : "\t",
3130 start = osmtpd_ltok_skip_cfws(checkpoint, 1);
3133 ncheckpoint = start;
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);
3148 ncheckpoint = osmtpd_ltok_skip_value(
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);
3169 } else if (strncmp(ncheckpoint, "reason=",
3170 sizeof("reason=") - 1) == 0) {
3171 ncheckpoint = osmtpd_ltok_skip_ar_reasonspec(
3175 ncheckpoint = osmtpd_ltok_skip_ar_propspec(
3179 if (ncheckpoint == NULL)
3182 if (*ncheckpoint == ';')
3186 osmtpd_filter_dataline(ctx, "%s%s", first ? "" : "\t", start);
3191 auth_ar_cat(char **ar, size_t *n, size_t aroff, const char *fmt, ...)
3199 size = vsnprintf(*ar + aroff, *n - aroff, fmt, ap);
3201 if (size + aroff < *n)
3202 return (ssize_t)size + aroff;
3203 nn = (((aroff + size) / 256) + 1) * 256;
3204 artmp = realloc(*ar, nn);
3210 size = vsnprintf(*ar + aroff, *n - aroff, fmt, ap);
3212 return (ssize_t)size + aroff;
3218 fprintf(stderr, "usage: filter-auth [-A] [authserv-id]\n");