Blame


1 f1509490 2022-03-20 martijn /*
2 f1509490 2022-03-20 martijn * Copyright (c) 2022 Martijn van Duren <martijn@openbsd.org>
3 f1509490 2022-03-20 martijn *
4 f1509490 2022-03-20 martijn * Permission to use, copy, modify, and distribute this software for any
5 f1509490 2022-03-20 martijn * purpose with or without fee is hereby granted, provided that the above
6 f1509490 2022-03-20 martijn * copyright notice and this permission notice appear in all copies.
7 f1509490 2022-03-20 martijn *
8 f1509490 2022-03-20 martijn * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 f1509490 2022-03-20 martijn * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 f1509490 2022-03-20 martijn * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 f1509490 2022-03-20 martijn * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 f1509490 2022-03-20 martijn * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 f1509490 2022-03-20 martijn * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 f1509490 2022-03-20 martijn * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 f1509490 2022-03-20 martijn */
16 f1509490 2022-03-20 martijn #include <sys/types.h>
17 f1509490 2022-03-20 martijn #include <sys/socket.h>
18 f1509490 2022-03-20 martijn
19 f1509490 2022-03-20 martijn #include <openssl/evp.h>
20 f1509490 2022-03-20 martijn #include <openssl/pem.h>
21 f1509490 2022-03-20 martijn #include <openssl/sha.h>
22 f1509490 2022-03-20 martijn #include <openssl/err.h>
23 f1509490 2022-03-20 martijn
24 f1509490 2022-03-20 martijn #include <arpa/nameser.h>
25 f1509490 2022-03-20 martijn
26 f1509490 2022-03-20 martijn #include <ctype.h>
27 f1509490 2022-03-20 martijn #include <errno.h>
28 f1509490 2022-03-20 martijn #include <event.h>
29 f1509490 2022-03-20 martijn #include <limits.h>
30 f1509490 2022-03-20 martijn #include <netdb.h>
31 b5876c84 2022-04-08 martijn #include <opensmtpd.h>
32 f1509490 2022-03-20 martijn #include <stdarg.h>
33 f1509490 2022-03-20 martijn #include <stdio.h>
34 f1509490 2022-03-20 martijn #include <stdlib.h>
35 f1509490 2022-03-20 martijn #include <string.h>
36 f1509490 2022-03-20 martijn #include <time.h>
37 f1509490 2022-03-20 martijn #include <unistd.h>
38 f1509490 2022-03-20 martijn #include <asr.h>
39 f1509490 2022-03-20 martijn
40 f1509490 2022-03-20 martijn #include "unpack_dns.h"
41 4e2dd90f 2022-03-26 martijn #include "ltok.h"
42 f1509490 2022-03-20 martijn
43 f1509490 2022-03-20 martijn /*
44 f1509490 2022-03-20 martijn * Use RFC8601 (Authentication-Results) codes instead of RFC6376 codes,
45 f1509490 2022-03-20 martijn * since they're more expressive.
46 f1509490 2022-03-20 martijn */
47 f1509490 2022-03-20 martijn enum state {
48 f1509490 2022-03-20 martijn DKIM_UNKNOWN,
49 f1509490 2022-03-20 martijn DKIM_PASS,
50 f1509490 2022-03-20 martijn DKIM_FAIL,
51 f1509490 2022-03-20 martijn DKIM_POLICY,
52 f1509490 2022-03-20 martijn DKIM_NEUTRAL,
53 f1509490 2022-03-20 martijn DKIM_TEMPERROR,
54 f1509490 2022-03-20 martijn DKIM_PERMERROR
55 f1509490 2022-03-20 martijn };
56 f1509490 2022-03-20 martijn
57 f1509490 2022-03-20 martijn struct signature {
58 f1509490 2022-03-20 martijn struct header *header;
59 f1509490 2022-03-20 martijn enum state state;
60 f1509490 2022-03-20 martijn const char *state_reason;
61 f1509490 2022-03-20 martijn int v;
62 4e2dd90f 2022-03-26 martijn const char *a;
63 f1509490 2022-03-20 martijn size_t asz;
64 f1509490 2022-03-20 martijn int ak;
65 338537df 2022-04-08 martijn int sephash;
66 f1509490 2022-03-20 martijn const EVP_MD *ah;
67 f1509490 2022-03-20 martijn char *b;
68 f1509490 2022-03-20 martijn size_t bsz;
69 4e2dd90f 2022-03-26 martijn const char *bheader;
70 ce4bcd16 2025-06-02 kirill size_t bheadersz;
71 ce4bcd16 2025-06-02 kirill #define HEADER_B_MAX_LEN 8
72 ce4bcd16 2025-06-02 kirill char bheaderclean[HEADER_B_MAX_LEN + 1];
73 f1509490 2022-03-20 martijn /* Make sure padding bits for base64 decoding fit */
74 f1509490 2022-03-20 martijn char bh[EVP_MAX_MD_SIZE + (3 - (EVP_MAX_MD_SIZE % 3))];
75 f1509490 2022-03-20 martijn size_t bhsz;
76 f1509490 2022-03-20 martijn EVP_MD_CTX *bhctx;
77 f1509490 2022-03-20 martijn int c;
78 f1509490 2022-03-20 martijn #define CANON_HEADER_SIMPLE 0
79 f1509490 2022-03-20 martijn #define CANON_HEADER_RELAXED 1
80 f1509490 2022-03-20 martijn #define CANON_HEADER 1
81 f1509490 2022-03-20 martijn #define CANON_BODY_SIMPLE 0
82 f1509490 2022-03-20 martijn #define CANON_BODY_RELAXED 1 << 1
83 f1509490 2022-03-20 martijn #define CANON_BODY 1 << 1
84 f1509490 2022-03-20 martijn #define CANON_DONE 1 << 2
85 f1509490 2022-03-20 martijn char d[HOST_NAME_MAX + 1];
86 f1509490 2022-03-20 martijn char **h;
87 4e2dd90f 2022-03-26 martijn const char *i;
88 f1509490 2022-03-20 martijn size_t isz;
89 f1509490 2022-03-20 martijn ssize_t l;
90 f1509490 2022-03-20 martijn int q;
91 f1509490 2022-03-20 martijn char s[HOST_NAME_MAX + 1];
92 f1509490 2022-03-20 martijn time_t t; /* Signature t=/timestamp */
93 f1509490 2022-03-20 martijn #define KT_Y 1
94 f1509490 2022-03-20 martijn #define KT_S 1 << 1
95 f1509490 2022-03-20 martijn int kt; /* Key t=/Flags */
96 f1509490 2022-03-20 martijn time_t x;
97 f1509490 2022-03-20 martijn int z;
98 f1509490 2022-03-20 martijn struct event_asr *query;
99 f1509490 2022-03-20 martijn EVP_PKEY *p;
100 3cb7796f 2025-06-02 kirill /* RFC 6376 doesn't care about CNAME, use simalr with SPF limit */
101 3cb7796f 2025-06-02 kirill #define DKIM_LOOKUP_LOOKUP_LIMIT 11
102 3cb7796f 2025-06-02 kirill int nqueries;
103 f1509490 2022-03-20 martijn };
104 f1509490 2022-03-20 martijn
105 f1509490 2022-03-20 martijn struct header {
106 f1509490 2022-03-20 martijn struct message *msg;
107 f1509490 2022-03-20 martijn uint8_t readdone;
108 f1509490 2022-03-20 martijn uint8_t parsed;
109 f1509490 2022-03-20 martijn char *buf;
110 f1509490 2022-03-20 martijn size_t buflen;
111 f1509490 2022-03-20 martijn struct signature *sig;
112 f1509490 2022-03-20 martijn };
113 f1509490 2022-03-20 martijn
114 f1509490 2022-03-20 martijn #define AUTHENTICATION_RESULTS_LINELEN 78
115 f1509490 2022-03-20 martijn #define MIN(a, b) ((a) < (b) ? (a) : (b))
116 f1509490 2022-03-20 martijn
117 f1509490 2022-03-20 martijn struct message {
118 f1509490 2022-03-20 martijn struct osmtpd_ctx *ctx;
119 f1509490 2022-03-20 martijn FILE *origf;
120 f1509490 2022-03-20 martijn int parsing_headers;
121 f1509490 2022-03-20 martijn size_t body_whitelines;
122 f1509490 2022-03-20 martijn int has_body;
123 f1509490 2022-03-20 martijn struct header *header;
124 f1509490 2022-03-20 martijn size_t nheaders;
125 f1509490 2022-03-20 martijn int readdone;
126 f1509490 2022-03-20 martijn };
127 f1509490 2022-03-20 martijn
128 f1509490 2022-03-20 martijn void usage(void);
129 f1509490 2022-03-20 martijn void dkim_conf(const char *, const char *);
130 f1509490 2022-03-20 martijn void dkim_dataline(struct osmtpd_ctx *, const char *);
131 f1509490 2022-03-20 martijn void *dkim_message_new(struct osmtpd_ctx *);
132 f1509490 2022-03-20 martijn void dkim_message_free(struct osmtpd_ctx *, void *);
133 f1509490 2022-03-20 martijn void dkim_header_add(struct osmtpd_ctx *, const char *);
134 f1509490 2022-03-20 martijn void dkim_signature_parse(struct header *);
135 4e2dd90f 2022-03-26 martijn void dkim_signature_parse_v(struct signature *, const char *, const char *);
136 4e2dd90f 2022-03-26 martijn void dkim_signature_parse_a(struct signature *, const char *, const char *);
137 4e2dd90f 2022-03-26 martijn void dkim_signature_parse_b(struct signature *, const char *, const char *);
138 4e2dd90f 2022-03-26 martijn void dkim_signature_parse_bh(struct signature *, const char *, const char *);
139 4e2dd90f 2022-03-26 martijn void dkim_signature_parse_c(struct signature *, const char *, const char *);
140 4e2dd90f 2022-03-26 martijn void dkim_signature_parse_d(struct signature *, const char *, const char *);
141 4e2dd90f 2022-03-26 martijn void dkim_signature_parse_h(struct signature *, const char *, const char *);
142 4e2dd90f 2022-03-26 martijn void dkim_signature_parse_i(struct signature *, const char *, const char *);
143 4e2dd90f 2022-03-26 martijn void dkim_signature_parse_l(struct signature *, const char *, const char *);
144 4e2dd90f 2022-03-26 martijn void dkim_signature_parse_q(struct signature *, const char *, const char *);
145 4e2dd90f 2022-03-26 martijn void dkim_signature_parse_s(struct signature *, const char *, const char *);
146 4e2dd90f 2022-03-26 martijn void dkim_signature_parse_t(struct signature *, const char *, const char *);
147 4e2dd90f 2022-03-26 martijn void dkim_signature_parse_x(struct signature *, const char *, const char *);
148 4e2dd90f 2022-03-26 martijn void dkim_signature_parse_z(struct signature *, const char *, const char *);
149 3cb7796f 2025-06-02 kirill void dkim_lookup_record(struct signature *sig, const char *domain);
150 f1509490 2022-03-20 martijn void dkim_signature_verify(struct signature *);
151 f1509490 2022-03-20 martijn void dkim_signature_header(EVP_MD_CTX *, struct signature *, struct header *);
152 f1509490 2022-03-20 martijn void dkim_signature_state(struct signature *, enum state, const char *);
153 f1509490 2022-03-20 martijn const char *dkim_state2str(enum state);
154 f1509490 2022-03-20 martijn void dkim_header_cat(struct osmtpd_ctx *, const char *);
155 f1509490 2022-03-20 martijn void dkim_body_parse(struct message *, const char *);
156 f1509490 2022-03-20 martijn void dkim_body_verify(struct signature *);
157 f1509490 2022-03-20 martijn void dkim_rr_resolve(struct asr_result *, void *);
158 f1509490 2022-03-20 martijn void dkim_message_verify(struct message *);
159 f1509490 2022-03-20 martijn ssize_t dkim_ar_cat(char **ar, size_t *n, size_t aroff, const char *fmt, ...)
160 f1509490 2022-03-20 martijn __attribute__((__format__ (printf, 4, 5)));
161 36daa2d4 2025-06-02 kirill int dkim_ar_print(struct osmtpd_ctx *, const char *);
162 4e2dd90f 2022-03-26 martijn int dkim_key_text_parse(struct signature *, const char *);
163 f1509490 2022-03-20 martijn
164 f1509490 2022-03-20 martijn char *authservid;
165 f1509490 2022-03-20 martijn EVP_ENCODE_CTX *ectx = NULL;
166 f1509490 2022-03-20 martijn
167 f1509490 2022-03-20 martijn int
168 f1509490 2022-03-20 martijn main(int argc, char *argv[])
169 f1509490 2022-03-20 martijn {
170 f1509490 2022-03-20 martijn if (argc != 1)
171 f1509490 2022-03-20 martijn osmtpd_errx(1, "Invalid argument count");
172 f1509490 2022-03-20 martijn
173 f1509490 2022-03-20 martijn OpenSSL_add_all_digests();
174 f1509490 2022-03-20 martijn
175 f1509490 2022-03-20 martijn if (pledge("tmppath stdio dns", NULL) == -1)
176 f1509490 2022-03-20 martijn osmtpd_err(1, "pledge");
177 f1509490 2022-03-20 martijn
178 f1509490 2022-03-20 martijn if ((ectx = EVP_ENCODE_CTX_new()) == NULL)
179 f1509490 2022-03-20 martijn osmtpd_err(1, "EVP_ENCODE_CTX_new");
180 f1509490 2022-03-20 martijn
181 f1509490 2022-03-20 martijn osmtpd_register_conf(dkim_conf);
182 f1509490 2022-03-20 martijn osmtpd_register_filter_dataline(dkim_dataline);
183 f1509490 2022-03-20 martijn osmtpd_local_message(dkim_message_new, dkim_message_free);
184 f1509490 2022-03-20 martijn osmtpd_run();
185 f1509490 2022-03-20 martijn
186 f1509490 2022-03-20 martijn return 0;
187 f1509490 2022-03-20 martijn }
188 f1509490 2022-03-20 martijn
189 f1509490 2022-03-20 martijn void
190 f1509490 2022-03-20 martijn dkim_conf(const char *key, const char *value)
191 f1509490 2022-03-20 martijn {
192 4e2dd90f 2022-03-26 martijn const char *end;
193 f1509490 2022-03-20 martijn
194 f1509490 2022-03-20 martijn if (key == NULL) {
195 f1509490 2022-03-20 martijn if (authservid == NULL)
196 f1509490 2022-03-20 martijn osmtpd_errx(1, "Didn't receive admd config option");
197 f1509490 2022-03-20 martijn return;
198 f1509490 2022-03-20 martijn }
199 f1509490 2022-03-20 martijn if (strcmp(key, "admd") == 0 && authservid == NULL) {
200 f1509490 2022-03-20 martijn if ((authservid = strdup(value)) == NULL)
201 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
202 4e2dd90f 2022-03-26 martijn end = osmtpd_ltok_skip_value(authservid, 0);
203 f1509490 2022-03-20 martijn if (authservid + strlen(authservid) != end)
204 f1509490 2022-03-20 martijn osmtpd_errx(1, "Invalid authservid");
205 f1509490 2022-03-20 martijn }
206 f1509490 2022-03-20 martijn }
207 f1509490 2022-03-20 martijn
208 f1509490 2022-03-20 martijn void
209 f1509490 2022-03-20 martijn dkim_dataline(struct osmtpd_ctx *ctx, const char *line)
210 f1509490 2022-03-20 martijn {
211 f1509490 2022-03-20 martijn struct message *msg = ctx->local_message;
212 f1509490 2022-03-20 martijn size_t i;
213 f1509490 2022-03-20 martijn
214 b64f38ad 2025-06-02 kirill if (fprintf(msg->origf, "%s\n", line) < 0)
215 b64f38ad 2025-06-02 kirill osmtpd_err(1, "Couldn't write to tempfile");
216 b64f38ad 2025-06-02 kirill
217 f1509490 2022-03-20 martijn if (line[0] == '.') {
218 f1509490 2022-03-20 martijn line++;
219 f1509490 2022-03-20 martijn if (line[0] == '\0') {
220 f1509490 2022-03-20 martijn msg->readdone = 1;
221 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++) {
222 f1509490 2022-03-20 martijn if (msg->header[i].sig == NULL)
223 f1509490 2022-03-20 martijn continue;
224 f1509490 2022-03-20 martijn dkim_body_verify(msg->header[i].sig);
225 f1509490 2022-03-20 martijn }
226 f1509490 2022-03-20 martijn dkim_message_verify(msg);
227 f1509490 2022-03-20 martijn return;
228 f1509490 2022-03-20 martijn }
229 f1509490 2022-03-20 martijn }
230 f1509490 2022-03-20 martijn if (msg->parsing_headers) {
231 f1509490 2022-03-20 martijn dkim_header_add(ctx, line);
232 f1509490 2022-03-20 martijn if (line[0] == '\0') {
233 f1509490 2022-03-20 martijn msg->parsing_headers = 0;
234 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++) {
235 f1509490 2022-03-20 martijn if (msg->header[i].sig == NULL)
236 f1509490 2022-03-20 martijn continue;
237 f1509490 2022-03-20 martijn if (msg->header[i].sig->query == NULL)
238 f1509490 2022-03-20 martijn dkim_signature_verify(
239 f1509490 2022-03-20 martijn msg->header[i].sig);
240 f1509490 2022-03-20 martijn }
241 f1509490 2022-03-20 martijn }
242 f1509490 2022-03-20 martijn return;
243 f1509490 2022-03-20 martijn } else {
244 f1509490 2022-03-20 martijn dkim_body_parse(msg, line);
245 f1509490 2022-03-20 martijn }
246 f1509490 2022-03-20 martijn }
247 f1509490 2022-03-20 martijn
248 f1509490 2022-03-20 martijn void *
249 f1509490 2022-03-20 martijn dkim_message_new(struct osmtpd_ctx *ctx)
250 f1509490 2022-03-20 martijn {
251 f1509490 2022-03-20 martijn struct message *msg;
252 f1509490 2022-03-20 martijn
253 f1509490 2022-03-20 martijn if ((msg = malloc(sizeof(*msg))) == NULL)
254 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
255 f1509490 2022-03-20 martijn
256 f1509490 2022-03-20 martijn if ((msg->origf = tmpfile()) == NULL) {
257 b64f38ad 2025-06-02 kirill osmtpd_warn(NULL, "Can't open tempfile");
258 348f2e63 2025-01-29 kirill free(msg);
259 f1509490 2022-03-20 martijn return NULL;
260 f1509490 2022-03-20 martijn }
261 f1509490 2022-03-20 martijn msg->ctx = ctx;
262 f1509490 2022-03-20 martijn msg->parsing_headers = 1;
263 f1509490 2022-03-20 martijn msg->body_whitelines = 0;
264 f1509490 2022-03-20 martijn msg->has_body = 0;
265 f1509490 2022-03-20 martijn msg->header = NULL;
266 f1509490 2022-03-20 martijn msg->nheaders = 0;
267 f1509490 2022-03-20 martijn msg->readdone = 0;
268 f1509490 2022-03-20 martijn
269 f1509490 2022-03-20 martijn return msg;
270 f1509490 2022-03-20 martijn }
271 f1509490 2022-03-20 martijn
272 f1509490 2022-03-20 martijn void
273 f1509490 2022-03-20 martijn dkim_message_free(struct osmtpd_ctx *ctx, void *data)
274 f1509490 2022-03-20 martijn {
275 f1509490 2022-03-20 martijn struct message *msg = data;
276 f1509490 2022-03-20 martijn size_t i, j;
277 f1509490 2022-03-20 martijn
278 f1509490 2022-03-20 martijn fclose(msg->origf);
279 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++) {
280 f1509490 2022-03-20 martijn if (msg->header[i].sig != NULL) {
281 f1509490 2022-03-20 martijn free(msg->header[i].sig->b);
282 f1509490 2022-03-20 martijn EVP_MD_CTX_free(msg->header[i].sig->bhctx);
283 f1509490 2022-03-20 martijn for (j = 0; msg->header[i].sig->h != NULL &&
284 f1509490 2022-03-20 martijn msg->header[i].sig->h[j] != NULL; j++)
285 f1509490 2022-03-20 martijn free(msg->header[i].sig->h[j]);
286 f1509490 2022-03-20 martijn free(msg->header[i].sig->h);
287 bf67701d 2022-03-29 martijn EVP_PKEY_free(msg->header[i].sig->p);
288 5ea05a05 2025-06-02 kirill if (msg->header[i].sig->query)
289 5ea05a05 2025-06-02 kirill event_asr_abort(msg->header[i].sig->query);
290 f1509490 2022-03-20 martijn }
291 8cc3b72a 2022-03-27 martijn free(msg->header[i].buf);
292 f1509490 2022-03-20 martijn free(msg->header[i].sig);
293 f1509490 2022-03-20 martijn }
294 f1509490 2022-03-20 martijn free(msg->header);
295 f1509490 2022-03-20 martijn free(msg);
296 f1509490 2022-03-20 martijn }
297 f1509490 2022-03-20 martijn
298 f1509490 2022-03-20 martijn void
299 f1509490 2022-03-20 martijn dkim_header_add(struct osmtpd_ctx *ctx, const char *line)
300 f1509490 2022-03-20 martijn {
301 f1509490 2022-03-20 martijn struct message *msg = ctx->local_message;
302 4e2dd90f 2022-03-26 martijn const char *start, *end, *verify;
303 f1509490 2022-03-20 martijn struct header *headers;
304 f1509490 2022-03-20 martijn size_t i;
305 f1509490 2022-03-20 martijn
306 f1509490 2022-03-20 martijn if (msg->nheaders > 0 &&
307 f1509490 2022-03-20 martijn msg->header[msg->nheaders - 1].readdone == 0) {
308 f1509490 2022-03-20 martijn if (line[0] != ' ' && line[0] != '\t') {
309 f1509490 2022-03-20 martijn msg->header[msg->nheaders - 1].readdone = 1;
310 f1509490 2022-03-20 martijn start = msg->header[msg->nheaders - 1].buf;
311 4e2dd90f 2022-03-26 martijn end = osmtpd_ltok_skip_field_name(start, 0);
312 f1509490 2022-03-20 martijn /* In case someone uses an obs-optional */
313 5c6a6550 2022-03-24 martijn if (end != NULL)
314 4e2dd90f 2022-03-26 martijn verify = osmtpd_ltok_skip_wsp(end, 1);
315 5c6a6550 2022-03-24 martijn if (end != NULL &&
316 5c6a6550 2022-03-24 martijn strncasecmp(
317 f1509490 2022-03-20 martijn start, "DKIM-Signature", end - start) == 0 &&
318 f1509490 2022-03-20 martijn verify[0] == ':')
319 f1509490 2022-03-20 martijn dkim_signature_parse(
320 f1509490 2022-03-20 martijn &msg->header[msg->nheaders - 1]);
321 f1509490 2022-03-20 martijn if (line[0] == '\0')
322 f1509490 2022-03-20 martijn return;
323 f1509490 2022-03-20 martijn } else {
324 f1509490 2022-03-20 martijn dkim_header_cat(ctx, line);
325 f1509490 2022-03-20 martijn return;
326 f1509490 2022-03-20 martijn }
327 f1509490 2022-03-20 martijn }
328 f1509490 2022-03-20 martijn if (msg->nheaders % 10 == 0) {
329 f1509490 2022-03-20 martijn if ((headers = recallocarray(msg->header, msg->nheaders,
330 b64f38ad 2025-06-02 kirill msg->nheaders + 10, sizeof(*msg->header))) == NULL)
331 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
332 f1509490 2022-03-20 martijn msg->header = headers;
333 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++) {
334 f1509490 2022-03-20 martijn if (msg->header[i].sig == NULL)
335 f1509490 2022-03-20 martijn continue;
336 f1509490 2022-03-20 martijn msg->header[i].sig->header = &msg->header[i];
337 f1509490 2022-03-20 martijn }
338 f1509490 2022-03-20 martijn }
339 f1509490 2022-03-20 martijn msg->header[msg->nheaders].msg = msg;
340 f1509490 2022-03-20 martijn msg->nheaders++;
341 f1509490 2022-03-20 martijn dkim_header_cat(ctx, line);
342 f1509490 2022-03-20 martijn }
343 f1509490 2022-03-20 martijn
344 f1509490 2022-03-20 martijn void
345 f1509490 2022-03-20 martijn dkim_header_cat(struct osmtpd_ctx *ctx, const char *line)
346 f1509490 2022-03-20 martijn {
347 f1509490 2022-03-20 martijn struct message *msg = ctx->local_message;
348 f1509490 2022-03-20 martijn struct header *header = &msg->header[msg->nheaders - 1];
349 f1509490 2022-03-20 martijn char *buf;
350 f1509490 2022-03-20 martijn
351 f1509490 2022-03-20 martijn size_t needed = header->buflen + strlen(line) + 2;
352 f1509490 2022-03-20 martijn
353 f1509490 2022-03-20 martijn if (needed > (header->buflen / 1024) + 1) {
354 f1509490 2022-03-20 martijn buf = reallocarray(header->buf, (needed / 1024) + 1, 1024);
355 b64f38ad 2025-06-02 kirill if (buf == NULL)
356 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
357 f1509490 2022-03-20 martijn header->buf = buf;
358 f1509490 2022-03-20 martijn }
359 f1509490 2022-03-20 martijn header->buflen += snprintf(header->buf + header->buflen,
360 f1509490 2022-03-20 martijn (((needed / 1024) + 1) * 1024) - header->buflen, "%s%s",
361 f1509490 2022-03-20 martijn header->buflen == 0 ? "" : "\r\n", line);
362 f1509490 2022-03-20 martijn }
363 f1509490 2022-03-20 martijn
364 f1509490 2022-03-20 martijn void
365 f1509490 2022-03-20 martijn dkim_signature_parse(struct header *header)
366 f1509490 2022-03-20 martijn {
367 cc08de51 2022-03-24 martijn struct signature *sig;
368 4e2dd90f 2022-03-26 martijn const char *buf, *i, *end;
369 4e2dd90f 2022-03-26 martijn char tagname[3];
370 f1509490 2022-03-20 martijn char subdomain[HOST_NAME_MAX + 1];
371 cc08de51 2022-03-24 martijn size_t ilen, dlen;
372 f1509490 2022-03-20 martijn
373 f1509490 2022-03-20 martijn /* Format checked by dkim_header_add */
374 4e2dd90f 2022-03-26 martijn buf = osmtpd_ltok_skip_field_name(header->buf, 0);
375 4e2dd90f 2022-03-26 martijn buf = osmtpd_ltok_skip_wsp(buf, 1) + 1;
376 f1509490 2022-03-20 martijn
377 b64f38ad 2025-06-02 kirill if ((header->sig = calloc(1, sizeof(*header->sig))) == NULL)
378 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
379 f1509490 2022-03-20 martijn sig = header->sig;
380 f1509490 2022-03-20 martijn sig->header = header;
381 f1509490 2022-03-20 martijn sig->l = -1;
382 f1509490 2022-03-20 martijn sig->t = -1;
383 f1509490 2022-03-20 martijn sig->x = -1;
384 f1509490 2022-03-20 martijn
385 4e2dd90f 2022-03-26 martijn end = osmtpd_ltok_skip_tag_list(buf, 0);
386 f1509490 2022-03-20 martijn if (end == NULL || end[0] != '\0') {
387 4e2dd90f 2022-03-26 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid tag-list");
388 f1509490 2022-03-20 martijn return;
389 f1509490 2022-03-20 martijn }
390 f1509490 2022-03-20 martijn
391 f1509490 2022-03-20 martijn while (buf[0] != '\0') {
392 4e2dd90f 2022-03-26 martijn buf = osmtpd_ltok_skip_fws(buf, 1);
393 4e2dd90f 2022-03-26 martijn end = osmtpd_ltok_skip_tag_name(buf, 0);
394 f1509490 2022-03-20 martijn
395 f1509490 2022-03-20 martijn /* Unknown tag-name */
396 f1509490 2022-03-20 martijn if ((size_t)(end - buf) >= sizeof(tagname))
397 f1509490 2022-03-20 martijn tagname[0] = '\0';
398 f1509490 2022-03-20 martijn else
399 f1509490 2022-03-20 martijn strlcpy(tagname, buf, (end - buf) + 1);
400 4e2dd90f 2022-03-26 martijn buf = osmtpd_ltok_skip_fws(end, 1);
401 f1509490 2022-03-20 martijn /* '=' */
402 4e2dd90f 2022-03-26 martijn buf = osmtpd_ltok_skip_fws(buf + 1, 1);
403 4e2dd90f 2022-03-26 martijn end = osmtpd_ltok_skip_tag_value(buf, 1);
404 f1509490 2022-03-20 martijn if (strcmp(tagname, "v") == 0)
405 f1509490 2022-03-20 martijn dkim_signature_parse_v(sig, buf, end);
406 f1509490 2022-03-20 martijn else if (strcmp(tagname, "a") == 0)
407 f1509490 2022-03-20 martijn dkim_signature_parse_a(sig, buf, end);
408 f1509490 2022-03-20 martijn else if (strcmp(tagname, "b") == 0)
409 f1509490 2022-03-20 martijn dkim_signature_parse_b(sig, buf, end);
410 f1509490 2022-03-20 martijn else if (strcmp(tagname, "bh") == 0)
411 f1509490 2022-03-20 martijn dkim_signature_parse_bh(sig, buf, end);
412 f1509490 2022-03-20 martijn else if (strcmp(tagname, "c") == 0)
413 f1509490 2022-03-20 martijn dkim_signature_parse_c(sig, buf, end);
414 f1509490 2022-03-20 martijn else if (strcmp(tagname, "d") == 0)
415 f1509490 2022-03-20 martijn dkim_signature_parse_d(sig, buf, end);
416 f1509490 2022-03-20 martijn else if (strcmp(tagname, "h") == 0)
417 f1509490 2022-03-20 martijn dkim_signature_parse_h(sig, buf, end);
418 f1509490 2022-03-20 martijn else if (strcmp(tagname, "i") == 0)
419 f1509490 2022-03-20 martijn dkim_signature_parse_i(sig, buf, end);
420 f1509490 2022-03-20 martijn else if (strcmp(tagname, "l") == 0)
421 f1509490 2022-03-20 martijn dkim_signature_parse_l(sig, buf, end);
422 f1509490 2022-03-20 martijn else if (strcmp(tagname, "q") == 0)
423 f1509490 2022-03-20 martijn dkim_signature_parse_q(sig, buf, end);
424 f1509490 2022-03-20 martijn else if (strcmp(tagname, "s") == 0)
425 f1509490 2022-03-20 martijn dkim_signature_parse_s(sig, buf, end);
426 f1509490 2022-03-20 martijn else if (strcmp(tagname, "t") == 0)
427 f1509490 2022-03-20 martijn dkim_signature_parse_t(sig, buf, end);
428 f1509490 2022-03-20 martijn else if (strcmp(tagname, "x") == 0)
429 f1509490 2022-03-20 martijn dkim_signature_parse_x(sig, buf, end);
430 f1509490 2022-03-20 martijn else if (strcmp(tagname, "z") == 0)
431 f1509490 2022-03-20 martijn dkim_signature_parse_z(sig, buf, end);
432 f1509490 2022-03-20 martijn
433 4e2dd90f 2022-03-26 martijn buf = osmtpd_ltok_skip_fws(end, 1);
434 f1509490 2022-03-20 martijn if (buf[0] == ';')
435 f1509490 2022-03-20 martijn buf++;
436 f1509490 2022-03-20 martijn else if (buf[0] != '\0') {
437 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR,
438 4e2dd90f 2022-03-26 martijn "Invalid tag-list");
439 f1509490 2022-03-20 martijn return;
440 f1509490 2022-03-20 martijn }
441 f1509490 2022-03-20 martijn }
442 f1509490 2022-03-20 martijn if (sig->state != DKIM_UNKNOWN)
443 f1509490 2022-03-20 martijn return;
444 f1509490 2022-03-20 martijn
445 f1509490 2022-03-20 martijn if (sig->v != 1)
446 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Missing v tag");
447 f1509490 2022-03-20 martijn else if (sig->ah == NULL)
448 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Missing a tag");
449 f1509490 2022-03-20 martijn else if (sig->b == NULL)
450 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Missing b tag");
451 f1509490 2022-03-20 martijn else if (sig->bhsz == 0)
452 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Missing bh tag");
453 f1509490 2022-03-20 martijn else if (sig->d[0] == '\0')
454 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Missing d tag");
455 f1509490 2022-03-20 martijn else if (sig->h == NULL)
456 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Missing h tag");
457 f1509490 2022-03-20 martijn else if (sig->s[0] == '\0')
458 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Missing s tag");
459 759a6c7b 2022-03-24 martijn if (sig->state != DKIM_UNKNOWN)
460 759a6c7b 2022-03-24 martijn return;
461 f1509490 2022-03-20 martijn
462 cc08de51 2022-03-24 martijn if (sig->i != NULL) {
463 4e2dd90f 2022-03-26 martijn i = osmtpd_ltok_skip_local_part(sig->i, 1) + 1;
464 efee726e 2022-03-27 martijn ilen = sig->isz - (size_t)(i - sig->i);
465 cc08de51 2022-03-24 martijn dlen = strlen(sig->d);
466 cc08de51 2022-03-24 martijn if (ilen < dlen) {
467 cc08de51 2022-03-24 martijn dkim_signature_state(sig, DKIM_PERMERROR,
468 f85da730 2022-03-27 martijn "i tag not subdomain of d");
469 cc08de51 2022-03-24 martijn return;
470 cc08de51 2022-03-24 martijn }
471 cc08de51 2022-03-24 martijn i += ilen - dlen;
472 efee726e 2022-03-27 martijn if ((i[-1] != '.' && i[-1] != '@') ||
473 7847bd66 2022-04-05 martijn strncasecmp(i, sig->d, dlen) != 0) {
474 cc08de51 2022-03-24 martijn dkim_signature_state(sig, DKIM_PERMERROR,
475 efee726e 2022-03-27 martijn "i tag not subdomain of d");
476 cc08de51 2022-03-24 martijn return;
477 cc08de51 2022-03-24 martijn }
478 cc08de51 2022-03-24 martijn }
479 2d83468e 2022-03-24 martijn if (sig->t != -1 && sig->x != -1 && sig->t > sig->x) {
480 2d83468e 2022-03-24 martijn dkim_signature_state(sig, DKIM_PERMERROR, "t tag after x tag");
481 2d83468e 2022-03-24 martijn return;
482 2d83468e 2022-03-24 martijn }
483 cc08de51 2022-03-24 martijn
484 f1509490 2022-03-20 martijn if ((size_t)snprintf(subdomain, sizeof(subdomain), "%s._domainkey.%s",
485 f1509490 2022-03-20 martijn sig->s, sig->d) >= sizeof(subdomain)) {
486 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR,
487 f1509490 2022-03-20 martijn "dns/txt query too long");
488 f1509490 2022-03-20 martijn return;
489 f1509490 2022-03-20 martijn }
490 f1509490 2022-03-20 martijn
491 3cb7796f 2025-06-02 kirill dkim_lookup_record(sig, subdomain);
492 f1509490 2022-03-20 martijn }
493 f1509490 2022-03-20 martijn
494 f1509490 2022-03-20 martijn void
495 3cb7796f 2025-06-02 kirill dkim_lookup_record(struct signature *sig, const char *domain)
496 3cb7796f 2025-06-02 kirill {
497 3cb7796f 2025-06-02 kirill struct asr_query *query;
498 3cb7796f 2025-06-02 kirill
499 3cb7796f 2025-06-02 kirill sig->nqueries++;
500 3cb7796f 2025-06-02 kirill
501 5ea05a05 2025-06-02 kirill if (sig->query != NULL) {
502 5ea05a05 2025-06-02 kirill event_asr_abort(sig->query);
503 5ea05a05 2025-06-02 kirill sig->query = NULL;
504 5ea05a05 2025-06-02 kirill }
505 3cb7796f 2025-06-02 kirill if ((query = res_query_async(domain, C_IN, T_TXT, NULL)) == NULL)
506 3cb7796f 2025-06-02 kirill osmtpd_err(1, "res_query_async");
507 3cb7796f 2025-06-02 kirill
508 3cb7796f 2025-06-02 kirill if ((sig->query = event_asr_run(query, dkim_rr_resolve, sig)) == NULL)
509 3cb7796f 2025-06-02 kirill osmtpd_err(1, "res_query_async");
510 3cb7796f 2025-06-02 kirill }
511 3cb7796f 2025-06-02 kirill
512 3cb7796f 2025-06-02 kirill void
513 4e2dd90f 2022-03-26 martijn dkim_signature_parse_v(struct signature *sig, const char *start, const char *end)
514 f1509490 2022-03-20 martijn {
515 f1509490 2022-03-20 martijn if (sig->v != 0) { /* Duplicate tag */
516 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate v tag");
517 f1509490 2022-03-20 martijn return;
518 f1509490 2022-03-20 martijn }
519 f1509490 2022-03-20 martijn /* Unsupported version */
520 f1509490 2022-03-20 martijn if (start[0] != '1' || start + 1 != end)
521 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_NEUTRAL, "Unsupported v tag");
522 f1509490 2022-03-20 martijn else
523 f1509490 2022-03-20 martijn sig->v = 1;
524 f1509490 2022-03-20 martijn }
525 f1509490 2022-03-20 martijn
526 f1509490 2022-03-20 martijn void
527 4e2dd90f 2022-03-26 martijn dkim_signature_parse_a(struct signature *sig, const char *start, const char *end)
528 f1509490 2022-03-20 martijn {
529 f1509490 2022-03-20 martijn char ah[sizeof("sha256")];
530 f1509490 2022-03-20 martijn
531 f1509490 2022-03-20 martijn if (sig->ah != NULL) {
532 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate a tag");
533 f1509490 2022-03-20 martijn return;
534 f1509490 2022-03-20 martijn }
535 f1509490 2022-03-20 martijn
536 4e2dd90f 2022-03-26 martijn if (osmtpd_ltok_skip_sig_a_tag_alg(start, 0) != end) {
537 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid a tag");
538 f1509490 2022-03-20 martijn return;
539 f1509490 2022-03-20 martijn }
540 f1509490 2022-03-20 martijn sig->a = start;
541 f1509490 2022-03-20 martijn sig->asz = (size_t)(end - start);
542 f1509490 2022-03-20 martijn if (strncmp(start, "rsa-", 4) == 0) {
543 f1509490 2022-03-20 martijn start += 4;
544 f1509490 2022-03-20 martijn sig->ak = EVP_PKEY_RSA;
545 338537df 2022-04-08 martijn sig->sephash = 0;
546 72580136 2022-12-16 martijn #if HAVE_ED25519
547 338537df 2022-04-08 martijn } else if (strncmp(start, "ed25519-", 8) == 0) {
548 338537df 2022-04-08 martijn start += 8;
549 338537df 2022-04-08 martijn sig->ak = EVP_PKEY_ED25519;
550 338537df 2022-04-08 martijn sig->sephash = 1;
551 72580136 2022-12-16 martijn #endif
552 f1509490 2022-03-20 martijn } else {
553 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag k");
554 f1509490 2022-03-20 martijn return;
555 f1509490 2022-03-20 martijn }
556 f1509490 2022-03-20 martijn if ((size_t)(end - start) >= sizeof(ah)) {
557 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag h");
558 f1509490 2022-03-20 martijn return;
559 f1509490 2022-03-20 martijn }
560 f1509490 2022-03-20 martijn strlcpy(ah, start, sizeof(ah));
561 f1509490 2022-03-20 martijn ah[end - start] = '\0';
562 f1509490 2022-03-20 martijn if ((sig->ah = EVP_get_digestbyname(ah)) == NULL) {
563 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_NEUTRAL, "Unsuppored a tag h");
564 f1509490 2022-03-20 martijn return;
565 f1509490 2022-03-20 martijn }
566 b64f38ad 2025-06-02 kirill if ((sig->bhctx = EVP_MD_CTX_new()) == NULL)
567 b64f38ad 2025-06-02 kirill osmtpd_err(1, "EVP_MD_CTX_new");
568 b64f38ad 2025-06-02 kirill
569 f1509490 2022-03-20 martijn if (EVP_DigestInit_ex(sig->bhctx, sig->ah, NULL) <= 0) {
570 b64f38ad 2025-06-02 kirill dkim_signature_state(sig, DKIM_FAIL, "Unsuppored a tag ah");
571 f1509490 2022-03-20 martijn return;
572 f1509490 2022-03-20 martijn }
573 f1509490 2022-03-20 martijn }
574 f1509490 2022-03-20 martijn
575 f1509490 2022-03-20 martijn void
576 4e2dd90f 2022-03-26 martijn dkim_signature_parse_b(struct signature *sig, const char *start, const char *end)
577 f1509490 2022-03-20 martijn {
578 f1509490 2022-03-20 martijn int decodesz;
579 ce4bcd16 2025-06-02 kirill size_t i, j;
580 f1509490 2022-03-20 martijn
581 f1509490 2022-03-20 martijn if (sig->b != NULL) {
582 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate b tag");
583 f1509490 2022-03-20 martijn return;
584 f1509490 2022-03-20 martijn }
585 f1509490 2022-03-20 martijn sig->bheader = start;
586 ce4bcd16 2025-06-02 kirill sig->bheadersz = end - start;
587 ce4bcd16 2025-06-02 kirill if ((sig->b = malloc(((sig->bheadersz / 4) + 1) * 3)) == NULL)
588 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
589 f1509490 2022-03-20 martijn /* EVP_DecodeBlock doesn't handle internal whitespace */
590 f1509490 2022-03-20 martijn EVP_DecodeInit(ectx);
591 ce4bcd16 2025-06-02 kirill if (EVP_DecodeUpdate(ectx, sig->b, &decodesz, sig->bheader,
592 ce4bcd16 2025-06-02 kirill (int) sig->bheadersz) == -1) {
593 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid b tag");
594 f1509490 2022-03-20 martijn return;
595 f1509490 2022-03-20 martijn }
596 f1509490 2022-03-20 martijn sig->bsz = decodesz;
597 f1509490 2022-03-20 martijn if (EVP_DecodeFinal(ectx, sig->b + sig->bsz,
598 f1509490 2022-03-20 martijn &decodesz) == -1) {
599 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid b tag");
600 f1509490 2022-03-20 martijn return;
601 f1509490 2022-03-20 martijn }
602 f1509490 2022-03-20 martijn sig->bsz += decodesz;
603 ce4bcd16 2025-06-02 kirill for (i = 0, j = 0;
604 ce4bcd16 2025-06-02 kirill i < sig->bheadersz && j < HEADER_B_MAX_LEN; i++) {
605 ce4bcd16 2025-06-02 kirill if (isalnum(sig->bheader[i]) || sig->bheader[i] == '/'
606 ce4bcd16 2025-06-02 kirill || sig->bheader[i] == '+' || sig->bheader[i] == '=')
607 ce4bcd16 2025-06-02 kirill sig->bheaderclean[j++] = sig->bheader[i];
608 ce4bcd16 2025-06-02 kirill }
609 ce4bcd16 2025-06-02 kirill sig->bheaderclean[j] = '\0';
610 f1509490 2022-03-20 martijn }
611 f1509490 2022-03-20 martijn
612 f1509490 2022-03-20 martijn void
613 4e2dd90f 2022-03-26 martijn dkim_signature_parse_bh(struct signature *sig, const char *start, const char *end)
614 f1509490 2022-03-20 martijn {
615 4e2dd90f 2022-03-26 martijn const char *b64;
616 f1509490 2022-03-20 martijn size_t n;
617 f1509490 2022-03-20 martijn int decodesz;
618 f1509490 2022-03-20 martijn
619 f1509490 2022-03-20 martijn if (sig->bhsz != 0) {
620 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate bh tag");
621 f1509490 2022-03-20 martijn return;
622 f1509490 2022-03-20 martijn }
623 f1509490 2022-03-20 martijn /*
624 f1509490 2022-03-20 martijn * EVP_Decode* expects sig->bh to be large enough,
625 f1509490 2022-03-20 martijn * so count the actual b64 characters.
626 f1509490 2022-03-20 martijn */
627 f1509490 2022-03-20 martijn b64 = start;
628 f1509490 2022-03-20 martijn n = 0;
629 f1509490 2022-03-20 martijn while (1) {
630 4e2dd90f 2022-03-26 martijn b64 = osmtpd_ltok_skip_fws(b64, 1);
631 4e2dd90f 2022-03-26 martijn if (osmtpd_ltok_skip_alphadigitps(b64, 0) == NULL)
632 f1509490 2022-03-20 martijn break;
633 f1509490 2022-03-20 martijn n++;
634 f1509490 2022-03-20 martijn b64++;
635 f1509490 2022-03-20 martijn }
636 f1509490 2022-03-20 martijn if (b64[0] == '=') {
637 f1509490 2022-03-20 martijn n++;
638 4e2dd90f 2022-03-26 martijn b64 = osmtpd_ltok_skip_fws(b64 + 1, 1);
639 f1509490 2022-03-20 martijn if (b64[0] == '=') {
640 f1509490 2022-03-20 martijn n++;
641 f1509490 2022-03-20 martijn b64++;
642 f1509490 2022-03-20 martijn }
643 f1509490 2022-03-20 martijn }
644 f1509490 2022-03-20 martijn /* Invalid tag value */
645 f1509490 2022-03-20 martijn if (b64 != end || n % 4 != 0 || (n / 4) * 3 > sizeof(sig->bh)) {
646 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
647 f1509490 2022-03-20 martijn return;
648 f1509490 2022-03-20 martijn }
649 f1509490 2022-03-20 martijn /* EVP_DecodeBlock doesn't handle internal whitespace */
650 f1509490 2022-03-20 martijn EVP_DecodeInit(ectx);
651 f1509490 2022-03-20 martijn if (EVP_DecodeUpdate(ectx, sig->bh, &decodesz, start,
652 f1509490 2022-03-20 martijn (int)(end - start)) == -1) {
653 f1509490 2022-03-20 martijn /* Paranoia check */
654 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
655 f1509490 2022-03-20 martijn return;
656 f1509490 2022-03-20 martijn }
657 f1509490 2022-03-20 martijn sig->bhsz = decodesz;
658 f1509490 2022-03-20 martijn if (EVP_DecodeFinal(ectx, sig->bh + sig->bhsz, &decodesz) == -1) {
659 f1509490 2022-03-20 martijn /* Paranoia check */
660 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid bh tag");
661 f1509490 2022-03-20 martijn return;
662 f1509490 2022-03-20 martijn }
663 f1509490 2022-03-20 martijn sig->bhsz += decodesz;
664 f1509490 2022-03-20 martijn }
665 f1509490 2022-03-20 martijn
666 f1509490 2022-03-20 martijn void
667 4e2dd90f 2022-03-26 martijn dkim_signature_parse_c(struct signature *sig, const char *start, const char *end)
668 f1509490 2022-03-20 martijn {
669 f1509490 2022-03-20 martijn if (sig->c != 0) {
670 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate c tag");
671 f1509490 2022-03-20 martijn return;
672 f1509490 2022-03-20 martijn }
673 f1509490 2022-03-20 martijn if (strncmp(start, "simple", 6) == 0) {
674 f1509490 2022-03-20 martijn sig->c = CANON_HEADER_SIMPLE;
675 f1509490 2022-03-20 martijn start += 6;
676 f1509490 2022-03-20 martijn } else if (strncmp(start, "relaxed", 7) == 0) {
677 f1509490 2022-03-20 martijn sig->c = CANON_HEADER_RELAXED;
678 f1509490 2022-03-20 martijn start += 7;
679 f1509490 2022-03-20 martijn } else {
680 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid c tag");
681 f1509490 2022-03-20 martijn return;
682 f1509490 2022-03-20 martijn }
683 f1509490 2022-03-20 martijn if (start[0] == '/') {
684 f1509490 2022-03-20 martijn start++;
685 f1509490 2022-03-20 martijn if (strncmp(start, "simple", 6) == 0) {
686 f1509490 2022-03-20 martijn sig->c |= CANON_BODY_SIMPLE;
687 f1509490 2022-03-20 martijn start += 6;
688 f1509490 2022-03-20 martijn } else if (strncmp(start, "relaxed", 7) == 0) {
689 f1509490 2022-03-20 martijn sig->c |= CANON_BODY_RELAXED;
690 f1509490 2022-03-20 martijn start += 7;
691 f1509490 2022-03-20 martijn } else {
692 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR,
693 f1509490 2022-03-20 martijn "Invalid c tag");
694 f1509490 2022-03-20 martijn return;
695 f1509490 2022-03-20 martijn }
696 f1509490 2022-03-20 martijn }
697 f1509490 2022-03-20 martijn
698 f1509490 2022-03-20 martijn if (start != end) {
699 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid c tag");
700 f1509490 2022-03-20 martijn return;
701 f1509490 2022-03-20 martijn }
702 f1509490 2022-03-20 martijn sig->c |= CANON_DONE;
703 f1509490 2022-03-20 martijn }
704 f1509490 2022-03-20 martijn
705 f1509490 2022-03-20 martijn void
706 4e2dd90f 2022-03-26 martijn dkim_signature_parse_d(struct signature *sig, const char *start, const char *end)
707 f1509490 2022-03-20 martijn {
708 f1509490 2022-03-20 martijn if (sig->d[0] != '\0') {
709 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate d tag");
710 f1509490 2022-03-20 martijn return;
711 f1509490 2022-03-20 martijn }
712 2817edca 2022-03-30 martijn if (osmtpd_ltok_skip_sig_d_tag_value(start, 0) != end ||
713 f1509490 2022-03-20 martijn (size_t)(end - start) >= sizeof(sig->d)) {
714 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid d tag");
715 f1509490 2022-03-20 martijn return;
716 f1509490 2022-03-20 martijn }
717 f1509490 2022-03-20 martijn strlcpy(sig->d, start, end - start + 1);
718 f1509490 2022-03-20 martijn }
719 f1509490 2022-03-20 martijn
720 f1509490 2022-03-20 martijn void
721 4e2dd90f 2022-03-26 martijn dkim_signature_parse_h(struct signature *sig, const char *start, const char *end)
722 f1509490 2022-03-20 martijn {
723 4e2dd90f 2022-03-26 martijn const char *h;
724 f1509490 2022-03-20 martijn size_t n = 0;
725 f1509490 2022-03-20 martijn
726 f1509490 2022-03-20 martijn if (sig->h != NULL) {
727 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate h tag");
728 f1509490 2022-03-20 martijn return;
729 f1509490 2022-03-20 martijn }
730 022782a1 2022-03-30 martijn if (osmtpd_ltok_skip_sig_h_tag_value(start, 0) < end) {
731 04225ff9 2022-03-30 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid h tag");
732 04225ff9 2022-03-30 martijn return;
733 04225ff9 2022-03-30 martijn }
734 f1509490 2022-03-20 martijn h = start;
735 f1509490 2022-03-20 martijn while (1) {
736 4e2dd90f 2022-03-26 martijn if ((h = osmtpd_ltok_skip_hdr_name(h, 0)) == NULL) {
737 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR,
738 f1509490 2022-03-20 martijn "Invalid h tag");
739 f1509490 2022-03-20 martijn return;
740 f1509490 2022-03-20 martijn }
741 f1509490 2022-03-20 martijn n++;
742 f1509490 2022-03-20 martijn /* ';' is part of hdr-name */
743 f1509490 2022-03-20 martijn if (h > end) {
744 f1509490 2022-03-20 martijn h = end;
745 f1509490 2022-03-20 martijn break;
746 f1509490 2022-03-20 martijn }
747 4e2dd90f 2022-03-26 martijn h = osmtpd_ltok_skip_fws(h, 1);
748 f1509490 2022-03-20 martijn if (h[0] != ':')
749 f1509490 2022-03-20 martijn break;
750 4e2dd90f 2022-03-26 martijn h = osmtpd_ltok_skip_fws(h + 1, 1);
751 f1509490 2022-03-20 martijn }
752 b64f38ad 2025-06-02 kirill if ((sig->h = calloc(n + 1, sizeof(*sig->h))) == NULL)
753 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
754 f1509490 2022-03-20 martijn n = 0;
755 f1509490 2022-03-20 martijn h = start;
756 f1509490 2022-03-20 martijn while (1) {
757 4e2dd90f 2022-03-26 martijn h = osmtpd_ltok_skip_hdr_name(start, 0);
758 f1509490 2022-03-20 martijn /* ';' is part of hdr-name */
759 f1509490 2022-03-20 martijn if (h > end) {
760 f1509490 2022-03-20 martijn sig->h[n] = strndup(start, end - start);
761 f1509490 2022-03-20 martijn break;
762 f1509490 2022-03-20 martijn }
763 b64f38ad 2025-06-02 kirill if ((sig->h[n++] = strndup(start, h - start)) == NULL)
764 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
765 4e2dd90f 2022-03-26 martijn start = osmtpd_ltok_skip_fws(h, 1);
766 f1509490 2022-03-20 martijn if (start[0] != ':')
767 f1509490 2022-03-20 martijn break;
768 4e2dd90f 2022-03-26 martijn start = osmtpd_ltok_skip_fws(start + 1, 1);
769 f1509490 2022-03-20 martijn }
770 f1509490 2022-03-20 martijn }
771 f1509490 2022-03-20 martijn
772 f1509490 2022-03-20 martijn void
773 4e2dd90f 2022-03-26 martijn dkim_signature_parse_i(struct signature *sig, const char *start, const char *end)
774 f1509490 2022-03-20 martijn {
775 f1509490 2022-03-20 martijn if (sig->i != NULL) {
776 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate i tag");
777 f1509490 2022-03-20 martijn return;
778 f1509490 2022-03-20 martijn }
779 2b49c695 2022-03-30 martijn if (osmtpd_ltok_skip_sig_i_tag_value(start, 0) != end) {
780 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid i tag");
781 f1509490 2022-03-20 martijn return;
782 f1509490 2022-03-20 martijn }
783 f1509490 2022-03-20 martijn sig->i = start;
784 f1509490 2022-03-20 martijn sig->isz = (size_t)(end - start);
785 f1509490 2022-03-20 martijn }
786 f1509490 2022-03-20 martijn
787 f1509490 2022-03-20 martijn void
788 4e2dd90f 2022-03-26 martijn dkim_signature_parse_l(struct signature *sig, const char *start, const char *end)
789 f1509490 2022-03-20 martijn {
790 f1509490 2022-03-20 martijn long long l;
791 f1509490 2022-03-20 martijn char *lend;
792 f1509490 2022-03-20 martijn
793 f1509490 2022-03-20 martijn if (sig->l != -1) { /* Duplicate tag */
794 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate l tag");
795 f1509490 2022-03-20 martijn return;
796 f1509490 2022-03-20 martijn }
797 f1509490 2022-03-20 martijn errno = 0;
798 f1509490 2022-03-20 martijn l = strtoll(start, &lend, 10);
799 f1509490 2022-03-20 martijn /* > 76 digits in stroll is an overflow */
800 4e2dd90f 2022-03-26 martijn if (osmtpd_ltok_skip_digit(start, 0) == NULL ||
801 f1509490 2022-03-20 martijn lend != end || errno != 0) {
802 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid l tag");
803 f1509490 2022-03-20 martijn return;
804 f1509490 2022-03-20 martijn }
805 f1509490 2022-03-20 martijn if (l > SSIZE_MAX) {
806 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "l tag too large");
807 f1509490 2022-03-20 martijn return;
808 f1509490 2022-03-20 martijn }
809 f1509490 2022-03-20 martijn sig->l = (ssize_t)l;
810 f1509490 2022-03-20 martijn }
811 f1509490 2022-03-20 martijn
812 f1509490 2022-03-20 martijn void
813 4e2dd90f 2022-03-26 martijn dkim_signature_parse_q(struct signature *sig, const char *start, const char *end)
814 f1509490 2022-03-20 martijn {
815 4e2dd90f 2022-03-26 martijn const char *qend;
816 f1509490 2022-03-20 martijn
817 f1509490 2022-03-20 martijn if (sig->q != 0) {
818 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate q tag");
819 f1509490 2022-03-20 martijn return;
820 f1509490 2022-03-20 martijn }
821 f1509490 2022-03-20 martijn
822 f1509490 2022-03-20 martijn while (1) {
823 4e2dd90f 2022-03-26 martijn start = osmtpd_ltok_skip_fws(start, 1);
824 4e2dd90f 2022-03-26 martijn qend = osmtpd_ltok_skip_sig_q_tag_method(start, 0);
825 f1509490 2022-03-20 martijn if (qend == NULL) {
826 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid q tag");
827 f1509490 2022-03-20 martijn return;
828 f1509490 2022-03-20 martijn }
829 f1509490 2022-03-20 martijn if (strncmp(start, "dns/txt", qend - start) == 0)
830 f1509490 2022-03-20 martijn sig->q = 1;
831 4e2dd90f 2022-03-26 martijn start = osmtpd_ltok_skip_fws(qend, 1);
832 f1509490 2022-03-20 martijn if (start[0] != ':')
833 f1509490 2022-03-20 martijn break;
834 f1509490 2022-03-20 martijn }
835 f1509490 2022-03-20 martijn if (start != end) {
836 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid q tag");
837 f1509490 2022-03-20 martijn return;
838 f1509490 2022-03-20 martijn }
839 f1509490 2022-03-20 martijn if (sig->q != 1) {
840 f1509490 2022-03-20 martijn sig->q = 1;
841 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_NEUTRAL, "No useable q found");
842 f1509490 2022-03-20 martijn return;
843 f1509490 2022-03-20 martijn }
844 f1509490 2022-03-20 martijn }
845 f1509490 2022-03-20 martijn
846 f1509490 2022-03-20 martijn void
847 4e2dd90f 2022-03-26 martijn dkim_signature_parse_s(struct signature *sig, const char *start, const char *end)
848 f1509490 2022-03-20 martijn {
849 f1509490 2022-03-20 martijn if (sig->s[0] != '\0') {
850 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate s tag");
851 f1509490 2022-03-20 martijn return;
852 f1509490 2022-03-20 martijn }
853 4e2dd90f 2022-03-26 martijn if (osmtpd_ltok_skip_selector(start, 0) != end) {
854 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid s tag");
855 f1509490 2022-03-20 martijn return;
856 f1509490 2022-03-20 martijn }
857 f1509490 2022-03-20 martijn strlcpy(sig->s, start, end - start + 1);
858 f1509490 2022-03-20 martijn }
859 f1509490 2022-03-20 martijn
860 f1509490 2022-03-20 martijn void
861 4e2dd90f 2022-03-26 martijn dkim_signature_parse_t(struct signature *sig, const char *start, const char *end)
862 f1509490 2022-03-20 martijn {
863 f1509490 2022-03-20 martijn char *tend;
864 f1509490 2022-03-20 martijn
865 f1509490 2022-03-20 martijn if (sig->t != -1) {
866 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate t tag");
867 f1509490 2022-03-20 martijn return;
868 f1509490 2022-03-20 martijn }
869 f1509490 2022-03-20 martijn errno = 0;
870 f1509490 2022-03-20 martijn sig->t = strtoll(start, &tend, 10);
871 4e2dd90f 2022-03-26 martijn if (osmtpd_ltok_skip_digit(start, 0) == NULL || tend != end ||
872 f1509490 2022-03-20 martijn tend - start > 12 || errno != 0) {
873 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid t tag");
874 f1509490 2022-03-20 martijn return;
875 f1509490 2022-03-20 martijn }
876 f1509490 2022-03-20 martijn }
877 f1509490 2022-03-20 martijn
878 f1509490 2022-03-20 martijn void
879 4e2dd90f 2022-03-26 martijn dkim_signature_parse_x(struct signature *sig, const char *start, const char *end)
880 f1509490 2022-03-20 martijn {
881 f1509490 2022-03-20 martijn char *xend;
882 f1509490 2022-03-20 martijn
883 f1509490 2022-03-20 martijn if (sig->x != -1) {
884 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate x tag");
885 f1509490 2022-03-20 martijn return;
886 f1509490 2022-03-20 martijn }
887 f1509490 2022-03-20 martijn errno = 0;
888 f1509490 2022-03-20 martijn sig->x = strtoll(start, &xend, 10);
889 4e2dd90f 2022-03-26 martijn if (osmtpd_ltok_skip_digit(start, 0) == NULL || xend != end ||
890 f1509490 2022-03-20 martijn xend - start > 12 || errno != 0) {
891 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid x tag");
892 f1509490 2022-03-20 martijn return;
893 f1509490 2022-03-20 martijn }
894 f1509490 2022-03-20 martijn }
895 f1509490 2022-03-20 martijn
896 f1509490 2022-03-20 martijn void
897 4e2dd90f 2022-03-26 martijn dkim_signature_parse_z(struct signature *sig, const char *start, const char *end)
898 f1509490 2022-03-20 martijn {
899 f1509490 2022-03-20 martijn if (sig->z != 0) {
900 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Duplicate z tag");
901 f1509490 2022-03-20 martijn return;
902 f1509490 2022-03-20 martijn }
903 f1509490 2022-03-20 martijn
904 f1509490 2022-03-20 martijn sig->z = 1;
905 4e2dd90f 2022-03-26 martijn if (osmtpd_ltok_skip_sig_z_tag_value(start, 0) != end) {
906 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Invalid z tag");
907 f1509490 2022-03-20 martijn return;
908 f1509490 2022-03-20 martijn }
909 f1509490 2022-03-20 martijn }
910 f1509490 2022-03-20 martijn
911 f1509490 2022-03-20 martijn void
912 f1509490 2022-03-20 martijn dkim_signature_verify(struct signature *sig)
913 f1509490 2022-03-20 martijn {
914 f1509490 2022-03-20 martijn struct message *msg = sig->header->msg;
915 f1509490 2022-03-20 martijn static EVP_MD_CTX *bctx = NULL;
916 338537df 2022-04-08 martijn char digest[EVP_MAX_MD_SIZE];
917 338537df 2022-04-08 martijn unsigned int digestsz;
918 4e2dd90f 2022-03-26 martijn const char *end;
919 f1509490 2022-03-20 martijn size_t i, header;
920 f1509490 2022-03-20 martijn
921 f1509490 2022-03-20 martijn if (sig->state != DKIM_UNKNOWN)
922 f1509490 2022-03-20 martijn return;
923 f1509490 2022-03-20 martijn
924 f1509490 2022-03-20 martijn if (bctx == NULL) {
925 b64f38ad 2025-06-02 kirill if ((bctx = EVP_MD_CTX_new()) == NULL)
926 b64f38ad 2025-06-02 kirill osmtpd_err(1, "EVP_MD_CTX_new");
927 f1509490 2022-03-20 martijn }
928 8015d1bc 2022-03-23 martijn EVP_MD_CTX_reset(bctx);
929 338537df 2022-04-08 martijn if (!sig->sephash) {
930 338537df 2022-04-08 martijn if (EVP_DigestVerifyInit(bctx, NULL, sig->ah, NULL,
931 b64f38ad 2025-06-02 kirill sig->p) != 1) {
932 b64f38ad 2025-06-02 kirill dkim_signature_state(sig, DKIM_FAIL, "ah tag");
933 338537df 2022-04-08 martijn return;
934 338537df 2022-04-08 martijn }
935 338537df 2022-04-08 martijn } else {
936 338537df 2022-04-08 martijn if (EVP_DigestInit_ex(bctx, sig->ah, NULL) != 1) {
937 b64f38ad 2025-06-02 kirill dkim_signature_state(sig, DKIM_FAIL, "ah tag");
938 338537df 2022-04-08 martijn return;
939 338537df 2022-04-08 martijn }
940 f1509490 2022-03-20 martijn }
941 f1509490 2022-03-20 martijn
942 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++)
943 f1509490 2022-03-20 martijn msg->header[i].parsed = 0;
944 f1509490 2022-03-20 martijn
945 f1509490 2022-03-20 martijn for (header = 0; sig->h[header] != NULL; header++) {
946 f1509490 2022-03-20 martijn for (i = msg->nheaders; i > 0; ) {
947 f1509490 2022-03-20 martijn i--;
948 f1509490 2022-03-20 martijn if (msg->header[i].parsed ||
949 f1509490 2022-03-20 martijn strncasecmp(msg->header[i].buf, sig->h[header],
950 f1509490 2022-03-20 martijn strlen(sig->h[header])) != 0 ||
951 f1509490 2022-03-20 martijn msg->header[i].sig == sig)
952 f1509490 2022-03-20 martijn continue;
953 4e2dd90f 2022-03-26 martijn end = osmtpd_ltok_skip_fws(
954 f1509490 2022-03-20 martijn msg->header[i].buf + strlen(sig->h[header]), 1);
955 f1509490 2022-03-20 martijn if (end[0] != ':')
956 f1509490 2022-03-20 martijn continue;
957 f1509490 2022-03-20 martijn dkim_signature_header(bctx, sig, &(msg->header[i]));
958 f1509490 2022-03-20 martijn msg->header[i].parsed = 1;
959 f185a96d 2025-01-29 kirill break;
960 f1509490 2022-03-20 martijn }
961 f1509490 2022-03-20 martijn }
962 f1509490 2022-03-20 martijn dkim_signature_header(bctx, sig, sig->header);
963 338537df 2022-04-08 martijn if (!sig->sephash) {
964 338537df 2022-04-08 martijn if (EVP_DigestVerifyFinal(bctx, sig->b, sig->bsz) != 1)
965 338537df 2022-04-08 martijn dkim_signature_state(sig, DKIM_FAIL, "b mismatch");
966 338537df 2022-04-08 martijn } else {
967 b64f38ad 2025-06-02 kirill if (EVP_DigestFinal_ex(bctx, digest, &digestsz) == 0)
968 b64f38ad 2025-06-02 kirill osmtpd_err(1, "EVP_DigestFinal_ex");
969 b64f38ad 2025-06-02 kirill
970 b64f38ad 2025-06-02 kirill if (EVP_DigestVerifyInit(bctx, NULL, NULL, NULL, sig->p) != 1)
971 b64f38ad 2025-06-02 kirill osmtpd_err(1, "EVP_DigestVerifyInit");
972 b64f38ad 2025-06-02 kirill
973 338537df 2022-04-08 martijn switch (EVP_DigestVerify(bctx, sig->b, sig->bsz, digest,
974 338537df 2022-04-08 martijn digestsz)) {
975 338537df 2022-04-08 martijn case 1:
976 338537df 2022-04-08 martijn break;
977 338537df 2022-04-08 martijn case 0:
978 338537df 2022-04-08 martijn dkim_signature_state(sig, DKIM_FAIL, "b mismatch");
979 338537df 2022-04-08 martijn break;
980 338537df 2022-04-08 martijn default:
981 b64f38ad 2025-06-02 kirill osmtpd_err(1, "EVP_DigestVerify");
982 338537df 2022-04-08 martijn }
983 338537df 2022-04-08 martijn }
984 f1509490 2022-03-20 martijn }
985 f1509490 2022-03-20 martijn
986 338537df 2022-04-08 martijn /* EVP_DigestVerifyUpdate is a macro, so we can't alias this on a variable */
987 338537df 2022-04-08 martijn #define dkim_b_digest_update(a, b, c) \
988 338537df 2022-04-08 martijn (sig->sephash ? EVP_DigestUpdate((a), (b), (c)) :\
989 338537df 2022-04-08 martijn EVP_DigestVerifyUpdate((a), (b), (c)))
990 338537df 2022-04-08 martijn
991 f1509490 2022-03-20 martijn void
992 f1509490 2022-03-20 martijn dkim_signature_header(EVP_MD_CTX *bctx, struct signature *sig,
993 f1509490 2022-03-20 martijn struct header *header)
994 f1509490 2022-03-20 martijn {
995 4e2dd90f 2022-03-26 martijn char c;
996 4e2dd90f 2022-03-26 martijn const char *ptr = header->buf, *end;
997 f1509490 2022-03-20 martijn int inhdrname = 1;
998 f1509490 2022-03-20 martijn int canon = sig->c & CANON_HEADER;
999 f1509490 2022-03-20 martijn
1000 f1509490 2022-03-20 martijn for (ptr = header->buf; ptr[0] != '\0'; ptr++) {
1001 f1509490 2022-03-20 martijn if (inhdrname) {
1002 f1509490 2022-03-20 martijn if (canon == CANON_HEADER_RELAXED) {
1003 4e2dd90f 2022-03-26 martijn ptr = osmtpd_ltok_skip_fws(ptr, 1);
1004 f1509490 2022-03-20 martijn c = tolower(ptr[0]);
1005 f1509490 2022-03-20 martijn } else
1006 f1509490 2022-03-20 martijn c = ptr[0];
1007 f1509490 2022-03-20 martijn if (c == ':') {
1008 f1509490 2022-03-20 martijn inhdrname = 0;
1009 f1509490 2022-03-20 martijn if (canon == CANON_HEADER_RELAXED)
1010 4e2dd90f 2022-03-26 martijn ptr = osmtpd_ltok_skip_fws(
1011 f1509490 2022-03-20 martijn ptr + 1, 1) - 1;
1012 f1509490 2022-03-20 martijn }
1013 b64f38ad 2025-06-02 kirill if (dkim_b_digest_update(bctx, &c, 1) == 0)
1014 b64f38ad 2025-06-02 kirill osmtpd_errx(1, "dkim_b_digest_update");
1015 f1509490 2022-03-20 martijn continue;
1016 f1509490 2022-03-20 martijn }
1017 4e2dd90f 2022-03-26 martijn end = osmtpd_ltok_skip_fws(ptr, 1);
1018 f1509490 2022-03-20 martijn if (end == ptr) {
1019 f1509490 2022-03-20 martijn if (sig->header == header && ptr == sig->bheader) {
1020 4e2dd90f 2022-03-26 martijn ptr = osmtpd_ltok_skip_tag_value(
1021 f1509490 2022-03-20 martijn ptr, 0) - 1;
1022 f1509490 2022-03-20 martijn continue;
1023 f1509490 2022-03-20 martijn }
1024 b64f38ad 2025-06-02 kirill if (dkim_b_digest_update(bctx, ptr, 1) == 0)
1025 b64f38ad 2025-06-02 kirill osmtpd_errx(1, "dkim_b_digest_update");
1026 f1509490 2022-03-20 martijn } else {
1027 f1509490 2022-03-20 martijn if (canon == CANON_HEADER_RELAXED) {
1028 f1509490 2022-03-20 martijn if (end[0] == '\0')
1029 f1509490 2022-03-20 martijn continue;
1030 b64f38ad 2025-06-02 kirill if (dkim_b_digest_update(bctx, " ", 1) == 0)
1031 b64f38ad 2025-06-02 kirill osmtpd_errx(1, "dkim_b_digest_update");
1032 f1509490 2022-03-20 martijn } else {
1033 338537df 2022-04-08 martijn if (dkim_b_digest_update(bctx, ptr,
1034 b64f38ad 2025-06-02 kirill end - ptr) == 0)
1035 b64f38ad 2025-06-02 kirill osmtpd_errx(1, "dkim_b_digest_update");
1036 f1509490 2022-03-20 martijn }
1037 28bea7cc 2022-03-21 martijn ptr = end - 1;
1038 f1509490 2022-03-20 martijn }
1039 f1509490 2022-03-20 martijn
1040 f1509490 2022-03-20 martijn }
1041 f1509490 2022-03-20 martijn if (sig->header != header) {
1042 b64f38ad 2025-06-02 kirill if (dkim_b_digest_update(bctx, "\r\n", 2) == 0)
1043 b64f38ad 2025-06-02 kirill osmtpd_errx(1, "dkim_b_digest_update");
1044 f1509490 2022-03-20 martijn }
1045 f1509490 2022-03-20 martijn }
1046 f1509490 2022-03-20 martijn
1047 f1509490 2022-03-20 martijn void
1048 f1509490 2022-03-20 martijn dkim_signature_state(struct signature *sig, enum state state,
1049 f1509490 2022-03-20 martijn const char *reason)
1050 f1509490 2022-03-20 martijn {
1051 f1509490 2022-03-20 martijn if (sig->query != NULL) {
1052 f1509490 2022-03-20 martijn event_asr_abort(sig->query);
1053 f1509490 2022-03-20 martijn sig->query = NULL;
1054 f1509490 2022-03-20 martijn }
1055 f1509490 2022-03-20 martijn switch (sig->state) {
1056 f1509490 2022-03-20 martijn case DKIM_UNKNOWN:
1057 f1509490 2022-03-20 martijn break;
1058 f1509490 2022-03-20 martijn case DKIM_PASS:
1059 f1509490 2022-03-20 martijn case DKIM_FAIL:
1060 f1509490 2022-03-20 martijn osmtpd_errx(1, "Unexpected transition");
1061 f1509490 2022-03-20 martijn case DKIM_POLICY:
1062 f1509490 2022-03-20 martijn if (state == DKIM_PASS)
1063 f1509490 2022-03-20 martijn return;
1064 f1509490 2022-03-20 martijn break;
1065 f1509490 2022-03-20 martijn case DKIM_NEUTRAL:
1066 f1509490 2022-03-20 martijn if (state == DKIM_PASS)
1067 f1509490 2022-03-20 martijn return;
1068 f1509490 2022-03-20 martijn if (state == DKIM_TEMPERROR || state == DKIM_PERMERROR)
1069 f1509490 2022-03-20 martijn break;
1070 f1509490 2022-03-20 martijn osmtpd_errx(1, "Unexpected transition");
1071 f1509490 2022-03-20 martijn case DKIM_TEMPERROR:
1072 f1509490 2022-03-20 martijn if (state == DKIM_PERMERROR)
1073 f1509490 2022-03-20 martijn break;
1074 f1509490 2022-03-20 martijn return;
1075 f1509490 2022-03-20 martijn case DKIM_PERMERROR:
1076 f1509490 2022-03-20 martijn return;
1077 f1509490 2022-03-20 martijn }
1078 f1509490 2022-03-20 martijn sig->state = state;
1079 f1509490 2022-03-20 martijn sig->state_reason = reason;
1080 f1509490 2022-03-20 martijn }
1081 f1509490 2022-03-20 martijn
1082 f1509490 2022-03-20 martijn const char *
1083 f1509490 2022-03-20 martijn dkim_state2str(enum state state)
1084 f1509490 2022-03-20 martijn {
1085 f1509490 2022-03-20 martijn switch (state)
1086 f1509490 2022-03-20 martijn {
1087 f1509490 2022-03-20 martijn case DKIM_UNKNOWN:
1088 f1509490 2022-03-20 martijn return "unknown";
1089 f1509490 2022-03-20 martijn case DKIM_PASS:
1090 f1509490 2022-03-20 martijn return "pass";
1091 f1509490 2022-03-20 martijn case DKIM_FAIL:
1092 f1509490 2022-03-20 martijn return "fail";
1093 f1509490 2022-03-20 martijn case DKIM_POLICY:
1094 f1509490 2022-03-20 martijn return "policy";
1095 f1509490 2022-03-20 martijn case DKIM_NEUTRAL:
1096 f1509490 2022-03-20 martijn return "neutral";
1097 f1509490 2022-03-20 martijn case DKIM_TEMPERROR:
1098 f1509490 2022-03-20 martijn return "temperror";
1099 f1509490 2022-03-20 martijn case DKIM_PERMERROR:
1100 f1509490 2022-03-20 martijn return "permerror";
1101 f1509490 2022-03-20 martijn }
1102 f1509490 2022-03-20 martijn }
1103 f1509490 2022-03-20 martijn
1104 f1509490 2022-03-20 martijn void
1105 f1509490 2022-03-20 martijn dkim_rr_resolve(struct asr_result *ar, void *arg)
1106 f1509490 2022-03-20 martijn {
1107 f1509490 2022-03-20 martijn struct signature *sig = arg;
1108 f1509490 2022-03-20 martijn char key[UINT16_MAX + 1];
1109 f1509490 2022-03-20 martijn const char *rr_txt;
1110 f1509490 2022-03-20 martijn size_t keylen, cstrlen;
1111 f1509490 2022-03-20 martijn struct unpack pack;
1112 f1509490 2022-03-20 martijn struct dns_header h;
1113 f1509490 2022-03-20 martijn struct dns_query q;
1114 f1509490 2022-03-20 martijn struct dns_rr rr;
1115 3cb7796f 2025-06-02 kirill char buf[HOST_NAME_MAX + 1];
1116 f1509490 2022-03-20 martijn
1117 f1509490 2022-03-20 martijn sig->query = NULL;
1118 f1509490 2022-03-20 martijn
1119 f1509490 2022-03-20 martijn if (ar->ar_h_errno == TRY_AGAIN || ar->ar_h_errno == NO_RECOVERY) {
1120 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_TEMPERROR,
1121 f1509490 2022-03-20 martijn hstrerror(ar->ar_h_errno));
1122 1c524edb 2022-03-24 martijn goto verify;
1123 f1509490 2022-03-20 martijn }
1124 f1509490 2022-03-20 martijn if (ar->ar_h_errno != NETDB_SUCCESS) {
1125 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR,
1126 f1509490 2022-03-20 martijn hstrerror(ar->ar_h_errno));
1127 1c524edb 2022-03-24 martijn goto verify;
1128 f1509490 2022-03-20 martijn }
1129 f1509490 2022-03-20 martijn
1130 f1509490 2022-03-20 martijn unpack_init(&pack, ar->ar_data, ar->ar_datalen);
1131 f1509490 2022-03-20 martijn if (unpack_header(&pack, &h) != 0 ||
1132 f1509490 2022-03-20 martijn unpack_query(&pack, &q) != 0) {
1133 3cb7796f 2025-06-02 kirill osmtpd_warn(sig->header->msg->ctx,
1134 3cb7796f 2025-06-02 kirill "Mallformed DKIM DNS response for domain %s: %s",
1135 3cb7796f 2025-06-02 kirill print_dname(q.q_dname, buf, sizeof(buf)),
1136 3cb7796f 2025-06-02 kirill pack.err);
1137 3cb7796f 2025-06-02 kirill dkim_signature_state(sig, DKIM_PERMERROR, pack.err);
1138 1c524edb 2022-03-24 martijn goto verify;
1139 f1509490 2022-03-20 martijn }
1140 3cb7796f 2025-06-02 kirill
1141 f1509490 2022-03-20 martijn for (; h.ancount > 0; h.ancount--) {
1142 3cb7796f 2025-06-02 kirill if (unpack_rr(&pack, &rr) != 0) {
1143 3cb7796f 2025-06-02 kirill osmtpd_warn(sig->header->msg->ctx,
1144 3cb7796f 2025-06-02 kirill "Mallformed DKIM DNS record for domain %s: %s",
1145 3cb7796f 2025-06-02 kirill print_dname(q.q_dname, buf, sizeof(buf)),
1146 3cb7796f 2025-06-02 kirill pack.err);
1147 f1509490 2022-03-20 martijn continue;
1148 3cb7796f 2025-06-02 kirill }
1149 f1509490 2022-03-20 martijn
1150 3cb7796f 2025-06-02 kirill /* If we below limit, follow CNAME*/
1151 3cb7796f 2025-06-02 kirill if (rr.rr_type == T_CNAME &&
1152 3cb7796f 2025-06-02 kirill sig->nqueries < DKIM_LOOKUP_LOOKUP_LIMIT ) {
1153 3cb7796f 2025-06-02 kirill print_dname(rr.rr.cname.cname, buf, sizeof(buf));
1154 3cb7796f 2025-06-02 kirill dkim_lookup_record(sig, buf);
1155 3cb7796f 2025-06-02 kirill free(ar->ar_data);
1156 3cb7796f 2025-06-02 kirill return;
1157 3cb7796f 2025-06-02 kirill }
1158 3cb7796f 2025-06-02 kirill
1159 3cb7796f 2025-06-02 kirill if (rr.rr_type != T_TXT) {
1160 3cb7796f 2025-06-02 kirill osmtpd_warn(sig->header->msg->ctx,
1161 3cb7796f 2025-06-02 kirill "Unexpected DKIM DNS record: %d for domain %s",
1162 3cb7796f 2025-06-02 kirill rr.rr_type,
1163 3cb7796f 2025-06-02 kirill print_dname(q.q_dname, buf, sizeof(buf)));
1164 3cb7796f 2025-06-02 kirill continue;
1165 3cb7796f 2025-06-02 kirill }
1166 3cb7796f 2025-06-02 kirill
1167 f1509490 2022-03-20 martijn keylen = 0;
1168 f1509490 2022-03-20 martijn rr_txt = rr.rr.other.rdata;
1169 f1509490 2022-03-20 martijn while (rr.rr.other.rdlen > 0) {
1170 f1509490 2022-03-20 martijn cstrlen = ((const unsigned char *)rr_txt)[0];
1171 f1509490 2022-03-20 martijn if (cstrlen >= rr.rr.other.rdlen ||
1172 f1509490 2022-03-20 martijn keylen + cstrlen >= sizeof(key))
1173 f1509490 2022-03-20 martijn break;
1174 f1509490 2022-03-20 martijn /*
1175 f1509490 2022-03-20 martijn * RFC 6376 Section 3.6.2.2
1176 f1509490 2022-03-20 martijn * Strings in a TXT RR MUST be concatenated together
1177 f1509490 2022-03-20 martijn * before use with no intervening whitespace.
1178 f1509490 2022-03-20 martijn */
1179 f1509490 2022-03-20 martijn strlcpy(key + keylen, rr_txt + 1, cstrlen + 1);
1180 f1509490 2022-03-20 martijn rr.rr.other.rdlen -= (cstrlen + 1);
1181 f1509490 2022-03-20 martijn rr_txt += (cstrlen + 1);
1182 f1509490 2022-03-20 martijn keylen += cstrlen;
1183 f1509490 2022-03-20 martijn }
1184 f1509490 2022-03-20 martijn if (rr.rr.other.rdlen > 0) /* Invalid TXT RDATA */
1185 f1509490 2022-03-20 martijn continue;
1186 f1509490 2022-03-20 martijn
1187 f1509490 2022-03-20 martijn if (dkim_key_text_parse(sig, key))
1188 f1509490 2022-03-20 martijn break;
1189 f1509490 2022-03-20 martijn }
1190 f1509490 2022-03-20 martijn
1191 f1509490 2022-03-20 martijn if (h.ancount == 0) {
1192 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR,
1193 f1509490 2022-03-20 martijn "No matching key found");
1194 f1509490 2022-03-20 martijn } else {
1195 f1509490 2022-03-20 martijn /* Only verify if all headers have been read */
1196 f1509490 2022-03-20 martijn if (!sig->header->msg->parsing_headers)
1197 f1509490 2022-03-20 martijn dkim_signature_verify(sig);
1198 f1509490 2022-03-20 martijn }
1199 1c524edb 2022-03-24 martijn verify:
1200 8986823b 2022-03-29 martijn free(ar->ar_data);
1201 f1509490 2022-03-20 martijn dkim_message_verify(sig->header->msg);
1202 f1509490 2022-03-20 martijn }
1203 f1509490 2022-03-20 martijn
1204 f1509490 2022-03-20 martijn int
1205 4e2dd90f 2022-03-26 martijn dkim_key_text_parse(struct signature *sig, const char *key)
1206 f1509490 2022-03-20 martijn {
1207 4e2dd90f 2022-03-26 martijn char tagname, *hashname;
1208 3082f098 2022-04-19 martijn const char *end, *tagvend;
1209 4e2dd90f 2022-03-26 martijn char pkraw[UINT16_MAX] = "", pkimp[UINT16_MAX];
1210 b3380529 2022-04-08 martijn size_t pkrawlen = 0, pkoff, linelen;
1211 b3380529 2022-04-08 martijn int h = 0, k = 0, n = 0, p = 0, s = 0, t = 0, first = 1;
1212 f1509490 2022-03-20 martijn BIO *bio;
1213 72580136 2022-12-16 martijn #ifdef HAVE_ED25519
1214 b3380529 2022-04-08 martijn size_t pklen;
1215 b3380529 2022-04-08 martijn int tmp;
1216 72580136 2022-12-16 martijn #endif
1217 f1509490 2022-03-20 martijn
1218 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(key, 1);
1219 f1509490 2022-03-20 martijn /* Validate syntax early */
1220 4e2dd90f 2022-03-26 martijn if ((end = osmtpd_ltok_skip_tag_list(key, 0)) == NULL)
1221 f1509490 2022-03-20 martijn return 0;
1222 f1509490 2022-03-20 martijn
1223 f1509490 2022-03-20 martijn while (key[0] != '\0') {
1224 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(key, 1);
1225 d842ff09 2022-03-27 martijn if ((end = osmtpd_ltok_skip_tag_name(key, 0)) == NULL)
1226 d842ff09 2022-03-27 martijn return 0;
1227 f1509490 2022-03-20 martijn
1228 bccb8fb3 2022-03-27 martijn if ((size_t)(end - key) != 1)
1229 bccb8fb3 2022-03-27 martijn tagname = '\0';
1230 bccb8fb3 2022-03-27 martijn else
1231 bccb8fb3 2022-03-27 martijn tagname = key[0];
1232 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(end, 1);
1233 f1509490 2022-03-20 martijn /* '=' */
1234 7e24437d 2022-03-27 martijn if (key[0] != '=')
1235 d842ff09 2022-03-27 martijn return 0;
1236 d842ff09 2022-03-27 martijn key = osmtpd_ltok_skip_fws(key + 1, 1);
1237 d842ff09 2022-03-27 martijn if ((end = osmtpd_ltok_skip_tag_value(key, 0)) == NULL)
1238 d842ff09 2022-03-27 martijn return 0;
1239 f1509490 2022-03-20 martijn switch (tagname) {
1240 f1509490 2022-03-20 martijn case 'v':
1241 2b1224a8 2022-03-27 martijn /*
1242 2b1224a8 2022-03-27 martijn * RFC 6376 section 3.6.1, v=:
1243 2b1224a8 2022-03-27 martijn * RECOMMENDED...This tag MUST be the first tag in the
1244 2b1224a8 2022-03-27 martijn * record.
1245 2b1224a8 2022-03-27 martijn */
1246 2b1224a8 2022-03-27 martijn if (!first ||
1247 2b1224a8 2022-03-27 martijn osmtpd_ltok_skip_key_v_tag_value(key, 0) != end)
1248 2b1224a8 2022-03-27 martijn return 0;
1249 6eeac6a1 2022-03-27 martijn key = end;
1250 2b1224a8 2022-03-27 martijn break;
1251 f1509490 2022-03-20 martijn case 'h':
1252 f1509490 2022-03-20 martijn if (h != 0) /* Duplicate tag */
1253 f1509490 2022-03-20 martijn return 0;
1254 f1509490 2022-03-20 martijn /* Invalid tag value */
1255 475eae65 2022-03-27 martijn if (osmtpd_ltok_skip_key_h_tag_value(key, 0) != end)
1256 f1509490 2022-03-20 martijn return 0;
1257 f1509490 2022-03-20 martijn while (1) {
1258 475eae65 2022-03-27 martijn if ((tagvend = osmtpd_ltok_skip_key_h_tag_alg(
1259 048fb98b 2022-03-21 martijn key, 0)) == NULL)
1260 f1509490 2022-03-20 martijn break;
1261 4e2dd90f 2022-03-26 martijn hashname = strndup(key, tagvend - key);
1262 b64f38ad 2025-06-02 kirill if (hashname == NULL)
1263 b64f38ad 2025-06-02 kirill osmtpd_err(1, "strndup");
1264 4e2dd90f 2022-03-26 martijn if (EVP_get_digestbyname(hashname) == sig->ah) {
1265 4e2dd90f 2022-03-26 martijn free(hashname);
1266 f1509490 2022-03-20 martijn h = 1;
1267 f1509490 2022-03-20 martijn break;
1268 f1509490 2022-03-20 martijn }
1269 4e2dd90f 2022-03-26 martijn free(hashname);
1270 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(tagvend, 1);
1271 f1509490 2022-03-20 martijn if (key[0] != ':')
1272 f1509490 2022-03-20 martijn break;
1273 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(key + 1, 1);
1274 f1509490 2022-03-20 martijn }
1275 f1509490 2022-03-20 martijn if (h != 1)
1276 f1509490 2022-03-20 martijn return 0;
1277 f1509490 2022-03-20 martijn key = end;
1278 f1509490 2022-03-20 martijn break;
1279 f1509490 2022-03-20 martijn case 'k':
1280 f1509490 2022-03-20 martijn if (k != 0) /* Duplicate tag */
1281 f1509490 2022-03-20 martijn return 0;
1282 f1509490 2022-03-20 martijn k = 1;
1283 338537df 2022-04-08 martijn if (strncmp(key, "rsa", end - key) == 0) {
1284 338537df 2022-04-08 martijn if (sig->ak != EVP_PKEY_RSA)
1285 338537df 2022-04-08 martijn return 0;
1286 72580136 2022-12-16 martijn #if HAVE_ED25519
1287 338537df 2022-04-08 martijn } else if (strncmp(key, "ed25519", end - key) == 0) {
1288 338537df 2022-04-08 martijn if (sig->ak != EVP_PKEY_ED25519)
1289 338537df 2022-04-08 martijn return 0;
1290 72580136 2022-12-16 martijn #endif
1291 338537df 2022-04-08 martijn } else
1292 f1509490 2022-03-20 martijn return 0;
1293 f1509490 2022-03-20 martijn key = end;
1294 f1509490 2022-03-20 martijn break;
1295 f1509490 2022-03-20 martijn case 'n':
1296 f1509490 2022-03-20 martijn if (n != 0) /* Duplicate tag */
1297 f1509490 2022-03-20 martijn return 0;
1298 f1509490 2022-03-20 martijn n = 1;
1299 749b2bf7 2022-03-28 martijn /* semicolon is part of safe-char */
1300 749b2bf7 2022-03-28 martijn if (osmtpd_ltok_skip_key_n_tag_value(key, 0) < end)
1301 4e2dd90f 2022-03-26 martijn return 0;
1302 f1509490 2022-03-20 martijn key = end;
1303 f1509490 2022-03-20 martijn break;
1304 f1509490 2022-03-20 martijn case 'p':
1305 62823db4 2022-04-08 martijn if (p != 0) /* Duplicate tag */
1306 f1509490 2022-03-20 martijn return 0;
1307 62823db4 2022-04-08 martijn p = 1;
1308 338537df 2022-04-08 martijn while (1) {
1309 3082f098 2022-04-19 martijn key = osmtpd_ltok_skip_fws(key, 1);
1310 338537df 2022-04-08 martijn if (osmtpd_ltok_skip_alphadigitps(
1311 3082f098 2022-04-19 martijn key, 0) == NULL)
1312 338537df 2022-04-08 martijn break;
1313 3082f098 2022-04-19 martijn pkraw[pkrawlen++] = key++[0];
1314 338537df 2022-04-08 martijn if (pkrawlen >= sizeof(pkraw))
1315 338537df 2022-04-08 martijn return 0;
1316 338537df 2022-04-08 martijn }
1317 3082f098 2022-04-19 martijn if (key[0] == '=') {
1318 338537df 2022-04-08 martijn pkraw[pkrawlen++] = '=';
1319 3082f098 2022-04-19 martijn key = osmtpd_ltok_skip_fws(key + 1, 1);
1320 338537df 2022-04-08 martijn if (pkrawlen >= sizeof(pkraw))
1321 338537df 2022-04-08 martijn return 0;
1322 3082f098 2022-04-19 martijn if (key[0] == '=') {
1323 338537df 2022-04-08 martijn pkraw[pkrawlen++] = '=';
1324 3082f098 2022-04-19 martijn key++;
1325 338537df 2022-04-08 martijn if (pkrawlen >= sizeof(pkraw))
1326 338537df 2022-04-08 martijn return 0;
1327 338537df 2022-04-08 martijn }
1328 338537df 2022-04-08 martijn }
1329 f1509490 2022-03-20 martijn /* Invalid tag value */
1330 3082f098 2022-04-19 martijn if (pkrawlen % 4 != 0 || key != end)
1331 f1509490 2022-03-20 martijn return 0;
1332 f1509490 2022-03-20 martijn break;
1333 f1509490 2022-03-20 martijn case 's':
1334 f1509490 2022-03-20 martijn if (s != 0) /* Duplicate tag */
1335 f1509490 2022-03-20 martijn return 0;
1336 f1509490 2022-03-20 martijn /* Invalid tag value */
1337 2e9f7a5e 2022-03-27 martijn if (osmtpd_ltok_skip_key_s_tag_value(key, 0) != end)
1338 f1509490 2022-03-20 martijn return 0;
1339 f1509490 2022-03-20 martijn while (1) {
1340 f1509490 2022-03-20 martijn if ((tagvend =
1341 4e2dd90f 2022-03-26 martijn osmtpd_ltok_skip_key_s_tag_type(
1342 71b63ce6 2022-03-22 martijn key, 0)) == NULL)
1343 f1509490 2022-03-20 martijn break;
1344 f1509490 2022-03-20 martijn if (strncmp(key, "*", tagvend - key) == 0 ||
1345 f1509490 2022-03-20 martijn strncmp(key, "email", tagvend - key) == 0) {
1346 f1509490 2022-03-20 martijn s = 1;
1347 f1509490 2022-03-20 martijn break;
1348 f1509490 2022-03-20 martijn }
1349 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(tagvend, 1);
1350 f1509490 2022-03-20 martijn if (key[0] != ':')
1351 f1509490 2022-03-20 martijn break;
1352 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(key + 1, 1);
1353 f1509490 2022-03-20 martijn }
1354 f1509490 2022-03-20 martijn if (s != 1)
1355 f1509490 2022-03-20 martijn return 0;
1356 f1509490 2022-03-20 martijn key = end;
1357 f1509490 2022-03-20 martijn break;
1358 f1509490 2022-03-20 martijn case 't':
1359 f1509490 2022-03-20 martijn if (t != 0) /* Duplicate tag */
1360 f1509490 2022-03-20 martijn return 0;
1361 f1509490 2022-03-20 martijn t = 1;
1362 2e9f7a5e 2022-03-27 martijn if (osmtpd_ltok_skip_key_t_tag_value(key, 0) != end)
1363 f1509490 2022-03-20 martijn return 0;
1364 f1509490 2022-03-20 martijn while (1) {
1365 2e9f7a5e 2022-03-27 martijn tagvend = osmtpd_ltok_skip_key_t_tag_flag(
1366 f1509490 2022-03-20 martijn key, 0);
1367 f1509490 2022-03-20 martijn if (strncmp(key, "y", tagvend - key) == 0)
1368 f1509490 2022-03-20 martijn sig->kt |= KT_Y;
1369 f1509490 2022-03-20 martijn else if (strncmp(key, "s", tagvend - key) == 0)
1370 f1509490 2022-03-20 martijn sig->kt |= KT_S;
1371 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(tagvend, 1);
1372 f1509490 2022-03-20 martijn if (key[0] != ':')
1373 f1509490 2022-03-20 martijn break;
1374 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(key + 1, 1);
1375 f1509490 2022-03-20 martijn }
1376 f1509490 2022-03-20 martijn break;
1377 f1509490 2022-03-20 martijn default:
1378 f1509490 2022-03-20 martijn key = end;
1379 f1509490 2022-03-20 martijn break;
1380 f1509490 2022-03-20 martijn }
1381 f1509490 2022-03-20 martijn
1382 2b1224a8 2022-03-27 martijn first = 0;
1383 4e2dd90f 2022-03-26 martijn key = osmtpd_ltok_skip_fws(key, 1);
1384 f1509490 2022-03-20 martijn if (key[0] == ';')
1385 f1509490 2022-03-20 martijn key++;
1386 f1509490 2022-03-20 martijn else if (key[0] != '\0')
1387 f1509490 2022-03-20 martijn return 0;
1388 f1509490 2022-03-20 martijn }
1389 f1509490 2022-03-20 martijn
1390 62823db4 2022-04-08 martijn if (!p) /* Missing tag */
1391 f1509490 2022-03-20 martijn return 0;
1392 f1509490 2022-03-20 martijn if (k == 0 && sig->ak != EVP_PKEY_RSA) /* Default to RSA */
1393 f1509490 2022-03-20 martijn return 0;
1394 f1509490 2022-03-20 martijn
1395 f1509490 2022-03-20 martijn if (pkraw[0] == '\0') {
1396 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_PERMERROR, "Key is revoked");
1397 f1509490 2022-03-20 martijn return 1;
1398 f1509490 2022-03-20 martijn }
1399 f1509490 2022-03-20 martijn
1400 f1509490 2022-03-20 martijn switch (sig->ak) {
1401 f1509490 2022-03-20 martijn case EVP_PKEY_RSA:
1402 f1509490 2022-03-20 martijn pkoff = strlcpy(pkimp, "-----BEGIN PUBLIC KEY-----\n",
1403 f1509490 2022-03-20 martijn sizeof(pkimp));
1404 f1509490 2022-03-20 martijn linelen = 0;
1405 f1509490 2022-03-20 martijn for (key = pkraw; key[0] != '\0';) {
1406 f1509490 2022-03-20 martijn if (pkoff + 2 >= sizeof(pkimp))
1407 f1509490 2022-03-20 martijn return 0;
1408 2732fb68 2022-04-08 martijn pkimp[pkoff++] = key++[0];
1409 f1509490 2022-03-20 martijn if (++linelen == 64) {
1410 f1509490 2022-03-20 martijn pkimp[pkoff++] = '\n';
1411 f1509490 2022-03-20 martijn linelen = 0;
1412 f1509490 2022-03-20 martijn }
1413 f1509490 2022-03-20 martijn }
1414 f1509490 2022-03-20 martijn /* Leverage pkoff check in loop */
1415 f1509490 2022-03-20 martijn if (linelen != 0)
1416 f1509490 2022-03-20 martijn pkimp[pkoff++] = '\n';
1417 f1509490 2022-03-20 martijn /* PEM_read_bio_PUBKEY will catch truncated keys */
1418 f1509490 2022-03-20 martijn pkoff += strlcpy(pkimp + pkoff, "-----END PUBLIC KEY-----\n",
1419 f1509490 2022-03-20 martijn sizeof(pkimp) - pkoff);
1420 b64f38ad 2025-06-02 kirill if ((bio = BIO_new_mem_buf(pkimp, pkoff)) == NULL)
1421 b64f38ad 2025-06-02 kirill osmtpd_err(1, "BIO_new_mem_buf");
1422 f1509490 2022-03-20 martijn sig->p = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
1423 f1509490 2022-03-20 martijn BIO_free(bio);
1424 338537df 2022-04-08 martijn break;
1425 72580136 2022-12-16 martijn #if HAVE_ED25519
1426 338537df 2022-04-08 martijn case EVP_PKEY_ED25519:
1427 338537df 2022-04-08 martijn if ((pkrawlen / 4) * 3 >= sizeof(pkimp))
1428 f1509490 2022-03-20 martijn return 0;
1429 338537df 2022-04-08 martijn EVP_DecodeInit(ectx);
1430 338537df 2022-04-08 martijn if (EVP_DecodeUpdate(ectx, pkimp, &tmp, pkraw, pkrawlen) == -1)
1431 338537df 2022-04-08 martijn return 0;
1432 338537df 2022-04-08 martijn pklen = tmp;
1433 338537df 2022-04-08 martijn if (EVP_DecodeFinal(ectx, pkimp, &tmp) == -1)
1434 338537df 2022-04-08 martijn return 0;
1435 338537df 2022-04-08 martijn pklen += tmp;
1436 338537df 2022-04-08 martijn sig->p = EVP_PKEY_new_raw_public_key(sig->ak, NULL, pkimp,
1437 338537df 2022-04-08 martijn pklen);
1438 f1509490 2022-03-20 martijn break;
1439 72580136 2022-12-16 martijn #endif
1440 f1509490 2022-03-20 martijn }
1441 338537df 2022-04-08 martijn if (sig->p == NULL) {
1442 338537df 2022-04-08 martijn /*
1443 338537df 2022-04-08 martijn * XXX No clue how to differentiate between invalid key and
1444 338537df 2022-04-08 martijn * temporary failure like *alloc.
1445 338537df 2022-04-08 martijn * Assume invalid key, because it's more likely.
1446 338537df 2022-04-08 martijn */
1447 338537df 2022-04-08 martijn return 0;
1448 338537df 2022-04-08 martijn }
1449 f1509490 2022-03-20 martijn return 1;
1450 f1509490 2022-03-20 martijn }
1451 f1509490 2022-03-20 martijn
1452 f1509490 2022-03-20 martijn void
1453 f1509490 2022-03-20 martijn dkim_body_parse(struct message *msg, const char *line)
1454 f1509490 2022-03-20 martijn {
1455 f1509490 2022-03-20 martijn struct signature *sig;
1456 31ffb916 2022-03-27 martijn const char *end = line, *hash, *prev;
1457 31ffb916 2022-03-27 martijn size_t hashn, len, i;
1458 31ffb916 2022-03-27 martijn int wsp, ret;
1459 f1509490 2022-03-20 martijn
1460 31ffb916 2022-03-27 martijn if (line[0] == '\0') {
1461 f1509490 2022-03-20 martijn msg->body_whitelines++;
1462 f1509490 2022-03-20 martijn return;
1463 f1509490 2022-03-20 martijn }
1464 f1509490 2022-03-20 martijn
1465 f1509490 2022-03-20 martijn while (msg->body_whitelines-- > 0) {
1466 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++) {
1467 5d58b443 2022-03-27 martijn if ((sig = msg->header[i].sig) == NULL ||
1468 5d58b443 2022-03-27 martijn sig->state != DKIM_UNKNOWN)
1469 f1509490 2022-03-20 martijn continue;
1470 31ffb916 2022-03-27 martijn hashn = sig->l == -1 ? 2 : MIN(2, sig->l);
1471 31ffb916 2022-03-27 martijn sig->l -= sig->l == -1 ? 0 : hashn;
1472 b64f38ad 2025-06-02 kirill if (EVP_DigestUpdate(sig->bhctx, "\r\n", hashn) == 0)
1473 b64f38ad 2025-06-02 kirill osmtpd_errx(1, "EVP_DigestUpdate");
1474 f1509490 2022-03-20 martijn }
1475 f1509490 2022-03-20 martijn }
1476 f1509490 2022-03-20 martijn msg->body_whitelines = 0;
1477 f1509490 2022-03-20 martijn msg->has_body = 1;
1478 f1509490 2022-03-20 martijn
1479 31ffb916 2022-03-27 martijn while (line[0] != '\0') {
1480 31ffb916 2022-03-27 martijn while (1) {
1481 31ffb916 2022-03-27 martijn prev = end;
1482 31ffb916 2022-03-27 martijn if ((end = osmtpd_ltok_skip_wsp(end, 0)) == NULL)
1483 31ffb916 2022-03-27 martijn break;
1484 31ffb916 2022-03-27 martijn }
1485 31ffb916 2022-03-27 martijn end = prev;
1486 31ffb916 2022-03-27 martijn wsp = end != line;
1487 31ffb916 2022-03-27 martijn if (!wsp) {
1488 31ffb916 2022-03-27 martijn while (osmtpd_ltok_skip_wsp(end, 0) == NULL &&
1489 31ffb916 2022-03-27 martijn end[0] != '\0')
1490 31ffb916 2022-03-27 martijn end++;
1491 31ffb916 2022-03-27 martijn }
1492 31ffb916 2022-03-27 martijn for (i = 0; i < msg->nheaders; i++) {
1493 31ffb916 2022-03-27 martijn sig = msg->header[i].sig;
1494 31ffb916 2022-03-27 martijn if (sig == NULL || sig->state != DKIM_UNKNOWN)
1495 31ffb916 2022-03-27 martijn continue;
1496 31ffb916 2022-03-27 martijn if (wsp &&
1497 31ffb916 2022-03-27 martijn (sig->c & CANON_BODY) == CANON_BODY_RELAXED) {
1498 31ffb916 2022-03-27 martijn hash = " ";
1499 31ffb916 2022-03-27 martijn len = end[0] == '\0' ? 0 : 1;
1500 31ffb916 2022-03-27 martijn } else {
1501 31ffb916 2022-03-27 martijn hash = line;
1502 31ffb916 2022-03-27 martijn len = (size_t)(end - line);
1503 31ffb916 2022-03-27 martijn }
1504 31ffb916 2022-03-27 martijn hashn = sig->l == -1 ? len : MIN(len, (size_t)sig->l);
1505 31ffb916 2022-03-27 martijn sig->l -= sig->l == -1 ? 0 : hashn;
1506 31ffb916 2022-03-27 martijn ret = EVP_DigestUpdate(sig->bhctx, hash, hashn);
1507 b64f38ad 2025-06-02 kirill if (ret == 0)
1508 b64f38ad 2025-06-02 kirill osmtpd_err(1, "EVP_DigestUpdate");
1509 31ffb916 2022-03-27 martijn }
1510 31ffb916 2022-03-27 martijn line = end;
1511 31ffb916 2022-03-27 martijn }
1512 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++) {
1513 f1509490 2022-03-20 martijn sig = msg->header[i].sig;
1514 31ffb916 2022-03-27 martijn if (sig == NULL || sig->state != DKIM_UNKNOWN)
1515 f1509490 2022-03-20 martijn continue;
1516 31ffb916 2022-03-27 martijn hashn = sig->l == -1 ? 2 : MIN(2, sig->l);
1517 f1509490 2022-03-20 martijn sig->l -= sig->l == -1 ? 0 : hashn;
1518 31ffb916 2022-03-27 martijn ret = EVP_DigestUpdate(sig->bhctx, "\r\n", hashn);
1519 b64f38ad 2025-06-02 kirill if (ret == 0)
1520 b64f38ad 2025-06-02 kirill osmtpd_err(1, "EVP_DigestUpdate");
1521 f1509490 2022-03-20 martijn }
1522 f1509490 2022-03-20 martijn }
1523 f1509490 2022-03-20 martijn
1524 f1509490 2022-03-20 martijn void
1525 f1509490 2022-03-20 martijn dkim_body_verify(struct signature *sig)
1526 f1509490 2022-03-20 martijn {
1527 f1509490 2022-03-20 martijn unsigned char digest[EVP_MAX_MD_SIZE];
1528 f1509490 2022-03-20 martijn unsigned int digestsz;
1529 f1509490 2022-03-20 martijn
1530 f1509490 2022-03-20 martijn if (sig->state != DKIM_UNKNOWN)
1531 f1509490 2022-03-20 martijn return;
1532 f1509490 2022-03-20 martijn
1533 f1509490 2022-03-20 martijn if ((sig->c & CANON_BODY) == CANON_BODY_SIMPLE &&
1534 f1509490 2022-03-20 martijn !sig->header->msg->has_body) {
1535 f1509490 2022-03-20 martijn if (EVP_DigestUpdate(sig->bhctx, "\r\n",
1536 b64f38ad 2025-06-02 kirill sig->l == -1 ? 2 : MIN(2, sig->l)) <= 0)
1537 b64f38ad 2025-06-02 kirill osmtpd_errx(1, "EVP_DigestUpdate");
1538 f1509490 2022-03-20 martijn }
1539 a9f7aa12 2022-03-24 martijn if (sig->l > 0) {
1540 a9f7aa12 2022-03-24 martijn dkim_signature_state(sig, DKIM_PERMERROR,
1541 a9f7aa12 2022-03-24 martijn "l tag larger than body");
1542 a9f7aa12 2022-03-24 martijn return;
1543 a9f7aa12 2022-03-24 martijn }
1544 f1509490 2022-03-20 martijn
1545 b64f38ad 2025-06-02 kirill if (EVP_DigestFinal_ex(sig->bhctx, digest, &digestsz) == 0)
1546 b64f38ad 2025-06-02 kirill osmtpd_err(1, "EVP_DigestFinal_ex");
1547 f1509490 2022-03-20 martijn
1548 f1509490 2022-03-20 martijn if (digestsz != sig->bhsz || memcmp(digest, sig->bh, digestsz) != 0)
1549 f1509490 2022-03-20 martijn dkim_signature_state(sig, DKIM_FAIL, "bh mismatch");
1550 f1509490 2022-03-20 martijn }
1551 f1509490 2022-03-20 martijn
1552 f1509490 2022-03-20 martijn void
1553 f1509490 2022-03-20 martijn dkim_message_verify(struct message *msg)
1554 f1509490 2022-03-20 martijn {
1555 f1509490 2022-03-20 martijn struct signature *sig;
1556 f1509490 2022-03-20 martijn size_t i;
1557 f1509490 2022-03-20 martijn ssize_t n, aroff = 0;
1558 f1509490 2022-03-20 martijn int found = 0;
1559 f1509490 2022-03-20 martijn char *line = NULL;
1560 f1509490 2022-03-20 martijn size_t linelen = 0;
1561 f1509490 2022-03-20 martijn
1562 f1509490 2022-03-20 martijn if (!msg->readdone)
1563 f1509490 2022-03-20 martijn return;
1564 f1509490 2022-03-20 martijn
1565 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++) {
1566 f1509490 2022-03-20 martijn if (msg->header[i].sig == NULL)
1567 f1509490 2022-03-20 martijn continue;
1568 f1509490 2022-03-20 martijn if (msg->header[i].sig->query != NULL)
1569 f1509490 2022-03-20 martijn return;
1570 f1509490 2022-03-20 martijn if (msg->header[i].sig->state != DKIM_UNKNOWN)
1571 f1509490 2022-03-20 martijn continue;
1572 f1509490 2022-03-20 martijn dkim_signature_state(msg->header[i].sig, DKIM_PASS, NULL);
1573 f1509490 2022-03-20 martijn }
1574 b64f38ad 2025-06-02 kirill
1575 f1509490 2022-03-20 martijn if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1576 b64f38ad 2025-06-02 kirill "Authentication-Results: %s", authservid)) == -1)
1577 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
1578 f1509490 2022-03-20 martijn for (i = 0; i < msg->nheaders; i++) {
1579 f1509490 2022-03-20 martijn sig = msg->header[i].sig;
1580 f1509490 2022-03-20 martijn if (sig == NULL)
1581 f1509490 2022-03-20 martijn continue;
1582 f1509490 2022-03-20 martijn found = 1;
1583 f1509490 2022-03-20 martijn if ((aroff = dkim_ar_cat(&line, &linelen, aroff, "; dkim=%s",
1584 b64f38ad 2025-06-02 kirill dkim_state2str(sig->state))) == -1)
1585 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
1586 f1509490 2022-03-20 martijn if (sig->state_reason != NULL) {
1587 f1509490 2022-03-20 martijn if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1588 b64f38ad 2025-06-02 kirill " reason=\"%s\"", sig->state_reason)) == -1)
1589 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
1590 f1509490 2022-03-20 martijn }
1591 f1509490 2022-03-20 martijn if (sig->s[0] != '\0') {
1592 f1509490 2022-03-20 martijn if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1593 b64f38ad 2025-06-02 kirill " header.s=%s", sig->s)) == -1)
1594 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
1595 f1509490 2022-03-20 martijn }
1596 f1509490 2022-03-20 martijn if (sig->d[0] != '\0') {
1597 f1509490 2022-03-20 martijn if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1598 b64f38ad 2025-06-02 kirill " header.d=%s", sig->d)) == -1)
1599 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
1600 f1509490 2022-03-20 martijn }
1601 f1509490 2022-03-20 martijn /*
1602 f1509490 2022-03-20 martijn * Don't print i-tag, since localpart can be a quoted-string,
1603 f1509490 2022-03-20 martijn * which can contain FWS and CFWS.
1604 f1509490 2022-03-20 martijn */
1605 f1509490 2022-03-20 martijn if (sig->a != NULL) {
1606 f1509490 2022-03-20 martijn if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1607 b64f38ad 2025-06-02 kirill " header.a=%.*s", (int)sig->asz, sig->a)) == -1)
1608 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
1609 f1509490 2022-03-20 martijn }
1610 ce4bcd16 2025-06-02 kirill if (sig->bheaderclean[0] != '\0') {
1611 ce4bcd16 2025-06-02 kirill if ((aroff = dkim_ar_cat(&line, &linelen, aroff,
1612 b64f38ad 2025-06-02 kirill " header.b=%s", sig->bheaderclean)) == -1)
1613 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
1614 ce4bcd16 2025-06-02 kirill }
1615 f1509490 2022-03-20 martijn }
1616 f1509490 2022-03-20 martijn if (!found) {
1617 f1509490 2022-03-20 martijn aroff = dkim_ar_cat(&line, &linelen, aroff, "; dkim=none");
1618 b64f38ad 2025-06-02 kirill if (aroff == -1)
1619 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: malloc", __func__);
1620 f1509490 2022-03-20 martijn }
1621 36daa2d4 2025-06-02 kirill if (dkim_ar_print(msg->ctx, line) != 0) {
1622 b64f38ad 2025-06-02 kirill osmtpd_warnx(msg->ctx, "Mallformed AR header");
1623 36daa2d4 2025-06-02 kirill goto fail;
1624 36daa2d4 2025-06-02 kirill }
1625 f1509490 2022-03-20 martijn
1626 f1509490 2022-03-20 martijn rewind(msg->origf);
1627 f1509490 2022-03-20 martijn while ((n = getline(&line, &linelen, msg->origf)) != -1) {
1628 f1509490 2022-03-20 martijn line[n - 1] = '\0';
1629 f1509490 2022-03-20 martijn osmtpd_filter_dataline(msg->ctx, "%s", line);
1630 f1509490 2022-03-20 martijn }
1631 f1509490 2022-03-20 martijn if (ferror(msg->origf))
1632 b64f38ad 2025-06-02 kirill osmtpd_err(1, "%s: ferror", __func__);
1633 f1509490 2022-03-20 martijn fail:
1634 f1509490 2022-03-20 martijn free(line);
1635 f1509490 2022-03-20 martijn return;
1636 f1509490 2022-03-20 martijn }
1637 f1509490 2022-03-20 martijn
1638 36daa2d4 2025-06-02 kirill int
1639 4e2dd90f 2022-03-26 martijn dkim_ar_print(struct osmtpd_ctx *ctx, const char *start)
1640 f1509490 2022-03-20 martijn {
1641 4e2dd90f 2022-03-26 martijn const char *scan, *checkpoint, *ncheckpoint;
1642 1256d7a0 2025-06-02 kirill int arlen = 0, first = 1, arid = 1;
1643 f1509490 2022-03-20 martijn
1644 f1509490 2022-03-20 martijn checkpoint = start;
1645 4e2dd90f 2022-03-26 martijn ncheckpoint = osmtpd_ltok_skip_hdr_name(start, 0) + 1;
1646 f1509490 2022-03-20 martijn for (scan = start; scan[0] != '\0'; scan++) {
1647 f1509490 2022-03-20 martijn if (scan[0] == '\t')
1648 f1509490 2022-03-20 martijn arlen = (arlen + 8) & ~7;
1649 f1509490 2022-03-20 martijn else
1650 f1509490 2022-03-20 martijn arlen++;
1651 f1509490 2022-03-20 martijn if (arlen >= AUTHENTICATION_RESULTS_LINELEN) {
1652 1256d7a0 2025-06-02 kirill arlen = (int)(checkpoint - start);
1653 1256d7a0 2025-06-02 kirill if (arlen <= 0) {
1654 1256d7a0 2025-06-02 kirill arlen = (int)(ncheckpoint - start);
1655 1256d7a0 2025-06-02 kirill checkpoint = ncheckpoint;
1656 1256d7a0 2025-06-02 kirill }
1657 f1509490 2022-03-20 martijn osmtpd_filter_dataline(ctx, "%s%.*s", first ? "" : "\t",
1658 1256d7a0 2025-06-02 kirill arlen, start);
1659 4e2dd90f 2022-03-26 martijn start = osmtpd_ltok_skip_cfws(checkpoint, 1);
1660 1256d7a0 2025-06-02 kirill if (*start == '\0')
1661 36daa2d4 2025-06-02 kirill return 0;
1662 7a470599 2025-06-02 kirill ncheckpoint = start;
1663 f1509490 2022-03-20 martijn scan = start;
1664 f1509490 2022-03-20 martijn arlen = 8;
1665 f1509490 2022-03-20 martijn first = 0;
1666 f1509490 2022-03-20 martijn }
1667 f1509490 2022-03-20 martijn if (scan == ncheckpoint) {
1668 f1509490 2022-03-20 martijn checkpoint = ncheckpoint;
1669 4e2dd90f 2022-03-26 martijn ncheckpoint = osmtpd_ltok_skip_cfws(ncheckpoint, 1);
1670 f1509490 2022-03-20 martijn /* authserv-id */
1671 f1509490 2022-03-20 martijn if (arid) {
1672 4e2dd90f 2022-03-26 martijn ncheckpoint = osmtpd_ltok_skip_value(
1673 f1509490 2022-03-20 martijn ncheckpoint, 0);
1674 f1509490 2022-03-20 martijn arid = 0;
1675 f1509490 2022-03-20 martijn /* methodspec */
1676 e460eef3 2025-06-02 kirill } else if (strncmp(ncheckpoint, "dkim=",
1677 e460eef3 2025-06-02 kirill sizeof("dkim=") - 1) == 0) {
1678 4e2dd90f 2022-03-26 martijn ncheckpoint = osmtpd_ltok_skip_keyword(
1679 e460eef3 2025-06-02 kirill ncheckpoint + sizeof("dkim=") - 1, 0);
1680 f1509490 2022-03-20 martijn /* reasonspec */
1681 e460eef3 2025-06-02 kirill } else if (strncmp(ncheckpoint, "reason=",
1682 e460eef3 2025-06-02 kirill sizeof("reason=") - 1) == 0) {
1683 7a470599 2025-06-02 kirill ncheckpoint = osmtpd_ltok_skip_ar_reasonspec(
1684 7a470599 2025-06-02 kirill ncheckpoint, 0);
1685 f1509490 2022-03-20 martijn /* propspec */
1686 f1509490 2022-03-20 martijn } else {
1687 7a470599 2025-06-02 kirill ncheckpoint = osmtpd_ltok_skip_ar_propspec(
1688 f1509490 2022-03-20 martijn ncheckpoint, 0);
1689 f1509490 2022-03-20 martijn }
1690 7a470599 2025-06-02 kirill
1691 7a470599 2025-06-02 kirill if (ncheckpoint == NULL)
1692 36daa2d4 2025-06-02 kirill return -1;
1693 7a470599 2025-06-02 kirill
1694 7a470599 2025-06-02 kirill if (*ncheckpoint == ';')
1695 7a470599 2025-06-02 kirill ncheckpoint++;
1696 f1509490 2022-03-20 martijn }
1697 f1509490 2022-03-20 martijn }
1698 f1509490 2022-03-20 martijn osmtpd_filter_dataline(ctx, "%s%s", first ? "" : "\t", start);
1699 36daa2d4 2025-06-02 kirill return 0;
1700 f1509490 2022-03-20 martijn }
1701 f1509490 2022-03-20 martijn
1702 f1509490 2022-03-20 martijn ssize_t
1703 f1509490 2022-03-20 martijn dkim_ar_cat(char **ar, size_t *n, size_t aroff, const char *fmt, ...)
1704 f1509490 2022-03-20 martijn {
1705 f1509490 2022-03-20 martijn va_list ap;
1706 f1509490 2022-03-20 martijn char *artmp;
1707 f1509490 2022-03-20 martijn int size;
1708 f1509490 2022-03-20 martijn size_t nn;
1709 f1509490 2022-03-20 martijn
1710 f1509490 2022-03-20 martijn va_start(ap, fmt);
1711 f1509490 2022-03-20 martijn size = vsnprintf(*ar + aroff, *n - aroff, fmt, ap);
1712 f1509490 2022-03-20 martijn va_end(ap);
1713 2293aea1 2025-06-02 kirill if (size + aroff < *n)
1714 f1509490 2022-03-20 martijn return (ssize_t)size + aroff;
1715 f1509490 2022-03-20 martijn nn = (((aroff + size) / 256) + 1) * 256;
1716 f1509490 2022-03-20 martijn artmp = realloc(*ar, nn);
1717 f1509490 2022-03-20 martijn if (artmp == NULL)
1718 f1509490 2022-03-20 martijn return -1;
1719 f1509490 2022-03-20 martijn *ar = artmp;
1720 f1509490 2022-03-20 martijn *n = nn;
1721 f1509490 2022-03-20 martijn va_start(ap, fmt);
1722 f1509490 2022-03-20 martijn size = vsnprintf(*ar + aroff, *n - aroff, fmt, ap);
1723 f1509490 2022-03-20 martijn va_end(ap);
1724 f1509490 2022-03-20 martijn return (ssize_t)size + aroff;
1725 f1509490 2022-03-20 martijn }
1726 f1509490 2022-03-20 martijn
1727 f1509490 2022-03-20 martijn __dead void
1728 f1509490 2022-03-20 martijn usage(void)
1729 f1509490 2022-03-20 martijn {
1730 f1509490 2022-03-20 martijn fprintf(stderr, "usage: filter-dkimverify\n");
1731 f1509490 2022-03-20 martijn exit(1);
1732 f1509490 2022-03-20 martijn }