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