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