Blame


1 48c4bdc1 2019-04-04 martijn /*
2 48c4bdc1 2019-04-04 martijn * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
3 48c4bdc1 2019-04-04 martijn *
4 48c4bdc1 2019-04-04 martijn * Permission to use, copy, modify, and distribute this software for any
5 48c4bdc1 2019-04-04 martijn * purpose with or without fee is hereby granted, provided that the above
6 48c4bdc1 2019-04-04 martijn * copyright notice and this permission notice appear in all copies.
7 48c4bdc1 2019-04-04 martijn *
8 48c4bdc1 2019-04-04 martijn * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 48c4bdc1 2019-04-04 martijn * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 48c4bdc1 2019-04-04 martijn * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 48c4bdc1 2019-04-04 martijn * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 48c4bdc1 2019-04-04 martijn * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 48c4bdc1 2019-04-04 martijn * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 48c4bdc1 2019-04-04 martijn * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 48c4bdc1 2019-04-04 martijn */
16 6bcbc798 2021-05-16 martijn #include <openssl/err.h>
17 48c4bdc1 2019-04-04 martijn #include <openssl/evp.h>
18 06f6b49b 2019-04-06 martijn #include <openssl/pem.h>
19 48c4bdc1 2019-04-04 martijn #include <openssl/sha.h>
20 48c4bdc1 2019-04-04 martijn
21 48c4bdc1 2019-04-04 martijn #include <ctype.h>
22 c826412d 2019-05-02 martijn #include <errno.h>
23 1308710c 2019-04-09 martijn #include <fcntl.h>
24 9b92b3ea 2025-05-19 martijn #include <limits.h>
25 48c4bdc1 2019-04-04 martijn #include <stdio.h>
26 48c4bdc1 2019-04-04 martijn #include <stdlib.h>
27 48c4bdc1 2019-04-04 martijn #include <string.h>
28 48c4bdc1 2019-04-04 martijn #include <syslog.h>
29 48c4bdc1 2019-04-04 martijn #include <time.h>
30 48c4bdc1 2019-04-04 martijn #include <unistd.h>
31 48c4bdc1 2019-04-04 martijn
32 babf5d5a 2021-05-11 martijn #include "openbsd-compat.h"
33 6840f1a1 2019-08-22 martijn #include "opensmtpd.h"
34 40cd76f4 2020-08-30 martijn #include "mheader.h"
35 48c4bdc1 2019-04-04 martijn
36 06f6b49b 2019-04-06 martijn struct dkim_signature {
37 06f6b49b 2019-04-06 martijn char *signature;
38 06f6b49b 2019-04-06 martijn size_t size;
39 06f6b49b 2019-04-06 martijn size_t len;
40 06f6b49b 2019-04-06 martijn };
41 06f6b49b 2019-04-06 martijn
42 6840f1a1 2019-08-22 martijn struct dkim_message {
43 48c4bdc1 2019-04-04 martijn FILE *origf;
44 48c4bdc1 2019-04-04 martijn int parsing_headers;
45 48c4bdc1 2019-04-04 martijn char **headers;
46 48c4bdc1 2019-04-04 martijn int lastheader;
47 48c4bdc1 2019-04-04 martijn size_t body_whitelines;
48 48c4bdc1 2019-04-04 martijn int has_body;
49 06f6b49b 2019-04-06 martijn struct dkim_signature signature;
50 6bcbc798 2021-05-16 martijn EVP_MD_CTX *dctx;
51 48c4bdc1 2019-04-04 martijn };
52 48c4bdc1 2019-04-04 martijn
53 37c6191b 2019-04-06 martijn /* RFC 6376 section 5.4.1 */
54 37c6191b 2019-04-06 martijn static char *dsign_headers[] = {
55 37c6191b 2019-04-06 martijn "from",
56 37c6191b 2019-04-06 martijn "reply-to",
57 37c6191b 2019-04-06 martijn "subject",
58 37c6191b 2019-04-06 martijn "date",
59 37c6191b 2019-04-06 martijn "to",
60 37c6191b 2019-04-06 martijn "cc",
61 37c6191b 2019-04-06 martijn "resent-date",
62 37c6191b 2019-04-06 martijn "resent-from",
63 37c6191b 2019-04-06 martijn "resent-to",
64 37c6191b 2019-04-06 martijn "resent-cc",
65 37c6191b 2019-04-06 martijn "in-reply-to",
66 37c6191b 2019-04-06 martijn "references",
67 37c6191b 2019-04-06 martijn "list-id",
68 37c6191b 2019-04-06 martijn "list-help",
69 37c6191b 2019-04-06 martijn "list-unsubscribe",
70 37c6191b 2019-04-06 martijn "list-subscribe",
71 37c6191b 2019-04-06 martijn "list-post",
72 37c6191b 2019-04-06 martijn "list-owner",
73 37c6191b 2019-04-06 martijn "list-archive"
74 37c6191b 2019-04-06 martijn };
75 37c6191b 2019-04-06 martijn static char **sign_headers = dsign_headers;
76 37c6191b 2019-04-06 martijn static size_t nsign_headers = sizeof(dsign_headers) / sizeof(*dsign_headers);
77 48c4bdc1 2019-04-04 martijn
78 fddd7e1e 2019-04-06 martijn static char *hashalg = "sha256";
79 fddd7e1e 2019-04-06 martijn static char *cryptalg = "rsa";
80 48c4bdc1 2019-04-04 martijn
81 48c4bdc1 2019-04-04 martijn #define CANON_SIMPLE 0
82 48c4bdc1 2019-04-04 martijn #define CANON_RELAXED 1
83 48c4bdc1 2019-04-04 martijn static int canonheader = CANON_SIMPLE;
84 48c4bdc1 2019-04-04 martijn static int canonbody = CANON_SIMPLE;
85 48c4bdc1 2019-04-04 martijn
86 e7076cef 2019-05-02 martijn static int addtime = 0;
87 e974922c 2019-05-02 martijn static long long addexpire = 0;
88 c826412d 2019-05-02 martijn static int addheaders = 0;
89 e7076cef 2019-05-02 martijn
90 40cd76f4 2020-08-30 martijn static char **domain = NULL;
91 40cd76f4 2020-08-30 martijn static size_t ndomains = 0;
92 06f6b49b 2019-04-06 martijn static char *selector = NULL;
93 48c4bdc1 2019-04-04 martijn
94 06f6b49b 2019-04-06 martijn static EVP_PKEY *pkey;
95 06f6b49b 2019-04-06 martijn static const EVP_MD *hash_md;
96 6bcbc798 2021-05-16 martijn static int keyid = EVP_PKEY_RSA;
97 6bcbc798 2021-05-16 martijn static int sephash = 0;
98 06f6b49b 2019-04-06 martijn
99 06f6b49b 2019-04-06 martijn #define DKIM_SIGNATURE_LINELEN 78
100 06f6b49b 2019-04-06 martijn
101 48c4bdc1 2019-04-04 martijn void usage(void);
102 5fdffb49 2022-01-27 martijn void dkim_adddomain(char *);
103 48c4bdc1 2019-04-04 martijn void dkim_headers_set(char *);
104 f388b764 2025-05-19 kirill int dkim_dataline(struct osmtpd_ctx *, const char *);
105 6840f1a1 2019-08-22 martijn void *dkim_message_new(struct osmtpd_ctx *);
106 6840f1a1 2019-08-22 martijn void dkim_message_free(struct osmtpd_ctx *, void *);
107 6840f1a1 2019-08-22 martijn void dkim_parse_header(struct dkim_message *, char *, int);
108 6840f1a1 2019-08-22 martijn void dkim_parse_body(struct dkim_message *, char *);
109 6840f1a1 2019-08-22 martijn void dkim_sign(struct osmtpd_ctx *);
110 540788da 2025-02-08 martijn void dkim_signature_printheader(struct dkim_message *, const char *);
111 540788da 2025-02-08 martijn void dkim_signature_printf(struct dkim_message *, char *, ...)
112 06f6b49b 2019-04-06 martijn __attribute__((__format__ (printf, 2, 3)));
113 540788da 2025-02-08 martijn void dkim_signature_normalize(struct dkim_message *);
114 40cd76f4 2020-08-30 martijn const char *dkim_domain_select(struct dkim_message *, char *);
115 540788da 2025-02-08 martijn void dkim_signature_need(struct dkim_message *, size_t);
116 6840f1a1 2019-08-22 martijn int dkim_sign_init(struct dkim_message *);
117 48c4bdc1 2019-04-04 martijn
118 48c4bdc1 2019-04-04 martijn int
119 48c4bdc1 2019-04-04 martijn main(int argc, char *argv[])
120 48c4bdc1 2019-04-04 martijn {
121 48c4bdc1 2019-04-04 martijn int ch;
122 5fdffb49 2022-01-27 martijn FILE *file;
123 5fdffb49 2022-01-27 martijn char *line;
124 5fdffb49 2022-01-27 martijn size_t linesz;
125 5fdffb49 2022-01-27 martijn ssize_t linelen;
126 e974922c 2019-05-02 martijn const char *errstr;
127 48c4bdc1 2019-04-04 martijn
128 5fdffb49 2022-01-27 martijn while ((ch = getopt(argc, argv, "a:c:D:d:h:k:s:tx:z")) != -1) {
129 48c4bdc1 2019-04-04 martijn switch (ch) {
130 48c4bdc1 2019-04-04 martijn case 'a':
131 6bcbc798 2021-05-16 martijn if (strncmp(optarg, "rsa-", 4) == 0) {
132 6bcbc798 2021-05-16 martijn cryptalg = "rsa";
133 6bcbc798 2021-05-16 martijn hashalg = optarg + 4;
134 6bcbc798 2021-05-16 martijn keyid = EVP_PKEY_RSA;
135 6bcbc798 2021-05-16 martijn sephash = 0;
136 6bcbc798 2021-05-16 martijn } else if (strncmp(optarg, "ed25519-", 8) == 0) {
137 6bcbc798 2021-05-16 martijn hashalg = optarg + 8;
138 6bcbc798 2021-05-16 martijn cryptalg = "ed25519";
139 6bcbc798 2021-05-16 martijn keyid = EVP_PKEY_ED25519;
140 6bcbc798 2021-05-16 martijn sephash = 1;
141 6bcbc798 2021-05-16 martijn } else
142 6bcbc798 2021-05-16 martijn osmtpd_errx(1, "invalid algorithm");
143 48c4bdc1 2019-04-04 martijn break;
144 48c4bdc1 2019-04-04 martijn case 'c':
145 48c4bdc1 2019-04-04 martijn if (strncmp(optarg, "simple", 6) == 0) {
146 48c4bdc1 2019-04-04 martijn canonheader = CANON_SIMPLE;
147 48c4bdc1 2019-04-04 martijn optarg += 6;
148 48c4bdc1 2019-04-04 martijn } else if (strncmp(optarg, "relaxed", 7) == 0) {
149 48c4bdc1 2019-04-04 martijn canonheader = CANON_RELAXED;
150 48c4bdc1 2019-04-04 martijn optarg += 7;
151 48c4bdc1 2019-04-04 martijn } else
152 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "Invalid canonicalization");
153 48c4bdc1 2019-04-04 martijn if (optarg[0] == '/') {
154 48c4bdc1 2019-04-04 martijn if (strcmp(optarg + 1, "simple") == 0)
155 48c4bdc1 2019-04-04 martijn canonbody = CANON_SIMPLE;
156 48c4bdc1 2019-04-04 martijn else if (strcmp(optarg + 1, "relaxed") == 0)
157 48c4bdc1 2019-04-04 martijn canonbody = CANON_RELAXED;
158 48c4bdc1 2019-04-04 martijn else
159 d581ea1c 2025-01-29 kirill osmtpd_errx(1,
160 6840f1a1 2019-08-22 martijn "Invalid canonicalization");
161 48c4bdc1 2019-04-04 martijn } else if (optarg[0] == '\0')
162 48c4bdc1 2019-04-04 martijn canonbody = CANON_SIMPLE;
163 48c4bdc1 2019-04-04 martijn else
164 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "Invalid canonicalization");
165 48c4bdc1 2019-04-04 martijn break;
166 5fdffb49 2022-01-27 martijn case 'D':
167 5fdffb49 2022-01-27 martijn if ((file = fopen(optarg, "r")) == NULL)
168 5fdffb49 2022-01-27 martijn osmtpd_err(1, "Can't open domain file (%s)",
169 5fdffb49 2022-01-27 martijn optarg);
170 5fdffb49 2022-01-27 martijn do {
171 5fdffb49 2022-01-27 martijn line = NULL;
172 5fdffb49 2022-01-27 martijn linesz = 0;
173 5fdffb49 2022-01-27 martijn linelen = getline(&line, &linesz, file);
174 5fdffb49 2022-01-27 martijn if (linelen > 0) {
175 5fdffb49 2022-01-27 martijn if (line[linelen - 1] == '\n')
176 5fdffb49 2022-01-27 martijn line[linelen - 1] = '\0';
177 3c3725bd 2025-01-27 kirill if (line[0] == '#')
178 3c3725bd 2025-01-27 kirill continue;
179 5fdffb49 2022-01-27 martijn dkim_adddomain(line);
180 5fdffb49 2022-01-27 martijn }
181 5fdffb49 2022-01-27 martijn } while (linelen != -1);
182 5fdffb49 2022-01-27 martijn if (ferror(file))
183 5fdffb49 2022-01-27 martijn osmtpd_err(1, "Error reading domain file (%s)",
184 5fdffb49 2022-01-27 martijn optarg);
185 5fdffb49 2022-01-27 martijn fclose(file);
186 5fdffb49 2022-01-27 martijn break;
187 48c4bdc1 2019-04-04 martijn case 'd':
188 5fdffb49 2022-01-27 martijn dkim_adddomain(optarg);
189 48c4bdc1 2019-04-04 martijn break;
190 06f6b49b 2019-04-06 martijn case 'h':
191 06f6b49b 2019-04-06 martijn dkim_headers_set(optarg);
192 06f6b49b 2019-04-06 martijn break;
193 06f6b49b 2019-04-06 martijn case 'k':
194 5fdffb49 2022-01-27 martijn if ((file = fopen(optarg, "r")) == NULL)
195 6840f1a1 2019-08-22 martijn osmtpd_err(1, "Can't open key file (%s)",
196 6840f1a1 2019-08-22 martijn optarg);
197 5fdffb49 2022-01-27 martijn pkey = PEM_read_PrivateKey(file, NULL, NULL, NULL);
198 06f6b49b 2019-04-06 martijn if (pkey == NULL)
199 6840f1a1 2019-08-22 martijn osmtpd_errx(1, "Can't read key file");
200 5fdffb49 2022-01-27 martijn fclose(file);
201 06f6b49b 2019-04-06 martijn break;
202 06f6b49b 2019-04-06 martijn case 's':
203 06f6b49b 2019-04-06 martijn selector = optarg;
204 06f6b49b 2019-04-06 martijn break;
205 e7076cef 2019-05-02 martijn case 't':
206 e7076cef 2019-05-02 martijn addtime = 1;
207 e7076cef 2019-05-02 martijn break;
208 e974922c 2019-05-02 martijn case 'x':
209 e974922c 2019-05-02 martijn addexpire = strtonum(optarg, 1, INT64_MAX, &errstr);
210 e974922c 2019-05-02 martijn if (addexpire == 0)
211 6840f1a1 2019-08-22 martijn osmtpd_errx(1, "Expire offset is %s", errstr);
212 e974922c 2019-05-02 martijn break;
213 c826412d 2019-05-02 martijn case 'z':
214 6840f1a1 2019-08-22 martijn addheaders++;
215 c826412d 2019-05-02 martijn break;
216 48c4bdc1 2019-04-04 martijn default:
217 48c4bdc1 2019-04-04 martijn usage();
218 48c4bdc1 2019-04-04 martijn }
219 48c4bdc1 2019-04-04 martijn }
220 fddd7e1e 2019-04-06 martijn
221 fddd7e1e 2019-04-06 martijn OpenSSL_add_all_digests();
222 fddd7e1e 2019-04-06 martijn
223 6840f1a1 2019-08-22 martijn if (pledge("tmppath stdio", NULL) == -1)
224 6840f1a1 2019-08-22 martijn osmtpd_err(1, "pledge");
225 48c4bdc1 2019-04-04 martijn
226 6bcbc798 2021-05-16 martijn if ((hash_md = EVP_get_digestbyname(hashalg)) == NULL)
227 6bcbc798 2021-05-16 martijn osmtpd_errx(1, "Can't find hash: %s", hashalg);
228 6bcbc798 2021-05-16 martijn
229 06f6b49b 2019-04-06 martijn if (domain == NULL || selector == NULL || pkey == NULL)
230 48c4bdc1 2019-04-04 martijn usage();
231 48c4bdc1 2019-04-04 martijn
232 6bcbc798 2021-05-16 martijn if (EVP_PKEY_id(pkey) != keyid)
233 6bcbc798 2021-05-16 martijn osmtpd_errx(1, "Key is not of type %s", cryptalg);
234 6bcbc798 2021-05-16 martijn
235 6840f1a1 2019-08-22 martijn osmtpd_register_filter_dataline(dkim_dataline);
236 6840f1a1 2019-08-22 martijn osmtpd_local_message(dkim_message_new, dkim_message_free);
237 6840f1a1 2019-08-22 martijn osmtpd_run();
238 48c4bdc1 2019-04-04 martijn
239 48c4bdc1 2019-04-04 martijn return 0;
240 48c4bdc1 2019-04-04 martijn }
241 48c4bdc1 2019-04-04 martijn
242 48c4bdc1 2019-04-04 martijn void
243 5fdffb49 2022-01-27 martijn dkim_adddomain(char *d)
244 5fdffb49 2022-01-27 martijn {
245 5fdffb49 2022-01-27 martijn domain = reallocarray(domain, ndomains + 1, sizeof(*domain));
246 5fdffb49 2022-01-27 martijn if (domain == NULL)
247 e1300062 2025-04-08 kirill osmtpd_err(1, "reallocarray");
248 5fdffb49 2022-01-27 martijn domain[ndomains++] = d;
249 5fdffb49 2022-01-27 martijn }
250 5fdffb49 2022-01-27 martijn
251 f388b764 2025-05-19 kirill int
252 6840f1a1 2019-08-22 martijn dkim_dataline(struct osmtpd_ctx *ctx, const char *line)
253 48c4bdc1 2019-04-04 martijn {
254 6840f1a1 2019-08-22 martijn struct dkim_message *message = ctx->local_message;
255 6840f1a1 2019-08-22 martijn char *linedup;
256 48c4bdc1 2019-04-04 martijn size_t linelen;
257 48c4bdc1 2019-04-04 martijn
258 48c4bdc1 2019-04-04 martijn linelen = strlen(line);
259 f388b764 2025-05-19 kirill if (fprintf(message->origf, "%s\n", line) < (int) linelen) {
260 9b92b3ea 2025-05-19 martijn osmtpd_warnx(ctx, "Couldn't write to tempfile");
261 f388b764 2025-05-19 kirill return -1;
262 f388b764 2025-05-19 kirill }
263 48c4bdc1 2019-04-04 martijn
264 7ecf4491 2019-05-01 martijn if (line[0] == '.' && line[1] =='\0') {
265 6840f1a1 2019-08-22 martijn dkim_sign(ctx);
266 6840f1a1 2019-08-22 martijn } else if (linelen != 0 && message->parsing_headers) {
267 f5286ea3 2019-04-08 martijn if (line[0] == '.')
268 f5286ea3 2019-04-08 martijn line++;
269 6840f1a1 2019-08-22 martijn if ((linedup = strdup(line)) == NULL)
270 e1300062 2025-04-08 kirill osmtpd_err(1, "strdup");
271 6840f1a1 2019-08-22 martijn dkim_parse_header(message, linedup, 0);
272 6840f1a1 2019-08-22 martijn free(linedup);
273 6840f1a1 2019-08-22 martijn } else if (linelen == 0 && message->parsing_headers) {
274 540788da 2025-02-08 martijn if (addheaders > 0)
275 540788da 2025-02-08 martijn dkim_signature_printf(message, "; ");
276 6840f1a1 2019-08-22 martijn message->parsing_headers = 0;
277 f5286ea3 2019-04-08 martijn } else {
278 f5286ea3 2019-04-08 martijn if (line[0] == '.')
279 f5286ea3 2019-04-08 martijn line++;
280 6840f1a1 2019-08-22 martijn if ((linedup = strdup(line)) == NULL)
281 e1300062 2025-04-08 kirill osmtpd_err(1, "strdup");
282 6840f1a1 2019-08-22 martijn dkim_parse_body(message, linedup);
283 6840f1a1 2019-08-22 martijn free(linedup);
284 f5286ea3 2019-04-08 martijn }
285 f388b764 2025-05-19 kirill
286 f388b764 2025-05-19 kirill return 0;
287 48c4bdc1 2019-04-04 martijn }
288 48c4bdc1 2019-04-04 martijn
289 6840f1a1 2019-08-22 martijn void *
290 6840f1a1 2019-08-22 martijn dkim_message_new(struct osmtpd_ctx *ctx)
291 48c4bdc1 2019-04-04 martijn {
292 6840f1a1 2019-08-22 martijn struct dkim_message *message;
293 48c4bdc1 2019-04-04 martijn
294 41815bcc 2021-05-14 martijn if ((message = calloc(1, sizeof(*message))) == NULL) {
295 9b92b3ea 2025-05-19 martijn osmtpd_warn(ctx, "calloc");
296 41815bcc 2021-05-14 martijn return NULL;
297 41815bcc 2021-05-14 martijn }
298 48c4bdc1 2019-04-04 martijn
299 6840f1a1 2019-08-22 martijn if ((message->origf = tmpfile()) == NULL) {
300 9b92b3ea 2025-05-19 martijn osmtpd_warn(ctx, "Failed to open tempfile");
301 41815bcc 2021-05-14 martijn goto fail;
302 48c4bdc1 2019-04-04 martijn }
303 6840f1a1 2019-08-22 martijn message->parsing_headers = 1;
304 48c4bdc1 2019-04-04 martijn
305 6840f1a1 2019-08-22 martijn message->body_whitelines = 0;
306 6840f1a1 2019-08-22 martijn message->headers = calloc(1, sizeof(*(message->headers)));
307 9b92b3ea 2025-05-19 martijn if (message->headers == NULL) {
308 9b92b3ea 2025-05-19 martijn osmtpd_warn(ctx, "calloc");
309 9b92b3ea 2025-05-19 martijn goto fail;
310 9b92b3ea 2025-05-19 martijn }
311 6840f1a1 2019-08-22 martijn message->lastheader = 0;
312 6840f1a1 2019-08-22 martijn message->signature.signature = NULL;
313 6840f1a1 2019-08-22 martijn message->signature.size = 0;
314 6840f1a1 2019-08-22 martijn message->signature.len = 0;
315 48c4bdc1 2019-04-04 martijn
316 540788da 2025-02-08 martijn dkim_signature_printf(message,
317 40cd76f4 2020-08-30 martijn "DKIM-Signature: v=%s; a=%s-%s; c=%s/%s; s=%s; ", "1",
318 fa10b9e8 2019-04-06 martijn cryptalg, hashalg,
319 06f6b49b 2019-04-06 martijn canonheader == CANON_SIMPLE ? "simple" : "relaxed",
320 540788da 2025-02-08 martijn canonbody == CANON_SIMPLE ? "simple" : "relaxed", selector);
321 540788da 2025-02-08 martijn if (addheaders > 0)
322 540788da 2025-02-08 martijn dkim_signature_printf(message, "z=");
323 06f6b49b 2019-04-06 martijn
324 9b92b3ea 2025-05-19 martijn if ((message->dctx = EVP_MD_CTX_new()) == NULL) {
325 9b92b3ea 2025-05-19 martijn osmtpd_warnx(ctx, "EVP_MD_CTX_new");
326 9b92b3ea 2025-05-19 martijn goto fail;
327 9b92b3ea 2025-05-19 martijn }
328 9b92b3ea 2025-05-19 martijn if (EVP_DigestInit_ex(message->dctx, hash_md, NULL) <= 0) {
329 9b92b3ea 2025-05-19 martijn osmtpd_warnx(ctx, "EVP_DigestInit_ex");
330 9b92b3ea 2025-05-19 martijn goto fail;
331 9b92b3ea 2025-05-19 martijn }
332 d581ea1c 2025-01-29 kirill
333 6840f1a1 2019-08-22 martijn return message;
334 41815bcc 2021-05-14 martijn fail:
335 500ea6d4 2022-01-27 martijn dkim_message_free(ctx, message);
336 41815bcc 2021-05-14 martijn return NULL;
337 48c4bdc1 2019-04-04 martijn }
338 48c4bdc1 2019-04-04 martijn
339 48c4bdc1 2019-04-04 martijn void
340 6840f1a1 2019-08-22 martijn dkim_message_free(struct osmtpd_ctx *ctx, void *data)
341 48c4bdc1 2019-04-04 martijn {
342 6840f1a1 2019-08-22 martijn struct dkim_message *message = data;
343 48c4bdc1 2019-04-04 martijn size_t i;
344 48c4bdc1 2019-04-04 martijn
345 6840f1a1 2019-08-22 martijn fclose(message->origf);
346 6bcbc798 2021-05-16 martijn EVP_MD_CTX_free(message->dctx);
347 6840f1a1 2019-08-22 martijn free(message->signature.signature);
348 500ea6d4 2022-01-27 martijn for (i = 0; message->headers != NULL &&
349 500ea6d4 2022-01-27 martijn message->headers[i] != NULL; i++)
350 6840f1a1 2019-08-22 martijn free(message->headers[i]);
351 6840f1a1 2019-08-22 martijn free(message->headers);
352 6840f1a1 2019-08-22 martijn free(message);
353 48c4bdc1 2019-04-04 martijn }
354 48c4bdc1 2019-04-04 martijn
355 48c4bdc1 2019-04-04 martijn void
356 48c4bdc1 2019-04-04 martijn dkim_headers_set(char *headers)
357 48c4bdc1 2019-04-04 martijn {
358 48c4bdc1 2019-04-04 martijn size_t i;
359 48c4bdc1 2019-04-04 martijn int has_from = 0;
360 48c4bdc1 2019-04-04 martijn
361 48c4bdc1 2019-04-04 martijn nsign_headers = 1;
362 48c4bdc1 2019-04-04 martijn
363 48c4bdc1 2019-04-04 martijn for (i = 0; headers[i] != '\0'; i++) {
364 48c4bdc1 2019-04-04 martijn /* RFC 5322 field-name */
365 48c4bdc1 2019-04-04 martijn if (!(headers[i] >= 33 && headers[i] <= 126))
366 6840f1a1 2019-08-22 martijn osmtpd_errx(1, "-h: invalid character");
367 48c4bdc1 2019-04-04 martijn if (headers[i] == ':') {
368 48c4bdc1 2019-04-04 martijn /* Test for empty headers */
369 48c4bdc1 2019-04-04 martijn if (i == 0 || headers[i - 1] == ':')
370 6840f1a1 2019-08-22 martijn osmtpd_errx(1, "-h: header can't be empty");
371 48c4bdc1 2019-04-04 martijn nsign_headers++;
372 48c4bdc1 2019-04-04 martijn }
373 48c4bdc1 2019-04-04 martijn headers[i] = tolower(headers[i]);
374 48c4bdc1 2019-04-04 martijn }
375 48c4bdc1 2019-04-04 martijn if (headers[i - 1] == ':')
376 6840f1a1 2019-08-22 martijn osmtpd_errx(1, "-h: header can't be empty");
377 48c4bdc1 2019-04-04 martijn
378 6840f1a1 2019-08-22 martijn if ((sign_headers = reallocarray(NULL, nsign_headers + 1,
379 6840f1a1 2019-08-22 martijn sizeof(*sign_headers))) == NULL)
380 e1300062 2025-04-08 kirill osmtpd_errx(1, "reallocarray");
381 48c4bdc1 2019-04-04 martijn
382 48c4bdc1 2019-04-04 martijn for (i = 0; i < nsign_headers; i++) {
383 48c4bdc1 2019-04-04 martijn sign_headers[i] = headers;
384 48c4bdc1 2019-04-04 martijn if (i != nsign_headers - 1) {
385 48c4bdc1 2019-04-04 martijn headers = strchr(headers, ':');
386 48c4bdc1 2019-04-04 martijn headers++[0] = '\0';
387 48c4bdc1 2019-04-04 martijn }
388 48c4bdc1 2019-04-04 martijn if (strcasecmp(sign_headers[i], "from") == 0)
389 48c4bdc1 2019-04-04 martijn has_from = 1;
390 48c4bdc1 2019-04-04 martijn }
391 48c4bdc1 2019-04-04 martijn if (!has_from)
392 6840f1a1 2019-08-22 martijn osmtpd_errx(1, "From header must be included");
393 48c4bdc1 2019-04-04 martijn }
394 48c4bdc1 2019-04-04 martijn
395 48c4bdc1 2019-04-04 martijn void
396 6840f1a1 2019-08-22 martijn dkim_parse_header(struct dkim_message *message, char *line, int force)
397 48c4bdc1 2019-04-04 martijn {
398 48c4bdc1 2019-04-04 martijn size_t i;
399 48c4bdc1 2019-04-04 martijn size_t r, w;
400 48c4bdc1 2019-04-04 martijn size_t linelen;
401 48c4bdc1 2019-04-04 martijn size_t lastheader;
402 06f6b49b 2019-04-06 martijn size_t hlen;
403 b6e37a5e 2019-04-08 martijn int fieldname = 0;
404 48c4bdc1 2019-04-04 martijn char **mtmp;
405 48c4bdc1 2019-04-04 martijn char *htmp;
406 63ac36ce 2019-04-08 martijn char *tmp;
407 48c4bdc1 2019-04-04 martijn
408 540788da 2025-02-08 martijn if (addheaders == 2 && !force)
409 540788da 2025-02-08 martijn dkim_signature_printheader(message, line);
410 c826412d 2019-05-02 martijn
411 6840f1a1 2019-08-22 martijn if ((line[0] == ' ' || line[0] == '\t') && !message->lastheader)
412 48c4bdc1 2019-04-04 martijn return;
413 48c4bdc1 2019-04-04 martijn if ((line[0] != ' ' && line[0] != '\t')) {
414 6840f1a1 2019-08-22 martijn message->lastheader = 0;
415 48c4bdc1 2019-04-04 martijn for (i = 0; i < nsign_headers; i++) {
416 06f6b49b 2019-04-06 martijn hlen = strlen(sign_headers[i]);
417 06f6b49b 2019-04-06 martijn if (strncasecmp(line, sign_headers[i], hlen) == 0) {
418 06f6b49b 2019-04-06 martijn while (line[hlen] == ' ' || line[hlen] == '\t')
419 06f6b49b 2019-04-06 martijn hlen++;
420 06f6b49b 2019-04-06 martijn if (line[hlen] != ':')
421 06f6b49b 2019-04-06 martijn continue;
422 48c4bdc1 2019-04-04 martijn break;
423 48c4bdc1 2019-04-04 martijn }
424 48c4bdc1 2019-04-04 martijn }
425 06f6b49b 2019-04-06 martijn if (i == nsign_headers && !force)
426 48c4bdc1 2019-04-04 martijn return;
427 48c4bdc1 2019-04-04 martijn }
428 48c4bdc1 2019-04-04 martijn
429 540788da 2025-02-08 martijn if (addheaders == 1 && !force)
430 540788da 2025-02-08 martijn dkim_signature_printheader(message, line);
431 c826412d 2019-05-02 martijn
432 48c4bdc1 2019-04-04 martijn if (canonheader == CANON_RELAXED) {
433 6840f1a1 2019-08-22 martijn if (!message->lastheader)
434 b6e37a5e 2019-04-08 martijn fieldname = 1;
435 48c4bdc1 2019-04-04 martijn for (r = w = 0; line[r] != '\0'; r++) {
436 5d06ccad 2019-04-08 martijn if (line[r] == ':' && fieldname) {
437 cd6f9b03 2019-10-23 martijn if (w > 0 && line[w - 1] == ' ')
438 48c4bdc1 2019-04-04 martijn line[w - 1] = ':';
439 48c4bdc1 2019-04-04 martijn else
440 48c4bdc1 2019-04-04 martijn line[w++] = ':';
441 48c4bdc1 2019-04-04 martijn fieldname = 0;
442 48c4bdc1 2019-04-04 martijn while (line[r + 1] == ' ' ||
443 48c4bdc1 2019-04-04 martijn line[r + 1] == '\t')
444 48c4bdc1 2019-04-04 martijn r++;
445 48c4bdc1 2019-04-04 martijn continue;
446 48c4bdc1 2019-04-04 martijn }
447 06f6b49b 2019-04-06 martijn if (line[r] == ' ' || line[r] == '\t' ||
448 06f6b49b 2019-04-06 martijn line[r] == '\r' || line[r] == '\n') {
449 cd6f9b03 2019-10-23 martijn if (r != 0 && w != 0 && line[w - 1] == ' ')
450 48c4bdc1 2019-04-04 martijn continue;
451 48c4bdc1 2019-04-04 martijn else
452 48c4bdc1 2019-04-04 martijn line[w++] = ' ';
453 48c4bdc1 2019-04-04 martijn } else if (fieldname) {
454 48c4bdc1 2019-04-04 martijn line[w++] = tolower(line[r]);
455 48c4bdc1 2019-04-04 martijn continue;
456 48c4bdc1 2019-04-04 martijn } else
457 48c4bdc1 2019-04-04 martijn line[w++] = line[r];
458 48c4bdc1 2019-04-04 martijn }
459 cd6f9b03 2019-10-23 martijn linelen = (w != 0 && line[w - 1] == ' ') ? w - 1 : w;
460 48c4bdc1 2019-04-04 martijn line[linelen] = '\0';
461 48c4bdc1 2019-04-04 martijn } else
462 48c4bdc1 2019-04-04 martijn linelen = strlen(line);
463 48c4bdc1 2019-04-04 martijn
464 6840f1a1 2019-08-22 martijn for (lastheader = 0; message->headers[lastheader] != NULL; lastheader++)
465 48c4bdc1 2019-04-04 martijn continue;
466 6840f1a1 2019-08-22 martijn if (!message->lastheader) {
467 6840f1a1 2019-08-22 martijn mtmp = recallocarray(message->headers, lastheader + 1,
468 a53d74c4 2019-04-08 martijn lastheader + 2, sizeof(*mtmp));
469 d581ea1c 2025-01-29 kirill if (mtmp == NULL)
470 e1300062 2025-04-08 kirill osmtpd_err(1, "reallocarray");
471 6840f1a1 2019-08-22 martijn message->headers = mtmp;
472 6840f1a1 2019-08-22 martijn
473 d581ea1c 2025-01-29 kirill if ((message->headers[lastheader] = strdup(line)) == NULL)
474 e1300062 2025-04-08 kirill osmtpd_err(1, "strdup");
475 6840f1a1 2019-08-22 martijn message->headers[lastheader + 1 ] = NULL;
476 6840f1a1 2019-08-22 martijn message->lastheader = 1;
477 48c4bdc1 2019-04-04 martijn } else {
478 48c4bdc1 2019-04-04 martijn lastheader--;
479 6840f1a1 2019-08-22 martijn linelen += strlen(message->headers[lastheader]);
480 48c4bdc1 2019-04-04 martijn if (canonheader == CANON_SIMPLE)
481 48c4bdc1 2019-04-04 martijn linelen += 2;
482 48c4bdc1 2019-04-04 martijn linelen++;
483 6840f1a1 2019-08-22 martijn htmp = reallocarray(message->headers[lastheader], linelen,
484 48c4bdc1 2019-04-04 martijn sizeof(*htmp));
485 d581ea1c 2025-01-29 kirill if (htmp == NULL)
486 e1300062 2025-04-08 kirill osmtpd_err(1, "reallocarray");
487 6840f1a1 2019-08-22 martijn message->headers[lastheader] = htmp;
488 48c4bdc1 2019-04-04 martijn if (canonheader == CANON_SIMPLE) {
489 9b92b3ea 2025-05-19 martijn (void)strlcat(htmp, "\r\n", linelen);
490 63ac36ce 2019-04-08 martijn } else if (canonheader == CANON_RELAXED &&
491 6840f1a1 2019-08-22 martijn (tmp = strchr(message->headers[lastheader], ':')) != NULL &&
492 63ac36ce 2019-04-08 martijn tmp[1] == '\0')
493 63ac36ce 2019-04-08 martijn line++;
494 63ac36ce 2019-04-08 martijn
495 9b92b3ea 2025-05-19 martijn (void)strlcat(htmp, line, linelen);
496 48c4bdc1 2019-04-04 martijn }
497 48c4bdc1 2019-04-04 martijn }
498 48c4bdc1 2019-04-04 martijn
499 48c4bdc1 2019-04-04 martijn void
500 6840f1a1 2019-08-22 martijn dkim_parse_body(struct dkim_message *message, char *line)
501 48c4bdc1 2019-04-04 martijn {
502 48c4bdc1 2019-04-04 martijn size_t r, w;
503 48c4bdc1 2019-04-04 martijn size_t linelen;
504 1308710c 2019-04-09 martijn
505 1308710c 2019-04-09 martijn if (canonbody == CANON_RELAXED) {
506 1308710c 2019-04-09 martijn for (r = w = 0; line[r] != '\0'; r++) {
507 1308710c 2019-04-09 martijn if (line[r] == ' ' || line[r] == '\t') {
508 1308710c 2019-04-09 martijn if (r != 0 && line[w - 1] == ' ')
509 1308710c 2019-04-09 martijn continue;
510 1308710c 2019-04-09 martijn else
511 1308710c 2019-04-09 martijn line[w++] = ' ';
512 1308710c 2019-04-09 martijn } else
513 1308710c 2019-04-09 martijn line[w++] = line[r];
514 1308710c 2019-04-09 martijn }
515 cd6f9b03 2019-10-23 martijn linelen = (w != 0 && line[w - 1] == ' ') ? w - 1 : w;
516 1308710c 2019-04-09 martijn line[linelen] = '\0';
517 1308710c 2019-04-09 martijn } else
518 1308710c 2019-04-09 martijn linelen = strlen(line);
519 1308710c 2019-04-09 martijn
520 48c4bdc1 2019-04-04 martijn if (line[0] == '\0') {
521 6840f1a1 2019-08-22 martijn message->body_whitelines++;
522 48c4bdc1 2019-04-04 martijn return;
523 48c4bdc1 2019-04-04 martijn }
524 48c4bdc1 2019-04-04 martijn
525 6840f1a1 2019-08-22 martijn while (message->body_whitelines--) {
526 d581ea1c 2025-01-29 kirill if (EVP_DigestUpdate(message->dctx, "\r\n", 2) == 0)
527 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "EVP_DigestUpdate");
528 48c4bdc1 2019-04-04 martijn }
529 6840f1a1 2019-08-22 martijn message->body_whitelines = 0;
530 6840f1a1 2019-08-22 martijn message->has_body = 1;
531 48c4bdc1 2019-04-04 martijn
532 6bcbc798 2021-05-16 martijn if (EVP_DigestUpdate(message->dctx, line, linelen) == 0 ||
533 d581ea1c 2025-01-29 kirill EVP_DigestUpdate(message->dctx, "\r\n", 2) == 0)
534 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "EVP_DigestUpdate");
535 48c4bdc1 2019-04-04 martijn }
536 48c4bdc1 2019-04-04 martijn
537 365862b1 2019-05-02 martijn void
538 6840f1a1 2019-08-22 martijn dkim_sign(struct osmtpd_ctx *ctx)
539 365862b1 2019-05-02 martijn {
540 6840f1a1 2019-08-22 martijn struct dkim_message *message = ctx->local_message;
541 365862b1 2019-05-02 martijn /* Use largest hash size here */
542 8e76d341 2021-06-02 martijn unsigned char bdigest[EVP_MAX_MD_SIZE];
543 8e76d341 2021-06-02 martijn unsigned char digest[(((sizeof(bdigest) + 2) / 3) * 4) + 1];
544 8e76d341 2021-06-02 martijn unsigned char *b;
545 40cd76f4 2020-08-30 martijn const char *sdomain = domain[0], *tsdomain;
546 e974922c 2019-05-02 martijn time_t now;
547 6840f1a1 2019-08-22 martijn ssize_t i;
548 6bcbc798 2021-05-16 martijn size_t linelen = 0;
549 365862b1 2019-05-02 martijn char *tmp, *tmp2;
550 8e76d341 2021-06-02 martijn unsigned int digestsz;
551 365862b1 2019-05-02 martijn
552 e974922c 2019-05-02 martijn if (addtime || addexpire)
553 e974922c 2019-05-02 martijn now = time(NULL);
554 540788da 2025-02-08 martijn if (addtime)
555 540788da 2025-02-08 martijn dkim_signature_printf(message, "t=%lld; ", (long long)now);
556 540788da 2025-02-08 martijn if (addexpire != 0)
557 540788da 2025-02-08 martijn dkim_signature_printf(message, "x=%lld; ",
558 9b92b3ea 2025-05-19 martijn now + addexpire < now ? LLONG_MAX : now + addexpire);
559 e974922c 2019-05-02 martijn
560 6840f1a1 2019-08-22 martijn if (canonbody == CANON_SIMPLE && !message->has_body) {
561 d581ea1c 2025-01-29 kirill if (EVP_DigestUpdate(message->dctx, "\r\n", 2) <= 0)
562 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "EVP_DigestUpdate");
563 365862b1 2019-05-02 martijn }
564 d581ea1c 2025-01-29 kirill if (EVP_DigestFinal_ex(message->dctx, bdigest, &digestsz) == 0)
565 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "EVP_DigestFinal_ex");
566 6bcbc798 2021-05-16 martijn EVP_EncodeBlock(digest, bdigest, digestsz);
567 540788da 2025-02-08 martijn dkim_signature_printf(message, "bh=%s; h=", digest);
568 365862b1 2019-05-02 martijn /* Reverse order for ease of use of RFC6367 section 5.4.2 */
569 6840f1a1 2019-08-22 martijn for (i = 0; message->headers[i] != NULL; i++)
570 365862b1 2019-05-02 martijn continue;
571 6bcbc798 2021-05-16 martijn EVP_MD_CTX_reset(message->dctx);
572 6bcbc798 2021-05-16 martijn if (!sephash) {
573 6bcbc798 2021-05-16 martijn if (EVP_DigestSignInit(message->dctx, NULL, hash_md, NULL,
574 d581ea1c 2025-01-29 kirill pkey) != 1)
575 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "EVP_DigestSignInit");
576 6bcbc798 2021-05-16 martijn } else {
577 d581ea1c 2025-01-29 kirill if (EVP_DigestInit_ex(message->dctx, hash_md, NULL) != 1)
578 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "EVP_DigestInit_ex");
579 6bcbc798 2021-05-16 martijn }
580 365862b1 2019-05-02 martijn for (i--; i >= 0; i--) {
581 6bcbc798 2021-05-16 martijn if (!sephash) {
582 6bcbc798 2021-05-16 martijn if (EVP_DigestSignUpdate(message->dctx,
583 6bcbc798 2021-05-16 martijn message->headers[i],
584 6bcbc798 2021-05-16 martijn strlen(message->headers[i])) != 1 ||
585 6bcbc798 2021-05-16 martijn EVP_DigestSignUpdate(message->dctx, "\r\n",
586 d581ea1c 2025-01-29 kirill 2) <= 0)
587 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "EVP_DigestSignUpdate");
588 6bcbc798 2021-05-16 martijn } else {
589 6bcbc798 2021-05-16 martijn if (EVP_DigestUpdate(message->dctx, message->headers[i],
590 6bcbc798 2021-05-16 martijn strlen(message->headers[i])) != 1 ||
591 d581ea1c 2025-01-29 kirill EVP_DigestUpdate(message->dctx, "\r\n", 2) <= 0)
592 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "EVP_DigestSignUpdate");
593 365862b1 2019-05-02 martijn }
594 40cd76f4 2020-08-30 martijn if ((tsdomain = dkim_domain_select(message, message->headers[i])) != NULL)
595 40cd76f4 2020-08-30 martijn sdomain = tsdomain;
596 365862b1 2019-05-02 martijn /* We're done with the cached header after hashing */
597 6840f1a1 2019-08-22 martijn for (tmp = message->headers[i]; tmp[0] != ':'; tmp++) {
598 365862b1 2019-05-02 martijn if (tmp[0] == ' ' || tmp[0] == '\t')
599 365862b1 2019-05-02 martijn break;
600 365862b1 2019-05-02 martijn tmp[0] = tolower(tmp[0]);
601 365862b1 2019-05-02 martijn }
602 365862b1 2019-05-02 martijn tmp[0] = '\0';
603 540788da 2025-02-08 martijn dkim_signature_printf(message, "%s%s",
604 6840f1a1 2019-08-22 martijn message->headers[i + 1] == NULL ? "" : ":",
605 540788da 2025-02-08 martijn message->headers[i]);
606 365862b1 2019-05-02 martijn }
607 40cd76f4 2020-08-30 martijn dkim_signature_printf(message, "; d=%s; b=", sdomain);
608 540788da 2025-02-08 martijn dkim_signature_normalize(message);
609 d581ea1c 2025-01-29 kirill if ((tmp = strdup(message->signature.signature)) == NULL)
610 e1300062 2025-04-08 kirill osmtpd_err(1, "strdup");
611 6840f1a1 2019-08-22 martijn dkim_parse_header(message, tmp, 1);
612 6bcbc798 2021-05-16 martijn if (!sephash) {
613 6bcbc798 2021-05-16 martijn if (EVP_DigestSignUpdate(message->dctx, tmp,
614 d581ea1c 2025-01-29 kirill strlen(tmp)) != 1)
615 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "EVP_DigestSignUpdate");
616 6bcbc798 2021-05-16 martijn } else {
617 d581ea1c 2025-01-29 kirill if (EVP_DigestUpdate(message->dctx, tmp, strlen(tmp)) != 1)
618 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "EVP_DigestUpdate");
619 365862b1 2019-05-02 martijn }
620 365862b1 2019-05-02 martijn free(tmp);
621 6bcbc798 2021-05-16 martijn if (!sephash) {
622 d581ea1c 2025-01-29 kirill if (EVP_DigestSignFinal(message->dctx, NULL, &linelen) != 1)
623 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "EVP_DigestSignFinal");
624 6bcbc798 2021-05-16 martijn } else {
625 6bcbc798 2021-05-16 martijn if (EVP_DigestFinal_ex(message->dctx, bdigest,
626 d581ea1c 2025-01-29 kirill &digestsz) != 1)
627 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "EVP_DigestFinal_ex");
628 6bcbc798 2021-05-16 martijn EVP_MD_CTX_reset(message->dctx);
629 6bcbc798 2021-05-16 martijn if (EVP_DigestSignInit(message->dctx, NULL, NULL, NULL,
630 d581ea1c 2025-01-29 kirill pkey) != 1)
631 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "EVP_DigestSignInit");
632 6bcbc798 2021-05-16 martijn if (EVP_DigestSign(message->dctx, NULL, &linelen, bdigest,
633 d581ea1c 2025-01-29 kirill digestsz) != 1)
634 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "EVP_DigestSign");
635 365862b1 2019-05-02 martijn }
636 d581ea1c 2025-01-29 kirill if ((tmp = malloc(linelen)) == NULL)
637 e1300062 2025-04-08 kirill osmtpd_err(1, "malloc");
638 6bcbc798 2021-05-16 martijn if (!sephash) {
639 d581ea1c 2025-01-29 kirill if (EVP_DigestSignFinal(message->dctx, tmp, &linelen) != 1)
640 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "EVP_DigestSignFinal");
641 6bcbc798 2021-05-16 martijn } else {
642 6bcbc798 2021-05-16 martijn if (EVP_DigestSign(message->dctx, tmp, &linelen, bdigest,
643 d581ea1c 2025-01-29 kirill digestsz) != 1)
644 d581ea1c 2025-01-29 kirill osmtpd_errx(1, "EVP_DigestSign");
645 365862b1 2019-05-02 martijn }
646 d581ea1c 2025-01-29 kirill if ((b = malloc((((linelen + 2) / 3) * 4) + 1)) == NULL)
647 e1300062 2025-04-08 kirill osmtpd_err(1, "malloc");
648 365862b1 2019-05-02 martijn EVP_EncodeBlock(b, tmp, linelen);
649 365862b1 2019-05-02 martijn free(tmp);
650 6840f1a1 2019-08-22 martijn dkim_signature_printf(message, "%s\r\n", b);
651 365862b1 2019-05-02 martijn free(b);
652 6840f1a1 2019-08-22 martijn dkim_signature_normalize(message);
653 6840f1a1 2019-08-22 martijn tmp = message->signature.signature;
654 365862b1 2019-05-02 martijn while ((tmp2 = strchr(tmp, '\r')) != NULL) {
655 365862b1 2019-05-02 martijn tmp2[0] = '\0';
656 6840f1a1 2019-08-22 martijn osmtpd_filter_dataline(ctx, "%s", tmp);
657 365862b1 2019-05-02 martijn tmp = tmp2 + 2;
658 365862b1 2019-05-02 martijn }
659 365862b1 2019-05-02 martijn tmp = NULL;
660 365862b1 2019-05-02 martijn linelen = 0;
661 6840f1a1 2019-08-22 martijn rewind(message->origf);
662 6840f1a1 2019-08-22 martijn while ((i = getline(&tmp, &linelen, message->origf)) != -1) {
663 365862b1 2019-05-02 martijn tmp[i - 1] = '\0';
664 6840f1a1 2019-08-22 martijn osmtpd_filter_dataline(ctx, "%s", tmp);
665 365862b1 2019-05-02 martijn }
666 be6d950d 2021-03-21 martijn free(tmp);
667 2c88ced1 2021-05-16 martijn return;
668 365862b1 2019-05-02 martijn }
669 365862b1 2019-05-02 martijn
670 540788da 2025-02-08 martijn void
671 6840f1a1 2019-08-22 martijn dkim_signature_normalize(struct dkim_message *message)
672 48c4bdc1 2019-04-04 martijn {
673 06f6b49b 2019-04-06 martijn size_t i;
674 06f6b49b 2019-04-06 martijn size_t linelen;
675 06f6b49b 2019-04-06 martijn size_t checkpoint;
676 06f6b49b 2019-04-06 martijn size_t skip;
677 6840f1a1 2019-08-22 martijn size_t *headerlen = &(message->signature.len);
678 06f6b49b 2019-04-06 martijn int headername = 1;
679 06f6b49b 2019-04-06 martijn char tag = '\0';
680 6840f1a1 2019-08-22 martijn char *sig = message->signature.signature;
681 48c4bdc1 2019-04-04 martijn
682 06f6b49b 2019-04-06 martijn for (linelen = i = 0; sig[i] != '\0'; i++) {
683 ffa37923 2019-04-09 martijn if (sig[i] == '\r' && sig[i + 1] == '\n') {
684 ffa37923 2019-04-09 martijn i++;
685 06f6b49b 2019-04-06 martijn checkpoint = 0;
686 06f6b49b 2019-04-06 martijn linelen = 0;
687 06f6b49b 2019-04-06 martijn continue;
688 06f6b49b 2019-04-06 martijn }
689 06f6b49b 2019-04-06 martijn if (sig[i] == '\t')
690 06f6b49b 2019-04-06 martijn linelen = (linelen + 8) & ~7;
691 48c4bdc1 2019-04-04 martijn else
692 06f6b49b 2019-04-06 martijn linelen++;
693 06f6b49b 2019-04-06 martijn if (headername) {
694 06f6b49b 2019-04-06 martijn if (sig[i] == ':') {
695 06f6b49b 2019-04-06 martijn headername = 0;
696 06f6b49b 2019-04-06 martijn checkpoint = i;
697 06f6b49b 2019-04-06 martijn }
698 06f6b49b 2019-04-06 martijn continue;
699 06f6b49b 2019-04-06 martijn }
700 ffa37923 2019-04-09 martijn if (linelen > DKIM_SIGNATURE_LINELEN && checkpoint != 0) {
701 06f6b49b 2019-04-06 martijn for (skip = checkpoint + 1;
702 06f6b49b 2019-04-06 martijn sig[skip] == ' ' || sig[skip] == '\t';
703 06f6b49b 2019-04-06 martijn skip++)
704 06f6b49b 2019-04-06 martijn continue;
705 06f6b49b 2019-04-06 martijn skip -= checkpoint + 1;
706 540788da 2025-02-08 martijn dkim_signature_need(message,
707 540788da 2025-02-08 martijn skip > 3 ? 0 : 3 - skip + 1);
708 6840f1a1 2019-08-22 martijn sig = message->signature.signature;
709 6840f1a1 2019-08-22 martijn
710 06f6b49b 2019-04-06 martijn memmove(sig + checkpoint + 3,
711 06f6b49b 2019-04-06 martijn sig + checkpoint + skip,
712 540f552a 2019-04-06 martijn *headerlen - skip - checkpoint + 1);
713 06f6b49b 2019-04-06 martijn sig[checkpoint + 1] = '\r';
714 06f6b49b 2019-04-06 martijn sig[checkpoint + 2] = '\n';
715 06f6b49b 2019-04-06 martijn sig[checkpoint + 3] = '\t';
716 06f6b49b 2019-04-06 martijn linelen = 8;
717 06f6b49b 2019-04-06 martijn *headerlen = *headerlen + 3 - skip;
718 06f6b49b 2019-04-06 martijn i = checkpoint + 3;
719 06f6b49b 2019-04-06 martijn checkpoint = 0;
720 06f6b49b 2019-04-06 martijn }
721 06f6b49b 2019-04-06 martijn if (sig[i] == ';') {
722 06f6b49b 2019-04-06 martijn checkpoint = i;
723 06f6b49b 2019-04-06 martijn tag = '\0';
724 06f6b49b 2019-04-06 martijn continue;
725 06f6b49b 2019-04-06 martijn }
726 06f6b49b 2019-04-06 martijn switch (tag) {
727 06f6b49b 2019-04-06 martijn case 'B':
728 06f6b49b 2019-04-06 martijn case 'b':
729 c826412d 2019-05-02 martijn case 'z':
730 06f6b49b 2019-04-06 martijn checkpoint = i;
731 06f6b49b 2019-04-06 martijn break;
732 06f6b49b 2019-04-06 martijn case 'h':
733 06f6b49b 2019-04-06 martijn if (sig[i] == ':')
734 06f6b49b 2019-04-06 martijn checkpoint = i;
735 c826412d 2019-05-02 martijn break;
736 06f6b49b 2019-04-06 martijn }
737 06f6b49b 2019-04-06 martijn if (tag == '\0' && sig[i] != ' ' && sig[i] != '\t') {
738 06f6b49b 2019-04-06 martijn if ((tag = sig[i]) == 'b' && sig[i + 1] == 'h' &&
739 06f6b49b 2019-04-06 martijn sig[i + 2] == '=') {
740 06f6b49b 2019-04-06 martijn tag = 'B';
741 06f6b49b 2019-04-06 martijn linelen += 2;
742 06f6b49b 2019-04-06 martijn i += 2;
743 06f6b49b 2019-04-06 martijn } else
744 06f6b49b 2019-04-06 martijn tag = sig[i];
745 06f6b49b 2019-04-06 martijn }
746 48c4bdc1 2019-04-04 martijn }
747 06f6b49b 2019-04-06 martijn }
748 48c4bdc1 2019-04-04 martijn
749 540788da 2025-02-08 martijn void
750 c08ad6cc 2025-02-08 martijn dkim_signature_printheader(struct dkim_message *message, const char *line)
751 c826412d 2019-05-02 martijn {
752 82ec5c6f 2025-02-08 martijn size_t i;
753 c826412d 2019-05-02 martijn int first;
754 c826412d 2019-05-02 martijn
755 6840f1a1 2019-08-22 martijn first = message->signature.signature[message->signature.len - 1] == '=';
756 82ec5c6f 2025-02-08 martijn for (i = 0; line[i] != '\0'; i++) {
757 c08ad6cc 2025-02-08 martijn if (i == 0 && line[i] != ' ' && line[i] != '\t' && !first)
758 540788da 2025-02-08 martijn dkim_signature_printf(message, "|");
759 c08ad6cc 2025-02-08 martijn if ((line[i] >= 0x21 && line[i] <= 0x3A) ||
760 c08ad6cc 2025-02-08 martijn (line[i] == 0x3C) ||
761 c08ad6cc 2025-02-08 martijn (line[i] >= 0x3E && line[i] <= 0x7B) ||
762 82ec5c6f 2025-02-08 martijn (line[i] >= 0x7D && line[i] <= 0x7E)) {
763 540788da 2025-02-08 martijn dkim_signature_printf(message, "%c", line[i]);
764 540788da 2025-02-08 martijn } else
765 540788da 2025-02-08 martijn dkim_signature_printf(message, "=%02hhX", line[i]);
766 c826412d 2019-05-02 martijn }
767 540788da 2025-02-08 martijn dkim_signature_printf(message, "=%02hhX=%02hhX", '\r', '\n');
768 c826412d 2019-05-02 martijn }
769 c826412d 2019-05-02 martijn
770 540788da 2025-02-08 martijn void
771 6840f1a1 2019-08-22 martijn dkim_signature_printf(struct dkim_message *message, char *fmt, ...)
772 06f6b49b 2019-04-06 martijn {
773 6840f1a1 2019-08-22 martijn struct dkim_signature *sig = &(message->signature);
774 06f6b49b 2019-04-06 martijn va_list ap;
775 06f6b49b 2019-04-06 martijn size_t len;
776 06f6b49b 2019-04-06 martijn
777 06f6b49b 2019-04-06 martijn va_start(ap, fmt);
778 06f6b49b 2019-04-06 martijn if ((len = vsnprintf(sig->signature + sig->len, sig->size - sig->len,
779 06f6b49b 2019-04-06 martijn fmt, ap)) >= sig->size - sig->len) {
780 06f6b49b 2019-04-06 martijn va_end(ap);
781 540788da 2025-02-08 martijn dkim_signature_need(message, len + 1);
782 06f6b49b 2019-04-06 martijn va_start(ap, fmt);
783 6840f1a1 2019-08-22 martijn if ((len = vsnprintf(sig->signature + sig->len,
784 6840f1a1 2019-08-22 martijn sig->size - sig->len, fmt, ap)) >= sig->size - sig->len)
785 6840f1a1 2019-08-22 martijn osmtpd_errx(1, "Miscalculated header size");
786 06f6b49b 2019-04-06 martijn }
787 06f6b49b 2019-04-06 martijn sig->len += len;
788 06f6b49b 2019-04-06 martijn va_end(ap);
789 06f6b49b 2019-04-06 martijn }
790 06f6b49b 2019-04-06 martijn
791 40cd76f4 2020-08-30 martijn const char *
792 40cd76f4 2020-08-30 martijn dkim_domain_select(struct dkim_message *message, char *from)
793 40cd76f4 2020-08-30 martijn {
794 40cd76f4 2020-08-30 martijn char *mdomain0, *mdomain;
795 40cd76f4 2020-08-30 martijn size_t i;
796 40cd76f4 2020-08-30 martijn
797 d581ea1c 2025-01-29 kirill if ((mdomain = mdomain0 = osmtpd_mheader_from_domain(from)) == NULL)
798 40cd76f4 2020-08-30 martijn return NULL;
799 40cd76f4 2020-08-30 martijn
800 40cd76f4 2020-08-30 martijn while (mdomain != NULL && mdomain[0] != '\0') {
801 40cd76f4 2020-08-30 martijn for (i = 0; i < ndomains; i++) {
802 40cd76f4 2020-08-30 martijn if (strcasecmp(mdomain, domain[i]) == 0) {
803 40cd76f4 2020-08-30 martijn free(mdomain0);
804 40cd76f4 2020-08-30 martijn return domain[i];
805 40cd76f4 2020-08-30 martijn }
806 40cd76f4 2020-08-30 martijn }
807 40cd76f4 2020-08-30 martijn if ((mdomain = strchr(mdomain, '.')) != NULL)
808 40cd76f4 2020-08-30 martijn mdomain++;
809 40cd76f4 2020-08-30 martijn }
810 40cd76f4 2020-08-30 martijn free(mdomain0);
811 40cd76f4 2020-08-30 martijn return NULL;
812 40cd76f4 2020-08-30 martijn }
813 40cd76f4 2020-08-30 martijn
814 540788da 2025-02-08 martijn void
815 6840f1a1 2019-08-22 martijn dkim_signature_need(struct dkim_message *message, size_t len)
816 06f6b49b 2019-04-06 martijn {
817 6840f1a1 2019-08-22 martijn struct dkim_signature *sig = &(message->signature);
818 06f6b49b 2019-04-06 martijn char *tmp;
819 06f6b49b 2019-04-06 martijn
820 a27b5ff7 2019-04-08 martijn if (sig->len + len < sig->size)
821 540788da 2025-02-08 martijn return;
822 503597cc 2019-04-08 martijn sig->size = (((len + sig->len) / 512) + 1) * 512;
823 d581ea1c 2025-01-29 kirill if ((tmp = realloc(sig->signature, sig->size)) == NULL)
824 e1300062 2025-04-08 kirill osmtpd_err(1, "malloc");
825 06f6b49b 2019-04-06 martijn sig->signature = tmp;
826 06f6b49b 2019-04-06 martijn }
827 06f6b49b 2019-04-06 martijn
828 48c4bdc1 2019-04-04 martijn __dead void
829 48c4bdc1 2019-04-04 martijn usage(void)
830 48c4bdc1 2019-04-04 martijn {
831 fe371fc9 2019-09-05 martijn fprintf(stderr, "usage: filter-dkimsign [-tz] [-a signalg] "
832 fe371fc9 2019-09-05 martijn "[-c canonicalization] \n [-h headerfields]"
833 5fdffb49 2022-01-27 martijn "[-x seconds] -D file -d domain -k keyfile -s selector\n");
834 48c4bdc1 2019-04-04 martijn exit(1);
835 48c4bdc1 2019-04-04 martijn }