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 "unpack_dns.h"
48 * Use RFC8601 (Authentication-Results) codes instead of RFC6376 codes,
49 * since they're more expressive.
61 struct dkim_signature {
62 struct header *header;
63 enum dkim_state state;
64 const char *state_reason;
75 #define HEADER_B_MAX_LEN 8
76 char bheaderclean[HEADER_B_MAX_LEN + 1];
77 /* Make sure padding bits for base64 decoding fit */
78 char bh[EVP_MAX_MD_SIZE + (3 - (EVP_MAX_MD_SIZE % 3))];
82 #define CANON_HEADER_SIMPLE 0
83 #define CANON_HEADER_RELAXED 1
84 #define CANON_HEADER 1
85 #define CANON_BODY_SIMPLE 0
86 #define CANON_BODY_RELAXED 1 << 1
87 #define CANON_BODY 1 << 1
88 #define CANON_DONE 1 << 2
89 char d[HOST_NAME_MAX + 1];
95 char s[HOST_NAME_MAX + 1];
96 time_t t; /* Signature t=/timestamp */
99 int kt; /* Key t=/Flags */
102 struct event_asr *query;
104 /* RFC 6376 doesn't care about CNAME, use simalr with SPF limit */
105 #define DKIM_LOOKUP_LOOKUP_LIMIT 11
110 * Use RFC7601 (Authentication-Results), anyway OpenSMTPD reports only pass or fail
132 * RFC 5321 doesn't limit record size, enforce some resanable limit
134 #define SPF_RECORD_MAX 4096
137 struct spf_record *spf;
138 struct event_asr *eva;
149 struct osmtpd_ctx *ctx;
150 enum spf_state state;
151 const char *state_reason;
157 /* RFC 7208 Section 4.6.4 limits to 10 DNS lookup,
158 * and one is reserved for the first query.
159 * To prevent of infinity loop I count each CNAME
160 * as dedicated lookup, same as A and AAAA.
161 * So, I use 41 as the limit. */
162 #define SPF_DNS_LOOKUP_LIMIT 41
163 struct spf_query queries[SPF_DNS_LOOKUP_LIMIT];
172 struct dkim_signature *sig;
175 #define AUTHENTICATION_RESULTS_LINELEN 78
176 #define MIN(a, b) ((a) < (b) ? (a) : (b))
179 struct osmtpd_ctx *ctx;
182 size_t body_whitelines;
184 struct header *header;
191 struct osmtpd_ctx *ctx;
192 enum iprev_state iprev;
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 dkim_header_add(struct osmtpd_ctx *, const char *);
213 void dkim_signature_parse(struct header *);
214 void dkim_signature_parse_v(struct dkim_signature *, const char *, const char *);
215 void dkim_signature_parse_a(struct dkim_signature *, const char *, const char *);
216 void dkim_signature_parse_b(struct dkim_signature *, const char *, const char *);
217 void dkim_signature_parse_bh(struct dkim_signature *, const char *, const char *);
218 void dkim_signature_parse_c(struct dkim_signature *, const char *, const char *);
219 void dkim_signature_parse_d(struct dkim_signature *, const char *, const char *);
220 void dkim_signature_parse_h(struct dkim_signature *, const char *, const char *);
221 void dkim_signature_parse_i(struct dkim_signature *, const char *, const char *);
222 void dkim_signature_parse_l(struct dkim_signature *, const char *, const char *);
223 void dkim_signature_parse_q(struct dkim_signature *, const char *, const char *);
224 void dkim_signature_parse_s(struct dkim_signature *, const char *, const char *);
225 void dkim_signature_parse_t(struct dkim_signature *, const char *, const char *);
226 void dkim_signature_parse_x(struct dkim_signature *, const char *, const char *);
227 void dkim_signature_parse_z(struct dkim_signature *, const char *, const char *);
228 void dkim_lookup_record(struct dkim_signature *sig, const char *domain);
229 void dkim_signature_verify(struct dkim_signature *);
230 void dkim_signature_header(EVP_MD_CTX *, struct dkim_signature *, struct header *);
231 void dkim_signature_state(struct dkim_signature *, enum dkim_state, const char *);
232 const char *dkim_state2str(enum dkim_state);
233 void dkim_header_cat(struct osmtpd_ctx *, const char *);
234 void dkim_body_parse(struct message *, const char *);
235 void dkim_body_verify(struct dkim_signature *);
236 void dkim_rr_resolve(struct asr_result *, void *);
237 const char *iprev_state2str(enum iprev_state);
238 char *spf_evaluate_domain(struct spf_record *, const char *);
239 void spf_lookup_record(struct spf_record *, const char *, int,
240 enum spf_state, int, int);
241 void spf_done(struct spf_record *, enum spf_state, const char *);
242 void spf_resolve(struct asr_result *, void *);
243 void spf_resolve_txt(struct dns_rr *, struct spf_query *);
244 void spf_resolve_mx(struct dns_rr *, struct spf_query *);
245 void spf_resolve_a(struct dns_rr *, struct spf_query *);
246 void spf_resolve_aaaa(struct dns_rr *, struct spf_query *);
247 void spf_resolve_cname(struct dns_rr *, struct spf_query *);
248 char* spf_parse_txt(const char *, size_t);
249 int spf_check_cidr(struct spf_record *, struct in_addr *, int );
250 int spf_check_cidr6(struct spf_record *, struct in6_addr *, int );
251 int spf_execute_txt(struct spf_query *);
252 const char *spf_state2str(enum spf_state);
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 ssize_t auth_ar_cat(char **ar, size_t *n, size_t aroff, const char *fmt, ...)
257 __attribute__((__format__ (printf, 4, 5)));
258 int auth_ar_print(struct osmtpd_ctx *, const char *);
259 int dkim_key_text_parse(struct dkim_signature *, const char *);
262 EVP_ENCODE_CTX *ectx = NULL;
265 main(int argc, char *argv[])
268 osmtpd_errx(1, "Invalid argument count");
270 OpenSSL_add_all_digests();
272 if (pledge("tmppath stdio dns", NULL) == -1)
273 osmtpd_err(1, "pledge");
275 if ((ectx = EVP_ENCODE_CTX_new()) == NULL)
276 osmtpd_err(1, "EVP_ENCODE_CTX_new");
278 osmtpd_need(OSMTPD_NEED_SRC|OSMTPD_NEED_FCRDNS|OSMTPD_NEED_IDENTITY|OSMTPD_NEED_GREETING);
279 osmtpd_register_conf(auth_conf);
280 osmtpd_register_filter_dataline(auth_dataline);
281 osmtpd_register_report_connect(1, auth_connect);
282 osmtpd_register_filter_helo(spf_identity);
283 osmtpd_register_filter_ehlo(spf_identity);
284 osmtpd_register_filter_mailfrom(spf_mailfrom);
285 osmtpd_local_session(auth_session_new, auth_session_free);
286 osmtpd_local_message(auth_message_new, auth_message_free);
293 auth_conf(const char *key, const char *value)
298 if (authservid == NULL)
299 osmtpd_errx(1, "Didn't receive admd config option");
302 if (strcmp(key, "admd") == 0 && authservid == NULL) {
303 if ((authservid = strdup(value)) == NULL)
304 osmtpd_err(1, "%s: malloc", __func__);
305 end = osmtpd_ltok_skip_value(authservid, 0);
306 if (authservid + strlen(authservid) != end)
307 osmtpd_errx(1, "Invalid authservid");
312 auth_connect(struct osmtpd_ctx *ctx, const char *rdns, enum osmtpd_status fcrdns,
313 struct sockaddr_storage *src, struct sockaddr_storage *dst)
315 struct session *ses = ctx->local_session;
317 if (fcrdns == OSMTPD_STATUS_OK)
318 ses->iprev = IPREV_PASS;
320 ses->iprev = IPREV_FAIL;
322 memcpy(&ses->src, src, sizeof(struct sockaddr_storage));
325 if ((ses->rdns = strdup(rdns)) == NULL)
326 osmtpd_err(1, "%s: malloc", __func__);
331 spf_identity(struct osmtpd_ctx *ctx, const char *identity)
333 char from[HOST_NAME_MAX + 12];
335 struct session *ses = ctx->local_session;
337 if (identity == NULL) {
338 osmtpd_filter_proceed(ctx);
342 if ((ses->identity = strdup(identity)) == NULL)
343 osmtpd_err(1, "%s: strdup", __func__);
345 if (strlen(identity) == 0) {
346 osmtpd_filter_proceed(ctx);
350 snprintf(from, sizeof(from), "postmaster@%s", identity);
352 if ((ses->spf_helo = spf_record_new(ctx, from)) == NULL)
353 osmtpd_filter_proceed(ctx);
357 spf_mailfrom(struct osmtpd_ctx *ctx, const char *from)
359 struct session *ses = ctx->local_session;
361 if (from == NULL || !strlen(from)) {
362 osmtpd_filter_proceed(ctx);
366 if (ses->spf_mailfrom)
367 spf_record_free(ses->spf_mailfrom);
369 if ((ses->spf_mailfrom = spf_record_new(ctx, from)) == NULL)
370 osmtpd_filter_proceed(ctx);
374 auth_dataline(struct osmtpd_ctx *ctx, const char *line)
376 struct message *msg = ctx->local_message;
379 if (fprintf(msg->origf, "%s\n", line) < 0)
380 osmtpd_err(1, "Couldn't write to tempfile");
382 if (line[0] == '.') {
384 if (line[0] == '\0') {
386 for (i = 0; i < msg->nheaders; i++) {
387 if (msg->header[i].sig == NULL)
389 dkim_body_verify(msg->header[i].sig);
391 auth_message_verify(msg);
395 if (msg->parsing_headers) {
396 dkim_header_add(ctx, line);
397 if (line[0] == '\0') {
398 msg->parsing_headers = 0;
399 for (i = 0; i < msg->nheaders; i++) {
400 if (msg->header[i].sig == NULL)
402 if (msg->header[i].sig->query == NULL)
403 dkim_signature_verify(
409 dkim_body_parse(msg, line);
414 spf_record_new(struct osmtpd_ctx *ctx, const char *from)
418 struct spf_record *spf;
420 if ((spf = malloc(sizeof(*spf))) == NULL)
421 osmtpd_err(1, "%s: malloc", __func__);
424 spf->state = SPF_NONE;
425 spf->state_reason = NULL;
430 for (i = 0; i < SPF_DNS_LOOKUP_LIMIT; i++) {
431 spf->queries[i].domain = NULL;
432 spf->queries[i].txt = NULL;
433 spf->queries[i].eva = NULL;
436 from = osmtpd_ltok_skip_cfws(from, 1);
438 if ((at = osmtpd_ltok_skip_local_part(from, 0)) == NULL)
441 if ((spf->sender_local = strndup(from, at - from)) == NULL)
442 osmtpd_err(1, "%s: malloc", __func__);
448 if ((from = osmtpd_ltok_skip_domain(at, 0)) == NULL)
452 if ((spf->sender_domain = strndup(at, from - at)) == NULL)
453 osmtpd_err(1, "%s: malloc", __func__);
456 spf, spf->sender_domain, T_TXT, SPF_PASS, 0, 0);
461 free(spf->sender_local);
469 spf_record_free(struct spf_record *spf)
473 for (i = 0; i < SPF_DNS_LOOKUP_LIMIT; i++) {
474 if (spf->queries[i].domain)
475 free(spf->queries[i].domain);
476 if (spf->queries[i].txt)
477 free(spf->queries[i].txt);
478 if (spf->queries[i].eva)
479 event_asr_abort(spf->queries[i].eva);
482 free(spf->sender_local);
483 free(spf->sender_domain);
489 auth_session_new(struct osmtpd_ctx *ctx)
493 if ((ses = malloc(sizeof(*ses))) == NULL)
494 osmtpd_err(1, "%s: malloc", __func__);
497 ses->iprev = IPREV_NONE;
499 ses->spf_helo = NULL;
500 ses->spf_mailfrom = NULL;
502 ses->identity = NULL;
509 auth_session_free(struct osmtpd_ctx *ctx, void *data)
511 struct session *ses = data;
514 spf_record_free(ses->spf_helo);
515 if (ses->spf_mailfrom)
516 spf_record_free(ses->spf_mailfrom);
526 auth_message_new(struct osmtpd_ctx *ctx)
530 if ((msg = malloc(sizeof(*msg))) == NULL)
531 osmtpd_err(1, "%s: malloc", __func__);
533 if ((msg->origf = tmpfile()) == NULL) {
534 osmtpd_warn(NULL, "Can't open tempfile");
539 msg->parsing_headers = 1;
540 msg->body_whitelines = 0;
551 auth_message_free(struct osmtpd_ctx *ctx, void *data)
553 struct message *msg = data;
557 for (i = 0; i < msg->nheaders; i++) {
558 if (msg->header[i].sig != NULL) {
559 free(msg->header[i].sig->b);
560 EVP_MD_CTX_free(msg->header[i].sig->bhctx);
561 for (j = 0; msg->header[i].sig->h != NULL &&
562 msg->header[i].sig->h[j] != NULL; j++)
563 free(msg->header[i].sig->h[j]);
564 free(msg->header[i].sig->h);
565 EVP_PKEY_free(msg->header[i].sig->p);
566 if (msg->header[i].sig->query)
567 event_asr_abort(msg->header[i].sig->query);
569 free(msg->header[i].buf);
570 free(msg->header[i].sig);
577 dkim_header_add(struct osmtpd_ctx *ctx, const char *line)
579 struct message *msg = ctx->local_message;
580 const char *start, *end, *verify;
581 struct header *headers;
584 if (msg->nheaders > 0 &&
585 msg->header[msg->nheaders - 1].readdone == 0) {
586 if (line[0] != ' ' && line[0] != '\t') {
587 msg->header[msg->nheaders - 1].readdone = 1;
588 start = msg->header[msg->nheaders - 1].buf;
589 end = osmtpd_ltok_skip_field_name(start, 0);
590 /* In case someone uses an obs-optional */
592 verify = osmtpd_ltok_skip_wsp(end, 1);
595 start, "DKIM-Signature", end - start) == 0 &&
597 dkim_signature_parse(
598 &msg->header[msg->nheaders - 1]);
602 dkim_header_cat(ctx, line);
606 if (msg->nheaders % 10 == 0) {
607 if ((headers = recallocarray(msg->header, msg->nheaders,
608 msg->nheaders + 10, sizeof(*msg->header))) == NULL)
609 osmtpd_err(1, "%s: malloc", __func__);
610 msg->header = headers;
611 for (i = 0; i < msg->nheaders; i++) {
612 if (msg->header[i].sig == NULL)
614 msg->header[i].sig->header = &msg->header[i];
617 msg->header[msg->nheaders].msg = msg;
619 dkim_header_cat(ctx, line);
623 dkim_header_cat(struct osmtpd_ctx *ctx, const char *line)
625 struct message *msg = ctx->local_message;
626 struct header *header = &msg->header[msg->nheaders - 1];
629 size_t needed = header->buflen + strlen(line) + 2;
631 if (needed > (header->buflen / 1024) + 1) {
632 buf = reallocarray(header->buf, (needed / 1024) + 1, 1024);
634 osmtpd_err(1, "%s: malloc", __func__);
637 header->buflen += snprintf(header->buf + header->buflen,
638 (((needed / 1024) + 1) * 1024) - header->buflen, "%s%s",
639 header->buflen == 0 ? "" : "\r\n", line);
643 dkim_signature_parse(struct header *header)
645 struct dkim_signature *sig;
646 const char *buf, *i, *end;
648 char subdomain[HOST_NAME_MAX + 1];
651 /* Format checked by dkim_header_add */
652 buf = osmtpd_ltok_skip_field_name(header->buf, 0);
653 buf = osmtpd_ltok_skip_wsp(buf, 1) + 1;
655 if ((header->sig = calloc(1, sizeof(*header->sig))) == NULL)
656 osmtpd_err(1, "%s: malloc", __func__);
658 sig->header = header;
663 end = osmtpd_ltok_skip_tag_list(buf, 0);
664 if (end == NULL || end[0] != '\0') {
665 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid tag-list");
669 while (buf[0] != '\0') {
670 buf = osmtpd_ltok_skip_fws(buf, 1);
671 end = osmtpd_ltok_skip_tag_name(buf, 0);
673 /* Unknown tag-name */
674 if ((size_t)(end - buf) >= sizeof(tagname))
677 strlcpy(tagname, buf, (end - buf) + 1);
678 buf = osmtpd_ltok_skip_fws(end, 1);
680 buf = osmtpd_ltok_skip_fws(buf + 1, 1);
681 end = osmtpd_ltok_skip_tag_value(buf, 1);
682 if (strcmp(tagname, "v") == 0)
683 dkim_signature_parse_v(sig, buf, end);
684 else if (strcmp(tagname, "a") == 0)
685 dkim_signature_parse_a(sig, buf, end);
686 else if (strcmp(tagname, "b") == 0)
687 dkim_signature_parse_b(sig, buf, end);
688 else if (strcmp(tagname, "bh") == 0)
689 dkim_signature_parse_bh(sig, buf, end);
690 else if (strcmp(tagname, "c") == 0)
691 dkim_signature_parse_c(sig, buf, end);
692 else if (strcmp(tagname, "d") == 0)
693 dkim_signature_parse_d(sig, buf, end);
694 else if (strcmp(tagname, "h") == 0)
695 dkim_signature_parse_h(sig, buf, end);
696 else if (strcmp(tagname, "i") == 0)
697 dkim_signature_parse_i(sig, buf, end);
698 else if (strcmp(tagname, "l") == 0)
699 dkim_signature_parse_l(sig, buf, end);
700 else if (strcmp(tagname, "q") == 0)
701 dkim_signature_parse_q(sig, buf, end);
702 else if (strcmp(tagname, "s") == 0)
703 dkim_signature_parse_s(sig, buf, end);
704 else if (strcmp(tagname, "t") == 0)
705 dkim_signature_parse_t(sig, buf, end);
706 else if (strcmp(tagname, "x") == 0)
707 dkim_signature_parse_x(sig, buf, end);
708 else if (strcmp(tagname, "z") == 0)
709 dkim_signature_parse_z(sig, buf, end);
711 buf = osmtpd_ltok_skip_fws(end, 1);
714 else if (buf[0] != '\0') {
715 dkim_signature_state(sig, DKIM_PERMERROR,
720 if (sig->state != DKIM_UNKNOWN)
724 dkim_signature_state(sig, DKIM_PERMERROR, "Missing v tag");
725 else if (sig->ah == NULL)
726 dkim_signature_state(sig, DKIM_PERMERROR, "Missing a tag");
727 else if (sig->b == NULL)
728 dkim_signature_state(sig, DKIM_PERMERROR, "Missing b tag");
729 else if (sig->bhsz == 0)
730 dkim_signature_state(sig, DKIM_PERMERROR, "Missing bh tag");
731 else if (sig->d[0] == '\0')
732 dkim_signature_state(sig, DKIM_PERMERROR, "Missing d tag");
733 else if (sig->h == NULL)
734 dkim_signature_state(sig, DKIM_PERMERROR, "Missing h tag");
735 else if (sig->s[0] == '\0')
736 dkim_signature_state(sig, DKIM_PERMERROR, "Missing s tag");
737 if (sig->state != DKIM_UNKNOWN)
740 if (sig->i != NULL) {
741 i = osmtpd_ltok_skip_local_part(sig->i, 1) + 1;
742 ilen = sig->isz - (size_t)(i - sig->i);
743 dlen = strlen(sig->d);
745 dkim_signature_state(sig, DKIM_PERMERROR,
746 "i tag not subdomain of d");
750 if ((i[-1] != '.' && i[-1] != '@') ||
751 strncasecmp(i, sig->d, dlen) != 0) {
752 dkim_signature_state(sig, DKIM_PERMERROR,
753 "i tag not subdomain of d");
757 if (sig->t != -1 && sig->x != -1 && sig->t > sig->x) {
758 dkim_signature_state(sig, DKIM_PERMERROR, "t tag after x tag");
762 if ((size_t)snprintf(subdomain, sizeof(subdomain), "%s._domainkey.%s",
763 sig->s, sig->d) >= sizeof(subdomain)) {
764 dkim_signature_state(sig, DKIM_PERMERROR,
765 "dns/txt query too long");
769 dkim_lookup_record(sig, subdomain);
773 dkim_lookup_record(struct dkim_signature *sig, const char *domain)
775 struct asr_query *query;
777 if (sig->state != DKIM_UNKNOWN)
782 if (sig->query != NULL) {
783 event_asr_abort(sig->query);
785 sig->header->msg->nqueries--;
787 if ((query = res_query_async(domain, C_IN, T_TXT, NULL)) == NULL)
788 osmtpd_err(1, "res_query_async");
790 if ((sig->query = event_asr_run(query, dkim_rr_resolve, sig)) == NULL)
791 osmtpd_err(1, "res_query_async");
793 sig->header->msg->nqueries++;
797 dkim_signature_parse_v(struct dkim_signature *sig, const char *start, const char *end)
799 if (sig->v != 0) { /* Duplicate tag */
800 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate v tag");
803 /* Unsupported version */
804 if (start[0] != '1' || start + 1 != end)
805 dkim_signature_state(sig, DKIM_NEUTRAL, "Unsupported v tag");
811 dkim_signature_parse_a(struct dkim_signature *sig, const char *start, const char *end)
813 char ah[sizeof("sha256")];
815 if (sig->ah != NULL) {
816 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate a tag");
820 if (osmtpd_ltok_skip_sig_a_tag_alg(start, 0) != end) {
821 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid a tag");
825 sig->asz = (size_t)(end - start);
826 if (strncmp(start, "rsa-", 4) == 0) {
828 sig->ak = EVP_PKEY_RSA;
831 } else if (strncmp(start, "ed25519-", 8) == 0) {
833 sig->ak = EVP_PKEY_ED25519;
837 dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag k");
840 if ((size_t)(end - start) >= sizeof(ah)) {
841 dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag h");
844 strlcpy(ah, start, sizeof(ah));
845 ah[end - start] = '\0';
846 if ((sig->ah = EVP_get_digestbyname(ah)) == NULL) {
847 dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag h");
850 if ((sig->bhctx = EVP_MD_CTX_new()) == NULL)
851 osmtpd_err(1, "EVP_MD_CTX_new");
853 if (EVP_DigestInit_ex(sig->bhctx, sig->ah, NULL) <= 0) {
854 dkim_signature_state(sig, DKIM_FAIL, "Unsuppored a tag ah");
860 dkim_signature_parse_b(struct dkim_signature *sig, const char *start, const char *end)
865 if (sig->b != NULL) {
866 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate b tag");
869 sig->bheader = start;
870 sig->bheadersz = end - start;
871 if ((sig->b = malloc(((sig->bheadersz / 4) + 1) * 3)) == NULL)
872 osmtpd_err(1, "%s: malloc", __func__);
873 /* EVP_DecodeBlock doesn't handle internal whitespace */
874 EVP_DecodeInit(ectx);
875 if (EVP_DecodeUpdate(ectx, sig->b, &decodesz, sig->bheader,
876 (int) sig->bheadersz) == -1) {
877 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid b tag");
881 if (EVP_DecodeFinal(ectx, sig->b + sig->bsz,
883 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid b tag");
886 sig->bsz += decodesz;
888 i < sig->bheadersz && j < HEADER_B_MAX_LEN; i++) {
889 if (isalnum(sig->bheader[i]) || sig->bheader[i] == '/'
890 || sig->bheader[i] == '+' || sig->bheader[i] == '=')
891 sig->bheaderclean[j++] = sig->bheader[i];
893 sig->bheaderclean[j] = '\0';
897 dkim_signature_parse_bh(struct dkim_signature *sig, const char *start, const char *end)
903 if (sig->bhsz != 0) {
904 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate bh tag");
908 * EVP_Decode* expects sig->bh to be large enough,
909 * so count the actual b64 characters.
914 b64 = osmtpd_ltok_skip_fws(b64, 1);
915 if (osmtpd_ltok_skip_alphadigitps(b64, 0) == NULL)
922 b64 = osmtpd_ltok_skip_fws(b64 + 1, 1);
928 /* Invalid tag value */
929 if (b64 != end || n % 4 != 0 || (n / 4) * 3 > sizeof(sig->bh)) {
930 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
933 /* EVP_DecodeBlock doesn't handle internal whitespace */
934 EVP_DecodeInit(ectx);
935 if (EVP_DecodeUpdate(ectx, sig->bh, &decodesz, start,
936 (int)(end - start)) == -1) {
938 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
941 sig->bhsz = decodesz;
942 if (EVP_DecodeFinal(ectx, sig->bh + sig->bhsz, &decodesz) == -1) {
944 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
947 sig->bhsz += decodesz;
951 dkim_signature_parse_c(struct dkim_signature *sig, const char *start, const char *end)
954 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate c tag");
957 if (strncmp(start, "simple", 6) == 0) {
958 sig->c = CANON_HEADER_SIMPLE;
960 } else if (strncmp(start, "relaxed", 7) == 0) {
961 sig->c = CANON_HEADER_RELAXED;
964 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid c tag");
967 if (start[0] == '/') {
969 if (strncmp(start, "simple", 6) == 0) {
970 sig->c |= CANON_BODY_SIMPLE;
972 } else if (strncmp(start, "relaxed", 7) == 0) {
973 sig->c |= CANON_BODY_RELAXED;
976 dkim_signature_state(sig, DKIM_PERMERROR,
983 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid c tag");
986 sig->c |= CANON_DONE;
990 dkim_signature_parse_d(struct dkim_signature *sig, const char *start, const char *end)
992 if (sig->d[0] != '\0') {
993 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate d tag");
996 if (osmtpd_ltok_skip_sig_d_tag_value(start, 0) != end ||
997 (size_t)(end - start) >= sizeof(sig->d)) {
998 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid d tag");
1001 strlcpy(sig->d, start, end - start + 1);
1005 dkim_signature_parse_h(struct dkim_signature *sig, const char *start, const char *end)
1010 if (sig->h != NULL) {
1011 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate h tag");
1014 if (osmtpd_ltok_skip_sig_h_tag_value(start, 0) < end) {
1015 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid h tag");
1020 if ((h = osmtpd_ltok_skip_hdr_name(h, 0)) == NULL) {
1021 dkim_signature_state(sig, DKIM_PERMERROR,
1026 /* ';' is part of hdr-name */
1031 h = osmtpd_ltok_skip_fws(h, 1);
1034 h = osmtpd_ltok_skip_fws(h + 1, 1);
1036 if ((sig->h = calloc(n + 1, sizeof(*sig->h))) == NULL)
1037 osmtpd_err(1, "%s: malloc", __func__);
1041 h = osmtpd_ltok_skip_hdr_name(start, 0);
1042 /* ';' is part of hdr-name */
1044 sig->h[n] = strndup(start, end - start);
1047 if ((sig->h[n++] = strndup(start, h - start)) == NULL)
1048 osmtpd_err(1, "%s: malloc", __func__);
1049 start = osmtpd_ltok_skip_fws(h, 1);
1050 if (start[0] != ':')
1052 start = osmtpd_ltok_skip_fws(start + 1, 1);
1057 dkim_signature_parse_i(struct dkim_signature *sig, const char *start, const char *end)
1059 if (sig->i != NULL) {
1060 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate i tag");
1063 if (osmtpd_ltok_skip_sig_i_tag_value(start, 0) != end) {
1064 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid i tag");
1068 sig->isz = (size_t)(end - start);
1072 dkim_signature_parse_l(struct dkim_signature *sig, const char *start, const char *end)
1077 if (sig->l != -1) { /* Duplicate tag */
1078 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate l tag");
1082 l = strtoll(start, &lend, 10);
1083 /* > 76 digits in stroll is an overflow */
1084 if (osmtpd_ltok_skip_digit(start, 0) == NULL ||
1085 lend != end || errno != 0) {
1086 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid l tag");
1089 if (l > SSIZE_MAX) {
1090 dkim_signature_state(sig, DKIM_PERMERROR, "l tag too large");
1093 sig->l = (ssize_t)l;
1097 dkim_signature_parse_q(struct dkim_signature *sig, const char *start, const char *end)
1102 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate q tag");
1107 start = osmtpd_ltok_skip_fws(start, 1);
1108 qend = osmtpd_ltok_skip_sig_q_tag_method(start, 0);
1110 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid q tag");
1113 if (strncmp(start, "dns/txt", qend - start) == 0)
1115 start = osmtpd_ltok_skip_fws(qend, 1);
1116 if (start[0] != ':')
1120 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid q tag");
1125 dkim_signature_state(sig, DKIM_NEUTRAL, "No useable q found");
1131 dkim_signature_parse_s(struct dkim_signature *sig, const char *start, const char *end)
1133 if (sig->s[0] != '\0') {
1134 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate s tag");
1137 if (osmtpd_ltok_skip_selector(start, 0) != end) {
1138 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid s tag");
1141 strlcpy(sig->s, start, end - start + 1);
1145 dkim_signature_parse_t(struct dkim_signature *sig, const char *start, const char *end)
1150 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate t tag");
1154 sig->t = strtoll(start, &tend, 10);
1155 if (osmtpd_ltok_skip_digit(start, 0) == NULL || tend != end ||
1156 tend - start > 12 || errno != 0) {
1157 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid t tag");
1163 dkim_signature_parse_x(struct dkim_signature *sig, const char *start, const char *end)
1168 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate x tag");
1172 sig->x = strtoll(start, &xend, 10);
1173 if (osmtpd_ltok_skip_digit(start, 0) == NULL || xend != end ||
1174 xend - start > 12 || errno != 0) {
1175 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid x tag");
1181 dkim_signature_parse_z(struct dkim_signature *sig, const char *start, const char *end)
1184 dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate z tag");
1189 if (osmtpd_ltok_skip_sig_z_tag_value(start, 0) != end) {
1190 dkim_signature_state(sig, DKIM_PERMERROR, "Invalid z tag");
1196 dkim_signature_verify(struct dkim_signature *sig)
1198 struct message *msg = sig->header->msg;
1199 static EVP_MD_CTX *bctx = NULL;
1200 char digest[EVP_MAX_MD_SIZE];
1201 unsigned int digestsz;
1205 if (sig->state != DKIM_UNKNOWN)
1209 if ((bctx = EVP_MD_CTX_new()) == NULL)
1210 osmtpd_err(1, "EVP_MD_CTX_new");
1212 EVP_MD_CTX_reset(bctx);
1213 if (!sig->sephash) {
1214 if (EVP_DigestVerifyInit(bctx, NULL, sig->ah, NULL,
1216 dkim_signature_state(sig, DKIM_FAIL, "ah tag");
1220 if (EVP_DigestInit_ex(bctx, sig->ah, NULL) != 1) {
1221 dkim_signature_state(sig, DKIM_FAIL, "ah tag");
1226 for (i = 0; i < msg->nheaders; i++)
1227 msg->header[i].parsed = 0;
1229 for (header = 0; sig->h[header] != NULL; header++) {
1230 for (i = msg->nheaders; i > 0; ) {
1232 if (msg->header[i].parsed ||
1233 strncasecmp(msg->header[i].buf, sig->h[header],
1234 strlen(sig->h[header])) != 0 ||
1235 msg->header[i].sig == sig)
1237 end = osmtpd_ltok_skip_fws(
1238 msg->header[i].buf + strlen(sig->h[header]), 1);
1241 dkim_signature_header(bctx, sig, &(msg->header[i]));
1242 msg->header[i].parsed = 1;
1246 dkim_signature_header(bctx, sig, sig->header);
1247 if (!sig->sephash) {
1248 if (EVP_DigestVerifyFinal(bctx, sig->b, sig->bsz) != 1)
1249 dkim_signature_state(sig, DKIM_FAIL, "b mismatch");
1251 if (EVP_DigestFinal_ex(bctx, digest, &digestsz) == 0)
1252 osmtpd_err(1, "EVP_DigestFinal_ex");
1254 if (EVP_DigestVerifyInit(bctx, NULL, NULL, NULL, sig->p) != 1)
1255 osmtpd_err(1, "EVP_DigestVerifyInit");
1257 switch (EVP_DigestVerify(bctx, sig->b, sig->bsz, digest,
1262 dkim_signature_state(sig, DKIM_FAIL, "b mismatch");
1265 osmtpd_err(1, "EVP_DigestVerify");
1270 /* EVP_DigestVerifyUpdate is a macro, so we can't alias this on a variable */
1271 #define dkim_b_digest_update(a, b, c) \
1272 (sig->sephash ? EVP_DigestUpdate((a), (b), (c)) :\
1273 EVP_DigestVerifyUpdate((a), (b), (c)))
1276 dkim_signature_header(EVP_MD_CTX *bctx, struct dkim_signature *sig,
1277 struct header *header)
1280 const char *ptr = header->buf, *end;
1282 int canon = sig->c & CANON_HEADER;
1284 for (ptr = header->buf; ptr[0] != '\0'; ptr++) {
1286 if (canon == CANON_HEADER_RELAXED) {
1287 ptr = osmtpd_ltok_skip_fws(ptr, 1);
1288 c = tolower(ptr[0]);
1293 if (canon == CANON_HEADER_RELAXED)
1294 ptr = osmtpd_ltok_skip_fws(
1297 if (dkim_b_digest_update(bctx, &c, 1) == 0)
1298 osmtpd_errx(1, "dkim_b_digest_update");
1301 end = osmtpd_ltok_skip_fws(ptr, 1);
1303 if (sig->header == header && ptr == sig->bheader) {
1304 ptr = osmtpd_ltok_skip_tag_value(
1308 if (dkim_b_digest_update(bctx, ptr, 1) == 0)
1309 osmtpd_errx(1, "dkim_b_digest_update");
1311 if (canon == CANON_HEADER_RELAXED) {
1314 if (dkim_b_digest_update(bctx, " ", 1) == 0)
1315 osmtpd_errx(1, "dkim_b_digest_update");
1317 if (dkim_b_digest_update(bctx, ptr,
1319 osmtpd_errx(1, "dkim_b_digest_update");
1325 if (sig->header != header) {
1326 if (dkim_b_digest_update(bctx, "\r\n", 2) == 0)
1327 osmtpd_errx(1, "dkim_b_digest_update");
1332 dkim_signature_state(struct dkim_signature *sig, enum dkim_state state,
1335 if (sig->query != NULL) {
1336 event_asr_abort(sig->query);
1338 sig->header->msg->nqueries--;
1340 switch (sig->state) {
1345 osmtpd_errx(1, "Unexpected transition");
1347 if (state == DKIM_PASS)
1351 if (state == DKIM_PASS)
1353 if (state == DKIM_TEMPERROR || state == DKIM_PERMERROR)
1355 osmtpd_errx(1, "Unexpected transition");
1356 case DKIM_TEMPERROR:
1357 if (state == DKIM_PERMERROR)
1360 case DKIM_PERMERROR:
1364 sig->state_reason = reason;
1368 dkim_state2str(enum dkim_state state)
1382 case DKIM_TEMPERROR:
1384 case DKIM_PERMERROR:
1390 dkim_rr_resolve(struct asr_result *ar, void *arg)
1392 struct dkim_signature *sig = arg;
1393 char key[UINT16_MAX + 1];
1395 size_t keylen, cstrlen;
1397 struct dns_header h;
1400 char buf[HOST_NAME_MAX + 1];
1403 sig->header->msg->nqueries--;
1405 if (sig->state != DKIM_UNKNOWN)
1408 if (ar->ar_h_errno == TRY_AGAIN || ar->ar_h_errno == NO_RECOVERY) {
1409 dkim_signature_state(sig, DKIM_TEMPERROR,
1410 hstrerror(ar->ar_h_errno));
1413 if (ar->ar_h_errno == HOST_NOT_FOUND) {
1414 dkim_signature_state(sig, DKIM_PERMERROR,
1415 hstrerror(ar->ar_h_errno));
1419 unpack_init(&pack, ar->ar_data, ar->ar_datalen);
1420 if (unpack_header(&pack, &h) != 0 ||
1421 unpack_query(&pack, &q) != 0) {
1422 osmtpd_warn(sig->header->msg->ctx,
1423 "Mallformed DKIM DNS response for domain %s: %s",
1424 print_dname(q.q_dname, buf, sizeof(buf)),
1426 dkim_signature_state(sig, DKIM_PERMERROR, pack.err);
1430 for (; h.ancount > 0; h.ancount--) {
1431 if (unpack_rr(&pack, &rr) != 0) {
1432 osmtpd_warn(sig->header->msg->ctx,
1433 "Mallformed DKIM DNS record for domain %s: %s",
1434 print_dname(q.q_dname, buf, sizeof(buf)),
1439 /* If we below limit, follow CNAME*/
1440 if (rr.rr_type == T_CNAME &&
1441 sig->nqueries < DKIM_LOOKUP_LOOKUP_LIMIT ) {
1442 print_dname(rr.rr.cname.cname, buf, sizeof(buf));
1443 dkim_lookup_record(sig, buf);
1448 if (rr.rr_type != T_TXT) {
1449 osmtpd_warn(sig->header->msg->ctx,
1450 "Unexpected DKIM DNS record: %d for domain %s",
1452 print_dname(q.q_dname, buf, sizeof(buf)));
1457 rr_txt = rr.rr.other.rdata;
1458 while (rr.rr.other.rdlen > 0) {
1459 cstrlen = ((const unsigned char *)rr_txt)[0];
1460 if (cstrlen >= rr.rr.other.rdlen ||
1461 keylen + cstrlen >= sizeof(key))
1464 * RFC 6376 Section 3.6.2.2
1465 * Strings in a TXT RR MUST be concatenated together
1466 * before use with no intervening whitespace.
1468 strlcpy(key + keylen, rr_txt + 1, cstrlen + 1);
1469 rr.rr.other.rdlen -= (cstrlen + 1);
1470 rr_txt += (cstrlen + 1);
1473 if (rr.rr.other.rdlen > 0) /* Invalid TXT RDATA */
1476 if (dkim_key_text_parse(sig, key))
1480 if (h.ancount == 0) {
1481 dkim_signature_state(sig, DKIM_PERMERROR,
1482 "No matching key found");
1484 /* Only verify if all headers have been read */
1485 if (!sig->header->msg->parsing_headers)
1486 dkim_signature_verify(sig);
1490 auth_message_verify(sig->header->msg);
1494 dkim_key_text_parse(struct dkim_signature *sig, const char *key)
1496 char tagname, *hashname;
1497 const char *end, *tagvend;
1498 char pkraw[UINT16_MAX] = "", pkimp[UINT16_MAX];
1499 size_t pkrawlen = 0, pkoff, linelen;
1500 int h = 0, k = 0, n = 0, p = 0, s = 0, t = 0, first = 1;
1507 key = osmtpd_ltok_skip_fws(key, 1);
1508 /* Validate syntax early */
1509 if ((end = osmtpd_ltok_skip_tag_list(key, 0)) == NULL)
1512 while (key[0] != '\0') {
1513 key = osmtpd_ltok_skip_fws(key, 1);
1514 if ((end = osmtpd_ltok_skip_tag_name(key, 0)) == NULL)
1517 if ((size_t)(end - key) != 1)
1521 key = osmtpd_ltok_skip_fws(end, 1);
1525 key = osmtpd_ltok_skip_fws(key + 1, 1);
1526 if ((end = osmtpd_ltok_skip_tag_value(key, 0)) == NULL)
1531 * RFC 6376 section 3.6.1, v=:
1532 * RECOMMENDED...This tag MUST be the first tag in the
1536 osmtpd_ltok_skip_key_v_tag_value(key, 0) != end)
1541 if (h != 0) /* Duplicate tag */
1543 /* Invalid tag value */
1544 if (osmtpd_ltok_skip_key_h_tag_value(key, 0) != end)
1547 if ((tagvend = osmtpd_ltok_skip_key_h_tag_alg(
1550 hashname = strndup(key, tagvend - key);
1551 if (hashname == NULL)
1552 osmtpd_err(1, "strndup");
1553 if (EVP_get_digestbyname(hashname) == sig->ah) {
1559 key = osmtpd_ltok_skip_fws(tagvend, 1);
1562 key = osmtpd_ltok_skip_fws(key + 1, 1);
1569 if (k != 0) /* Duplicate tag */
1572 if (strncmp(key, "rsa", end - key) == 0) {
1573 if (sig->ak != EVP_PKEY_RSA)
1576 } else if (strncmp(key, "ed25519", end - key) == 0) {
1577 if (sig->ak != EVP_PKEY_ED25519)
1585 if (n != 0) /* Duplicate tag */
1588 /* semicolon is part of safe-char */
1589 if (osmtpd_ltok_skip_key_n_tag_value(key, 0) < end)
1594 if (p != 0) /* Duplicate tag */
1598 key = osmtpd_ltok_skip_fws(key, 1);
1599 if (osmtpd_ltok_skip_alphadigitps(
1602 pkraw[pkrawlen++] = key++[0];
1603 if (pkrawlen >= sizeof(pkraw))
1606 if (key[0] == '=') {
1607 pkraw[pkrawlen++] = '=';
1608 key = osmtpd_ltok_skip_fws(key + 1, 1);
1609 if (pkrawlen >= sizeof(pkraw))
1611 if (key[0] == '=') {
1612 pkraw[pkrawlen++] = '=';
1614 if (pkrawlen >= sizeof(pkraw))
1618 /* Invalid tag value */
1619 if (pkrawlen % 4 != 0 || key != end)
1623 if (s != 0) /* Duplicate tag */
1625 /* Invalid tag value */
1626 if (osmtpd_ltok_skip_key_s_tag_value(key, 0) != end)
1630 osmtpd_ltok_skip_key_s_tag_type(
1633 if (strncmp(key, "*", tagvend - key) == 0 ||
1634 strncmp(key, "email", tagvend - key) == 0) {
1638 key = osmtpd_ltok_skip_fws(tagvend, 1);
1641 key = osmtpd_ltok_skip_fws(key + 1, 1);
1648 if (t != 0) /* Duplicate tag */
1651 if (osmtpd_ltok_skip_key_t_tag_value(key, 0) != end)
1654 tagvend = osmtpd_ltok_skip_key_t_tag_flag(
1656 if (strncmp(key, "y", tagvend - key) == 0)
1658 else if (strncmp(key, "s", tagvend - key) == 0)
1660 key = osmtpd_ltok_skip_fws(tagvend, 1);
1663 key = osmtpd_ltok_skip_fws(key + 1, 1);
1672 key = osmtpd_ltok_skip_fws(key, 1);
1675 else if (key[0] != '\0')
1679 if (!p) /* Missing tag */
1681 if (k == 0 && sig->ak != EVP_PKEY_RSA) /* Default to RSA */
1684 if (pkraw[0] == '\0') {
1685 dkim_signature_state(sig, DKIM_PERMERROR, "Key is revoked");
1691 pkoff = strlcpy(pkimp, "-----BEGIN PUBLIC KEY-----\n",
1694 for (key = pkraw; key[0] != '\0';) {
1695 if (pkoff + 2 >= sizeof(pkimp))
1697 pkimp[pkoff++] = key++[0];
1698 if (++linelen == 64) {
1699 pkimp[pkoff++] = '\n';
1703 /* Leverage pkoff check in loop */
1705 pkimp[pkoff++] = '\n';
1706 /* PEM_read_bio_PUBKEY will catch truncated keys */
1707 pkoff += strlcpy(pkimp + pkoff, "-----END PUBLIC KEY-----\n",
1708 sizeof(pkimp) - pkoff);
1709 if ((bio = BIO_new_mem_buf(pkimp, pkoff)) == NULL)
1710 osmtpd_err(1, "BIO_new_mem_buf");
1711 sig->p = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
1715 case EVP_PKEY_ED25519:
1716 if ((pkrawlen / 4) * 3 >= sizeof(pkimp))
1718 EVP_DecodeInit(ectx);
1719 if (EVP_DecodeUpdate(ectx, pkimp, &tmp, pkraw, pkrawlen) == -1)
1722 if (EVP_DecodeFinal(ectx, pkimp, &tmp) == -1)
1725 sig->p = EVP_PKEY_new_raw_public_key(sig->ak, NULL, pkimp,
1730 if (sig->p == NULL) {
1732 * XXX No clue how to differentiate between invalid key and
1733 * temporary failure like *alloc.
1734 * Assume invalid key, because it's more likely.
1742 dkim_body_parse(struct message *msg, const char *line)
1744 struct dkim_signature *sig;
1745 const char *end = line, *hash, *prev;
1746 size_t hashn, len, i;
1749 if (line[0] == '\0') {
1750 msg->body_whitelines++;
1754 while (msg->body_whitelines-- > 0) {
1755 for (i = 0; i < msg->nheaders; i++) {
1756 if ((sig = msg->header[i].sig) == NULL ||
1757 sig->state != DKIM_UNKNOWN)
1759 hashn = sig->l == -1 ? 2 : MIN(2, sig->l);
1760 sig->l -= sig->l == -1 ? 0 : hashn;
1761 if (EVP_DigestUpdate(sig->bhctx, "\r\n", hashn) == 0)
1762 osmtpd_errx(1, "EVP_DigestUpdate");
1765 msg->body_whitelines = 0;
1768 while (line[0] != '\0') {
1771 if ((end = osmtpd_ltok_skip_wsp(end, 0)) == NULL)
1777 while (osmtpd_ltok_skip_wsp(end, 0) == NULL &&
1781 for (i = 0; i < msg->nheaders; i++) {
1782 sig = msg->header[i].sig;
1783 if (sig == NULL || sig->state != DKIM_UNKNOWN)
1786 (sig->c & CANON_BODY) == CANON_BODY_RELAXED) {
1788 len = end[0] == '\0' ? 0 : 1;
1791 len = (size_t)(end - line);
1793 hashn = sig->l == -1 ? len : MIN(len, (size_t)sig->l);
1794 sig->l -= sig->l == -1 ? 0 : hashn;
1795 ret = EVP_DigestUpdate(sig->bhctx, hash, hashn);
1797 osmtpd_err(1, "EVP_DigestUpdate");
1801 for (i = 0; i < msg->nheaders; i++) {
1802 sig = msg->header[i].sig;
1803 if (sig == NULL || sig->state != DKIM_UNKNOWN)
1805 hashn = sig->l == -1 ? 2 : MIN(2, sig->l);
1806 sig->l -= sig->l == -1 ? 0 : hashn;
1807 ret = EVP_DigestUpdate(sig->bhctx, "\r\n", hashn);
1809 osmtpd_err(1, "EVP_DigestUpdate");
1814 dkim_body_verify(struct dkim_signature *sig)
1816 unsigned char digest[EVP_MAX_MD_SIZE];
1817 unsigned int digestsz;
1819 if (sig->state != DKIM_UNKNOWN)
1822 if ((sig->c & CANON_BODY) == CANON_BODY_SIMPLE &&
1823 !sig->header->msg->has_body) {
1824 if (EVP_DigestUpdate(sig->bhctx, "\r\n",
1825 sig->l == -1 ? 2 : MIN(2, sig->l)) <= 0)
1826 osmtpd_errx(1, "EVP_DigestUpdate");
1829 dkim_signature_state(sig, DKIM_PERMERROR,
1830 "l tag larger than body");
1834 if (EVP_DigestFinal_ex(sig->bhctx, digest, &digestsz) == 0)
1835 osmtpd_err(1, "EVP_DigestFinal_ex");
1837 if (digestsz != sig->bhsz || memcmp(digest, sig->bh, digestsz) != 0)
1838 dkim_signature_state(sig, DKIM_FAIL, "bh mismatch");
1842 iprev_state2str(enum iprev_state state)
1856 spf_evaluate_domain(struct spf_record *spf, const char *domain)
1858 struct session *ses = spf->ctx->local_session;
1860 char spec[HOST_NAME_MAX + 1];
1861 char macro[HOST_NAME_MAX + 1], smacro[sizeof(macro)];
1862 char delimiters[sizeof(".-+,/_=")];
1869 if (domain == NULL || domain[0] == '\0') {
1870 spf_done(spf, SPF_PERMERROR, "Empty domain");
1875 domain[0] != ' ' && domain[0] != '\0' && i < sizeof(spec);
1878 if (domain[0] < 0x21 || domain[0] > 0x7e) {
1880 spf, SPF_PERMERROR, "Invalid character in domain-spec");
1884 if (domain[0] != '%') {
1885 spec[i++] = domain[0];
1890 switch (domain[0]) {
1898 if (i + 3 >= sizeof(spec)) {
1900 spf, SPF_PERMERROR, "domain-spec too large");
1912 delimiters[0] = '\0';
1914 switch (domain[0]) {
1917 mlen = (size_t) snprintf(macro, sizeof(macro),
1918 "%s@%s", spf->sender_local,
1919 spf->sender_domain);
1923 mlen = strlcpy(macro,
1924 spf->sender_local, sizeof(macro));
1928 mlen = strlcpy(macro,
1934 if (spf->nqueries < 1) {
1935 spf_done(spf, SPF_PERMERROR,
1936 "no domain for d macro");
1939 mlen = strlcpy(macro,
1940 spf->queries[spf->nqueries - 1].domain,
1945 if (ses->src.ss_family == AF_INET) {
1946 addr = (u_char *)(&((struct sockaddr_in *)
1947 &(ses->src))->sin_addr);
1948 mlen = snprintf(macro, sizeof(macro),
1950 (addr[0] & 0xff), (addr[1] & 0xff),
1951 (addr[2] & 0xff), (addr[3] & 0xff));
1952 } else if (ses->src.ss_family == AF_INET6) {
1953 addr = (u_char *)(&((struct sockaddr_in6 *)
1954 &(ses->src))->sin6_addr);
1955 mlen = snprintf(macro, sizeof(macro),
1956 "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
1957 "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
1958 "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
1959 "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx",
1960 (u_char) ((addr[0] >> 4) & 0x0f), (u_char) (addr[0] & 0x0f),
1961 (u_char) ((addr[1] >> 4) & 0x0f), (u_char) (addr[1] & 0x0f),
1962 (u_char) ((addr[2] >> 4) & 0x0f), (u_char) (addr[2] & 0x0f),
1963 (u_char) ((addr[3] >> 4) & 0x0f), (u_char) (addr[3] & 0x0f),
1964 (u_char) ((addr[4] >> 4) & 0x0f), (u_char) (addr[4] & 0x0f),
1965 (u_char) ((addr[5] >> 4) & 0x0f), (u_char) (addr[5] & 0x0f),
1966 (u_char) ((addr[6] >> 4) & 0x0f), (u_char) (addr[6] & 0x0f),
1967 (u_char) ((addr[7] >> 4) & 0x0f), (u_char) (addr[7] & 0x0f),
1968 (u_char) ((addr[8] >> 4) & 0x0f), (u_char) (addr[8] & 0x0f),
1969 (u_char) ((addr[9] >> 4) & 0x0f), (u_char) (addr[9] & 0x0f),
1970 (u_char) ((addr[10] >> 4) & 0x0f), (u_char) (addr[10] & 0x0f),
1971 (u_char) ((addr[11] >> 4) & 0x0f), (u_char) (addr[11] & 0x0f),
1972 (u_char) ((addr[12] >> 4) & 0x0f), (u_char) (addr[12] & 0x0f),
1973 (u_char) ((addr[13] >> 4) & 0x0f), (u_char) (addr[13] & 0x0f),
1974 (u_char) ((addr[14] >> 4) & 0x0f), (u_char) (addr[14] & 0x0f),
1975 (u_char) ((addr[15] >> 4) & 0x0f), (u_char) (addr[15] & 0x0f));
1977 spf_done(spf, SPF_PERMERROR,
1978 "unsupported type of address");
1984 mlen = strlcpy(macro, ses->rdns, sizeof(macro));
1988 if (ses->src.ss_family == AF_INET)
1989 mlen = strlcpy(macro, "in-addr",
1991 else if (ses->src.ss_family == AF_INET6)
1992 mlen = strlcpy(macro, "ip6",
1995 spf_done(spf, SPF_PERMERROR,
1996 "unsupported type of address");
2002 mlen = strlcpy(macro, ses->identity,
2006 spf_done(spf, SPF_PERMERROR,
2007 "Unexpected macro in domain-spec");
2011 if (mlen >= sizeof(macro)) {
2012 spf_done(spf, SPF_PERMERROR,
2013 "Macro expansions too large");
2018 if (isdigit(domain[0])) {
2019 digits = strtol(domain, &endptr, 10);
2021 spf_done(spf, SPF_PERMERROR,
2022 "digits in macro can't be 0");
2028 if (domain[0] == 'r') {
2033 for (; strchr(".-+,/_=", domain[0]) != NULL; domain++) {
2034 if (strchr(delimiters, domain[0]) == NULL) {
2035 delimiters[strlen(delimiters) + 1] = '\0';
2036 delimiters[strlen(delimiters)] = domain[0];
2040 if (delimiters[0] == '\0') {
2041 delimiters[0] = '.';
2042 delimiters[1] = '\0';
2045 if (domain[0] != '}') {
2046 spf_done(spf, SPF_PERMERROR,
2047 "Mallformed macro, expected end");
2053 tmp = macro + strlen(macro) - 1;
2055 * DIGIT rightmost elements after reversal is DIGIT
2056 * lefmost elements before reversal
2059 while (tmp > macro &&
2060 strchr(delimiters, tmp[0]) == NULL)
2070 if (smacro[0] != '\0')
2071 strlcat(smacro, ".", sizeof(smacro));
2072 strlcat(smacro, tmp + 1, sizeof(smacro));
2076 if (smacro[0] != '\0')
2077 strlcat(smacro, ".", sizeof(smacro));
2078 strlcat(smacro, macro, sizeof(smacro));
2083 endptr = macro + strlen(macro);
2084 while (digits > 0) {
2085 while (tmp < endptr &&
2086 strchr(delimiters, tmp[0]) == NULL)
2098 strlcpy(smacro, macro, sizeof(smacro));
2102 i = strlcat(spec, smacro, sizeof(spec));
2103 if (i >= sizeof(spec)) {
2105 spf, SPF_PERMERROR, "domain-spec too large");
2111 spf_done(spf, SPF_PERMERROR,
2112 "Mallformed macro, unexpected character after %");
2117 if ((tmp = strndup(spec, i)) == NULL)
2118 osmtpd_err(1, "%s: strndup", __func__);
2124 spf_lookup_record(struct spf_record *spf, const char *domain, int type,
2125 enum spf_state qualifier, int include, int exists)
2127 struct asr_query *aq;
2128 struct spf_query *query;
2133 if (spf->nqueries >= SPF_DNS_LOOKUP_LIMIT) {
2134 spf_done(spf, SPF_PERMERROR, "To many DNS queries");
2138 query = &spf->queries[spf->nqueries];
2141 query->q = qualifier;
2142 query->include = include;
2143 query->exists = exists;
2147 if ((query->domain = spf_evaluate_domain(spf, domain)) == NULL)
2150 if (domain == NULL || !strlen(domain)) {
2151 spf_done(spf, SPF_PERMERROR, "Empty domain");
2155 if ((aq = res_query_async(query->domain, C_IN, type, NULL)) == NULL)
2156 osmtpd_err(1, "res_query_async");
2158 if ((query->eva = event_asr_run(aq, spf_resolve, query)) == NULL)
2159 osmtpd_err(1, "event_asr_run");
2166 spf_resolve(struct asr_result *ar, void *arg)
2170 struct spf_query *query = arg;
2171 struct spf_record *spf = query->spf;
2173 struct dns_header h;
2176 char buf[HOST_NAME_MAX + 1];
2179 query->spf->running--;
2181 if (ar->ar_h_errno == TRY_AGAIN
2182 || ar->ar_h_errno == NO_RECOVERY) {
2183 spf_done(query->spf, SPF_TEMPERROR, hstrerror(ar->ar_h_errno));
2187 if (ar->ar_h_errno == HOST_NOT_FOUND) {
2188 if (query->include && !query->exists)
2189 spf_done(query->spf,
2190 SPF_PERMERROR, hstrerror(ar->ar_h_errno));
2194 unpack_init(&pack, ar->ar_data, ar->ar_datalen);
2195 if (unpack_header(&pack, &h) != 0 ||
2196 unpack_query(&pack, &q) != 0) {
2197 osmtpd_warn(query->spf->ctx,
2198 "Mallformed SPF DNS response for domain %s: %s",
2199 print_dname(q.q_dname, buf, sizeof(buf)),
2201 spf_done(query->spf, SPF_TEMPERROR, pack.err);
2205 for (; h.ancount; h.ancount--) {
2206 if (unpack_rr(&pack, &rr) != 0) {
2207 osmtpd_warn(query->spf->ctx,
2208 "Mallformed SPF DNS record for domain %s: %s",
2209 print_dname(q.q_dname, buf, sizeof(buf)),
2217 spf_resolve_txt(&rr, query);
2221 spf_resolve_mx(&rr, query);
2225 spf_resolve_a(&rr, query);
2229 spf_resolve_aaaa(&rr, query);
2233 spf_resolve_cname(&rr, query);
2237 osmtpd_warn(spf->ctx,
2238 "Unexpected SPF DNS record: %d for domain %s",
2239 rr.rr_type, query->domain);
2240 spf_done(query->spf, SPF_TEMPERROR, "Unexpected record");
2249 if (spf->running > 0)
2252 for (i = spf->nqueries - 1; i >= 0; i--) {
2253 if (spf->queries[i].txt != NULL) {
2254 if (spf_execute_txt(&spf->queries[i]) != 0)
2261 if (!spf->done && spf->running == 0)
2262 spf_done(spf, SPF_NONE, NULL);
2266 spf_resolve_txt(struct dns_rr *rr, struct spf_query *query)
2269 txt = spf_parse_txt(rr->rr.other.rdata, rr->rr.other.rdlen);
2271 osmtpd_warn(NULL, "spf_parse_txt");
2275 if (strncasecmp("v=spf1 ", txt, 7)) {
2280 if (query->txt != NULL) {
2282 spf_done(query->spf, SPF_PERMERROR, "Duplicated SPF record");
2288 spf_execute_txt(query);
2292 spf_resolve_mx(struct dns_rr *rr, struct spf_query *query)
2294 char buf[HOST_NAME_MAX + 1];
2296 char *domain = print_dname(rr->rr.mx.exchange, buf, sizeof(buf));
2298 spf_lookup_record(query->spf, domain, T_A,
2299 query->q, query->include, 0);
2300 spf_lookup_record(query->spf, domain, T_AAAA,
2301 query->q, query->include, 0);
2305 spf_resolve_a(struct dns_rr *rr, struct spf_query *query)
2307 if (query->exists ||
2308 spf_check_cidr(query->spf, &rr->rr.in_a.addr, 32) == 0) {
2309 spf_done(query->spf, query->q, NULL);
2314 spf_resolve_aaaa(struct dns_rr *rr, struct spf_query *query)
2316 if (spf_check_cidr6(query->spf, &rr->rr.in_aaaa.addr6, 128) == 0) {
2317 spf_done(query->spf, query->q, NULL);
2322 spf_resolve_cname(struct dns_rr *rr, struct spf_query *query)
2324 char buf[HOST_NAME_MAX + 1];
2326 char *domain = print_dname(rr->rr.cname.cname, buf, sizeof(buf));
2328 spf_lookup_record(query->spf, domain, query->type,
2329 query->q, query->include, 0);
2333 spf_parse_txt(const char *rdata, size_t rdatalen)
2335 size_t len, dstsz = SPF_RECORD_MAX - 1;
2339 if (rdatalen >= dstsz) {
2344 odst = dst = malloc(dstsz);
2346 osmtpd_err(1, "%s: malloc", __func__);
2349 len = *(const unsigned char *)rdata;
2350 if (len >= rdatalen) {
2365 memmove(dst, rdata, len);
2380 spf_check_cidr(struct spf_record *spf, struct in_addr *net, int bits)
2382 struct in_addr *addr;
2383 struct session *ses = spf->ctx->local_session;
2385 if (ses->src.ss_family != AF_INET)
2391 addr = &(((struct sockaddr_in *)(&ses->src))->sin_addr);
2393 return ((addr->s_addr ^ net->s_addr) & htonl(0xFFFFFFFFu << (32 - bits)));
2397 spf_check_cidr6(struct spf_record *spf, struct in6_addr *net, int bits)
2400 uint32_t *a, *n, whole, incomplete;
2401 struct in6_addr *addr;
2402 struct session *ses = spf->ctx->local_session;
2404 if (ses->src.ss_family != AF_INET6)
2410 addr = &(((struct sockaddr_in6 *)(&ses->src))->sin6_addr);
2412 a = addr->__u6_addr.__u6_addr32;
2413 n = net->__u6_addr.__u6_addr32;
2416 incomplete = bits & 0x1f;
2418 rc = memcmp(a, n, whole << 2);
2423 return (a[whole] ^ n[whole]) & htonl((0xffffffffu) << (32 - incomplete));
2429 spf_execute_txt(struct spf_query *query)
2432 struct in6_addr in6a;
2434 char *in = query->txt + query->pos;
2438 enum spf_state q = query->q;
2440 while ((ap = strsep(&in, " ")) != NULL) {
2441 if (strcasecmp(ap, "v=spf1") == 0)
2444 end = ap + strlen(ap)-1;
2451 } else if (*ap == '-') {
2454 } else if (*ap == '~') {
2457 } else if (*ap == '?') {
2462 if (q != SPF_PASS && query->include)
2465 if (strncasecmp("all", ap, 3) == 0) {
2466 spf_done(query->spf, q, NULL);
2469 if (strncasecmp("ip4:", ap, 4) == 0) {
2470 if ((bits = inet_net_pton(AF_INET, ap + 4, &ina, sizeof(ina))) == -1)
2473 if (spf_check_cidr(query->spf, &ina, bits) == 0) {
2474 spf_done(query->spf, q, NULL);
2479 if (strncasecmp("ip6:", ap, 4) == 0) {
2480 if ((bits = inet_net_pton(AF_INET6, ap + 4, &ina, sizeof(ina))) == -1)
2483 if (spf_check_cidr6(query->spf, &in6a, bits) == 0) {
2484 spf_done(query->spf, q, NULL);
2489 if (strcasecmp("a", ap) == 0) {
2490 spf_lookup_record(query->spf, query->domain, T_A,
2491 q, query->include, 0);
2492 spf_lookup_record(query->spf, query->domain, T_AAAA,
2493 q, query->include, 0);
2496 if (strncasecmp("a:", ap, 2) == 0) {
2497 spf_lookup_record(query->spf, ap + 2, T_A,
2498 q, query->include, 0);
2499 spf_lookup_record(query->spf, ap + 2, T_AAAA,
2500 q, query->include, 0);
2503 if (strncasecmp("exists:", ap, 7) == 0) {
2504 spf_lookup_record(query->spf, ap + 7, T_A,
2505 q, query->include, 1);
2508 if (strncasecmp("include:", ap, 8) == 0) {
2509 spf_lookup_record(query->spf, ap + 8, T_TXT, q, 1, 0);
2512 if (strncasecmp("redirect=", ap, 9) == 0) {
2515 spf_lookup_record(query->spf, ap + 9, T_TXT,
2516 q, query->include, 0);
2519 if (strcasecmp("mx", ap) == 0) {
2520 spf_lookup_record(query->spf, query->domain, T_MX,
2521 q, query->include, 0);
2524 if (strncasecmp("mx:", ap, 3) == 0) {
2525 spf_lookup_record(query->spf, ap + 3, T_MX,
2526 q, query->include, 0);
2534 query->pos = in - query->txt;
2540 spf_done(struct spf_record *spf, enum spf_state state, const char *reason)
2547 for (i = 0; i < spf->nqueries; i++) {
2548 if (spf->queries[i].eva) {
2549 event_asr_abort(spf->queries[i].eva);
2550 spf->queries[i].eva = NULL;
2557 spf->state_reason = reason;
2560 osmtpd_filter_proceed(spf->ctx);
2564 spf_state2str(enum spf_state state)
2586 spf_ar_cat(const char *type, struct spf_record *spf, char **line, size_t *linelen, ssize_t *aroff)
2590 auth_ar_cat(line, linelen, *aroff,
2591 "; spf=none %s=none", type)
2599 auth_ar_cat(line, linelen, *aroff,
2600 "; spf=%s", spf_state2str(spf->state))
2606 auth_ar_cat(line, linelen, *aroff,
2615 if (spf->state_reason != NULL) {
2617 auth_ar_cat(line, linelen, *aroff,
2618 " reason=\"%s\"", spf->state_reason)
2628 auth_message_verify(struct message *msg)
2632 if (!msg->readdone || msg->nqueries > 0)
2635 for (i = 0; i < msg->nheaders; i++) {
2636 if (msg->header[i].sig == NULL)
2638 if (msg->header[i].sig->query != NULL)
2640 if (msg->header[i].sig->state != DKIM_UNKNOWN)
2642 dkim_signature_state(msg->header[i].sig, DKIM_PASS, NULL);
2645 auth_ar_create(msg->ctx);
2649 auth_ar_create(struct osmtpd_ctx *ctx)
2651 struct dkim_signature *sig;
2653 ssize_t n, aroff = 0;
2657 struct session *ses = ctx->local_session;
2658 struct message *msg = ctx->local_message;
2660 if ((aroff = auth_ar_cat(&line, &linelen, aroff,
2661 "Authentication-Results: %s", authservid)) == -1)
2662 osmtpd_err(1, "%s: malloc", __func__);
2663 for (i = 0; i < msg->nheaders; i++) {
2664 sig = msg->header[i].sig;
2668 if ((aroff = auth_ar_cat(&line, &linelen, aroff, "; dkim=%s",
2669 dkim_state2str(sig->state))) == -1)
2670 osmtpd_err(1, "%s: malloc", __func__);
2671 if (sig->state_reason != NULL) {
2672 if ((aroff = auth_ar_cat(&line, &linelen, aroff,
2673 " reason=\"%s\"", sig->state_reason)) == -1)
2674 osmtpd_err(1, "%s: malloc", __func__);
2676 if (sig->s[0] != '\0') {
2677 if ((aroff = auth_ar_cat(&line, &linelen, aroff,
2678 " header.s=%s", sig->s)) == -1)
2679 osmtpd_err(1, "%s: malloc", __func__);
2681 if (sig->d[0] != '\0') {
2682 if ((aroff = auth_ar_cat(&line, &linelen, aroff,
2683 " header.d=%s", sig->d)) == -1)
2684 osmtpd_err(1, "%s: malloc", __func__);
2687 * Don't print i-tag, since localpart can be a quoted-string,
2688 * which can contain FWS and CFWS.
2690 if (sig->a != NULL) {
2691 if ((aroff = auth_ar_cat(&line, &linelen, aroff,
2692 " header.a=%.*s", (int)sig->asz, sig->a)) == -1)
2693 osmtpd_err(1, "%s: malloc", __func__);
2695 if (sig->bheaderclean[0] != '\0') {
2696 if ((aroff = auth_ar_cat(&line, &linelen, aroff,
2697 " header.b=%s", sig->bheaderclean)) == -1)
2698 osmtpd_err(1, "%s: malloc", __func__);
2702 aroff = auth_ar_cat(&line, &linelen, aroff, "; dkim=none");
2704 osmtpd_err(1, "%s: malloc", __func__);
2707 if ((aroff = auth_ar_cat(&line, &linelen, aroff,
2708 "; iprev=%s", iprev_state2str(ses->iprev))) == -1)
2709 osmtpd_err(1, "%s: malloc", __func__);
2711 if (spf_ar_cat("smtp.helo", ses->spf_helo,
2712 &line, &linelen, &aroff) != 0)
2713 osmtpd_err(1, "%s: malloc", __func__);
2715 if (ses->spf_mailfrom != NULL) {
2716 if (spf_ar_cat("smtp.mailfrom", ses->spf_mailfrom,
2717 &line, &linelen, &aroff) != 0)
2718 osmtpd_err(1, "%s: malloc", __func__);
2720 if (spf_ar_cat("smtp.mailfrom", ses->spf_helo,
2721 &line, &linelen, &aroff) != 0)
2722 osmtpd_err(1, "%s: malloc", __func__);
2726 osmtpd_err(1, "%s: malloc", __func__);
2728 if (auth_ar_print(msg->ctx, line) != 0)
2729 osmtpd_warn(msg->ctx, "Invalid AR line: %s", line);
2732 while ((n = getline(&line, &linelen, msg->origf)) != -1) {
2734 osmtpd_filter_dataline(msg->ctx, "%s", line);
2736 if (ferror(msg->origf))
2737 osmtpd_err(1, "%s: ferror", __func__);
2743 auth_ar_print(struct osmtpd_ctx *ctx, const char *start)
2745 const char *scan, *checkpoint, *ncheckpoint;
2746 int arlen = 0, first = 1, arid = 1;
2749 ncheckpoint = osmtpd_ltok_skip_hdr_name(start, 0) + 1;
2750 for (scan = start; scan[0] != '\0'; scan++) {
2751 if (scan[0] == '\t')
2752 arlen = (arlen + 8) & ~7;
2755 if (arlen >= AUTHENTICATION_RESULTS_LINELEN) {
2756 arlen = (int)(checkpoint - start);
2758 arlen = (int)(ncheckpoint - start);
2759 checkpoint = ncheckpoint;
2761 osmtpd_filter_dataline(ctx, "%s%.*s", first ? "" : "\t",
2763 start = osmtpd_ltok_skip_cfws(checkpoint, 1);
2766 ncheckpoint = start;
2771 if (scan == ncheckpoint) {
2772 checkpoint = ncheckpoint;
2773 ncheckpoint = osmtpd_ltok_skip_cfws(ncheckpoint, 1);
2776 ncheckpoint = osmtpd_ltok_skip_value(
2780 } else if (strncmp(ncheckpoint, "dkim=",
2781 sizeof("dkim=") - 1) == 0) {
2782 ncheckpoint = osmtpd_ltok_skip_keyword(
2783 ncheckpoint + sizeof("dkim=") - 1, 0);
2784 } else if (strncmp(ncheckpoint, "iprev=",
2785 sizeof("iprev=") - 1) == 0) {
2786 ncheckpoint = osmtpd_ltok_skip_keyword(
2787 ncheckpoint + sizeof("iprev=") - 1, 0);
2788 } else if (strncmp(ncheckpoint, "spf=",
2789 sizeof("spf=") - 1) == 0) {
2790 ncheckpoint = osmtpd_ltok_skip_keyword(
2791 ncheckpoint + sizeof("spf=") - 1, 0);
2793 } else if (strncmp(ncheckpoint, "reason=",
2794 sizeof("reason=") - 1) == 0) {
2795 ncheckpoint = osmtpd_ltok_skip_ar_reasonspec(
2799 ncheckpoint = osmtpd_ltok_skip_ar_propspec(
2803 if (ncheckpoint == NULL)
2806 if (*ncheckpoint == ';')
2810 osmtpd_filter_dataline(ctx, "%s%s", first ? "" : "\t", start);
2815 auth_ar_cat(char **ar, size_t *n, size_t aroff, const char *fmt, ...)
2823 size = vsnprintf(*ar + aroff, *n - aroff, fmt, ap);
2825 if (size + aroff < *n)
2826 return (ssize_t)size + aroff;
2827 nn = (((aroff + size) / 256) + 1) * 256;
2828 artmp = realloc(*ar, nn);
2834 size = vsnprintf(*ar + aroff, *n - aroff, fmt, ap);
2836 return (ssize_t)size + aroff;
2842 fprintf(stderr, "usage: filter-auth\n");