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 f1509490 2022-03-20 martijn #include "unpack_dns.h"
45 4e2dd90f 2022-03-26 martijn #include "ltok.h"
48 f1509490 2022-03-20 martijn * Use RFC8601 (Authentication-Results) codes instead of RFC6376 codes,
49 f1509490 2022-03-20 martijn * since they're more expressive.
51 0b081549 2025-06-02 kirill enum dkim_state {
52 f1509490 2022-03-20 martijn DKIM_UNKNOWN,
53 f1509490 2022-03-20 martijn DKIM_PASS,
54 f1509490 2022-03-20 martijn DKIM_FAIL,
55 f1509490 2022-03-20 martijn DKIM_POLICY,
56 f1509490 2022-03-20 martijn DKIM_NEUTRAL,
57 f1509490 2022-03-20 martijn DKIM_TEMPERROR,
58 f1509490 2022-03-20 martijn DKIM_PERMERROR
61 0b081549 2025-06-02 kirill struct dkim_signature {
62 f1509490 2022-03-20 martijn struct header *header;
63 0b081549 2025-06-02 kirill enum dkim_state state;
64 f1509490 2022-03-20 martijn const char *state_reason;
66 4e2dd90f 2022-03-26 martijn const char *a;
67 f1509490 2022-03-20 martijn size_t asz;
69 338537df 2022-04-08 martijn int sephash;
70 f1509490 2022-03-20 martijn const EVP_MD *ah;
72 f1509490 2022-03-20 martijn size_t bsz;
73 4e2dd90f 2022-03-26 martijn const char *bheader;
74 ce4bcd16 2025-06-02 kirill size_t bheadersz;
75 ce4bcd16 2025-06-02 kirill #define HEADER_B_MAX_LEN 8
76 ce4bcd16 2025-06-02 kirill char bheaderclean[HEADER_B_MAX_LEN + 1];
77 f1509490 2022-03-20 martijn /* Make sure padding bits for base64 decoding fit */
78 f1509490 2022-03-20 martijn char bh[EVP_MAX_MD_SIZE + (3 - (EVP_MAX_MD_SIZE % 3))];
79 f1509490 2022-03-20 martijn size_t bhsz;
80 f1509490 2022-03-20 martijn EVP_MD_CTX *bhctx;
82 f1509490 2022-03-20 martijn #define CANON_HEADER_SIMPLE 0
83 f1509490 2022-03-20 martijn #define CANON_HEADER_RELAXED 1
84 f1509490 2022-03-20 martijn #define CANON_HEADER 1
85 f1509490 2022-03-20 martijn #define CANON_BODY_SIMPLE 0
86 f1509490 2022-03-20 martijn #define CANON_BODY_RELAXED 1 << 1
87 f1509490 2022-03-20 martijn #define CANON_BODY 1 << 1
88 f1509490 2022-03-20 martijn #define CANON_DONE 1 << 2
89 f1509490 2022-03-20 martijn char d[HOST_NAME_MAX + 1];
90 f1509490 2022-03-20 martijn char **h;
91 4e2dd90f 2022-03-26 martijn const char *i;
92 f1509490 2022-03-20 martijn size_t isz;
93 f1509490 2022-03-20 martijn ssize_t l;
95 f1509490 2022-03-20 martijn char s[HOST_NAME_MAX + 1];
96 f1509490 2022-03-20 martijn time_t t; /* Signature t=/timestamp */
97 f1509490 2022-03-20 martijn #define KT_Y 1
98 f1509490 2022-03-20 martijn #define KT_S 1 << 1
99 f1509490 2022-03-20 martijn int kt; /* Key t=/Flags */
100 f1509490 2022-03-20 martijn time_t x;
102 f1509490 2022-03-20 martijn struct event_asr *query;
103 f1509490 2022-03-20 martijn EVP_PKEY *p;
104 3cb7796f 2025-06-02 kirill /* RFC 6376 doesn't care about CNAME, use simalr with SPF limit */
105 3cb7796f 2025-06-02 kirill #define DKIM_LOOKUP_LOOKUP_LIMIT 11
106 3cb7796f 2025-06-02 kirill int nqueries;
110 a9b0d8da 2025-06-02 kirill * Use RFC7601 (Authentication-Results), anyway OpenSMTPD reports only pass or fail
112 a9b0d8da 2025-06-02 kirill enum iprev_state {
113 a9b0d8da 2025-06-02 kirill IPREV_NONE,
114 a9b0d8da 2025-06-02 kirill IPREV_PASS,
115 a9b0d8da 2025-06-02 kirill IPREV_FAIL
119 40991a5f 2025-06-02 kirill * Base on RFC7208
121 40991a5f 2025-06-02 kirill enum spf_state {
122 40991a5f 2025-06-02 kirill SPF_NONE,
123 40991a5f 2025-06-02 kirill SPF_NEUTRAL,
124 40991a5f 2025-06-02 kirill SPF_PASS,
125 40991a5f 2025-06-02 kirill SPF_FAIL,
126 40991a5f 2025-06-02 kirill SPF_SOFTFAIL,
127 40991a5f 2025-06-02 kirill SPF_TEMPERROR,
128 40991a5f 2025-06-02 kirill SPF_PERMERROR
132 40991a5f 2025-06-02 kirill * RFC 5321 doesn't limit record size, enforce some resanable limit
134 40991a5f 2025-06-02 kirill #define SPF_RECORD_MAX 4096
136 40991a5f 2025-06-02 kirill struct spf_query {
137 40991a5f 2025-06-02 kirill struct spf_record *spf;
138 40991a5f 2025-06-02 kirill struct event_asr *eva;
139 40991a5f 2025-06-02 kirill int type;
140 40991a5f 2025-06-02 kirill enum spf_state q;
141 40991a5f 2025-06-02 kirill int include;
142 40991a5f 2025-06-02 kirill int exists;
143 40991a5f 2025-06-02 kirill char *domain;
144 40991a5f 2025-06-02 kirill char *txt;
148 40991a5f 2025-06-02 kirill struct spf_record {
149 40991a5f 2025-06-02 kirill struct osmtpd_ctx *ctx;
150 40991a5f 2025-06-02 kirill enum spf_state state;
151 40991a5f 2025-06-02 kirill const char *state_reason;
152 40991a5f 2025-06-02 kirill char *sender_local;
153 40991a5f 2025-06-02 kirill char *sender_domain;
154 40991a5f 2025-06-02 kirill int nqueries;
155 40991a5f 2025-06-02 kirill int running;
156 40991a5f 2025-06-02 kirill int done;
157 40991a5f 2025-06-02 kirill /* RFC 7208 Section 4.6.4 limits to 10 DNS lookup,
158 40991a5f 2025-06-02 kirill * and one is reserved for the first query.
159 40991a5f 2025-06-02 kirill * To prevent of infinity loop I count each CNAME
160 40991a5f 2025-06-02 kirill * as dedicated lookup, same as A and AAAA.
161 40991a5f 2025-06-02 kirill * So, I use 41 as the limit. */
162 40991a5f 2025-06-02 kirill #define SPF_DNS_LOOKUP_LIMIT 41
163 40991a5f 2025-06-02 kirill struct spf_query queries[SPF_DNS_LOOKUP_LIMIT];
166 f1509490 2022-03-20 martijn struct header {
167 f1509490 2022-03-20 martijn struct message *msg;
168 f1509490 2022-03-20 martijn uint8_t readdone;
169 f1509490 2022-03-20 martijn uint8_t parsed;
170 f1509490 2022-03-20 martijn char *buf;
171 f1509490 2022-03-20 martijn size_t buflen;
172 0b081549 2025-06-02 kirill struct dkim_signature *sig;
175 f1509490 2022-03-20 martijn #define AUTHENTICATION_RESULTS_LINELEN 78
176 f1509490 2022-03-20 martijn #define MIN(a, b) ((a) < (b) ? (a) : (b))
178 f1509490 2022-03-20 martijn struct message {
179 f1509490 2022-03-20 martijn struct osmtpd_ctx *ctx;
180 f1509490 2022-03-20 martijn FILE *origf;
181 f1509490 2022-03-20 martijn int parsing_headers;
182 f1509490 2022-03-20 martijn size_t body_whitelines;
183 f1509490 2022-03-20 martijn int has_body;
184 f1509490 2022-03-20 martijn struct header *header;
185 f1509490 2022-03-20 martijn size_t nheaders;
186 f1509490 2022-03-20 martijn int readdone;
187 40991a5f 2025-06-02 kirill int nqueries;
190 a9b0d8da 2025-06-02 kirill struct session {
191 a9b0d8da 2025-06-02 kirill struct osmtpd_ctx *ctx;
192 a9b0d8da 2025-06-02 kirill enum iprev_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 f1509490 2022-03-20 martijn void dkim_header_add(struct osmtpd_ctx *, const char *);
213 f1509490 2022-03-20 martijn void dkim_signature_parse(struct header *);
214 0b081549 2025-06-02 kirill void dkim_signature_parse_v(struct dkim_signature *, const char *, const char *);
215 0b081549 2025-06-02 kirill void dkim_signature_parse_a(struct dkim_signature *, const char *, const char *);
216 0b081549 2025-06-02 kirill void dkim_signature_parse_b(struct dkim_signature *, const char *, const char *);
217 0b081549 2025-06-02 kirill void dkim_signature_parse_bh(struct dkim_signature *, const char *, const char *);
218 0b081549 2025-06-02 kirill void dkim_signature_parse_c(struct dkim_signature *, const char *, const char *);
219 0b081549 2025-06-02 kirill void dkim_signature_parse_d(struct dkim_signature *, const char *, const char *);
220 0b081549 2025-06-02 kirill void dkim_signature_parse_h(struct dkim_signature *, const char *, const char *);
221 0b081549 2025-06-02 kirill void dkim_signature_parse_i(struct dkim_signature *, const char *, const char *);
222 0b081549 2025-06-02 kirill void dkim_signature_parse_l(struct dkim_signature *, const char *, const char *);
223 0b081549 2025-06-02 kirill void dkim_signature_parse_q(struct dkim_signature *, const char *, const char *);
224 0b081549 2025-06-02 kirill void dkim_signature_parse_s(struct dkim_signature *, const char *, const char *);
225 0b081549 2025-06-02 kirill void dkim_signature_parse_t(struct dkim_signature *, const char *, const char *);
226 0b081549 2025-06-02 kirill void dkim_signature_parse_x(struct dkim_signature *, const char *, const char *);
227 0b081549 2025-06-02 kirill void dkim_signature_parse_z(struct dkim_signature *, const char *, const char *);
228 0b081549 2025-06-02 kirill void dkim_lookup_record(struct dkim_signature *sig, const char *domain);
229 0b081549 2025-06-02 kirill void dkim_signature_verify(struct dkim_signature *);
230 0b081549 2025-06-02 kirill void dkim_signature_header(EVP_MD_CTX *, struct dkim_signature *, struct header *);
231 0b081549 2025-06-02 kirill void dkim_signature_state(struct dkim_signature *, enum dkim_state, const char *);
232 0b081549 2025-06-02 kirill const char *dkim_state2str(enum dkim_state);
233 f1509490 2022-03-20 martijn void dkim_header_cat(struct osmtpd_ctx *, const char *);
234 f1509490 2022-03-20 martijn void dkim_body_parse(struct message *, const char *);
235 0b081549 2025-06-02 kirill void dkim_body_verify(struct dkim_signature *);
236 f1509490 2022-03-20 martijn void dkim_rr_resolve(struct asr_result *, void *);
237 a9b0d8da 2025-06-02 kirill const char *iprev_state2str(enum iprev_state);
238 40991a5f 2025-06-02 kirill char *spf_evaluate_domain(struct spf_record *, const char *);
239 40991a5f 2025-06-02 kirill void spf_lookup_record(struct spf_record *, const char *, int,
240 40991a5f 2025-06-02 kirill enum spf_state, int, int);
241 40991a5f 2025-06-02 kirill void spf_done(struct spf_record *, enum spf_state, const char *);
242 40991a5f 2025-06-02 kirill void spf_resolve(struct asr_result *, void *);
243 40991a5f 2025-06-02 kirill void spf_resolve_txt(struct dns_rr *, struct spf_query *);
244 40991a5f 2025-06-02 kirill void spf_resolve_mx(struct dns_rr *, struct spf_query *);
245 40991a5f 2025-06-02 kirill void spf_resolve_a(struct dns_rr *, struct spf_query *);
246 40991a5f 2025-06-02 kirill void spf_resolve_aaaa(struct dns_rr *, struct spf_query *);
247 40991a5f 2025-06-02 kirill void spf_resolve_cname(struct dns_rr *, struct spf_query *);
248 40991a5f 2025-06-02 kirill char* spf_parse_txt(const char *, size_t);
249 40991a5f 2025-06-02 kirill int spf_check_cidr(struct spf_record *, struct in_addr *, int );
250 40991a5f 2025-06-02 kirill int spf_check_cidr6(struct spf_record *, struct in6_addr *, int );
251 40991a5f 2025-06-02 kirill int spf_execute_txt(struct spf_query *);
252 40991a5f 2025-06-02 kirill const char *spf_state2str(enum spf_state);
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 0b081549 2025-06-02 kirill ssize_t auth_ar_cat(char **ar, size_t *n, size_t aroff, const char *fmt, ...)
257 f1509490 2022-03-20 martijn __attribute__((__format__ (printf, 4, 5)));
258 0b081549 2025-06-02 kirill int auth_ar_print(struct osmtpd_ctx *, const char *);
259 0b081549 2025-06-02 kirill int dkim_key_text_parse(struct dkim_signature *, const char *);
261 f1509490 2022-03-20 martijn char *authservid;
262 f1509490 2022-03-20 martijn EVP_ENCODE_CTX *ectx = NULL;
265 f1509490 2022-03-20 martijn main(int argc, char *argv[])
267 f1509490 2022-03-20 martijn if (argc != 1)
268 f1509490 2022-03-20 martijn osmtpd_errx(1, "Invalid argument count");
270 f1509490 2022-03-20 martijn OpenSSL_add_all_digests();
272 f1509490 2022-03-20 martijn if (pledge("tmppath stdio dns", NULL) == -1)
273 f1509490 2022-03-20 martijn osmtpd_err(1, "pledge");
275 f1509490 2022-03-20 martijn if ((ectx = EVP_ENCODE_CTX_new()) == NULL)
276 f1509490 2022-03-20 martijn osmtpd_err(1, "EVP_ENCODE_CTX_new");
278 40991a5f 2025-06-02 kirill osmtpd_need(OSMTPD_NEED_SRC|OSMTPD_NEED_FCRDNS|OSMTPD_NEED_IDENTITY|OSMTPD_NEED_GREETING);
279 0b081549 2025-06-02 kirill osmtpd_register_conf(auth_conf);
280 0b081549 2025-06-02 kirill osmtpd_register_filter_dataline(auth_dataline);
281 a9b0d8da 2025-06-02 kirill osmtpd_register_report_connect(1, auth_connect);
282 40991a5f 2025-06-02 kirill osmtpd_register_filter_helo(spf_identity);
283 40991a5f 2025-06-02 kirill osmtpd_register_filter_ehlo(spf_identity);
284 40991a5f 2025-06-02 kirill osmtpd_register_filter_mailfrom(spf_mailfrom);
285 a9b0d8da 2025-06-02 kirill osmtpd_local_session(auth_session_new, auth_session_free);
286 0b081549 2025-06-02 kirill osmtpd_local_message(auth_message_new, auth_message_free);
287 f1509490 2022-03-20 martijn osmtpd_run();
289 f1509490 2022-03-20 martijn return 0;
293 0b081549 2025-06-02 kirill auth_conf(const char *key, const char *value)
295 4e2dd90f 2022-03-26 martijn const char *end;
297 f1509490 2022-03-20 martijn if (key == NULL) {
298 f1509490 2022-03-20 martijn if (authservid == NULL)
299 f1509490 2022-03-20 martijn osmtpd_errx(1, "Didn't receive admd config option");
302 f1509490 2022-03-20 martijn if (strcmp(key, "admd") == 0 && authservid == NULL) {
303 f1509490 2022-03-20 martijn if ((authservid = strdup(value)) == NULL)
304 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
305 4e2dd90f 2022-03-26 martijn end = osmtpd_ltok_skip_value(authservid, 0);
306 f1509490 2022-03-20 martijn if (authservid + strlen(authservid) != end)
307 f1509490 2022-03-20 martijn osmtpd_errx(1, "Invalid authservid");
312 a9b0d8da 2025-06-02 kirill auth_connect(struct osmtpd_ctx *ctx, const char *rdns, enum osmtpd_status fcrdns,
313 a9b0d8da 2025-06-02 kirill struct sockaddr_storage *src, struct sockaddr_storage *dst)
315 a9b0d8da 2025-06-02 kirill struct session *ses = ctx->local_session;
317 a9b0d8da 2025-06-02 kirill if (fcrdns == OSMTPD_STATUS_OK)
318 a9b0d8da 2025-06-02 kirill ses->iprev = IPREV_PASS;
320 a9b0d8da 2025-06-02 kirill ses->iprev = IPREV_FAIL;
322 40991a5f 2025-06-02 kirill memcpy(&ses->src, src, sizeof(struct sockaddr_storage));
324 40991a5f 2025-06-02 kirill if (rdns != NULL) {
325 40991a5f 2025-06-02 kirill if ((ses->rdns = strdup(rdns)) == NULL)
326 40991a5f 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
331 40991a5f 2025-06-02 kirill spf_identity(struct osmtpd_ctx *ctx, const char *identity)
333 40991a5f 2025-06-02 kirill char from[HOST_NAME_MAX + 12];
335 40991a5f 2025-06-02 kirill struct session *ses = ctx->local_session;
337 40991a5f 2025-06-02 kirill if (identity == NULL) {
338 40991a5f 2025-06-02 kirill osmtpd_filter_proceed(ctx);
342 40991a5f 2025-06-02 kirill if ((ses->identity = strdup(identity)) == NULL)
343 40991a5f 2025-06-02 kirill osmtpd_err(1, "%s: strdup", __func__);
345 40991a5f 2025-06-02 kirill if (strlen(identity) == 0) {
346 40991a5f 2025-06-02 kirill osmtpd_filter_proceed(ctx);
350 40991a5f 2025-06-02 kirill snprintf(from, sizeof(from), "postmaster@%s", identity);
352 40991a5f 2025-06-02 kirill if ((ses->spf_helo = spf_record_new(ctx, from)) == NULL)
353 40991a5f 2025-06-02 kirill osmtpd_filter_proceed(ctx);
357 40991a5f 2025-06-02 kirill spf_mailfrom(struct osmtpd_ctx *ctx, const char *from)
359 40991a5f 2025-06-02 kirill struct session *ses = ctx->local_session;
361 40991a5f 2025-06-02 kirill if (from == NULL || !strlen(from)) {
362 40991a5f 2025-06-02 kirill osmtpd_filter_proceed(ctx);
366 40991a5f 2025-06-02 kirill if (ses->spf_mailfrom)
367 40991a5f 2025-06-02 kirill spf_record_free(ses->spf_mailfrom);
369 40991a5f 2025-06-02 kirill if ((ses->spf_mailfrom = spf_record_new(ctx, from)) == NULL)
370 40991a5f 2025-06-02 kirill osmtpd_filter_proceed(ctx);
374 0b081549 2025-06-02 kirill auth_dataline(struct osmtpd_ctx *ctx, const char *line)
376 f1509490 2022-03-20 martijn struct message *msg = ctx->local_message;
377 f1509490 2022-03-20 martijn size_t i;
379 b64f38ad 2025-06-02 kirill if (fprintf(msg->origf, "%s\n", line) < 0)
380 b64f38ad 2025-06-02 kirill osmtpd_err(1, "Couldn't write to tempfile");
382 f1509490 2022-03-20 martijn if (line[0] == '.') {
384 f1509490 2022-03-20 martijn if (line[0] == '\0') {
385 f1509490 2022-03-20 martijn msg->readdone = 1;
386 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++) {
387 f1509490 2022-03-20 martijn if (msg->header[i].sig == NULL)
388 f1509490 2022-03-20 martijn continue;
389 f1509490 2022-03-20 martijn dkim_body_verify(msg->header[i].sig);
391 0b081549 2025-06-02 kirill auth_message_verify(msg);
395 f1509490 2022-03-20 martijn if (msg->parsing_headers) {
396 f1509490 2022-03-20 martijn dkim_header_add(ctx, line);
397 f1509490 2022-03-20 martijn if (line[0] == '\0') {
398 f1509490 2022-03-20 martijn msg->parsing_headers = 0;
399 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++) {
400 f1509490 2022-03-20 martijn if (msg->header[i].sig == NULL)
401 f1509490 2022-03-20 martijn continue;
402 f1509490 2022-03-20 martijn if (msg->header[i].sig->query == NULL)
403 f1509490 2022-03-20 martijn dkim_signature_verify(
404 f1509490 2022-03-20 martijn msg->header[i].sig);
408 f1509490 2022-03-20 martijn } else {
409 f1509490 2022-03-20 martijn dkim_body_parse(msg, line);
414 40991a5f 2025-06-02 kirill spf_record_new(struct osmtpd_ctx *ctx, const char *from)
417 40991a5f 2025-06-02 kirill const char *at;
418 40991a5f 2025-06-02 kirill struct spf_record *spf;
420 40991a5f 2025-06-02 kirill if ((spf = malloc(sizeof(*spf))) == NULL)
421 40991a5f 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
423 40991a5f 2025-06-02 kirill spf->ctx = ctx;
424 40991a5f 2025-06-02 kirill spf->state = SPF_NONE;
425 40991a5f 2025-06-02 kirill spf->state_reason = NULL;
426 40991a5f 2025-06-02 kirill spf->nqueries = 0;
427 40991a5f 2025-06-02 kirill spf->running = 0;
428 40991a5f 2025-06-02 kirill spf->done = 0;
430 40991a5f 2025-06-02 kirill for (i = 0; i < SPF_DNS_LOOKUP_LIMIT; i++) {
431 40991a5f 2025-06-02 kirill spf->queries[i].domain = NULL;
432 40991a5f 2025-06-02 kirill spf->queries[i].txt = NULL;
433 40991a5f 2025-06-02 kirill spf->queries[i].eva = NULL;
436 40991a5f 2025-06-02 kirill from = osmtpd_ltok_skip_cfws(from, 1);
438 40991a5f 2025-06-02 kirill if ((at = osmtpd_ltok_skip_local_part(from, 0)) == NULL)
439 40991a5f 2025-06-02 kirill goto fail;
441 40991a5f 2025-06-02 kirill if ((spf->sender_local = strndup(from, at - from)) == NULL)
442 40991a5f 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
444 40991a5f 2025-06-02 kirill if (*at != '@')
445 40991a5f 2025-06-02 kirill goto fail_local;
448 40991a5f 2025-06-02 kirill if ((from = osmtpd_ltok_skip_domain(at, 0)) == NULL)
449 40991a5f 2025-06-02 kirill goto fail_local;
452 40991a5f 2025-06-02 kirill if ((spf->sender_domain = strndup(at, from - at)) == NULL)
453 40991a5f 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
455 40991a5f 2025-06-02 kirill spf_lookup_record(
456 40991a5f 2025-06-02 kirill spf, spf->sender_domain, T_TXT, SPF_PASS, 0, 0);
458 40991a5f 2025-06-02 kirill return spf;
460 40991a5f 2025-06-02 kirill fail_local:
461 40991a5f 2025-06-02 kirill free(spf->sender_local);
464 40991a5f 2025-06-02 kirill free(spf);
465 40991a5f 2025-06-02 kirill return NULL;
469 40991a5f 2025-06-02 kirill spf_record_free(struct spf_record *spf)
473 40991a5f 2025-06-02 kirill for (i = 0; i < SPF_DNS_LOOKUP_LIMIT; i++) {
474 40991a5f 2025-06-02 kirill if (spf->queries[i].domain)
475 40991a5f 2025-06-02 kirill free(spf->queries[i].domain);
476 40991a5f 2025-06-02 kirill if (spf->queries[i].txt)
477 40991a5f 2025-06-02 kirill free(spf->queries[i].txt);
478 40991a5f 2025-06-02 kirill if (spf->queries[i].eva)
479 40991a5f 2025-06-02 kirill event_asr_abort(spf->queries[i].eva);
482 40991a5f 2025-06-02 kirill free(spf->sender_local);
483 40991a5f 2025-06-02 kirill free(spf->sender_domain);
485 40991a5f 2025-06-02 kirill free(spf);
489 a9b0d8da 2025-06-02 kirill auth_session_new(struct osmtpd_ctx *ctx)
491 a9b0d8da 2025-06-02 kirill struct session *ses;
493 a9b0d8da 2025-06-02 kirill if ((ses = malloc(sizeof(*ses))) == NULL)
494 a9b0d8da 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
496 a9b0d8da 2025-06-02 kirill ses->ctx = ctx;
497 a9b0d8da 2025-06-02 kirill ses->iprev = IPREV_NONE;
499 40991a5f 2025-06-02 kirill ses->spf_helo = NULL;
500 40991a5f 2025-06-02 kirill ses->spf_mailfrom = NULL;
502 40991a5f 2025-06-02 kirill ses->identity = NULL;
503 40991a5f 2025-06-02 kirill ses->rdns = NULL;
505 a9b0d8da 2025-06-02 kirill return ses;
509 a9b0d8da 2025-06-02 kirill auth_session_free(struct osmtpd_ctx *ctx, void *data)
511 a9b0d8da 2025-06-02 kirill struct session *ses = data;
513 40991a5f 2025-06-02 kirill if (ses->spf_helo)
514 40991a5f 2025-06-02 kirill spf_record_free(ses->spf_helo);
515 40991a5f 2025-06-02 kirill if (ses->spf_mailfrom)
516 40991a5f 2025-06-02 kirill spf_record_free(ses->spf_mailfrom);
517 40991a5f 2025-06-02 kirill if (ses->identity)
518 40991a5f 2025-06-02 kirill free(ses->identity);
519 40991a5f 2025-06-02 kirill if (ses->rdns)
520 40991a5f 2025-06-02 kirill free(ses->rdns);
522 a9b0d8da 2025-06-02 kirill free(ses);
526 0b081549 2025-06-02 kirill auth_message_new(struct osmtpd_ctx *ctx)
528 f1509490 2022-03-20 martijn struct message *msg;
530 f1509490 2022-03-20 martijn if ((msg = malloc(sizeof(*msg))) == NULL)
531 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
533 f1509490 2022-03-20 martijn if ((msg->origf = tmpfile()) == NULL) {
534 b64f38ad 2025-06-02 kirill osmtpd_warn(NULL, "Can't open tempfile");
535 348f2e63 2025-01-29 kirill free(msg);
536 f1509490 2022-03-20 martijn return NULL;
538 f1509490 2022-03-20 martijn msg->ctx = ctx;
539 f1509490 2022-03-20 martijn msg->parsing_headers = 1;
540 f1509490 2022-03-20 martijn msg->body_whitelines = 0;
541 f1509490 2022-03-20 martijn msg->has_body = 0;
542 f1509490 2022-03-20 martijn msg->header = NULL;
543 f1509490 2022-03-20 martijn msg->nheaders = 0;
544 f1509490 2022-03-20 martijn msg->readdone = 0;
545 40991a5f 2025-06-02 kirill msg->nqueries = 0;
547 f1509490 2022-03-20 martijn return msg;
551 0b081549 2025-06-02 kirill auth_message_free(struct osmtpd_ctx *ctx, void *data)
553 f1509490 2022-03-20 martijn struct message *msg = data;
554 f1509490 2022-03-20 martijn size_t i, j;
556 f1509490 2022-03-20 martijn fclose(msg->origf);
557 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++) {
558 f1509490 2022-03-20 martijn if (msg->header[i].sig != NULL) {
559 f1509490 2022-03-20 martijn free(msg->header[i].sig->b);
560 f1509490 2022-03-20 martijn EVP_MD_CTX_free(msg->header[i].sig->bhctx);
561 f1509490 2022-03-20 martijn for (j = 0; msg->header[i].sig->h != NULL &&
562 f1509490 2022-03-20 martijn msg->header[i].sig->h[j] != NULL; j++)
563 f1509490 2022-03-20 martijn free(msg->header[i].sig->h[j]);
564 f1509490 2022-03-20 martijn free(msg->header[i].sig->h);
565 bf67701d 2022-03-29 martijn EVP_PKEY_free(msg->header[i].sig->p);
566 5ea05a05 2025-06-02 kirill if (msg->header[i].sig->query)
567 5ea05a05 2025-06-02 kirill event_asr_abort(msg->header[i].sig->query);
569 8cc3b72a 2022-03-27 martijn free(msg->header[i].buf);
570 f1509490 2022-03-20 martijn free(msg->header[i].sig);
572 f1509490 2022-03-20 martijn free(msg->header);
573 f1509490 2022-03-20 martijn free(msg);
577 f1509490 2022-03-20 martijn dkim_header_add(struct osmtpd_ctx *ctx, const char *line)
579 f1509490 2022-03-20 martijn struct message *msg = ctx->local_message;
580 4e2dd90f 2022-03-26 martijn const char *start, *end, *verify;
581 f1509490 2022-03-20 martijn struct header *headers;
582 f1509490 2022-03-20 martijn size_t i;
584 f1509490 2022-03-20 martijn if (msg->nheaders > 0 &&
585 f1509490 2022-03-20 martijn msg->header[msg->nheaders - 1].readdone == 0) {
586 f1509490 2022-03-20 martijn if (line[0] != ' ' && line[0] != '\t') {
587 f1509490 2022-03-20 martijn msg->header[msg->nheaders - 1].readdone = 1;
588 f1509490 2022-03-20 martijn start = msg->header[msg->nheaders - 1].buf;
589 4e2dd90f 2022-03-26 martijn end = osmtpd_ltok_skip_field_name(start, 0);
590 f1509490 2022-03-20 martijn /* In case someone uses an obs-optional */
591 5c6a6550 2022-03-24 martijn if (end != NULL)
592 4e2dd90f 2022-03-26 martijn verify = osmtpd_ltok_skip_wsp(end, 1);
593 5c6a6550 2022-03-24 martijn if (end != NULL &&
594 5c6a6550 2022-03-24 martijn strncasecmp(
595 f1509490 2022-03-20 martijn start, "DKIM-Signature", end - start) == 0 &&
596 f1509490 2022-03-20 martijn verify[0] == ':')
597 f1509490 2022-03-20 martijn dkim_signature_parse(
598 f1509490 2022-03-20 martijn &msg->header[msg->nheaders - 1]);
599 f1509490 2022-03-20 martijn if (line[0] == '\0')
601 f1509490 2022-03-20 martijn } else {
602 f1509490 2022-03-20 martijn dkim_header_cat(ctx, line);
606 f1509490 2022-03-20 martijn if (msg->nheaders % 10 == 0) {
607 f1509490 2022-03-20 martijn if ((headers = recallocarray(msg->header, msg->nheaders,
608 b64f38ad 2025-06-02 kirill msg->nheaders + 10, sizeof(*msg->header))) == NULL)
609 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
610 f1509490 2022-03-20 martijn msg->header = headers;
611 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++) {
612 f1509490 2022-03-20 martijn if (msg->header[i].sig == NULL)
613 f1509490 2022-03-20 martijn continue;
614 f1509490 2022-03-20 martijn msg->header[i].sig->header = &msg->header[i];
617 f1509490 2022-03-20 martijn msg->header[msg->nheaders].msg = msg;
618 f1509490 2022-03-20 martijn msg->nheaders++;
619 f1509490 2022-03-20 martijn dkim_header_cat(ctx, line);
623 f1509490 2022-03-20 martijn dkim_header_cat(struct osmtpd_ctx *ctx, const char *line)
625 f1509490 2022-03-20 martijn struct message *msg = ctx->local_message;
626 f1509490 2022-03-20 martijn struct header *header = &msg->header[msg->nheaders - 1];
627 f1509490 2022-03-20 martijn char *buf;
629 f1509490 2022-03-20 martijn size_t needed = header->buflen + strlen(line) + 2;
631 f1509490 2022-03-20 martijn if (needed > (header->buflen / 1024) + 1) {
632 f1509490 2022-03-20 martijn buf = reallocarray(header->buf, (needed / 1024) + 1, 1024);
633 b64f38ad 2025-06-02 kirill if (buf == NULL)
634 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
635 f1509490 2022-03-20 martijn header->buf = buf;
637 f1509490 2022-03-20 martijn header->buflen += snprintf(header->buf + header->buflen,
638 f1509490 2022-03-20 martijn (((needed / 1024) + 1) * 1024) - header->buflen, "%s%s",
639 f1509490 2022-03-20 martijn header->buflen == 0 ? "" : "\r\n", line);
643 f1509490 2022-03-20 martijn dkim_signature_parse(struct header *header)
645 0b081549 2025-06-02 kirill struct dkim_signature *sig;
646 4e2dd90f 2022-03-26 martijn const char *buf, *i, *end;
647 4e2dd90f 2022-03-26 martijn char tagname[3];
648 f1509490 2022-03-20 martijn char subdomain[HOST_NAME_MAX + 1];
649 cc08de51 2022-03-24 martijn size_t ilen, dlen;
651 f1509490 2022-03-20 martijn /* Format checked by dkim_header_add */
652 4e2dd90f 2022-03-26 martijn buf = osmtpd_ltok_skip_field_name(header->buf, 0);
653 4e2dd90f 2022-03-26 martijn buf = osmtpd_ltok_skip_wsp(buf, 1) + 1;
655 b64f38ad 2025-06-02 kirill if ((header->sig = calloc(1, sizeof(*header->sig))) == NULL)
656 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
657 f1509490 2022-03-20 martijn sig = header->sig;
658 f1509490 2022-03-20 martijn sig->header = header;
659 f1509490 2022-03-20 martijn sig->l = -1;
660 f1509490 2022-03-20 martijn sig->t = -1;
661 f1509490 2022-03-20 martijn sig->x = -1;
663 4e2dd90f 2022-03-26 martijn end = osmtpd_ltok_skip_tag_list(buf, 0);
664 f1509490 2022-03-20 martijn if (end == NULL || end[0] != '\0') {
665 4e2dd90f 2022-03-26 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid tag-list");
669 f1509490 2022-03-20 martijn while (buf[0] != '\0') {
670 4e2dd90f 2022-03-26 martijn buf = osmtpd_ltok_skip_fws(buf, 1);
671 4e2dd90f 2022-03-26 martijn end = osmtpd_ltok_skip_tag_name(buf, 0);
673 f1509490 2022-03-20 martijn /* Unknown tag-name */
674 f1509490 2022-03-20 martijn if ((size_t)(end - buf) >= sizeof(tagname))
675 f1509490 2022-03-20 martijn tagname[0] = '\0';
677 f1509490 2022-03-20 martijn strlcpy(tagname, buf, (end - buf) + 1);
678 4e2dd90f 2022-03-26 martijn buf = osmtpd_ltok_skip_fws(end, 1);
679 f1509490 2022-03-20 martijn /* '=' */
680 4e2dd90f 2022-03-26 martijn buf = osmtpd_ltok_skip_fws(buf + 1, 1);
681 4e2dd90f 2022-03-26 martijn end = osmtpd_ltok_skip_tag_value(buf, 1);
682 f1509490 2022-03-20 martijn if (strcmp(tagname, "v") == 0)
683 f1509490 2022-03-20 martijn dkim_signature_parse_v(sig, buf, end);
684 f1509490 2022-03-20 martijn else if (strcmp(tagname, "a") == 0)
685 f1509490 2022-03-20 martijn dkim_signature_parse_a(sig, buf, end);
686 f1509490 2022-03-20 martijn else if (strcmp(tagname, "b") == 0)
687 f1509490 2022-03-20 martijn dkim_signature_parse_b(sig, buf, end);
688 f1509490 2022-03-20 martijn else if (strcmp(tagname, "bh") == 0)
689 f1509490 2022-03-20 martijn dkim_signature_parse_bh(sig, buf, end);
690 f1509490 2022-03-20 martijn else if (strcmp(tagname, "c") == 0)
691 f1509490 2022-03-20 martijn dkim_signature_parse_c(sig, buf, end);
692 f1509490 2022-03-20 martijn else if (strcmp(tagname, "d") == 0)
693 f1509490 2022-03-20 martijn dkim_signature_parse_d(sig, buf, end);
694 f1509490 2022-03-20 martijn else if (strcmp(tagname, "h") == 0)
695 f1509490 2022-03-20 martijn dkim_signature_parse_h(sig, buf, end);
696 f1509490 2022-03-20 martijn else if (strcmp(tagname, "i") == 0)
697 f1509490 2022-03-20 martijn dkim_signature_parse_i(sig, buf, end);
698 f1509490 2022-03-20 martijn else if (strcmp(tagname, "l") == 0)
699 f1509490 2022-03-20 martijn dkim_signature_parse_l(sig, buf, end);
700 f1509490 2022-03-20 martijn else if (strcmp(tagname, "q") == 0)
701 f1509490 2022-03-20 martijn dkim_signature_parse_q(sig, buf, end);
702 f1509490 2022-03-20 martijn else if (strcmp(tagname, "s") == 0)
703 f1509490 2022-03-20 martijn dkim_signature_parse_s(sig, buf, end);
704 f1509490 2022-03-20 martijn else if (strcmp(tagname, "t") == 0)
705 f1509490 2022-03-20 martijn dkim_signature_parse_t(sig, buf, end);
706 f1509490 2022-03-20 martijn else if (strcmp(tagname, "x") == 0)
707 f1509490 2022-03-20 martijn dkim_signature_parse_x(sig, buf, end);
708 f1509490 2022-03-20 martijn else if (strcmp(tagname, "z") == 0)
709 f1509490 2022-03-20 martijn dkim_signature_parse_z(sig, buf, end);
711 4e2dd90f 2022-03-26 martijn buf = osmtpd_ltok_skip_fws(end, 1);
712 f1509490 2022-03-20 martijn if (buf[0] == ';')
714 f1509490 2022-03-20 martijn else if (buf[0] != '\0') {
715 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR,
716 4e2dd90f 2022-03-26 martijn "Invalid tag-list");
720 f1509490 2022-03-20 martijn if (sig->state != DKIM_UNKNOWN)
723 f1509490 2022-03-20 martijn if (sig->v != 1)
724 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Missing v tag");
725 f1509490 2022-03-20 martijn else if (sig->ah == NULL)
726 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Missing a tag");
727 f1509490 2022-03-20 martijn else if (sig->b == NULL)
728 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Missing b tag");
729 f1509490 2022-03-20 martijn else if (sig->bhsz == 0)
730 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Missing bh tag");
731 f1509490 2022-03-20 martijn else if (sig->d[0] == '\0')
732 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Missing d tag");
733 f1509490 2022-03-20 martijn else if (sig->h == NULL)
734 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Missing h tag");
735 f1509490 2022-03-20 martijn else if (sig->s[0] == '\0')
736 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Missing s tag");
737 759a6c7b 2022-03-24 martijn if (sig->state != DKIM_UNKNOWN)
740 cc08de51 2022-03-24 martijn if (sig->i != NULL) {
741 4e2dd90f 2022-03-26 martijn i = osmtpd_ltok_skip_local_part(sig->i, 1) + 1;
742 efee726e 2022-03-27 martijn ilen = sig->isz - (size_t)(i - sig->i);
743 cc08de51 2022-03-24 martijn dlen = strlen(sig->d);
744 cc08de51 2022-03-24 martijn if (ilen < dlen) {
745 cc08de51 2022-03-24 martijn dkim_signature_state(sig, DKIM_PERMERROR,
746 f85da730 2022-03-27 martijn "i tag not subdomain of d");
749 cc08de51 2022-03-24 martijn i += ilen - dlen;
750 efee726e 2022-03-27 martijn if ((i[-1] != '.' && i[-1] != '@') ||
751 7847bd66 2022-04-05 martijn strncasecmp(i, sig->d, dlen) != 0) {
752 cc08de51 2022-03-24 martijn dkim_signature_state(sig, DKIM_PERMERROR,
753 efee726e 2022-03-27 martijn "i tag not subdomain of d");
757 2d83468e 2022-03-24 martijn if (sig->t != -1 && sig->x != -1 && sig->t > sig->x) {
758 2d83468e 2022-03-24 martijn dkim_signature_state(sig, DKIM_PERMERROR, "t tag after x tag");
762 f1509490 2022-03-20 martijn if ((size_t)snprintf(subdomain, sizeof(subdomain), "%s._domainkey.%s",
763 f1509490 2022-03-20 martijn sig->s, sig->d) >= sizeof(subdomain)) {
764 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR,
765 f1509490 2022-03-20 martijn "dns/txt query too long");
769 3cb7796f 2025-06-02 kirill dkim_lookup_record(sig, subdomain);
773 0b081549 2025-06-02 kirill dkim_lookup_record(struct dkim_signature *sig, const char *domain)
775 3cb7796f 2025-06-02 kirill struct asr_query *query;
777 ede84c7b 2025-06-02 kirill if (sig->state != DKIM_UNKNOWN)
780 3cb7796f 2025-06-02 kirill sig->nqueries++;
782 5ea05a05 2025-06-02 kirill if (sig->query != NULL) {
783 5ea05a05 2025-06-02 kirill event_asr_abort(sig->query);
784 5ea05a05 2025-06-02 kirill sig->query = NULL;
785 40991a5f 2025-06-02 kirill sig->header->msg->nqueries--;
787 3cb7796f 2025-06-02 kirill if ((query = res_query_async(domain, C_IN, T_TXT, NULL)) == NULL)
788 3cb7796f 2025-06-02 kirill osmtpd_err(1, "res_query_async");
790 3cb7796f 2025-06-02 kirill if ((sig->query = event_asr_run(query, dkim_rr_resolve, sig)) == NULL)
791 3cb7796f 2025-06-02 kirill osmtpd_err(1, "res_query_async");
793 40991a5f 2025-06-02 kirill sig->header->msg->nqueries++;
797 0b081549 2025-06-02 kirill dkim_signature_parse_v(struct dkim_signature *sig, const char *start, const char *end)
799 f1509490 2022-03-20 martijn if (sig->v != 0) { /* Duplicate tag */
800 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate v tag");
803 f1509490 2022-03-20 martijn /* Unsupported version */
804 f1509490 2022-03-20 martijn if (start[0] != '1' || start + 1 != end)
805 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_NEUTRAL, "Unsupported v tag");
807 f1509490 2022-03-20 martijn sig->v = 1;
811 0b081549 2025-06-02 kirill dkim_signature_parse_a(struct dkim_signature *sig, const char *start, const char *end)
813 f1509490 2022-03-20 martijn char ah[sizeof("sha256")];
815 f1509490 2022-03-20 martijn if (sig->ah != NULL) {
816 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate a tag");
820 4e2dd90f 2022-03-26 martijn if (osmtpd_ltok_skip_sig_a_tag_alg(start, 0) != end) {
821 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid a tag");
824 f1509490 2022-03-20 martijn sig->a = start;
825 f1509490 2022-03-20 martijn sig->asz = (size_t)(end - start);
826 f1509490 2022-03-20 martijn if (strncmp(start, "rsa-", 4) == 0) {
827 f1509490 2022-03-20 martijn start += 4;
828 f1509490 2022-03-20 martijn sig->ak = EVP_PKEY_RSA;
829 338537df 2022-04-08 martijn sig->sephash = 0;
830 72580136 2022-12-16 martijn #if HAVE_ED25519
831 338537df 2022-04-08 martijn } else if (strncmp(start, "ed25519-", 8) == 0) {
832 338537df 2022-04-08 martijn start += 8;
833 338537df 2022-04-08 martijn sig->ak = EVP_PKEY_ED25519;
834 338537df 2022-04-08 martijn sig->sephash = 1;
836 f1509490 2022-03-20 martijn } else {
837 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag k");
840 f1509490 2022-03-20 martijn if ((size_t)(end - start) >= sizeof(ah)) {
841 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag h");
844 f1509490 2022-03-20 martijn strlcpy(ah, start, sizeof(ah));
845 f1509490 2022-03-20 martijn ah[end - start] = '\0';
846 f1509490 2022-03-20 martijn if ((sig->ah = EVP_get_digestbyname(ah)) == NULL) {
847 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag h");
850 b64f38ad 2025-06-02 kirill if ((sig->bhctx = EVP_MD_CTX_new()) == NULL)
851 b64f38ad 2025-06-02 kirill osmtpd_err(1, "EVP_MD_CTX_new");
853 f1509490 2022-03-20 martijn if (EVP_DigestInit_ex(sig->bhctx, sig->ah, NULL) <= 0) {
854 b64f38ad 2025-06-02 kirill dkim_signature_state(sig, DKIM_FAIL, "Unsuppored a tag ah");
860 0b081549 2025-06-02 kirill dkim_signature_parse_b(struct dkim_signature *sig, const char *start, const char *end)
862 f1509490 2022-03-20 martijn int decodesz;
863 ce4bcd16 2025-06-02 kirill size_t i, j;
865 f1509490 2022-03-20 martijn if (sig->b != NULL) {
866 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate b tag");
869 f1509490 2022-03-20 martijn sig->bheader = start;
870 ce4bcd16 2025-06-02 kirill sig->bheadersz = end - start;
871 ce4bcd16 2025-06-02 kirill if ((sig->b = malloc(((sig->bheadersz / 4) + 1) * 3)) == NULL)
872 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
873 f1509490 2022-03-20 martijn /* EVP_DecodeBlock doesn't handle internal whitespace */
874 f1509490 2022-03-20 martijn EVP_DecodeInit(ectx);
875 ce4bcd16 2025-06-02 kirill if (EVP_DecodeUpdate(ectx, sig->b, &decodesz, sig->bheader,
876 ce4bcd16 2025-06-02 kirill (int) sig->bheadersz) == -1) {
877 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid b tag");
880 f1509490 2022-03-20 martijn sig->bsz = decodesz;
881 f1509490 2022-03-20 martijn if (EVP_DecodeFinal(ectx, sig->b + sig->bsz,
882 f1509490 2022-03-20 martijn &decodesz) == -1) {
883 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid b tag");
886 f1509490 2022-03-20 martijn sig->bsz += decodesz;
887 ce4bcd16 2025-06-02 kirill for (i = 0, j = 0;
888 ce4bcd16 2025-06-02 kirill i < sig->bheadersz && j < HEADER_B_MAX_LEN; i++) {
889 ce4bcd16 2025-06-02 kirill if (isalnum(sig->bheader[i]) || sig->bheader[i] == '/'
890 ce4bcd16 2025-06-02 kirill || sig->bheader[i] == '+' || sig->bheader[i] == '=')
891 ce4bcd16 2025-06-02 kirill sig->bheaderclean[j++] = sig->bheader[i];
893 ce4bcd16 2025-06-02 kirill sig->bheaderclean[j] = '\0';
897 0b081549 2025-06-02 kirill dkim_signature_parse_bh(struct dkim_signature *sig, const char *start, const char *end)
899 4e2dd90f 2022-03-26 martijn const char *b64;
900 f1509490 2022-03-20 martijn size_t n;
901 f1509490 2022-03-20 martijn int decodesz;
903 f1509490 2022-03-20 martijn if (sig->bhsz != 0) {
904 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate bh tag");
908 f1509490 2022-03-20 martijn * EVP_Decode* expects sig->bh to be large enough,
909 f1509490 2022-03-20 martijn * so count the actual b64 characters.
911 f1509490 2022-03-20 martijn b64 = start;
913 f1509490 2022-03-20 martijn while (1) {
914 4e2dd90f 2022-03-26 martijn b64 = osmtpd_ltok_skip_fws(b64, 1);
915 4e2dd90f 2022-03-26 martijn if (osmtpd_ltok_skip_alphadigitps(b64, 0) == NULL)
920 f1509490 2022-03-20 martijn if (b64[0] == '=') {
922 4e2dd90f 2022-03-26 martijn b64 = osmtpd_ltok_skip_fws(b64 + 1, 1);
923 f1509490 2022-03-20 martijn if (b64[0] == '=') {
928 f1509490 2022-03-20 martijn /* Invalid tag value */
929 f1509490 2022-03-20 martijn if (b64 != end || n % 4 != 0 || (n / 4) * 3 > sizeof(sig->bh)) {
930 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
933 f1509490 2022-03-20 martijn /* EVP_DecodeBlock doesn't handle internal whitespace */
934 f1509490 2022-03-20 martijn EVP_DecodeInit(ectx);
935 f1509490 2022-03-20 martijn if (EVP_DecodeUpdate(ectx, sig->bh, &decodesz, start,
936 f1509490 2022-03-20 martijn (int)(end - start)) == -1) {
937 f1509490 2022-03-20 martijn /* Paranoia check */
938 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
941 f1509490 2022-03-20 martijn sig->bhsz = decodesz;
942 f1509490 2022-03-20 martijn if (EVP_DecodeFinal(ectx, sig->bh + sig->bhsz, &decodesz) == -1) {
943 f1509490 2022-03-20 martijn /* Paranoia check */
944 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
947 f1509490 2022-03-20 martijn sig->bhsz += decodesz;
951 0b081549 2025-06-02 kirill dkim_signature_parse_c(struct dkim_signature *sig, const char *start, const char *end)
953 f1509490 2022-03-20 martijn if (sig->c != 0) {
954 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate c tag");
957 f1509490 2022-03-20 martijn if (strncmp(start, "simple", 6) == 0) {
958 f1509490 2022-03-20 martijn sig->c = CANON_HEADER_SIMPLE;
959 f1509490 2022-03-20 martijn start += 6;
960 f1509490 2022-03-20 martijn } else if (strncmp(start, "relaxed", 7) == 0) {
961 f1509490 2022-03-20 martijn sig->c = CANON_HEADER_RELAXED;
962 f1509490 2022-03-20 martijn start += 7;
963 f1509490 2022-03-20 martijn } else {
964 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid c tag");
967 f1509490 2022-03-20 martijn if (start[0] == '/') {
968 f1509490 2022-03-20 martijn start++;
969 f1509490 2022-03-20 martijn if (strncmp(start, "simple", 6) == 0) {
970 f1509490 2022-03-20 martijn sig->c |= CANON_BODY_SIMPLE;
971 f1509490 2022-03-20 martijn start += 6;
972 f1509490 2022-03-20 martijn } else if (strncmp(start, "relaxed", 7) == 0) {
973 f1509490 2022-03-20 martijn sig->c |= CANON_BODY_RELAXED;
974 f1509490 2022-03-20 martijn start += 7;
975 f1509490 2022-03-20 martijn } else {
976 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR,
977 f1509490 2022-03-20 martijn "Invalid c tag");
982 f1509490 2022-03-20 martijn if (start != end) {
983 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid c tag");
986 f1509490 2022-03-20 martijn sig->c |= CANON_DONE;
990 0b081549 2025-06-02 kirill dkim_signature_parse_d(struct dkim_signature *sig, const char *start, const char *end)
992 f1509490 2022-03-20 martijn if (sig->d[0] != '\0') {
993 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate d tag");
996 2817edca 2022-03-30 martijn if (osmtpd_ltok_skip_sig_d_tag_value(start, 0) != end ||
997 f1509490 2022-03-20 martijn (size_t)(end - start) >= sizeof(sig->d)) {
998 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid d tag");
1001 f1509490 2022-03-20 martijn strlcpy(sig->d, start, end - start + 1);
1005 0b081549 2025-06-02 kirill dkim_signature_parse_h(struct dkim_signature *sig, const char *start, const char *end)
1007 4e2dd90f 2022-03-26 martijn const char *h;
1008 f1509490 2022-03-20 martijn size_t n = 0;
1010 f1509490 2022-03-20 martijn if (sig->h != NULL) {
1011 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate h tag");
1012 f1509490 2022-03-20 martijn return;
1014 022782a1 2022-03-30 martijn if (osmtpd_ltok_skip_sig_h_tag_value(start, 0) < end) {
1015 04225ff9 2022-03-30 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid h tag");
1016 04225ff9 2022-03-30 martijn return;
1018 f1509490 2022-03-20 martijn h = start;
1019 f1509490 2022-03-20 martijn while (1) {
1020 4e2dd90f 2022-03-26 martijn if ((h = osmtpd_ltok_skip_hdr_name(h, 0)) == NULL) {
1021 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR,
1022 f1509490 2022-03-20 martijn "Invalid h tag");
1023 f1509490 2022-03-20 martijn return;
1026 f1509490 2022-03-20 martijn /* ';' is part of hdr-name */
1027 f1509490 2022-03-20 martijn if (h > end) {
1028 f1509490 2022-03-20 martijn h = end;
1031 4e2dd90f 2022-03-26 martijn h = osmtpd_ltok_skip_fws(h, 1);
1032 f1509490 2022-03-20 martijn if (h[0] != ':')
1034 4e2dd90f 2022-03-26 martijn h = osmtpd_ltok_skip_fws(h + 1, 1);
1036 b64f38ad 2025-06-02 kirill if ((sig->h = calloc(n + 1, sizeof(*sig->h))) == NULL)
1037 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
1039 f1509490 2022-03-20 martijn h = start;
1040 f1509490 2022-03-20 martijn while (1) {
1041 4e2dd90f 2022-03-26 martijn h = osmtpd_ltok_skip_hdr_name(start, 0);
1042 f1509490 2022-03-20 martijn /* ';' is part of hdr-name */
1043 f1509490 2022-03-20 martijn if (h > end) {
1044 f1509490 2022-03-20 martijn sig->h[n] = strndup(start, end - start);
1047 b64f38ad 2025-06-02 kirill if ((sig->h[n++] = strndup(start, h - start)) == NULL)
1048 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
1049 4e2dd90f 2022-03-26 martijn start = osmtpd_ltok_skip_fws(h, 1);
1050 f1509490 2022-03-20 martijn if (start[0] != ':')
1052 4e2dd90f 2022-03-26 martijn start = osmtpd_ltok_skip_fws(start + 1, 1);
1057 0b081549 2025-06-02 kirill dkim_signature_parse_i(struct dkim_signature *sig, const char *start, const char *end)
1059 f1509490 2022-03-20 martijn if (sig->i != NULL) {
1060 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate i tag");
1061 f1509490 2022-03-20 martijn return;
1063 2b49c695 2022-03-30 martijn if (osmtpd_ltok_skip_sig_i_tag_value(start, 0) != end) {
1064 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid i tag");
1065 f1509490 2022-03-20 martijn return;
1067 f1509490 2022-03-20 martijn sig->i = start;
1068 f1509490 2022-03-20 martijn sig->isz = (size_t)(end - start);
1072 0b081549 2025-06-02 kirill dkim_signature_parse_l(struct dkim_signature *sig, const char *start, const char *end)
1074 f1509490 2022-03-20 martijn long long l;
1075 f1509490 2022-03-20 martijn char *lend;
1077 f1509490 2022-03-20 martijn if (sig->l != -1) { /* Duplicate tag */
1078 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate l tag");
1079 f1509490 2022-03-20 martijn return;
1081 f1509490 2022-03-20 martijn errno = 0;
1082 f1509490 2022-03-20 martijn l = strtoll(start, &lend, 10);
1083 f1509490 2022-03-20 martijn /* > 76 digits in stroll is an overflow */
1084 4e2dd90f 2022-03-26 martijn if (osmtpd_ltok_skip_digit(start, 0) == NULL ||
1085 f1509490 2022-03-20 martijn lend != end || errno != 0) {
1086 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid l tag");
1087 f1509490 2022-03-20 martijn return;
1089 f1509490 2022-03-20 martijn if (l > SSIZE_MAX) {
1090 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "l tag too large");
1091 f1509490 2022-03-20 martijn return;
1093 f1509490 2022-03-20 martijn sig->l = (ssize_t)l;
1097 0b081549 2025-06-02 kirill dkim_signature_parse_q(struct dkim_signature *sig, const char *start, const char *end)
1099 4e2dd90f 2022-03-26 martijn const char *qend;
1101 f1509490 2022-03-20 martijn if (sig->q != 0) {
1102 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate q tag");
1103 f1509490 2022-03-20 martijn return;
1106 f1509490 2022-03-20 martijn while (1) {
1107 4e2dd90f 2022-03-26 martijn start = osmtpd_ltok_skip_fws(start, 1);
1108 4e2dd90f 2022-03-26 martijn qend = osmtpd_ltok_skip_sig_q_tag_method(start, 0);
1109 f1509490 2022-03-20 martijn if (qend == NULL) {
1110 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid q tag");
1111 f1509490 2022-03-20 martijn return;
1113 f1509490 2022-03-20 martijn if (strncmp(start, "dns/txt", qend - start) == 0)
1114 f1509490 2022-03-20 martijn sig->q = 1;
1115 4e2dd90f 2022-03-26 martijn start = osmtpd_ltok_skip_fws(qend, 1);
1116 f1509490 2022-03-20 martijn if (start[0] != ':')
1119 f1509490 2022-03-20 martijn if (start != end) {
1120 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid q tag");
1121 f1509490 2022-03-20 martijn return;
1123 f1509490 2022-03-20 martijn if (sig->q != 1) {
1124 f1509490 2022-03-20 martijn sig->q = 1;
1125 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_NEUTRAL, "No useable q found");
1126 f1509490 2022-03-20 martijn return;
1131 0b081549 2025-06-02 kirill dkim_signature_parse_s(struct dkim_signature *sig, const char *start, const char *end)
1133 f1509490 2022-03-20 martijn if (sig->s[0] != '\0') {
1134 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate s tag");
1135 f1509490 2022-03-20 martijn return;
1137 4e2dd90f 2022-03-26 martijn if (osmtpd_ltok_skip_selector(start, 0) != end) {
1138 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid s tag");
1139 f1509490 2022-03-20 martijn return;
1141 f1509490 2022-03-20 martijn strlcpy(sig->s, start, end - start + 1);
1145 0b081549 2025-06-02 kirill dkim_signature_parse_t(struct dkim_signature *sig, const char *start, const char *end)
1147 f1509490 2022-03-20 martijn char *tend;
1149 f1509490 2022-03-20 martijn if (sig->t != -1) {
1150 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate t tag");
1151 f1509490 2022-03-20 martijn return;
1153 f1509490 2022-03-20 martijn errno = 0;
1154 f1509490 2022-03-20 martijn sig->t = strtoll(start, &tend, 10);
1155 4e2dd90f 2022-03-26 martijn if (osmtpd_ltok_skip_digit(start, 0) == NULL || tend != end ||
1156 f1509490 2022-03-20 martijn tend - start > 12 || errno != 0) {
1157 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid t tag");
1158 f1509490 2022-03-20 martijn return;
1163 0b081549 2025-06-02 kirill dkim_signature_parse_x(struct dkim_signature *sig, const char *start, const char *end)
1165 f1509490 2022-03-20 martijn char *xend;
1167 f1509490 2022-03-20 martijn if (sig->x != -1) {
1168 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate x tag");
1169 f1509490 2022-03-20 martijn return;
1171 f1509490 2022-03-20 martijn errno = 0;
1172 f1509490 2022-03-20 martijn sig->x = strtoll(start, &xend, 10);
1173 4e2dd90f 2022-03-26 martijn if (osmtpd_ltok_skip_digit(start, 0) == NULL || xend != end ||
1174 f1509490 2022-03-20 martijn xend - start > 12 || errno != 0) {
1175 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid x tag");
1176 f1509490 2022-03-20 martijn return;
1181 0b081549 2025-06-02 kirill dkim_signature_parse_z(struct dkim_signature *sig, const char *start, const char *end)
1183 f1509490 2022-03-20 martijn if (sig->z != 0) {
1184 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate z tag");
1185 f1509490 2022-03-20 martijn return;
1188 f1509490 2022-03-20 martijn sig->z = 1;
1189 4e2dd90f 2022-03-26 martijn if (osmtpd_ltok_skip_sig_z_tag_value(start, 0) != end) {
1190 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid z tag");
1191 f1509490 2022-03-20 martijn return;
1196 0b081549 2025-06-02 kirill dkim_signature_verify(struct dkim_signature *sig)
1198 f1509490 2022-03-20 martijn struct message *msg = sig->header->msg;
1199 f1509490 2022-03-20 martijn static EVP_MD_CTX *bctx = NULL;
1200 338537df 2022-04-08 martijn char digest[EVP_MAX_MD_SIZE];
1201 338537df 2022-04-08 martijn unsigned int digestsz;
1202 4e2dd90f 2022-03-26 martijn const char *end;
1203 f1509490 2022-03-20 martijn size_t i, header;
1205 f1509490 2022-03-20 martijn if (sig->state != DKIM_UNKNOWN)
1206 f1509490 2022-03-20 martijn return;
1208 f1509490 2022-03-20 martijn if (bctx == NULL) {
1209 b64f38ad 2025-06-02 kirill if ((bctx = EVP_MD_CTX_new()) == NULL)
1210 b64f38ad 2025-06-02 kirill osmtpd_err(1, "EVP_MD_CTX_new");
1212 8015d1bc 2022-03-23 martijn EVP_MD_CTX_reset(bctx);
1213 338537df 2022-04-08 martijn if (!sig->sephash) {
1214 338537df 2022-04-08 martijn if (EVP_DigestVerifyInit(bctx, NULL, sig->ah, NULL,
1215 b64f38ad 2025-06-02 kirill sig->p) != 1) {
1216 b64f38ad 2025-06-02 kirill dkim_signature_state(sig, DKIM_FAIL, "ah tag");
1217 338537df 2022-04-08 martijn return;
1219 338537df 2022-04-08 martijn } else {
1220 338537df 2022-04-08 martijn if (EVP_DigestInit_ex(bctx, sig->ah, NULL) != 1) {
1221 b64f38ad 2025-06-02 kirill dkim_signature_state(sig, DKIM_FAIL, "ah tag");
1222 338537df 2022-04-08 martijn return;
1226 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++)
1227 f1509490 2022-03-20 martijn msg->header[i].parsed = 0;
1229 f1509490 2022-03-20 martijn for (header = 0; sig->h[header] != NULL; header++) {
1230 f1509490 2022-03-20 martijn for (i = msg->nheaders; i > 0; ) {
1232 f1509490 2022-03-20 martijn if (msg->header[i].parsed ||
1233 f1509490 2022-03-20 martijn strncasecmp(msg->header[i].buf, sig->h[header],
1234 f1509490 2022-03-20 martijn strlen(sig->h[header])) != 0 ||
1235 f1509490 2022-03-20 martijn msg->header[i].sig == sig)
1236 f1509490 2022-03-20 martijn continue;
1237 4e2dd90f 2022-03-26 martijn end = osmtpd_ltok_skip_fws(
1238 f1509490 2022-03-20 martijn msg->header[i].buf + strlen(sig->h[header]), 1);
1239 f1509490 2022-03-20 martijn if (end[0] != ':')
1240 f1509490 2022-03-20 martijn continue;
1241 f1509490 2022-03-20 martijn dkim_signature_header(bctx, sig, &(msg->header[i]));
1242 f1509490 2022-03-20 martijn msg->header[i].parsed = 1;
1246 f1509490 2022-03-20 martijn dkim_signature_header(bctx, sig, sig->header);
1247 338537df 2022-04-08 martijn if (!sig->sephash) {
1248 338537df 2022-04-08 martijn if (EVP_DigestVerifyFinal(bctx, sig->b, sig->bsz) != 1)
1249 338537df 2022-04-08 martijn dkim_signature_state(sig, DKIM_FAIL, "b mismatch");
1250 338537df 2022-04-08 martijn } else {
1251 b64f38ad 2025-06-02 kirill if (EVP_DigestFinal_ex(bctx, digest, &digestsz) == 0)
1252 b64f38ad 2025-06-02 kirill osmtpd_err(1, "EVP_DigestFinal_ex");
1254 b64f38ad 2025-06-02 kirill if (EVP_DigestVerifyInit(bctx, NULL, NULL, NULL, sig->p) != 1)
1255 b64f38ad 2025-06-02 kirill osmtpd_err(1, "EVP_DigestVerifyInit");
1257 338537df 2022-04-08 martijn switch (EVP_DigestVerify(bctx, sig->b, sig->bsz, digest,
1258 338537df 2022-04-08 martijn digestsz)) {
1259 338537df 2022-04-08 martijn case 1:
1261 338537df 2022-04-08 martijn case 0:
1262 338537df 2022-04-08 martijn dkim_signature_state(sig, DKIM_FAIL, "b mismatch");
1264 338537df 2022-04-08 martijn default:
1265 b64f38ad 2025-06-02 kirill osmtpd_err(1, "EVP_DigestVerify");
1270 338537df 2022-04-08 martijn /* EVP_DigestVerifyUpdate is a macro, so we can't alias this on a variable */
1271 338537df 2022-04-08 martijn #define dkim_b_digest_update(a, b, c) \
1272 338537df 2022-04-08 martijn (sig->sephash ? EVP_DigestUpdate((a), (b), (c)) :\
1273 338537df 2022-04-08 martijn EVP_DigestVerifyUpdate((a), (b), (c)))
1276 0b081549 2025-06-02 kirill dkim_signature_header(EVP_MD_CTX *bctx, struct dkim_signature *sig,
1277 f1509490 2022-03-20 martijn struct header *header)
1279 4e2dd90f 2022-03-26 martijn char c;
1280 4e2dd90f 2022-03-26 martijn const char *ptr = header->buf, *end;
1281 f1509490 2022-03-20 martijn int inhdrname = 1;
1282 f1509490 2022-03-20 martijn int canon = sig->c & CANON_HEADER;
1284 f1509490 2022-03-20 martijn for (ptr = header->buf; ptr[0] != '\0'; ptr++) {
1285 f1509490 2022-03-20 martijn if (inhdrname) {
1286 f1509490 2022-03-20 martijn if (canon == CANON_HEADER_RELAXED) {
1287 4e2dd90f 2022-03-26 martijn ptr = osmtpd_ltok_skip_fws(ptr, 1);
1288 f1509490 2022-03-20 martijn c = tolower(ptr[0]);
1290 f1509490 2022-03-20 martijn c = ptr[0];
1291 f1509490 2022-03-20 martijn if (c == ':') {
1292 f1509490 2022-03-20 martijn inhdrname = 0;
1293 f1509490 2022-03-20 martijn if (canon == CANON_HEADER_RELAXED)
1294 4e2dd90f 2022-03-26 martijn ptr = osmtpd_ltok_skip_fws(
1295 f1509490 2022-03-20 martijn ptr + 1, 1) - 1;
1297 b64f38ad 2025-06-02 kirill if (dkim_b_digest_update(bctx, &c, 1) == 0)
1298 b64f38ad 2025-06-02 kirill osmtpd_errx(1, "dkim_b_digest_update");
1299 f1509490 2022-03-20 martijn continue;
1301 4e2dd90f 2022-03-26 martijn end = osmtpd_ltok_skip_fws(ptr, 1);
1302 f1509490 2022-03-20 martijn if (end == ptr) {
1303 f1509490 2022-03-20 martijn if (sig->header == header && ptr == sig->bheader) {
1304 4e2dd90f 2022-03-26 martijn ptr = osmtpd_ltok_skip_tag_value(
1305 f1509490 2022-03-20 martijn ptr, 0) - 1;
1306 f1509490 2022-03-20 martijn continue;
1308 b64f38ad 2025-06-02 kirill if (dkim_b_digest_update(bctx, ptr, 1) == 0)
1309 b64f38ad 2025-06-02 kirill osmtpd_errx(1, "dkim_b_digest_update");
1310 f1509490 2022-03-20 martijn } else {
1311 f1509490 2022-03-20 martijn if (canon == CANON_HEADER_RELAXED) {
1312 f1509490 2022-03-20 martijn if (end[0] == '\0')
1313 f1509490 2022-03-20 martijn continue;
1314 b64f38ad 2025-06-02 kirill if (dkim_b_digest_update(bctx, " ", 1) == 0)
1315 b64f38ad 2025-06-02 kirill osmtpd_errx(1, "dkim_b_digest_update");
1316 f1509490 2022-03-20 martijn } else {
1317 338537df 2022-04-08 martijn if (dkim_b_digest_update(bctx, ptr,
1318 b64f38ad 2025-06-02 kirill end - ptr) == 0)
1319 b64f38ad 2025-06-02 kirill osmtpd_errx(1, "dkim_b_digest_update");
1321 28bea7cc 2022-03-21 martijn ptr = end - 1;
1325 f1509490 2022-03-20 martijn if (sig->header != header) {
1326 b64f38ad 2025-06-02 kirill if (dkim_b_digest_update(bctx, "\r\n", 2) == 0)
1327 b64f38ad 2025-06-02 kirill osmtpd_errx(1, "dkim_b_digest_update");
1332 0b081549 2025-06-02 kirill dkim_signature_state(struct dkim_signature *sig, enum dkim_state state,
1333 f1509490 2022-03-20 martijn const char *reason)
1335 f1509490 2022-03-20 martijn if (sig->query != NULL) {
1336 f1509490 2022-03-20 martijn event_asr_abort(sig->query);
1337 f1509490 2022-03-20 martijn sig->query = NULL;
1338 40991a5f 2025-06-02 kirill sig->header->msg->nqueries--;
1340 f1509490 2022-03-20 martijn switch (sig->state) {
1341 f1509490 2022-03-20 martijn case DKIM_UNKNOWN:
1343 f1509490 2022-03-20 martijn case DKIM_PASS:
1344 f1509490 2022-03-20 martijn case DKIM_FAIL:
1345 f1509490 2022-03-20 martijn osmtpd_errx(1, "Unexpected transition");
1346 f1509490 2022-03-20 martijn case DKIM_POLICY:
1347 f1509490 2022-03-20 martijn if (state == DKIM_PASS)
1348 f1509490 2022-03-20 martijn return;
1350 f1509490 2022-03-20 martijn case DKIM_NEUTRAL:
1351 f1509490 2022-03-20 martijn if (state == DKIM_PASS)
1352 f1509490 2022-03-20 martijn return;
1353 f1509490 2022-03-20 martijn if (state == DKIM_TEMPERROR || state == DKIM_PERMERROR)
1355 f1509490 2022-03-20 martijn osmtpd_errx(1, "Unexpected transition");
1356 f1509490 2022-03-20 martijn case DKIM_TEMPERROR:
1357 f1509490 2022-03-20 martijn if (state == DKIM_PERMERROR)
1359 f1509490 2022-03-20 martijn return;
1360 f1509490 2022-03-20 martijn case DKIM_PERMERROR:
1361 f1509490 2022-03-20 martijn return;
1363 f1509490 2022-03-20 martijn sig->state = state;
1364 f1509490 2022-03-20 martijn sig->state_reason = reason;
1367 f1509490 2022-03-20 martijn const char *
1368 0b081549 2025-06-02 kirill dkim_state2str(enum dkim_state state)
1370 f1509490 2022-03-20 martijn switch (state)
1372 f1509490 2022-03-20 martijn case DKIM_UNKNOWN:
1373 f1509490 2022-03-20 martijn return "unknown";
1374 f1509490 2022-03-20 martijn case DKIM_PASS:
1375 f1509490 2022-03-20 martijn return "pass";
1376 f1509490 2022-03-20 martijn case DKIM_FAIL:
1377 f1509490 2022-03-20 martijn return "fail";
1378 f1509490 2022-03-20 martijn case DKIM_POLICY:
1379 f1509490 2022-03-20 martijn return "policy";
1380 f1509490 2022-03-20 martijn case DKIM_NEUTRAL:
1381 f1509490 2022-03-20 martijn return "neutral";
1382 f1509490 2022-03-20 martijn case DKIM_TEMPERROR:
1383 f1509490 2022-03-20 martijn return "temperror";
1384 f1509490 2022-03-20 martijn case DKIM_PERMERROR:
1385 f1509490 2022-03-20 martijn return "permerror";
1390 f1509490 2022-03-20 martijn dkim_rr_resolve(struct asr_result *ar, void *arg)
1392 0b081549 2025-06-02 kirill struct dkim_signature *sig = arg;
1393 f1509490 2022-03-20 martijn char key[UINT16_MAX + 1];
1394 f1509490 2022-03-20 martijn const char *rr_txt;
1395 f1509490 2022-03-20 martijn size_t keylen, cstrlen;
1396 f1509490 2022-03-20 martijn struct unpack pack;
1397 f1509490 2022-03-20 martijn struct dns_header h;
1398 f1509490 2022-03-20 martijn struct dns_query q;
1399 f1509490 2022-03-20 martijn struct dns_rr rr;
1400 3cb7796f 2025-06-02 kirill char buf[HOST_NAME_MAX + 1];
1402 f1509490 2022-03-20 martijn sig->query = NULL;
1403 40991a5f 2025-06-02 kirill sig->header->msg->nqueries--;
1405 ede84c7b 2025-06-02 kirill if (sig->state != DKIM_UNKNOWN)
1406 ede84c7b 2025-06-02 kirill goto verify;
1408 f1509490 2022-03-20 martijn if (ar->ar_h_errno == TRY_AGAIN || ar->ar_h_errno == NO_RECOVERY) {
1409 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_TEMPERROR,
1410 f1509490 2022-03-20 martijn hstrerror(ar->ar_h_errno));
1411 1c524edb 2022-03-24 martijn goto verify;
1413 6d309264 2025-06-02 kirill if (ar->ar_h_errno == HOST_NOT_FOUND) {
1414 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR,
1415 f1509490 2022-03-20 martijn hstrerror(ar->ar_h_errno));
1416 1c524edb 2022-03-24 martijn goto verify;
1419 f1509490 2022-03-20 martijn unpack_init(&pack, ar->ar_data, ar->ar_datalen);
1420 f1509490 2022-03-20 martijn if (unpack_header(&pack, &h) != 0 ||
1421 f1509490 2022-03-20 martijn unpack_query(&pack, &q) != 0) {
1422 3cb7796f 2025-06-02 kirill osmtpd_warn(sig->header->msg->ctx,
1423 3cb7796f 2025-06-02 kirill "Mallformed DKIM DNS response for domain %s: %s",
1424 3cb7796f 2025-06-02 kirill print_dname(q.q_dname, buf, sizeof(buf)),
1425 3cb7796f 2025-06-02 kirill pack.err);
1426 3cb7796f 2025-06-02 kirill dkim_signature_state(sig, DKIM_PERMERROR, pack.err);
1427 1c524edb 2022-03-24 martijn goto verify;
1430 f1509490 2022-03-20 martijn for (; h.ancount > 0; h.ancount--) {
1431 3cb7796f 2025-06-02 kirill if (unpack_rr(&pack, &rr) != 0) {
1432 3cb7796f 2025-06-02 kirill osmtpd_warn(sig->header->msg->ctx,
1433 3cb7796f 2025-06-02 kirill "Mallformed DKIM DNS record for domain %s: %s",
1434 3cb7796f 2025-06-02 kirill print_dname(q.q_dname, buf, sizeof(buf)),
1435 3cb7796f 2025-06-02 kirill pack.err);
1436 f1509490 2022-03-20 martijn continue;
1439 3cb7796f 2025-06-02 kirill /* If we below limit, follow CNAME*/
1440 3cb7796f 2025-06-02 kirill if (rr.rr_type == T_CNAME &&
1441 3cb7796f 2025-06-02 kirill sig->nqueries < DKIM_LOOKUP_LOOKUP_LIMIT ) {
1442 3cb7796f 2025-06-02 kirill print_dname(rr.rr.cname.cname, buf, sizeof(buf));
1443 3cb7796f 2025-06-02 kirill dkim_lookup_record(sig, buf);
1444 3cb7796f 2025-06-02 kirill free(ar->ar_data);
1448 3cb7796f 2025-06-02 kirill if (rr.rr_type != T_TXT) {
1449 3cb7796f 2025-06-02 kirill osmtpd_warn(sig->header->msg->ctx,
1450 3cb7796f 2025-06-02 kirill "Unexpected DKIM DNS record: %d for domain %s",
1451 3cb7796f 2025-06-02 kirill rr.rr_type,
1452 3cb7796f 2025-06-02 kirill print_dname(q.q_dname, buf, sizeof(buf)));
1453 3cb7796f 2025-06-02 kirill continue;
1456 f1509490 2022-03-20 martijn keylen = 0;
1457 f1509490 2022-03-20 martijn rr_txt = rr.rr.other.rdata;
1458 f1509490 2022-03-20 martijn while (rr.rr.other.rdlen > 0) {
1459 f1509490 2022-03-20 martijn cstrlen = ((const unsigned char *)rr_txt)[0];
1460 f1509490 2022-03-20 martijn if (cstrlen >= rr.rr.other.rdlen ||
1461 f1509490 2022-03-20 martijn keylen + cstrlen >= sizeof(key))
1464 f1509490 2022-03-20 martijn * RFC 6376 Section 3.6.2.2
1465 f1509490 2022-03-20 martijn * Strings in a TXT RR MUST be concatenated together
1466 f1509490 2022-03-20 martijn * before use with no intervening whitespace.
1468 f1509490 2022-03-20 martijn strlcpy(key + keylen, rr_txt + 1, cstrlen + 1);
1469 f1509490 2022-03-20 martijn rr.rr.other.rdlen -= (cstrlen + 1);
1470 f1509490 2022-03-20 martijn rr_txt += (cstrlen + 1);
1471 f1509490 2022-03-20 martijn keylen += cstrlen;
1473 f1509490 2022-03-20 martijn if (rr.rr.other.rdlen > 0) /* Invalid TXT RDATA */
1474 f1509490 2022-03-20 martijn continue;
1476 f1509490 2022-03-20 martijn if (dkim_key_text_parse(sig, key))
1480 f1509490 2022-03-20 martijn if (h.ancount == 0) {
1481 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR,
1482 f1509490 2022-03-20 martijn "No matching key found");
1483 f1509490 2022-03-20 martijn } else {
1484 f1509490 2022-03-20 martijn /* Only verify if all headers have been read */
1485 f1509490 2022-03-20 martijn if (!sig->header->msg->parsing_headers)
1486 f1509490 2022-03-20 martijn dkim_signature_verify(sig);
1488 1c524edb 2022-03-24 martijn verify:
1489 8986823b 2022-03-29 martijn free(ar->ar_data);
1490 0b081549 2025-06-02 kirill auth_message_verify(sig->header->msg);
1494 0b081549 2025-06-02 kirill dkim_key_text_parse(struct dkim_signature *sig, const char *key)
1496 4e2dd90f 2022-03-26 martijn char tagname, *hashname;
1497 3082f098 2022-04-19 martijn const char *end, *tagvend;
1498 4e2dd90f 2022-03-26 martijn char pkraw[UINT16_MAX] = "", pkimp[UINT16_MAX];
1499 b3380529 2022-04-08 martijn size_t pkrawlen = 0, pkoff, linelen;
1500 b3380529 2022-04-08 martijn int h = 0, k = 0, n = 0, p = 0, s = 0, t = 0, first = 1;
1501 f1509490 2022-03-20 martijn BIO *bio;
1502 72580136 2022-12-16 martijn #ifdef HAVE_ED25519
1503 b3380529 2022-04-08 martijn size_t pklen;
1504 b3380529 2022-04-08 martijn int tmp;
1507 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(key, 1);
1508 f1509490 2022-03-20 martijn /* Validate syntax early */
1509 4e2dd90f 2022-03-26 martijn if ((end = osmtpd_ltok_skip_tag_list(key, 0)) == NULL)
1510 f1509490 2022-03-20 martijn return 0;
1512 f1509490 2022-03-20 martijn while (key[0] != '\0') {
1513 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(key, 1);
1514 d842ff09 2022-03-27 martijn if ((end = osmtpd_ltok_skip_tag_name(key, 0)) == NULL)
1515 d842ff09 2022-03-27 martijn return 0;
1517 bccb8fb3 2022-03-27 martijn if ((size_t)(end - key) != 1)
1518 bccb8fb3 2022-03-27 martijn tagname = '\0';
1520 bccb8fb3 2022-03-27 martijn tagname = key[0];
1521 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(end, 1);
1522 f1509490 2022-03-20 martijn /* '=' */
1523 7e24437d 2022-03-27 martijn if (key[0] != '=')
1524 d842ff09 2022-03-27 martijn return 0;
1525 d842ff09 2022-03-27 martijn key = osmtpd_ltok_skip_fws(key + 1, 1);
1526 d842ff09 2022-03-27 martijn if ((end = osmtpd_ltok_skip_tag_value(key, 0)) == NULL)
1527 d842ff09 2022-03-27 martijn return 0;
1528 f1509490 2022-03-20 martijn switch (tagname) {
1529 f1509490 2022-03-20 martijn case 'v':
1531 2b1224a8 2022-03-27 martijn * RFC 6376 section 3.6.1, v=:
1532 2b1224a8 2022-03-27 martijn * RECOMMENDED...This tag MUST be the first tag in the
1533 2b1224a8 2022-03-27 martijn * record.
1535 2b1224a8 2022-03-27 martijn if (!first ||
1536 2b1224a8 2022-03-27 martijn osmtpd_ltok_skip_key_v_tag_value(key, 0) != end)
1537 2b1224a8 2022-03-27 martijn return 0;
1538 6eeac6a1 2022-03-27 martijn key = end;
1540 f1509490 2022-03-20 martijn case 'h':
1541 f1509490 2022-03-20 martijn if (h != 0) /* Duplicate tag */
1542 f1509490 2022-03-20 martijn return 0;
1543 f1509490 2022-03-20 martijn /* Invalid tag value */
1544 475eae65 2022-03-27 martijn if (osmtpd_ltok_skip_key_h_tag_value(key, 0) != end)
1545 f1509490 2022-03-20 martijn return 0;
1546 f1509490 2022-03-20 martijn while (1) {
1547 475eae65 2022-03-27 martijn if ((tagvend = osmtpd_ltok_skip_key_h_tag_alg(
1548 048fb98b 2022-03-21 martijn key, 0)) == NULL)
1550 4e2dd90f 2022-03-26 martijn hashname = strndup(key, tagvend - key);
1551 b64f38ad 2025-06-02 kirill if (hashname == NULL)
1552 b64f38ad 2025-06-02 kirill osmtpd_err(1, "strndup");
1553 4e2dd90f 2022-03-26 martijn if (EVP_get_digestbyname(hashname) == sig->ah) {
1554 4e2dd90f 2022-03-26 martijn free(hashname);
1558 4e2dd90f 2022-03-26 martijn free(hashname);
1559 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(tagvend, 1);
1560 f1509490 2022-03-20 martijn if (key[0] != ':')
1562 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(key + 1, 1);
1564 f1509490 2022-03-20 martijn if (h != 1)
1565 f1509490 2022-03-20 martijn return 0;
1566 f1509490 2022-03-20 martijn key = end;
1568 f1509490 2022-03-20 martijn case 'k':
1569 f1509490 2022-03-20 martijn if (k != 0) /* Duplicate tag */
1570 f1509490 2022-03-20 martijn return 0;
1572 338537df 2022-04-08 martijn if (strncmp(key, "rsa", end - key) == 0) {
1573 338537df 2022-04-08 martijn if (sig->ak != EVP_PKEY_RSA)
1574 338537df 2022-04-08 martijn return 0;
1575 72580136 2022-12-16 martijn #if HAVE_ED25519
1576 338537df 2022-04-08 martijn } else if (strncmp(key, "ed25519", end - key) == 0) {
1577 338537df 2022-04-08 martijn if (sig->ak != EVP_PKEY_ED25519)
1578 338537df 2022-04-08 martijn return 0;
1581 f1509490 2022-03-20 martijn return 0;
1582 f1509490 2022-03-20 martijn key = end;
1584 f1509490 2022-03-20 martijn case 'n':
1585 f1509490 2022-03-20 martijn if (n != 0) /* Duplicate tag */
1586 f1509490 2022-03-20 martijn return 0;
1588 749b2bf7 2022-03-28 martijn /* semicolon is part of safe-char */
1589 749b2bf7 2022-03-28 martijn if (osmtpd_ltok_skip_key_n_tag_value(key, 0) < end)
1590 4e2dd90f 2022-03-26 martijn return 0;
1591 f1509490 2022-03-20 martijn key = end;
1593 f1509490 2022-03-20 martijn case 'p':
1594 62823db4 2022-04-08 martijn if (p != 0) /* Duplicate tag */
1595 f1509490 2022-03-20 martijn return 0;
1597 338537df 2022-04-08 martijn while (1) {
1598 3082f098 2022-04-19 martijn key = osmtpd_ltok_skip_fws(key, 1);
1599 338537df 2022-04-08 martijn if (osmtpd_ltok_skip_alphadigitps(
1600 3082f098 2022-04-19 martijn key, 0) == NULL)
1602 3082f098 2022-04-19 martijn pkraw[pkrawlen++] = key++[0];
1603 338537df 2022-04-08 martijn if (pkrawlen >= sizeof(pkraw))
1604 338537df 2022-04-08 martijn return 0;
1606 3082f098 2022-04-19 martijn if (key[0] == '=') {
1607 338537df 2022-04-08 martijn pkraw[pkrawlen++] = '=';
1608 3082f098 2022-04-19 martijn key = osmtpd_ltok_skip_fws(key + 1, 1);
1609 338537df 2022-04-08 martijn if (pkrawlen >= sizeof(pkraw))
1610 338537df 2022-04-08 martijn return 0;
1611 3082f098 2022-04-19 martijn if (key[0] == '=') {
1612 338537df 2022-04-08 martijn pkraw[pkrawlen++] = '=';
1614 338537df 2022-04-08 martijn if (pkrawlen >= sizeof(pkraw))
1615 338537df 2022-04-08 martijn return 0;
1618 f1509490 2022-03-20 martijn /* Invalid tag value */
1619 3082f098 2022-04-19 martijn if (pkrawlen % 4 != 0 || key != end)
1620 f1509490 2022-03-20 martijn return 0;
1622 f1509490 2022-03-20 martijn case 's':
1623 f1509490 2022-03-20 martijn if (s != 0) /* Duplicate tag */
1624 f1509490 2022-03-20 martijn return 0;
1625 f1509490 2022-03-20 martijn /* Invalid tag value */
1626 2e9f7a5e 2022-03-27 martijn if (osmtpd_ltok_skip_key_s_tag_value(key, 0) != end)
1627 f1509490 2022-03-20 martijn return 0;
1628 f1509490 2022-03-20 martijn while (1) {
1629 f1509490 2022-03-20 martijn if ((tagvend =
1630 4e2dd90f 2022-03-26 martijn osmtpd_ltok_skip_key_s_tag_type(
1631 71b63ce6 2022-03-22 martijn key, 0)) == NULL)
1633 f1509490 2022-03-20 martijn if (strncmp(key, "*", tagvend - key) == 0 ||
1634 f1509490 2022-03-20 martijn strncmp(key, "email", tagvend - key) == 0) {
1638 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(tagvend, 1);
1639 f1509490 2022-03-20 martijn if (key[0] != ':')
1641 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(key + 1, 1);
1643 f1509490 2022-03-20 martijn if (s != 1)
1644 f1509490 2022-03-20 martijn return 0;
1645 f1509490 2022-03-20 martijn key = end;
1647 f1509490 2022-03-20 martijn case 't':
1648 f1509490 2022-03-20 martijn if (t != 0) /* Duplicate tag */
1649 f1509490 2022-03-20 martijn return 0;
1651 2e9f7a5e 2022-03-27 martijn if (osmtpd_ltok_skip_key_t_tag_value(key, 0) != end)
1652 f1509490 2022-03-20 martijn return 0;
1653 f1509490 2022-03-20 martijn while (1) {
1654 2e9f7a5e 2022-03-27 martijn tagvend = osmtpd_ltok_skip_key_t_tag_flag(
1655 f1509490 2022-03-20 martijn key, 0);
1656 f1509490 2022-03-20 martijn if (strncmp(key, "y", tagvend - key) == 0)
1657 f1509490 2022-03-20 martijn sig->kt |= KT_Y;
1658 f1509490 2022-03-20 martijn else if (strncmp(key, "s", tagvend - key) == 0)
1659 f1509490 2022-03-20 martijn sig->kt |= KT_S;
1660 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(tagvend, 1);
1661 f1509490 2022-03-20 martijn if (key[0] != ':')
1663 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(key + 1, 1);
1666 f1509490 2022-03-20 martijn default:
1667 f1509490 2022-03-20 martijn key = end;
1671 2b1224a8 2022-03-27 martijn first = 0;
1672 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(key, 1);
1673 f1509490 2022-03-20 martijn if (key[0] == ';')
1675 f1509490 2022-03-20 martijn else if (key[0] != '\0')
1676 f1509490 2022-03-20 martijn return 0;
1679 62823db4 2022-04-08 martijn if (!p) /* Missing tag */
1680 f1509490 2022-03-20 martijn return 0;
1681 f1509490 2022-03-20 martijn if (k == 0 && sig->ak != EVP_PKEY_RSA) /* Default to RSA */
1682 f1509490 2022-03-20 martijn return 0;
1684 f1509490 2022-03-20 martijn if (pkraw[0] == '\0') {
1685 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Key is revoked");
1686 f1509490 2022-03-20 martijn return 1;
1689 f1509490 2022-03-20 martijn switch (sig->ak) {
1690 f1509490 2022-03-20 martijn case EVP_PKEY_RSA:
1691 f1509490 2022-03-20 martijn pkoff = strlcpy(pkimp, "-----BEGIN PUBLIC KEY-----\n",
1692 f1509490 2022-03-20 martijn sizeof(pkimp));
1693 f1509490 2022-03-20 martijn linelen = 0;
1694 f1509490 2022-03-20 martijn for (key = pkraw; key[0] != '\0';) {
1695 f1509490 2022-03-20 martijn if (pkoff + 2 >= sizeof(pkimp))
1696 f1509490 2022-03-20 martijn return 0;
1697 2732fb68 2022-04-08 martijn pkimp[pkoff++] = key++[0];
1698 f1509490 2022-03-20 martijn if (++linelen == 64) {
1699 f1509490 2022-03-20 martijn pkimp[pkoff++] = '\n';
1700 f1509490 2022-03-20 martijn linelen = 0;
1703 f1509490 2022-03-20 martijn /* Leverage pkoff check in loop */
1704 f1509490 2022-03-20 martijn if (linelen != 0)
1705 f1509490 2022-03-20 martijn pkimp[pkoff++] = '\n';
1706 f1509490 2022-03-20 martijn /* PEM_read_bio_PUBKEY will catch truncated keys */
1707 f1509490 2022-03-20 martijn pkoff += strlcpy(pkimp + pkoff, "-----END PUBLIC KEY-----\n",
1708 f1509490 2022-03-20 martijn sizeof(pkimp) - pkoff);
1709 b64f38ad 2025-06-02 kirill if ((bio = BIO_new_mem_buf(pkimp, pkoff)) == NULL)
1710 b64f38ad 2025-06-02 kirill osmtpd_err(1, "BIO_new_mem_buf");
1711 f1509490 2022-03-20 martijn sig->p = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
1712 f1509490 2022-03-20 martijn BIO_free(bio);
1714 72580136 2022-12-16 martijn #if HAVE_ED25519
1715 338537df 2022-04-08 martijn case EVP_PKEY_ED25519:
1716 338537df 2022-04-08 martijn if ((pkrawlen / 4) * 3 >= sizeof(pkimp))
1717 f1509490 2022-03-20 martijn return 0;
1718 338537df 2022-04-08 martijn EVP_DecodeInit(ectx);
1719 338537df 2022-04-08 martijn if (EVP_DecodeUpdate(ectx, pkimp, &tmp, pkraw, pkrawlen) == -1)
1720 338537df 2022-04-08 martijn return 0;
1721 338537df 2022-04-08 martijn pklen = tmp;
1722 338537df 2022-04-08 martijn if (EVP_DecodeFinal(ectx, pkimp, &tmp) == -1)
1723 338537df 2022-04-08 martijn return 0;
1724 338537df 2022-04-08 martijn pklen += tmp;
1725 338537df 2022-04-08 martijn sig->p = EVP_PKEY_new_raw_public_key(sig->ak, NULL, pkimp,
1726 338537df 2022-04-08 martijn pklen);
1730 338537df 2022-04-08 martijn if (sig->p == NULL) {
1732 338537df 2022-04-08 martijn * XXX No clue how to differentiate between invalid key and
1733 338537df 2022-04-08 martijn * temporary failure like *alloc.
1734 338537df 2022-04-08 martijn * Assume invalid key, because it's more likely.
1736 338537df 2022-04-08 martijn return 0;
1738 f1509490 2022-03-20 martijn return 1;
1742 f1509490 2022-03-20 martijn dkim_body_parse(struct message *msg, const char *line)
1744 0b081549 2025-06-02 kirill struct dkim_signature *sig;
1745 31ffb916 2022-03-27 martijn const char *end = line, *hash, *prev;
1746 31ffb916 2022-03-27 martijn size_t hashn, len, i;
1747 31ffb916 2022-03-27 martijn int wsp, ret;
1749 31ffb916 2022-03-27 martijn if (line[0] == '\0') {
1750 f1509490 2022-03-20 martijn msg->body_whitelines++;
1751 f1509490 2022-03-20 martijn return;
1754 f1509490 2022-03-20 martijn while (msg->body_whitelines-- > 0) {
1755 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++) {
1756 5d58b443 2022-03-27 martijn if ((sig = msg->header[i].sig) == NULL ||
1757 5d58b443 2022-03-27 martijn sig->state != DKIM_UNKNOWN)
1758 f1509490 2022-03-20 martijn continue;
1759 31ffb916 2022-03-27 martijn hashn = sig->l == -1 ? 2 : MIN(2, sig->l);
1760 31ffb916 2022-03-27 martijn sig->l -= sig->l == -1 ? 0 : hashn;
1761 b64f38ad 2025-06-02 kirill if (EVP_DigestUpdate(sig->bhctx, "\r\n", hashn) == 0)
1762 b64f38ad 2025-06-02 kirill osmtpd_errx(1, "EVP_DigestUpdate");
1765 f1509490 2022-03-20 martijn msg->body_whitelines = 0;
1766 f1509490 2022-03-20 martijn msg->has_body = 1;
1768 31ffb916 2022-03-27 martijn while (line[0] != '\0') {
1769 31ffb916 2022-03-27 martijn while (1) {
1770 31ffb916 2022-03-27 martijn prev = end;
1771 31ffb916 2022-03-27 martijn if ((end = osmtpd_ltok_skip_wsp(end, 0)) == NULL)
1774 31ffb916 2022-03-27 martijn end = prev;
1775 31ffb916 2022-03-27 martijn wsp = end != line;
1776 31ffb916 2022-03-27 martijn if (!wsp) {
1777 31ffb916 2022-03-27 martijn while (osmtpd_ltok_skip_wsp(end, 0) == NULL &&
1778 31ffb916 2022-03-27 martijn end[0] != '\0')
1781 31ffb916 2022-03-27 martijn for (i = 0; i < msg->nheaders; i++) {
1782 31ffb916 2022-03-27 martijn sig = msg->header[i].sig;
1783 31ffb916 2022-03-27 martijn if (sig == NULL || sig->state != DKIM_UNKNOWN)
1784 31ffb916 2022-03-27 martijn continue;
1785 31ffb916 2022-03-27 martijn if (wsp &&
1786 31ffb916 2022-03-27 martijn (sig->c & CANON_BODY) == CANON_BODY_RELAXED) {
1787 31ffb916 2022-03-27 martijn hash = " ";
1788 31ffb916 2022-03-27 martijn len = end[0] == '\0' ? 0 : 1;
1789 31ffb916 2022-03-27 martijn } else {
1790 31ffb916 2022-03-27 martijn hash = line;
1791 31ffb916 2022-03-27 martijn len = (size_t)(end - line);
1793 31ffb916 2022-03-27 martijn hashn = sig->l == -1 ? len : MIN(len, (size_t)sig->l);
1794 31ffb916 2022-03-27 martijn sig->l -= sig->l == -1 ? 0 : hashn;
1795 31ffb916 2022-03-27 martijn ret = EVP_DigestUpdate(sig->bhctx, hash, hashn);
1796 b64f38ad 2025-06-02 kirill if (ret == 0)
1797 b64f38ad 2025-06-02 kirill osmtpd_err(1, "EVP_DigestUpdate");
1799 31ffb916 2022-03-27 martijn line = end;
1801 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++) {
1802 f1509490 2022-03-20 martijn sig = msg->header[i].sig;
1803 31ffb916 2022-03-27 martijn if (sig == NULL || sig->state != DKIM_UNKNOWN)
1804 f1509490 2022-03-20 martijn continue;
1805 31ffb916 2022-03-27 martijn hashn = sig->l == -1 ? 2 : MIN(2, sig->l);
1806 f1509490 2022-03-20 martijn sig->l -= sig->l == -1 ? 0 : hashn;
1807 31ffb916 2022-03-27 martijn ret = EVP_DigestUpdate(sig->bhctx, "\r\n", hashn);
1808 b64f38ad 2025-06-02 kirill if (ret == 0)
1809 b64f38ad 2025-06-02 kirill osmtpd_err(1, "EVP_DigestUpdate");
1814 0b081549 2025-06-02 kirill dkim_body_verify(struct dkim_signature *sig)
1816 f1509490 2022-03-20 martijn unsigned char digest[EVP_MAX_MD_SIZE];
1817 f1509490 2022-03-20 martijn unsigned int digestsz;
1819 f1509490 2022-03-20 martijn if (sig->state != DKIM_UNKNOWN)
1820 f1509490 2022-03-20 martijn return;
1822 f1509490 2022-03-20 martijn if ((sig->c & CANON_BODY) == CANON_BODY_SIMPLE &&
1823 f1509490 2022-03-20 martijn !sig->header->msg->has_body) {
1824 f1509490 2022-03-20 martijn if (EVP_DigestUpdate(sig->bhctx, "\r\n",
1825 b64f38ad 2025-06-02 kirill sig->l == -1 ? 2 : MIN(2, sig->l)) <= 0)
1826 b64f38ad 2025-06-02 kirill osmtpd_errx(1, "EVP_DigestUpdate");
1828 a9f7aa12 2022-03-24 martijn if (sig->l > 0) {
1829 a9f7aa12 2022-03-24 martijn dkim_signature_state(sig, DKIM_PERMERROR,
1830 a9f7aa12 2022-03-24 martijn "l tag larger than body");
1831 a9f7aa12 2022-03-24 martijn return;
1834 b64f38ad 2025-06-02 kirill if (EVP_DigestFinal_ex(sig->bhctx, digest, &digestsz) == 0)
1835 b64f38ad 2025-06-02 kirill osmtpd_err(1, "EVP_DigestFinal_ex");
1837 f1509490 2022-03-20 martijn if (digestsz != sig->bhsz || memcmp(digest, sig->bh, digestsz) != 0)
1838 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_FAIL, "bh mismatch");
1841 a9b0d8da 2025-06-02 kirill const char *
1842 a9b0d8da 2025-06-02 kirill iprev_state2str(enum iprev_state state)
1844 a9b0d8da 2025-06-02 kirill switch (state)
1846 a9b0d8da 2025-06-02 kirill case IPREV_NONE:
1847 a9b0d8da 2025-06-02 kirill return "none";
1848 a9b0d8da 2025-06-02 kirill case IPREV_PASS:
1849 a9b0d8da 2025-06-02 kirill return "pass";
1850 a9b0d8da 2025-06-02 kirill case IPREV_FAIL:
1851 40991a5f 2025-06-02 kirill return "fail";
1856 40991a5f 2025-06-02 kirill spf_evaluate_domain(struct spf_record *spf, const char *domain)
1858 40991a5f 2025-06-02 kirill struct session *ses = spf->ctx->local_session;
1860 40991a5f 2025-06-02 kirill char spec[HOST_NAME_MAX + 1];
1861 40991a5f 2025-06-02 kirill char macro[HOST_NAME_MAX + 1], smacro[sizeof(macro)];
1862 40991a5f 2025-06-02 kirill char delimiters[sizeof(".-+,/_=")];
1863 40991a5f 2025-06-02 kirill char *endptr, *tmp;
1864 40991a5f 2025-06-02 kirill const u_char *addr;
1865 40991a5f 2025-06-02 kirill size_t i, mlen;
1866 40991a5f 2025-06-02 kirill long digits;
1867 40991a5f 2025-06-02 kirill int reverse;
1869 40991a5f 2025-06-02 kirill if (domain == NULL || domain[0] == '\0') {
1870 40991a5f 2025-06-02 kirill spf_done(spf, SPF_PERMERROR, "Empty domain");
1871 40991a5f 2025-06-02 kirill return NULL;
1874 40991a5f 2025-06-02 kirill for (i = 0;
1875 40991a5f 2025-06-02 kirill domain[0] != ' ' && domain[0] != '\0' && i < sizeof(spec);
1876 40991a5f 2025-06-02 kirill domain++) {
1878 40991a5f 2025-06-02 kirill if (domain[0] < 0x21 || domain[0] > 0x7e) {
1879 40991a5f 2025-06-02 kirill spf_done(
1880 40991a5f 2025-06-02 kirill spf, SPF_PERMERROR, "Invalid character in domain-spec");
1881 40991a5f 2025-06-02 kirill return NULL;
1884 40991a5f 2025-06-02 kirill if (domain[0] != '%') {
1885 40991a5f 2025-06-02 kirill spec[i++] = domain[0];
1886 40991a5f 2025-06-02 kirill continue;
1888 40991a5f 2025-06-02 kirill domain++;
1890 40991a5f 2025-06-02 kirill switch (domain[0]) {
1891 40991a5f 2025-06-02 kirill case '%':
1892 40991a5f 2025-06-02 kirill spec[i++] = '%';
1894 40991a5f 2025-06-02 kirill case '_':
1895 40991a5f 2025-06-02 kirill spec[i++] = ' ';
1897 40991a5f 2025-06-02 kirill case '-':
1898 40991a5f 2025-06-02 kirill if (i + 3 >= sizeof(spec)) {
1899 40991a5f 2025-06-02 kirill spf_done(
1900 40991a5f 2025-06-02 kirill spf, SPF_PERMERROR, "domain-spec too large");
1901 40991a5f 2025-06-02 kirill return NULL;
1904 40991a5f 2025-06-02 kirill spec[i++] = '%';
1905 40991a5f 2025-06-02 kirill spec[i++] = '2';
1906 40991a5f 2025-06-02 kirill spec[i++] = '0';
1908 40991a5f 2025-06-02 kirill case '{':
1909 40991a5f 2025-06-02 kirill domain++;
1910 40991a5f 2025-06-02 kirill digits = -1;
1911 40991a5f 2025-06-02 kirill reverse = 0;
1912 40991a5f 2025-06-02 kirill delimiters[0] = '\0';
1914 40991a5f 2025-06-02 kirill switch (domain[0]) {
1915 40991a5f 2025-06-02 kirill case 'S':
1916 40991a5f 2025-06-02 kirill case 's':
1917 40991a5f 2025-06-02 kirill mlen = (size_t) snprintf(macro, sizeof(macro),
1918 40991a5f 2025-06-02 kirill "%s@%s", spf->sender_local,
1919 40991a5f 2025-06-02 kirill spf->sender_domain);
1921 40991a5f 2025-06-02 kirill case 'L':
1922 40991a5f 2025-06-02 kirill case 'l':
1923 40991a5f 2025-06-02 kirill mlen = strlcpy(macro,
1924 40991a5f 2025-06-02 kirill spf->sender_local, sizeof(macro));
1926 40991a5f 2025-06-02 kirill case 'O':
1927 40991a5f 2025-06-02 kirill case 'o':
1928 40991a5f 2025-06-02 kirill mlen = strlcpy(macro,
1929 40991a5f 2025-06-02 kirill spf->sender_domain,
1930 40991a5f 2025-06-02 kirill sizeof(macro));
1932 40991a5f 2025-06-02 kirill case 'D':
1933 40991a5f 2025-06-02 kirill case 'd':
1934 40991a5f 2025-06-02 kirill if (spf->nqueries < 1) {
1935 40991a5f 2025-06-02 kirill spf_done(spf, SPF_PERMERROR,
1936 40991a5f 2025-06-02 kirill "no domain for d macro");
1937 40991a5f 2025-06-02 kirill return NULL;
1939 40991a5f 2025-06-02 kirill mlen = strlcpy(macro,
1940 40991a5f 2025-06-02 kirill spf->queries[spf->nqueries - 1].domain,
1941 40991a5f 2025-06-02 kirill sizeof(macro));
1943 40991a5f 2025-06-02 kirill case 'I':
1944 40991a5f 2025-06-02 kirill case 'i':
1945 40991a5f 2025-06-02 kirill if (ses->src.ss_family == AF_INET) {
1946 40991a5f 2025-06-02 kirill addr = (u_char *)(&((struct sockaddr_in *)
1947 40991a5f 2025-06-02 kirill &(ses->src))->sin_addr);
1948 40991a5f 2025-06-02 kirill mlen = snprintf(macro, sizeof(macro),
1949 40991a5f 2025-06-02 kirill "%u.%u.%u.%u",
1950 40991a5f 2025-06-02 kirill (addr[0] & 0xff), (addr[1] & 0xff),
1951 40991a5f 2025-06-02 kirill (addr[2] & 0xff), (addr[3] & 0xff));
1952 40991a5f 2025-06-02 kirill } else if (ses->src.ss_family == AF_INET6) {
1953 40991a5f 2025-06-02 kirill addr = (u_char *)(&((struct sockaddr_in6 *)
1954 40991a5f 2025-06-02 kirill &(ses->src))->sin6_addr);
1955 40991a5f 2025-06-02 kirill mlen = snprintf(macro, sizeof(macro),
1956 40991a5f 2025-06-02 kirill "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
1957 40991a5f 2025-06-02 kirill "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
1958 40991a5f 2025-06-02 kirill "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx."
1959 40991a5f 2025-06-02 kirill "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx",
1960 40991a5f 2025-06-02 kirill (u_char) ((addr[0] >> 4) & 0x0f), (u_char) (addr[0] & 0x0f),
1961 40991a5f 2025-06-02 kirill (u_char) ((addr[1] >> 4) & 0x0f), (u_char) (addr[1] & 0x0f),
1962 40991a5f 2025-06-02 kirill (u_char) ((addr[2] >> 4) & 0x0f), (u_char) (addr[2] & 0x0f),
1963 40991a5f 2025-06-02 kirill (u_char) ((addr[3] >> 4) & 0x0f), (u_char) (addr[3] & 0x0f),
1964 40991a5f 2025-06-02 kirill (u_char) ((addr[4] >> 4) & 0x0f), (u_char) (addr[4] & 0x0f),
1965 40991a5f 2025-06-02 kirill (u_char) ((addr[5] >> 4) & 0x0f), (u_char) (addr[5] & 0x0f),
1966 40991a5f 2025-06-02 kirill (u_char) ((addr[6] >> 4) & 0x0f), (u_char) (addr[6] & 0x0f),
1967 40991a5f 2025-06-02 kirill (u_char) ((addr[7] >> 4) & 0x0f), (u_char) (addr[7] & 0x0f),
1968 40991a5f 2025-06-02 kirill (u_char) ((addr[8] >> 4) & 0x0f), (u_char) (addr[8] & 0x0f),
1969 40991a5f 2025-06-02 kirill (u_char) ((addr[9] >> 4) & 0x0f), (u_char) (addr[9] & 0x0f),
1970 40991a5f 2025-06-02 kirill (u_char) ((addr[10] >> 4) & 0x0f), (u_char) (addr[10] & 0x0f),
1971 40991a5f 2025-06-02 kirill (u_char) ((addr[11] >> 4) & 0x0f), (u_char) (addr[11] & 0x0f),
1972 40991a5f 2025-06-02 kirill (u_char) ((addr[12] >> 4) & 0x0f), (u_char) (addr[12] & 0x0f),
1973 40991a5f 2025-06-02 kirill (u_char) ((addr[13] >> 4) & 0x0f), (u_char) (addr[13] & 0x0f),
1974 40991a5f 2025-06-02 kirill (u_char) ((addr[14] >> 4) & 0x0f), (u_char) (addr[14] & 0x0f),
1975 40991a5f 2025-06-02 kirill (u_char) ((addr[15] >> 4) & 0x0f), (u_char) (addr[15] & 0x0f));
1976 40991a5f 2025-06-02 kirill } else {
1977 40991a5f 2025-06-02 kirill spf_done(spf, SPF_PERMERROR,
1978 40991a5f 2025-06-02 kirill "unsupported type of address");
1979 40991a5f 2025-06-02 kirill return NULL;
1982 40991a5f 2025-06-02 kirill case 'P':
1983 40991a5f 2025-06-02 kirill case 'p':
1984 40991a5f 2025-06-02 kirill mlen = strlcpy(macro, ses->rdns, sizeof(macro));
1986 40991a5f 2025-06-02 kirill case 'V':
1987 40991a5f 2025-06-02 kirill case 'v':
1988 40991a5f 2025-06-02 kirill if (ses->src.ss_family == AF_INET)
1989 40991a5f 2025-06-02 kirill mlen = strlcpy(macro, "in-addr",
1990 40991a5f 2025-06-02 kirill sizeof(macro));
1991 40991a5f 2025-06-02 kirill else if (ses->src.ss_family == AF_INET6)
1992 40991a5f 2025-06-02 kirill mlen = strlcpy(macro, "ip6",
1993 40991a5f 2025-06-02 kirill sizeof(macro));
1995 40991a5f 2025-06-02 kirill spf_done(spf, SPF_PERMERROR,
1996 40991a5f 2025-06-02 kirill "unsupported type of address");
1997 40991a5f 2025-06-02 kirill return NULL;
2000 40991a5f 2025-06-02 kirill case 'H':
2001 40991a5f 2025-06-02 kirill case 'h':
2002 40991a5f 2025-06-02 kirill mlen = strlcpy(macro, ses->identity,
2003 40991a5f 2025-06-02 kirill sizeof(macro));
2005 40991a5f 2025-06-02 kirill default:
2006 40991a5f 2025-06-02 kirill spf_done(spf, SPF_PERMERROR,
2007 40991a5f 2025-06-02 kirill "Unexpected macro in domain-spec");
2008 40991a5f 2025-06-02 kirill return NULL;
2011 40991a5f 2025-06-02 kirill if (mlen >= sizeof(macro)) {
2012 40991a5f 2025-06-02 kirill spf_done(spf, SPF_PERMERROR,
2013 40991a5f 2025-06-02 kirill "Macro expansions too large");
2014 40991a5f 2025-06-02 kirill return NULL;
2017 40991a5f 2025-06-02 kirill domain++;
2018 40991a5f 2025-06-02 kirill if (isdigit(domain[0])) {
2019 40991a5f 2025-06-02 kirill digits = strtol(domain, &endptr, 10);
2020 40991a5f 2025-06-02 kirill if (digits < 1) {
2021 40991a5f 2025-06-02 kirill spf_done(spf, SPF_PERMERROR,
2022 40991a5f 2025-06-02 kirill "digits in macro can't be 0");
2023 40991a5f 2025-06-02 kirill return NULL;
2025 40991a5f 2025-06-02 kirill domain = endptr;
2028 40991a5f 2025-06-02 kirill if (domain[0] == 'r') {
2029 40991a5f 2025-06-02 kirill domain++;
2030 40991a5f 2025-06-02 kirill reverse = 1;
2033 40991a5f 2025-06-02 kirill for (; strchr(".-+,/_=", domain[0]) != NULL; domain++) {
2034 40991a5f 2025-06-02 kirill if (strchr(delimiters, domain[0]) == NULL) {
2035 40991a5f 2025-06-02 kirill delimiters[strlen(delimiters) + 1] = '\0';
2036 40991a5f 2025-06-02 kirill delimiters[strlen(delimiters)] = domain[0];
2040 40991a5f 2025-06-02 kirill if (delimiters[0] == '\0') {
2041 40991a5f 2025-06-02 kirill delimiters[0] = '.';
2042 40991a5f 2025-06-02 kirill delimiters[1] = '\0';
2045 40991a5f 2025-06-02 kirill if (domain[0] != '}') {
2046 40991a5f 2025-06-02 kirill spf_done(spf, SPF_PERMERROR,
2047 40991a5f 2025-06-02 kirill "Mallformed macro, expected end");
2048 40991a5f 2025-06-02 kirill return NULL;
2051 40991a5f 2025-06-02 kirill if (reverse) {
2052 40991a5f 2025-06-02 kirill smacro[0] = '\0';
2053 40991a5f 2025-06-02 kirill tmp = macro + strlen(macro) - 1;
2055 40991a5f 2025-06-02 kirill * DIGIT rightmost elements after reversal is DIGIT
2056 40991a5f 2025-06-02 kirill * lefmost elements before reversal
2058 40991a5f 2025-06-02 kirill while (1) {
2059 40991a5f 2025-06-02 kirill while (tmp > macro &&
2060 40991a5f 2025-06-02 kirill strchr(delimiters, tmp[0]) == NULL)
2062 40991a5f 2025-06-02 kirill if (tmp == macro)
2064 40991a5f 2025-06-02 kirill if (digits == 0)
2066 40991a5f 2025-06-02 kirill if (digits > 0)
2067 40991a5f 2025-06-02 kirill digits--;
2069 40991a5f 2025-06-02 kirill tmp[0] = '\0';
2070 40991a5f 2025-06-02 kirill if (smacro[0] != '\0')
2071 40991a5f 2025-06-02 kirill strlcat(smacro, ".", sizeof(smacro));
2072 40991a5f 2025-06-02 kirill strlcat(smacro, tmp + 1, sizeof(smacro));
2075 40991a5f 2025-06-02 kirill if (digits != 0) {
2076 40991a5f 2025-06-02 kirill if (smacro[0] != '\0')
2077 40991a5f 2025-06-02 kirill strlcat(smacro, ".", sizeof(smacro));
2078 40991a5f 2025-06-02 kirill strlcat(smacro, macro, sizeof(smacro));
2080 40991a5f 2025-06-02 kirill } else {
2081 40991a5f 2025-06-02 kirill if (digits != -1) {
2082 40991a5f 2025-06-02 kirill tmp = macro;
2083 40991a5f 2025-06-02 kirill endptr = macro + strlen(macro);
2084 40991a5f 2025-06-02 kirill while (digits > 0) {
2085 40991a5f 2025-06-02 kirill while (tmp < endptr &&
2086 40991a5f 2025-06-02 kirill strchr(delimiters, tmp[0]) == NULL)
2088 40991a5f 2025-06-02 kirill if (tmp == endptr)
2090 40991a5f 2025-06-02 kirill if (digits == 1) {
2091 40991a5f 2025-06-02 kirill tmp[0] = '\0';
2094 40991a5f 2025-06-02 kirill digits--;
2098 40991a5f 2025-06-02 kirill strlcpy(smacro, macro, sizeof(smacro));
2101 40991a5f 2025-06-02 kirill spec[i] = '\0';
2102 40991a5f 2025-06-02 kirill i = strlcat(spec, smacro, sizeof(spec));
2103 40991a5f 2025-06-02 kirill if (i >= sizeof(spec)) {
2104 40991a5f 2025-06-02 kirill spf_done(
2105 40991a5f 2025-06-02 kirill spf, SPF_PERMERROR, "domain-spec too large");
2106 40991a5f 2025-06-02 kirill return NULL;
2110 40991a5f 2025-06-02 kirill default:
2111 40991a5f 2025-06-02 kirill spf_done(spf, SPF_PERMERROR,
2112 40991a5f 2025-06-02 kirill "Mallformed macro, unexpected character after %");
2113 40991a5f 2025-06-02 kirill return NULL;
2117 40991a5f 2025-06-02 kirill if ((tmp = strndup(spec, i)) == NULL)
2118 40991a5f 2025-06-02 kirill osmtpd_err(1, "%s: strndup", __func__);
2120 40991a5f 2025-06-02 kirill return tmp;
2124 40991a5f 2025-06-02 kirill spf_lookup_record(struct spf_record *spf, const char *domain, int type,
2125 40991a5f 2025-06-02 kirill enum spf_state qualifier, int include, int exists)
2127 40991a5f 2025-06-02 kirill struct asr_query *aq;
2128 40991a5f 2025-06-02 kirill struct spf_query *query;
2130 40991a5f 2025-06-02 kirill if (spf->done)
2133 40991a5f 2025-06-02 kirill if (spf->nqueries >= SPF_DNS_LOOKUP_LIMIT) {
2134 40991a5f 2025-06-02 kirill spf_done(spf, SPF_PERMERROR, "To many DNS queries");
2138 40991a5f 2025-06-02 kirill query = &spf->queries[spf->nqueries];
2139 40991a5f 2025-06-02 kirill query->spf = spf;
2140 40991a5f 2025-06-02 kirill query->type = type;
2141 40991a5f 2025-06-02 kirill query->q = qualifier;
2142 40991a5f 2025-06-02 kirill query->include = include;
2143 40991a5f 2025-06-02 kirill query->exists = exists;
2144 40991a5f 2025-06-02 kirill query->txt = NULL;
2145 40991a5f 2025-06-02 kirill query->eva = NULL;
2147 40991a5f 2025-06-02 kirill if ((query->domain = spf_evaluate_domain(spf, domain)) == NULL)
2150 40991a5f 2025-06-02 kirill if (domain == NULL || !strlen(domain)) {
2151 40991a5f 2025-06-02 kirill spf_done(spf, SPF_PERMERROR, "Empty domain");
2155 40991a5f 2025-06-02 kirill if ((aq = res_query_async(query->domain, C_IN, type, NULL)) == NULL)
2156 40991a5f 2025-06-02 kirill osmtpd_err(1, "res_query_async");
2158 40991a5f 2025-06-02 kirill if ((query->eva = event_asr_run(aq, spf_resolve, query)) == NULL)
2159 40991a5f 2025-06-02 kirill osmtpd_err(1, "event_asr_run");
2161 40991a5f 2025-06-02 kirill spf->running++;
2162 40991a5f 2025-06-02 kirill spf->nqueries++;
2166 40991a5f 2025-06-02 kirill spf_resolve(struct asr_result *ar, void *arg)
2170 40991a5f 2025-06-02 kirill struct spf_query *query = arg;
2171 40991a5f 2025-06-02 kirill struct spf_record *spf = query->spf;
2172 40991a5f 2025-06-02 kirill struct unpack pack;
2173 40991a5f 2025-06-02 kirill struct dns_header h;
2174 40991a5f 2025-06-02 kirill struct dns_query q;
2175 40991a5f 2025-06-02 kirill struct dns_rr rr;
2176 40991a5f 2025-06-02 kirill char buf[HOST_NAME_MAX + 1];
2178 40991a5f 2025-06-02 kirill query->eva = NULL;
2179 40991a5f 2025-06-02 kirill query->spf->running--;
2181 40991a5f 2025-06-02 kirill if (ar->ar_h_errno == TRY_AGAIN
2182 40991a5f 2025-06-02 kirill || ar->ar_h_errno == NO_RECOVERY) {
2183 40991a5f 2025-06-02 kirill spf_done(query->spf, SPF_TEMPERROR, hstrerror(ar->ar_h_errno));
2184 40991a5f 2025-06-02 kirill goto end;
2187 40991a5f 2025-06-02 kirill if (ar->ar_h_errno == HOST_NOT_FOUND) {
2188 40991a5f 2025-06-02 kirill if (query->include && !query->exists)
2189 40991a5f 2025-06-02 kirill spf_done(query->spf,
2190 40991a5f 2025-06-02 kirill SPF_PERMERROR, hstrerror(ar->ar_h_errno));
2191 40991a5f 2025-06-02 kirill goto consume;
2194 40991a5f 2025-06-02 kirill unpack_init(&pack, ar->ar_data, ar->ar_datalen);
2195 40991a5f 2025-06-02 kirill if (unpack_header(&pack, &h) != 0 ||
2196 40991a5f 2025-06-02 kirill unpack_query(&pack, &q) != 0) {
2197 40991a5f 2025-06-02 kirill osmtpd_warn(query->spf->ctx,
2198 40991a5f 2025-06-02 kirill "Mallformed SPF DNS response for domain %s: %s",
2199 40991a5f 2025-06-02 kirill print_dname(q.q_dname, buf, sizeof(buf)),
2200 40991a5f 2025-06-02 kirill pack.err);
2201 40991a5f 2025-06-02 kirill spf_done(query->spf, SPF_TEMPERROR, pack.err);
2202 40991a5f 2025-06-02 kirill goto end;
2205 40991a5f 2025-06-02 kirill for (; h.ancount; h.ancount--) {
2206 40991a5f 2025-06-02 kirill if (unpack_rr(&pack, &rr) != 0) {
2207 40991a5f 2025-06-02 kirill osmtpd_warn(query->spf->ctx,
2208 40991a5f 2025-06-02 kirill "Mallformed SPF DNS record for domain %s: %s",
2209 40991a5f 2025-06-02 kirill print_dname(q.q_dname, buf, sizeof(buf)),
2210 40991a5f 2025-06-02 kirill pack.err);
2211 40991a5f 2025-06-02 kirill continue;
2214 40991a5f 2025-06-02 kirill switch (rr.rr_type)
2216 40991a5f 2025-06-02 kirill case T_TXT:
2217 40991a5f 2025-06-02 kirill spf_resolve_txt(&rr, query);
2220 40991a5f 2025-06-02 kirill case T_MX:
2221 40991a5f 2025-06-02 kirill spf_resolve_mx(&rr, query);
2224 40991a5f 2025-06-02 kirill case T_A:
2225 40991a5f 2025-06-02 kirill spf_resolve_a(&rr, query);
2228 40991a5f 2025-06-02 kirill case T_AAAA:
2229 40991a5f 2025-06-02 kirill spf_resolve_aaaa(&rr, query);
2232 40991a5f 2025-06-02 kirill case T_CNAME:
2233 40991a5f 2025-06-02 kirill spf_resolve_cname(&rr, query);
2236 40991a5f 2025-06-02 kirill default:
2237 40991a5f 2025-06-02 kirill osmtpd_warn(spf->ctx,
2238 40991a5f 2025-06-02 kirill "Unexpected SPF DNS record: %d for domain %s",
2239 40991a5f 2025-06-02 kirill rr.rr_type, query->domain);
2240 40991a5f 2025-06-02 kirill spf_done(query->spf, SPF_TEMPERROR, "Unexpected record");
2244 40991a5f 2025-06-02 kirill if (spf->done)
2245 40991a5f 2025-06-02 kirill goto end;
2248 40991a5f 2025-06-02 kirill consume:
2249 40991a5f 2025-06-02 kirill if (spf->running > 0)
2250 40991a5f 2025-06-02 kirill goto end;
2252 40991a5f 2025-06-02 kirill for (i = spf->nqueries - 1; i >= 0; i--) {
2253 40991a5f 2025-06-02 kirill if (spf->queries[i].txt != NULL) {
2254 40991a5f 2025-06-02 kirill if (spf_execute_txt(&spf->queries[i]) != 0)
2260 40991a5f 2025-06-02 kirill free(ar->ar_data);
2261 40991a5f 2025-06-02 kirill if (!spf->done && spf->running == 0)
2262 40991a5f 2025-06-02 kirill spf_done(spf, SPF_NONE, NULL);
2266 40991a5f 2025-06-02 kirill spf_resolve_txt(struct dns_rr *rr, struct spf_query *query)
2268 40991a5f 2025-06-02 kirill char *txt;
2269 40991a5f 2025-06-02 kirill txt = spf_parse_txt(rr->rr.other.rdata, rr->rr.other.rdlen);
2270 40991a5f 2025-06-02 kirill if (txt == NULL) {
2271 40991a5f 2025-06-02 kirill osmtpd_warn(NULL, "spf_parse_txt");
2275 40991a5f 2025-06-02 kirill if (strncasecmp("v=spf1 ", txt, 7)) {
2276 40991a5f 2025-06-02 kirill free(txt);
2280 40991a5f 2025-06-02 kirill if (query->txt != NULL) {
2281 40991a5f 2025-06-02 kirill free(txt);
2282 40991a5f 2025-06-02 kirill spf_done(query->spf, SPF_PERMERROR, "Duplicated SPF record");
2286 40991a5f 2025-06-02 kirill query->txt = txt;
2287 40991a5f 2025-06-02 kirill query->pos = 0;
2288 40991a5f 2025-06-02 kirill spf_execute_txt(query);
2292 40991a5f 2025-06-02 kirill spf_resolve_mx(struct dns_rr *rr, struct spf_query *query)
2294 40991a5f 2025-06-02 kirill char buf[HOST_NAME_MAX + 1];
2296 40991a5f 2025-06-02 kirill char *domain = print_dname(rr->rr.mx.exchange, buf, sizeof(buf));
2298 40991a5f 2025-06-02 kirill spf_lookup_record(query->spf, domain, T_A,
2299 40991a5f 2025-06-02 kirill query->q, query->include, 0);
2300 40991a5f 2025-06-02 kirill spf_lookup_record(query->spf, domain, T_AAAA,
2301 40991a5f 2025-06-02 kirill query->q, query->include, 0);
2305 40991a5f 2025-06-02 kirill spf_resolve_a(struct dns_rr *rr, struct spf_query *query)
2307 40991a5f 2025-06-02 kirill if (query->exists ||
2308 40991a5f 2025-06-02 kirill spf_check_cidr(query->spf, &rr->rr.in_a.addr, 32) == 0) {
2309 40991a5f 2025-06-02 kirill spf_done(query->spf, query->q, NULL);
2314 40991a5f 2025-06-02 kirill spf_resolve_aaaa(struct dns_rr *rr, struct spf_query *query)
2316 40991a5f 2025-06-02 kirill if (spf_check_cidr6(query->spf, &rr->rr.in_aaaa.addr6, 128) == 0) {
2317 40991a5f 2025-06-02 kirill spf_done(query->spf, query->q, NULL);
2322 40991a5f 2025-06-02 kirill spf_resolve_cname(struct dns_rr *rr, struct spf_query *query)
2324 40991a5f 2025-06-02 kirill char buf[HOST_NAME_MAX + 1];
2326 40991a5f 2025-06-02 kirill char *domain = print_dname(rr->rr.cname.cname, buf, sizeof(buf));
2328 40991a5f 2025-06-02 kirill spf_lookup_record(query->spf, domain, query->type,
2329 40991a5f 2025-06-02 kirill query->q, query->include, 0);
2333 40991a5f 2025-06-02 kirill spf_parse_txt(const char *rdata, size_t rdatalen)
2335 40991a5f 2025-06-02 kirill size_t len, dstsz = SPF_RECORD_MAX - 1;
2336 40991a5f 2025-06-02 kirill ssize_t r = 0;
2337 40991a5f 2025-06-02 kirill char *dst, *odst;
2339 40991a5f 2025-06-02 kirill if (rdatalen >= dstsz) {
2340 40991a5f 2025-06-02 kirill errno = EOVERFLOW;
2341 40991a5f 2025-06-02 kirill return NULL;
2344 40991a5f 2025-06-02 kirill odst = dst = malloc(dstsz);
2345 40991a5f 2025-06-02 kirill if (dst == NULL)
2346 40991a5f 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
2348 40991a5f 2025-06-02 kirill while (rdatalen) {
2349 40991a5f 2025-06-02 kirill len = *(const unsigned char *)rdata;
2350 40991a5f 2025-06-02 kirill if (len >= rdatalen) {
2351 40991a5f 2025-06-02 kirill errno = EINVAL;
2352 40991a5f 2025-06-02 kirill return NULL;
2355 40991a5f 2025-06-02 kirill rdata++;
2356 40991a5f 2025-06-02 kirill rdatalen--;
2358 40991a5f 2025-06-02 kirill if (len == 0)
2359 40991a5f 2025-06-02 kirill continue;
2361 40991a5f 2025-06-02 kirill if (len >= dstsz) {
2362 40991a5f 2025-06-02 kirill errno = EOVERFLOW;
2363 40991a5f 2025-06-02 kirill return NULL;
2365 40991a5f 2025-06-02 kirill memmove(dst, rdata, len);
2366 40991a5f 2025-06-02 kirill dst += len;
2367 40991a5f 2025-06-02 kirill dstsz -= len;
2369 40991a5f 2025-06-02 kirill rdata += len;
2370 40991a5f 2025-06-02 kirill rdatalen -= len;
2371 40991a5f 2025-06-02 kirill r += len;
2374 40991a5f 2025-06-02 kirill odst[r] = '\0';
2376 40991a5f 2025-06-02 kirill return odst;
2380 40991a5f 2025-06-02 kirill spf_check_cidr(struct spf_record *spf, struct in_addr *net, int bits)
2382 40991a5f 2025-06-02 kirill struct in_addr *addr;
2383 40991a5f 2025-06-02 kirill struct session *ses = spf->ctx->local_session;
2385 40991a5f 2025-06-02 kirill if (ses->src.ss_family != AF_INET)
2386 40991a5f 2025-06-02 kirill return -1;
2388 40991a5f 2025-06-02 kirill if (bits == 0)
2389 40991a5f 2025-06-02 kirill return 0;
2391 40991a5f 2025-06-02 kirill addr = &(((struct sockaddr_in *)(&ses->src))->sin_addr);
2393 40991a5f 2025-06-02 kirill return ((addr->s_addr ^ net->s_addr) & htonl(0xFFFFFFFFu << (32 - bits)));
2397 40991a5f 2025-06-02 kirill spf_check_cidr6(struct spf_record *spf, struct in6_addr *net, int bits)
2400 40991a5f 2025-06-02 kirill uint32_t *a, *n, whole, incomplete;
2401 40991a5f 2025-06-02 kirill struct in6_addr *addr;
2402 40991a5f 2025-06-02 kirill struct session *ses = spf->ctx->local_session;
2404 40991a5f 2025-06-02 kirill if (ses->src.ss_family != AF_INET6)
2405 40991a5f 2025-06-02 kirill return -1;
2407 40991a5f 2025-06-02 kirill if (bits == 0)
2408 40991a5f 2025-06-02 kirill return 0;
2410 40991a5f 2025-06-02 kirill addr = &(((struct sockaddr_in6 *)(&ses->src))->sin6_addr);
2412 40991a5f 2025-06-02 kirill a = addr->__u6_addr.__u6_addr32;
2413 40991a5f 2025-06-02 kirill n = net->__u6_addr.__u6_addr32;
2415 40991a5f 2025-06-02 kirill whole = bits >> 5;
2416 40991a5f 2025-06-02 kirill incomplete = bits & 0x1f;
2417 40991a5f 2025-06-02 kirill if (whole) {
2418 40991a5f 2025-06-02 kirill rc = memcmp(a, n, whole << 2);
2420 40991a5f 2025-06-02 kirill return rc;
2422 40991a5f 2025-06-02 kirill if (incomplete)
2423 40991a5f 2025-06-02 kirill return (a[whole] ^ n[whole]) & htonl((0xffffffffu) << (32 - incomplete));
2425 40991a5f 2025-06-02 kirill return 0;
2429 40991a5f 2025-06-02 kirill spf_execute_txt(struct spf_query *query)
2431 40991a5f 2025-06-02 kirill struct in_addr ina;
2432 40991a5f 2025-06-02 kirill struct in6_addr in6a;
2433 40991a5f 2025-06-02 kirill char *ap = NULL;
2434 40991a5f 2025-06-02 kirill char *in = query->txt + query->pos;
2435 40991a5f 2025-06-02 kirill char *end;
2436 40991a5f 2025-06-02 kirill int bits;
2438 40991a5f 2025-06-02 kirill enum spf_state q = query->q;
2440 40991a5f 2025-06-02 kirill while ((ap = strsep(&in, " ")) != NULL) {
2441 40991a5f 2025-06-02 kirill if (strcasecmp(ap, "v=spf1") == 0)
2442 40991a5f 2025-06-02 kirill continue;
2444 40991a5f 2025-06-02 kirill end = ap + strlen(ap)-1;
2445 40991a5f 2025-06-02 kirill if (*end == '.')
2446 40991a5f 2025-06-02 kirill *end = '\0';
2448 40991a5f 2025-06-02 kirill if (*ap == '+') {
2449 40991a5f 2025-06-02 kirill q = SPF_PASS;
2451 40991a5f 2025-06-02 kirill } else if (*ap == '-') {
2452 40991a5f 2025-06-02 kirill q = SPF_FAIL;
2454 40991a5f 2025-06-02 kirill } else if (*ap == '~') {
2455 40991a5f 2025-06-02 kirill q = SPF_SOFTFAIL;
2457 40991a5f 2025-06-02 kirill } else if (*ap == '?') {
2458 40991a5f 2025-06-02 kirill q = SPF_NEUTRAL;
2462 40991a5f 2025-06-02 kirill if (q != SPF_PASS && query->include)
2463 40991a5f 2025-06-02 kirill continue;
2465 40991a5f 2025-06-02 kirill if (strncasecmp("all", ap, 3) == 0) {
2466 40991a5f 2025-06-02 kirill spf_done(query->spf, q, NULL);
2467 40991a5f 2025-06-02 kirill return 0;
2469 40991a5f 2025-06-02 kirill if (strncasecmp("ip4:", ap, 4) == 0) {
2470 40991a5f 2025-06-02 kirill if ((bits = inet_net_pton(AF_INET, ap + 4, &ina, sizeof(ina))) == -1)
2471 40991a5f 2025-06-02 kirill continue;
2473 40991a5f 2025-06-02 kirill if (spf_check_cidr(query->spf, &ina, bits) == 0) {
2474 40991a5f 2025-06-02 kirill spf_done(query->spf, q, NULL);
2475 40991a5f 2025-06-02 kirill return 0;
2477 40991a5f 2025-06-02 kirill continue;
2479 40991a5f 2025-06-02 kirill if (strncasecmp("ip6:", ap, 4) == 0) {
2480 40991a5f 2025-06-02 kirill if ((bits = inet_net_pton(AF_INET6, ap + 4, &ina, sizeof(ina))) == -1)
2481 40991a5f 2025-06-02 kirill continue;
2483 40991a5f 2025-06-02 kirill if (spf_check_cidr6(query->spf, &in6a, bits) == 0) {
2484 40991a5f 2025-06-02 kirill spf_done(query->spf, q, NULL);
2485 40991a5f 2025-06-02 kirill return 0;
2487 40991a5f 2025-06-02 kirill continue;
2489 40991a5f 2025-06-02 kirill if (strcasecmp("a", ap) == 0) {
2490 40991a5f 2025-06-02 kirill spf_lookup_record(query->spf, query->domain, T_A,
2491 40991a5f 2025-06-02 kirill q, query->include, 0);
2492 40991a5f 2025-06-02 kirill spf_lookup_record(query->spf, query->domain, T_AAAA,
2493 40991a5f 2025-06-02 kirill q, query->include, 0);
2496 40991a5f 2025-06-02 kirill if (strncasecmp("a:", ap, 2) == 0) {
2497 40991a5f 2025-06-02 kirill spf_lookup_record(query->spf, ap + 2, T_A,
2498 40991a5f 2025-06-02 kirill q, query->include, 0);
2499 40991a5f 2025-06-02 kirill spf_lookup_record(query->spf, ap + 2, T_AAAA,
2500 40991a5f 2025-06-02 kirill q, query->include, 0);
2503 40991a5f 2025-06-02 kirill if (strncasecmp("exists:", ap, 7) == 0) {
2504 40991a5f 2025-06-02 kirill spf_lookup_record(query->spf, ap + 7, T_A,
2505 40991a5f 2025-06-02 kirill q, query->include, 1);
2508 40991a5f 2025-06-02 kirill if (strncasecmp("include:", ap, 8) == 0) {
2509 40991a5f 2025-06-02 kirill spf_lookup_record(query->spf, ap + 8, T_TXT, q, 1, 0);
2512 40991a5f 2025-06-02 kirill if (strncasecmp("redirect=", ap, 9) == 0) {
2513 40991a5f 2025-06-02 kirill if (in != NULL)
2514 40991a5f 2025-06-02 kirill continue;
2515 40991a5f 2025-06-02 kirill spf_lookup_record(query->spf, ap + 9, T_TXT,
2516 40991a5f 2025-06-02 kirill q, query->include, 0);
2517 40991a5f 2025-06-02 kirill return 0;
2519 40991a5f 2025-06-02 kirill if (strcasecmp("mx", ap) == 0) {
2520 40991a5f 2025-06-02 kirill spf_lookup_record(query->spf, query->domain, T_MX,
2521 40991a5f 2025-06-02 kirill q, query->include, 0);
2524 40991a5f 2025-06-02 kirill if (strncasecmp("mx:", ap, 3) == 0) {
2525 40991a5f 2025-06-02 kirill spf_lookup_record(query->spf, ap + 3, T_MX,
2526 40991a5f 2025-06-02 kirill q, query->include, 0);
2531 40991a5f 2025-06-02 kirill if (in == NULL)
2532 40991a5f 2025-06-02 kirill return 0;
2534 40991a5f 2025-06-02 kirill query->pos = in - query->txt;
2536 40991a5f 2025-06-02 kirill return query->pos;
2540 40991a5f 2025-06-02 kirill spf_done(struct spf_record *spf, enum spf_state state, const char *reason)
2544 40991a5f 2025-06-02 kirill if (spf->done)
2547 40991a5f 2025-06-02 kirill for (i = 0; i < spf->nqueries; i++) {
2548 40991a5f 2025-06-02 kirill if (spf->queries[i].eva) {
2549 40991a5f 2025-06-02 kirill event_asr_abort(spf->queries[i].eva);
2550 40991a5f 2025-06-02 kirill spf->queries[i].eva = NULL;
2554 40991a5f 2025-06-02 kirill spf->nqueries = 0;
2555 40991a5f 2025-06-02 kirill spf->running = 0;
2556 40991a5f 2025-06-02 kirill spf->state = state;
2557 40991a5f 2025-06-02 kirill spf->state_reason = reason;
2558 40991a5f 2025-06-02 kirill spf->done = 1;
2560 40991a5f 2025-06-02 kirill osmtpd_filter_proceed(spf->ctx);
2563 40991a5f 2025-06-02 kirill const char *
2564 40991a5f 2025-06-02 kirill spf_state2str(enum spf_state state)
2566 40991a5f 2025-06-02 kirill switch (state)
2568 40991a5f 2025-06-02 kirill case SPF_NONE:
2569 40991a5f 2025-06-02 kirill return "none";
2570 40991a5f 2025-06-02 kirill case SPF_NEUTRAL:
2571 40991a5f 2025-06-02 kirill return "neutral";
2572 40991a5f 2025-06-02 kirill case SPF_PASS:
2573 40991a5f 2025-06-02 kirill return "pass";
2574 40991a5f 2025-06-02 kirill case SPF_FAIL:
2575 a9b0d8da 2025-06-02 kirill return "fail";
2576 40991a5f 2025-06-02 kirill case SPF_SOFTFAIL:
2577 40991a5f 2025-06-02 kirill return "softfail";
2578 40991a5f 2025-06-02 kirill case SPF_TEMPERROR:
2579 40991a5f 2025-06-02 kirill return "temperror";
2580 40991a5f 2025-06-02 kirill case SPF_PERMERROR:
2581 40991a5f 2025-06-02 kirill return "permerror";
2586 40991a5f 2025-06-02 kirill spf_ar_cat(const char *type, struct spf_record *spf, char **line, size_t *linelen, ssize_t *aroff)
2588 40991a5f 2025-06-02 kirill if (spf == NULL) {
2589 40991a5f 2025-06-02 kirill if ((*aroff =
2590 40991a5f 2025-06-02 kirill auth_ar_cat(line, linelen, *aroff,
2591 40991a5f 2025-06-02 kirill "; spf=none %s=none", type)
2592 40991a5f 2025-06-02 kirill ) == -1) {
2593 40991a5f 2025-06-02 kirill return -1;
2595 40991a5f 2025-06-02 kirill return 0;
2598 40991a5f 2025-06-02 kirill if ((*aroff =
2599 40991a5f 2025-06-02 kirill auth_ar_cat(line, linelen, *aroff,
2600 40991a5f 2025-06-02 kirill "; spf=%s", spf_state2str(spf->state))
2601 40991a5f 2025-06-02 kirill ) == -1) {
2602 40991a5f 2025-06-02 kirill return -1;
2605 40991a5f 2025-06-02 kirill if ((*aroff =
2606 40991a5f 2025-06-02 kirill auth_ar_cat(line, linelen, *aroff,
2607 40991a5f 2025-06-02 kirill " %s=%s@%s",
2609 40991a5f 2025-06-02 kirill spf->sender_local,
2610 40991a5f 2025-06-02 kirill spf->sender_domain)
2611 40991a5f 2025-06-02 kirill ) == -1) {
2612 40991a5f 2025-06-02 kirill return -1;
2615 40991a5f 2025-06-02 kirill if (spf->state_reason != NULL) {
2616 40991a5f 2025-06-02 kirill if ((*aroff =
2617 40991a5f 2025-06-02 kirill auth_ar_cat(line, linelen, *aroff,
2618 40991a5f 2025-06-02 kirill " reason=\"%s\"", spf->state_reason)
2619 40991a5f 2025-06-02 kirill ) == -1) {
2620 40991a5f 2025-06-02 kirill return -1;
2624 40991a5f 2025-06-02 kirill return 0;
2628 0b081549 2025-06-02 kirill auth_message_verify(struct message *msg)
2630 f1509490 2022-03-20 martijn size_t i;
2632 40991a5f 2025-06-02 kirill if (!msg->readdone || msg->nqueries > 0)
2633 f1509490 2022-03-20 martijn return;
2635 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++) {
2636 f1509490 2022-03-20 martijn if (msg->header[i].sig == NULL)
2637 f1509490 2022-03-20 martijn continue;
2638 f1509490 2022-03-20 martijn if (msg->header[i].sig->query != NULL)
2639 f1509490 2022-03-20 martijn return;
2640 f1509490 2022-03-20 martijn if (msg->header[i].sig->state != DKIM_UNKNOWN)
2641 f1509490 2022-03-20 martijn continue;
2642 f1509490 2022-03-20 martijn dkim_signature_state(msg->header[i].sig, DKIM_PASS, NULL);
2645 40991a5f 2025-06-02 kirill auth_ar_create(msg->ctx);
2649 40991a5f 2025-06-02 kirill auth_ar_create(struct osmtpd_ctx *ctx)
2651 40991a5f 2025-06-02 kirill struct dkim_signature *sig;
2652 40991a5f 2025-06-02 kirill size_t i;
2653 40991a5f 2025-06-02 kirill ssize_t n, aroff = 0;
2654 40991a5f 2025-06-02 kirill int found = 0;
2655 40991a5f 2025-06-02 kirill char *line = NULL;
2656 40991a5f 2025-06-02 kirill size_t linelen = 0;
2657 40991a5f 2025-06-02 kirill struct session *ses = ctx->local_session;
2658 40991a5f 2025-06-02 kirill struct message *msg = ctx->local_message;
2660 0b081549 2025-06-02 kirill if ((aroff = auth_ar_cat(&line, &linelen, aroff,
2661 b64f38ad 2025-06-02 kirill "Authentication-Results: %s", authservid)) == -1)
2662 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
2663 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++) {
2664 f1509490 2022-03-20 martijn sig = msg->header[i].sig;
2665 f1509490 2022-03-20 martijn if (sig == NULL)
2666 f1509490 2022-03-20 martijn continue;
2667 f1509490 2022-03-20 martijn found = 1;
2668 0b081549 2025-06-02 kirill if ((aroff = auth_ar_cat(&line, &linelen, aroff, "; dkim=%s",
2669 b64f38ad 2025-06-02 kirill dkim_state2str(sig->state))) == -1)
2670 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
2671 f1509490 2022-03-20 martijn if (sig->state_reason != NULL) {
2672 0b081549 2025-06-02 kirill if ((aroff = auth_ar_cat(&line, &linelen, aroff,
2673 b64f38ad 2025-06-02 kirill " reason=\"%s\"", sig->state_reason)) == -1)
2674 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
2676 f1509490 2022-03-20 martijn if (sig->s[0] != '\0') {
2677 0b081549 2025-06-02 kirill if ((aroff = auth_ar_cat(&line, &linelen, aroff,
2678 b64f38ad 2025-06-02 kirill " header.s=%s", sig->s)) == -1)
2679 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
2681 f1509490 2022-03-20 martijn if (sig->d[0] != '\0') {
2682 0b081549 2025-06-02 kirill if ((aroff = auth_ar_cat(&line, &linelen, aroff,
2683 b64f38ad 2025-06-02 kirill " header.d=%s", sig->d)) == -1)
2684 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
2687 f1509490 2022-03-20 martijn * Don't print i-tag, since localpart can be a quoted-string,
2688 f1509490 2022-03-20 martijn * which can contain FWS and CFWS.
2690 f1509490 2022-03-20 martijn if (sig->a != NULL) {
2691 0b081549 2025-06-02 kirill if ((aroff = auth_ar_cat(&line, &linelen, aroff,
2692 b64f38ad 2025-06-02 kirill " header.a=%.*s", (int)sig->asz, sig->a)) == -1)
2693 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
2695 ce4bcd16 2025-06-02 kirill if (sig->bheaderclean[0] != '\0') {
2696 0b081549 2025-06-02 kirill if ((aroff = auth_ar_cat(&line, &linelen, aroff,
2697 b64f38ad 2025-06-02 kirill " header.b=%s", sig->bheaderclean)) == -1)
2698 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
2701 f1509490 2022-03-20 martijn if (!found) {
2702 0b081549 2025-06-02 kirill aroff = auth_ar_cat(&line, &linelen, aroff, "; dkim=none");
2703 b64f38ad 2025-06-02 kirill if (aroff == -1)
2704 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
2707 a9b0d8da 2025-06-02 kirill if ((aroff = auth_ar_cat(&line, &linelen, aroff,
2708 a9b0d8da 2025-06-02 kirill "; iprev=%s", iprev_state2str(ses->iprev))) == -1)
2709 a9b0d8da 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
2711 40991a5f 2025-06-02 kirill if (spf_ar_cat("smtp.helo", ses->spf_helo,
2712 40991a5f 2025-06-02 kirill &line, &linelen, &aroff) != 0)
2713 40991a5f 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
2715 40991a5f 2025-06-02 kirill if (ses->spf_mailfrom != NULL) {
2716 40991a5f 2025-06-02 kirill if (spf_ar_cat("smtp.mailfrom", ses->spf_mailfrom,
2717 40991a5f 2025-06-02 kirill &line, &linelen, &aroff) != 0)
2718 40991a5f 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
2719 40991a5f 2025-06-02 kirill } else {
2720 40991a5f 2025-06-02 kirill if (spf_ar_cat("smtp.mailfrom", ses->spf_helo,
2721 40991a5f 2025-06-02 kirill &line, &linelen, &aroff) != 0)
2722 40991a5f 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
2725 a9b0d8da 2025-06-02 kirill if (aroff == -1)
2726 a9b0d8da 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
2728 40991a5f 2025-06-02 kirill if (auth_ar_print(msg->ctx, line) != 0)
2729 40991a5f 2025-06-02 kirill osmtpd_warn(msg->ctx, "Invalid AR line: %s", line);
2731 f1509490 2022-03-20 martijn rewind(msg->origf);
2732 f1509490 2022-03-20 martijn while ((n = getline(&line, &linelen, msg->origf)) != -1) {
2733 f1509490 2022-03-20 martijn line[n - 1] = '\0';
2734 f1509490 2022-03-20 martijn osmtpd_filter_dataline(msg->ctx, "%s", line);
2736 f1509490 2022-03-20 martijn if (ferror(msg->origf))
2737 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: ferror", __func__);
2738 f1509490 2022-03-20 martijn free(line);
2739 f1509490 2022-03-20 martijn return;
2743 0b081549 2025-06-02 kirill auth_ar_print(struct osmtpd_ctx *ctx, const char *start)
2745 4e2dd90f 2022-03-26 martijn const char *scan, *checkpoint, *ncheckpoint;
2746 1256d7a0 2025-06-02 kirill int arlen = 0, first = 1, arid = 1;
2748 f1509490 2022-03-20 martijn checkpoint = start;
2749 4e2dd90f 2022-03-26 martijn ncheckpoint = osmtpd_ltok_skip_hdr_name(start, 0) + 1;
2750 f1509490 2022-03-20 martijn for (scan = start; scan[0] != '\0'; scan++) {
2751 f1509490 2022-03-20 martijn if (scan[0] == '\t')
2752 f1509490 2022-03-20 martijn arlen = (arlen + 8) & ~7;
2754 f1509490 2022-03-20 martijn arlen++;
2755 f1509490 2022-03-20 martijn if (arlen >= AUTHENTICATION_RESULTS_LINELEN) {
2756 1256d7a0 2025-06-02 kirill arlen = (int)(checkpoint - start);
2757 1256d7a0 2025-06-02 kirill if (arlen <= 0) {
2758 1256d7a0 2025-06-02 kirill arlen = (int)(ncheckpoint - start);
2759 1256d7a0 2025-06-02 kirill checkpoint = ncheckpoint;
2761 f1509490 2022-03-20 martijn osmtpd_filter_dataline(ctx, "%s%.*s", first ? "" : "\t",
2762 1256d7a0 2025-06-02 kirill arlen, start);
2763 4e2dd90f 2022-03-26 martijn start = osmtpd_ltok_skip_cfws(checkpoint, 1);
2764 1256d7a0 2025-06-02 kirill if (*start == '\0')
2765 36daa2d4 2025-06-02 kirill return 0;
2766 7a470599 2025-06-02 kirill ncheckpoint = start;
2767 f1509490 2022-03-20 martijn scan = start;
2768 f1509490 2022-03-20 martijn arlen = 8;
2769 f1509490 2022-03-20 martijn first = 0;
2771 f1509490 2022-03-20 martijn if (scan == ncheckpoint) {
2772 f1509490 2022-03-20 martijn checkpoint = ncheckpoint;
2773 4e2dd90f 2022-03-26 martijn ncheckpoint = osmtpd_ltok_skip_cfws(ncheckpoint, 1);
2774 f1509490 2022-03-20 martijn /* authserv-id */
2775 f1509490 2022-03-20 martijn if (arid) {
2776 4e2dd90f 2022-03-26 martijn ncheckpoint = osmtpd_ltok_skip_value(
2777 f1509490 2022-03-20 martijn ncheckpoint, 0);
2778 f1509490 2022-03-20 martijn arid = 0;
2779 f1509490 2022-03-20 martijn /* methodspec */
2780 e460eef3 2025-06-02 kirill } else if (strncmp(ncheckpoint, "dkim=",
2781 e460eef3 2025-06-02 kirill sizeof("dkim=") - 1) == 0) {
2782 4e2dd90f 2022-03-26 martijn ncheckpoint = osmtpd_ltok_skip_keyword(
2783 e460eef3 2025-06-02 kirill ncheckpoint + sizeof("dkim=") - 1, 0);
2784 a9b0d8da 2025-06-02 kirill } else if (strncmp(ncheckpoint, "iprev=",
2785 a9b0d8da 2025-06-02 kirill sizeof("iprev=") - 1) == 0) {
2786 a9b0d8da 2025-06-02 kirill ncheckpoint = osmtpd_ltok_skip_keyword(
2787 a9b0d8da 2025-06-02 kirill ncheckpoint + sizeof("iprev=") - 1, 0);
2788 40991a5f 2025-06-02 kirill } else if (strncmp(ncheckpoint, "spf=",
2789 40991a5f 2025-06-02 kirill sizeof("spf=") - 1) == 0) {
2790 40991a5f 2025-06-02 kirill ncheckpoint = osmtpd_ltok_skip_keyword(
2791 40991a5f 2025-06-02 kirill ncheckpoint + sizeof("spf=") - 1, 0);
2792 f1509490 2022-03-20 martijn /* reasonspec */
2793 e460eef3 2025-06-02 kirill } else if (strncmp(ncheckpoint, "reason=",
2794 e460eef3 2025-06-02 kirill sizeof("reason=") - 1) == 0) {
2795 7a470599 2025-06-02 kirill ncheckpoint = osmtpd_ltok_skip_ar_reasonspec(
2796 7a470599 2025-06-02 kirill ncheckpoint, 0);
2797 f1509490 2022-03-20 martijn /* propspec */
2798 f1509490 2022-03-20 martijn } else {
2799 7a470599 2025-06-02 kirill ncheckpoint = osmtpd_ltok_skip_ar_propspec(
2800 f1509490 2022-03-20 martijn ncheckpoint, 0);
2803 7a470599 2025-06-02 kirill if (ncheckpoint == NULL)
2804 36daa2d4 2025-06-02 kirill return -1;
2806 7a470599 2025-06-02 kirill if (*ncheckpoint == ';')
2807 7a470599 2025-06-02 kirill ncheckpoint++;
2810 f1509490 2022-03-20 martijn osmtpd_filter_dataline(ctx, "%s%s", first ? "" : "\t", start);
2811 36daa2d4 2025-06-02 kirill return 0;
2814 f1509490 2022-03-20 martijn ssize_t
2815 0b081549 2025-06-02 kirill auth_ar_cat(char **ar, size_t *n, size_t aroff, const char *fmt, ...)
2817 f1509490 2022-03-20 martijn va_list ap;
2818 f1509490 2022-03-20 martijn char *artmp;
2819 f1509490 2022-03-20 martijn int size;
2820 f1509490 2022-03-20 martijn size_t nn;
2822 f1509490 2022-03-20 martijn va_start(ap, fmt);
2823 f1509490 2022-03-20 martijn size = vsnprintf(*ar + aroff, *n - aroff, fmt, ap);
2824 f1509490 2022-03-20 martijn va_end(ap);
2825 2293aea1 2025-06-02 kirill if (size + aroff < *n)
2826 f1509490 2022-03-20 martijn return (ssize_t)size + aroff;
2827 f1509490 2022-03-20 martijn nn = (((aroff + size) / 256) + 1) * 256;
2828 f1509490 2022-03-20 martijn artmp = realloc(*ar, nn);
2829 f1509490 2022-03-20 martijn if (artmp == NULL)
2830 f1509490 2022-03-20 martijn return -1;
2831 f1509490 2022-03-20 martijn *ar = artmp;
2832 f1509490 2022-03-20 martijn *n = nn;
2833 f1509490 2022-03-20 martijn va_start(ap, fmt);
2834 f1509490 2022-03-20 martijn size = vsnprintf(*ar + aroff, *n - aroff, fmt, ap);
2835 f1509490 2022-03-20 martijn va_end(ap);
2836 f1509490 2022-03-20 martijn return (ssize_t)size + aroff;
2839 f1509490 2022-03-20 martijn __dead void
2840 f1509490 2022-03-20 martijn usage(void)
2842 0b081549 2025-06-02 kirill fprintf(stderr, "usage: filter-auth\n");
2843 f1509490 2022-03-20 martijn exit(1);