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