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 <sys/tree.h>
17 48c4bdc1 2019-04-04 martijn
18 48c4bdc1 2019-04-04 martijn #include <openssl/evp.h>
19 06f6b49b 2019-04-06 martijn #include <openssl/pem.h>
20 48c4bdc1 2019-04-04 martijn #include <openssl/sha.h>
21 48c4bdc1 2019-04-04 martijn
22 48c4bdc1 2019-04-04 martijn #include <ctype.h>
23 48c4bdc1 2019-04-04 martijn #include <err.h>
24 1308710c 2019-04-09 martijn #include <fcntl.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 48c4bdc1 2019-04-04 martijn #include "log.h"
33 48c4bdc1 2019-04-04 martijn #include "smtp_proc.h"
34 48c4bdc1 2019-04-04 martijn
35 06f6b49b 2019-04-06 martijn struct dkim_signature {
36 06f6b49b 2019-04-06 martijn char *signature;
37 06f6b49b 2019-04-06 martijn size_t size;
38 06f6b49b 2019-04-06 martijn size_t len;
39 06f6b49b 2019-04-06 martijn };
40 06f6b49b 2019-04-06 martijn
41 48c4bdc1 2019-04-04 martijn struct dkim_session {
42 48c4bdc1 2019-04-04 martijn uint64_t reqid;
43 48c4bdc1 2019-04-04 martijn uint64_t token;
44 48c4bdc1 2019-04-04 martijn FILE *origf;
45 48c4bdc1 2019-04-04 martijn int parsing_headers;
46 48c4bdc1 2019-04-04 martijn char **headers;
47 48c4bdc1 2019-04-04 martijn int lastheader;
48 48c4bdc1 2019-04-04 martijn size_t body_whitelines;
49 48c4bdc1 2019-04-04 martijn int has_body;
50 06f6b49b 2019-04-06 martijn struct dkim_signature signature;
51 6b9e2bc6 2019-04-06 martijn EVP_MD_CTX *b;
52 37df18b6 2019-04-06 martijn EVP_MD_CTX *bh;
53 48c4bdc1 2019-04-04 martijn RB_ENTRY(dkim_session) entry;
54 48c4bdc1 2019-04-04 martijn };
55 48c4bdc1 2019-04-04 martijn
56 48c4bdc1 2019-04-04 martijn RB_HEAD(dkim_sessions, dkim_session) dkim_sessions = RB_INITIALIZER(NULL);
57 48c4bdc1 2019-04-04 martijn RB_PROTOTYPE(dkim_sessions, dkim_session, entry, dkim_session_cmp);
58 48c4bdc1 2019-04-04 martijn
59 37c6191b 2019-04-06 martijn /* RFC 6376 section 5.4.1 */
60 37c6191b 2019-04-06 martijn static char *dsign_headers[] = {
61 37c6191b 2019-04-06 martijn "from",
62 37c6191b 2019-04-06 martijn "reply-to",
63 37c6191b 2019-04-06 martijn "subject",
64 37c6191b 2019-04-06 martijn "date",
65 37c6191b 2019-04-06 martijn "to",
66 37c6191b 2019-04-06 martijn "cc",
67 37c6191b 2019-04-06 martijn "resent-date",
68 37c6191b 2019-04-06 martijn "resent-from",
69 37c6191b 2019-04-06 martijn "resent-to",
70 37c6191b 2019-04-06 martijn "resent-cc",
71 37c6191b 2019-04-06 martijn "in-reply-to",
72 37c6191b 2019-04-06 martijn "references",
73 37c6191b 2019-04-06 martijn "list-id",
74 37c6191b 2019-04-06 martijn "list-help",
75 37c6191b 2019-04-06 martijn "list-unsubscribe",
76 37c6191b 2019-04-06 martijn "list-subscribe",
77 37c6191b 2019-04-06 martijn "list-post",
78 37c6191b 2019-04-06 martijn "list-owner",
79 37c6191b 2019-04-06 martijn "list-archive"
80 37c6191b 2019-04-06 martijn };
81 37c6191b 2019-04-06 martijn static char **sign_headers = dsign_headers;
82 37c6191b 2019-04-06 martijn static size_t nsign_headers = sizeof(dsign_headers) / sizeof(*dsign_headers);
83 48c4bdc1 2019-04-04 martijn
84 fddd7e1e 2019-04-06 martijn static char *hashalg = "sha256";
85 fddd7e1e 2019-04-06 martijn static char *cryptalg = "rsa";
86 48c4bdc1 2019-04-04 martijn
87 48c4bdc1 2019-04-04 martijn #define CANON_SIMPLE 0
88 48c4bdc1 2019-04-04 martijn #define CANON_RELAXED 1
89 48c4bdc1 2019-04-04 martijn static int canonheader = CANON_SIMPLE;
90 48c4bdc1 2019-04-04 martijn static int canonbody = CANON_SIMPLE;
91 48c4bdc1 2019-04-04 martijn
92 48c4bdc1 2019-04-04 martijn static char *domain = NULL;
93 06f6b49b 2019-04-06 martijn static char *selector = NULL;
94 48c4bdc1 2019-04-04 martijn
95 06f6b49b 2019-04-06 martijn static EVP_PKEY *pkey;
96 06f6b49b 2019-04-06 martijn static const EVP_MD *hash_md;
97 06f6b49b 2019-04-06 martijn
98 06f6b49b 2019-04-06 martijn #define DKIM_SIGNATURE_LINELEN 78
99 06f6b49b 2019-04-06 martijn
100 48c4bdc1 2019-04-04 martijn void usage(void);
101 48c4bdc1 2019-04-04 martijn void dkim_err(struct dkim_session *, char *);
102 48c4bdc1 2019-04-04 martijn void dkim_errx(struct dkim_session *, char *);
103 48c4bdc1 2019-04-04 martijn void dkim_headers_set(char *);
104 48c4bdc1 2019-04-04 martijn void dkim_dataline(char *, int, struct timespec *, char *, char *, uint64_t,
105 48c4bdc1 2019-04-04 martijn uint64_t, char *);
106 48c4bdc1 2019-04-04 martijn void dkim_disconnect(char *, int, struct timespec *, char *, char *, uint64_t);
107 48c4bdc1 2019-04-04 martijn struct dkim_session *dkim_session_new(uint64_t);
108 48c4bdc1 2019-04-04 martijn void dkim_session_free(struct dkim_session *);
109 48c4bdc1 2019-04-04 martijn int dkim_session_cmp(struct dkim_session *, struct dkim_session *);
110 06f6b49b 2019-04-06 martijn void dkim_parse_header(struct dkim_session *, char *, int);
111 48c4bdc1 2019-04-04 martijn void dkim_parse_body(struct dkim_session *, char *);
112 06f6b49b 2019-04-06 martijn int dkim_signature_printf(struct dkim_session *, char *, ...)
113 06f6b49b 2019-04-06 martijn __attribute__((__format__ (printf, 2, 3)));
114 06f6b49b 2019-04-06 martijn int dkim_signature_normalize(struct dkim_session *);
115 06f6b49b 2019-04-06 martijn int dkim_signature_need(struct dkim_session *, size_t);
116 06f6b49b 2019-04-06 martijn int dkim_sign_init(struct dkim_session *);
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 48c4bdc1 2019-04-04 martijn int i;
123 48c4bdc1 2019-04-04 martijn int debug = 0;
124 06f6b49b 2019-04-06 martijn FILE *keyfile;
125 48c4bdc1 2019-04-04 martijn
126 06f6b49b 2019-04-06 martijn while ((ch = getopt(argc, argv, "a:c:Dd:h:k:s:")) != -1) {
127 48c4bdc1 2019-04-04 martijn switch (ch) {
128 48c4bdc1 2019-04-04 martijn case 'a':
129 fddd7e1e 2019-04-06 martijn if (strncmp(optarg, "rsa-", 4) != 0)
130 48c4bdc1 2019-04-04 martijn err(1, "invalid algorithm");
131 fddd7e1e 2019-04-06 martijn hashalg = optarg + 4;
132 48c4bdc1 2019-04-04 martijn break;
133 48c4bdc1 2019-04-04 martijn case 'c':
134 48c4bdc1 2019-04-04 martijn if (strncmp(optarg, "simple", 6) == 0) {
135 48c4bdc1 2019-04-04 martijn canonheader = CANON_SIMPLE;
136 48c4bdc1 2019-04-04 martijn optarg += 6;
137 48c4bdc1 2019-04-04 martijn } else if (strncmp(optarg, "relaxed", 7) == 0) {
138 48c4bdc1 2019-04-04 martijn canonheader = CANON_RELAXED;
139 48c4bdc1 2019-04-04 martijn optarg += 7;
140 48c4bdc1 2019-04-04 martijn } else
141 48c4bdc1 2019-04-04 martijn err(1, "Invalid canonicalization");
142 48c4bdc1 2019-04-04 martijn if (optarg[0] == '/') {
143 48c4bdc1 2019-04-04 martijn if (strcmp(optarg + 1, "simple") == 0)
144 48c4bdc1 2019-04-04 martijn canonbody = CANON_SIMPLE;
145 48c4bdc1 2019-04-04 martijn else if (strcmp(optarg + 1, "relaxed") == 0)
146 48c4bdc1 2019-04-04 martijn canonbody = CANON_RELAXED;
147 48c4bdc1 2019-04-04 martijn else
148 48c4bdc1 2019-04-04 martijn err(1, "Invalid canonicalization");
149 48c4bdc1 2019-04-04 martijn } else if (optarg[0] == '\0')
150 48c4bdc1 2019-04-04 martijn canonbody = CANON_SIMPLE;
151 48c4bdc1 2019-04-04 martijn else
152 48c4bdc1 2019-04-04 martijn err(1, "Invalid canonicalization");
153 48c4bdc1 2019-04-04 martijn break;
154 48c4bdc1 2019-04-04 martijn case 'd':
155 48c4bdc1 2019-04-04 martijn domain = optarg;
156 48c4bdc1 2019-04-04 martijn break;
157 06f6b49b 2019-04-06 martijn case 'h':
158 06f6b49b 2019-04-06 martijn dkim_headers_set(optarg);
159 06f6b49b 2019-04-06 martijn break;
160 06f6b49b 2019-04-06 martijn case 'k':
161 06f6b49b 2019-04-06 martijn if ((keyfile = fopen(optarg, "r")) == NULL)
162 06f6b49b 2019-04-06 martijn err(1, "Can't open key file");
163 06f6b49b 2019-04-06 martijn pkey = PEM_read_PrivateKey(keyfile, NULL, NULL, NULL);
164 06f6b49b 2019-04-06 martijn if (pkey == NULL)
165 06f6b49b 2019-04-06 martijn errx(1, "Can't read key file");
166 06f6b49b 2019-04-06 martijn if (EVP_PKEY_get0_RSA(pkey) == NULL)
167 06f6b49b 2019-04-06 martijn err(1, "Key is not of type rsa");
168 06f6b49b 2019-04-06 martijn fclose(keyfile);
169 06f6b49b 2019-04-06 martijn break;
170 06f6b49b 2019-04-06 martijn case 's':
171 06f6b49b 2019-04-06 martijn selector = optarg;
172 06f6b49b 2019-04-06 martijn break;
173 48c4bdc1 2019-04-04 martijn case 'D':
174 48c4bdc1 2019-04-04 martijn debug = 1;
175 48c4bdc1 2019-04-04 martijn break;
176 48c4bdc1 2019-04-04 martijn default:
177 48c4bdc1 2019-04-04 martijn usage();
178 48c4bdc1 2019-04-04 martijn }
179 48c4bdc1 2019-04-04 martijn }
180 48c4bdc1 2019-04-04 martijn log_init(debug, LOG_MAIL);
181 fddd7e1e 2019-04-06 martijn
182 fddd7e1e 2019-04-06 martijn OpenSSL_add_all_digests();
183 fddd7e1e 2019-04-06 martijn if ((hash_md = EVP_get_digestbyname(hashalg)) == NULL)
184 fddd7e1e 2019-04-06 martijn fatalx("Can't find hash: %s", hashalg);
185 fddd7e1e 2019-04-06 martijn
186 48c4bdc1 2019-04-04 martijn if (pledge("tmppath stdio", NULL) == -1)
187 48c4bdc1 2019-04-04 martijn fatal("pledge");
188 48c4bdc1 2019-04-04 martijn
189 06f6b49b 2019-04-06 martijn if (domain == NULL || selector == NULL || pkey == NULL)
190 48c4bdc1 2019-04-04 martijn usage();
191 48c4bdc1 2019-04-04 martijn
192 48c4bdc1 2019-04-04 martijn smtp_register_filter_dataline(dkim_dataline);
193 48c4bdc1 2019-04-04 martijn smtp_in_register_report_disconnect(dkim_disconnect);
194 48c4bdc1 2019-04-04 martijn smtp_run(debug);
195 48c4bdc1 2019-04-04 martijn
196 48c4bdc1 2019-04-04 martijn return 0;
197 48c4bdc1 2019-04-04 martijn }
198 48c4bdc1 2019-04-04 martijn
199 48c4bdc1 2019-04-04 martijn void
200 48c4bdc1 2019-04-04 martijn dkim_disconnect(char *type, int version, struct timespec *tm, char *direction,
201 48c4bdc1 2019-04-04 martijn char *phase, uint64_t reqid)
202 48c4bdc1 2019-04-04 martijn {
203 48c4bdc1 2019-04-04 martijn struct dkim_session *session, search;
204 48c4bdc1 2019-04-04 martijn
205 48c4bdc1 2019-04-04 martijn search.reqid = reqid;
206 48c4bdc1 2019-04-04 martijn if ((session = RB_FIND(dkim_sessions, &dkim_sessions, &search)) != NULL)
207 48c4bdc1 2019-04-04 martijn dkim_session_free(session);
208 48c4bdc1 2019-04-04 martijn }
209 48c4bdc1 2019-04-04 martijn
210 48c4bdc1 2019-04-04 martijn void
211 48c4bdc1 2019-04-04 martijn dkim_dataline(char *type, int version, struct timespec *tm, char *direction,
212 48c4bdc1 2019-04-04 martijn char *phase, uint64_t reqid, uint64_t token, char *line)
213 48c4bdc1 2019-04-04 martijn {
214 48c4bdc1 2019-04-04 martijn struct dkim_session *session, search;
215 06f6b49b 2019-04-06 martijn struct dkim_signature sig;
216 14854bc8 2019-04-06 martijn /* Use largest hash size her */
217 37df18b6 2019-04-06 martijn char bbh[EVP_MAX_MD_SIZE];
218 37df18b6 2019-04-06 martijn char bh[(((sizeof(bbh) + 2) / 3) * 4) + 1];
219 14854bc8 2019-04-06 martijn char *b;
220 06f6b49b 2019-04-06 martijn ssize_t i, j;
221 48c4bdc1 2019-04-04 martijn size_t linelen;
222 06f6b49b 2019-04-06 martijn char *tmp, *tmp2;
223 06f6b49b 2019-04-06 martijn char tmpchar;
224 48c4bdc1 2019-04-04 martijn
225 48c4bdc1 2019-04-04 martijn search.reqid = reqid;
226 48c4bdc1 2019-04-04 martijn session = RB_FIND(dkim_sessions, &dkim_sessions, &search);
227 48c4bdc1 2019-04-04 martijn if (session == NULL) {
228 06f6b49b 2019-04-06 martijn if ((session = dkim_session_new(reqid)) == NULL)
229 06f6b49b 2019-04-06 martijn return;
230 48c4bdc1 2019-04-04 martijn session->token = token;
231 48c4bdc1 2019-04-04 martijn } else if (session->token != token)
232 48c4bdc1 2019-04-04 martijn fatalx("Token incorrect");
233 48c4bdc1 2019-04-04 martijn
234 48c4bdc1 2019-04-04 martijn linelen = strlen(line);
235 a883a4eb 2019-04-09 martijn if (fprintf(session->origf, "%s\n", line) < linelen)
236 48c4bdc1 2019-04-04 martijn dkim_err(session, "Couldn't write to tempfile");
237 48c4bdc1 2019-04-04 martijn
238 f5286ea3 2019-04-08 martijn if (line[0] == '.' && line[1] =='\0') {
239 37df18b6 2019-04-06 martijn /* This entire section needs an error handling revamp */
240 48c4bdc1 2019-04-04 martijn if (canonbody == CANON_SIMPLE && !session->has_body) {
241 37df18b6 2019-04-06 martijn if (EVP_DigestUpdate(session->bh, "\r\n", 2) <= 0) {
242 37df18b6 2019-04-06 martijn dkim_err(session, "Can't update hash context");
243 48c4bdc1 2019-04-04 martijn return;
244 37df18b6 2019-04-06 martijn }
245 48c4bdc1 2019-04-04 martijn }
246 37df18b6 2019-04-06 martijn if (EVP_DigestFinal_ex(session->bh, bbh, NULL) == 0) {
247 37df18b6 2019-04-06 martijn dkim_err(session, "Can't finalize hash context");
248 48c4bdc1 2019-04-04 martijn return;
249 37df18b6 2019-04-06 martijn }
250 37df18b6 2019-04-06 martijn EVP_EncodeBlock(bh, bbh, EVP_MD_CTX_size(session->bh));
251 14854bc8 2019-04-06 martijn if (!dkim_signature_printf(session, "bh=%s; h=", bh))
252 06f6b49b 2019-04-06 martijn return;
253 06f6b49b 2019-04-06 martijn /* Reverse order for ease of use of RFC6367 section 5.4.2 */
254 06f6b49b 2019-04-06 martijn for (i = 0; session->headers[i] != NULL; i++)
255 06f6b49b 2019-04-06 martijn continue;
256 06f6b49b 2019-04-06 martijn for (i--; i >= 0; i--) {
257 6b9e2bc6 2019-04-06 martijn if (EVP_DigestSignUpdate(session->b,
258 06f6b49b 2019-04-06 martijn session->headers[i],
259 06f6b49b 2019-04-06 martijn strlen(session->headers[i])) <= 0 ||
260 6b9e2bc6 2019-04-06 martijn EVP_DigestSignUpdate(session->b,
261 06f6b49b 2019-04-06 martijn "\r\n", 2) <= 0) {
262 06f6b49b 2019-04-06 martijn dkim_errx(session,
263 06f6b49b 2019-04-06 martijn "Failed to update digest context");
264 06f6b49b 2019-04-06 martijn return;
265 06f6b49b 2019-04-06 martijn }
266 06f6b49b 2019-04-06 martijn /* We're done with the cashed header after hashing */
267 06f6b49b 2019-04-06 martijn for (tmp = session->headers[i]; tmp[0] != ':'; tmp++) {
268 06f6b49b 2019-04-06 martijn if (tmp[0] == ' ' || tmp[0] == '\t')
269 06f6b49b 2019-04-06 martijn break;
270 06f6b49b 2019-04-06 martijn tmp[0] = tolower(tmp[0]);
271 06f6b49b 2019-04-06 martijn }
272 06f6b49b 2019-04-06 martijn tmp[0] = '\0';
273 06f6b49b 2019-04-06 martijn if (!dkim_signature_printf(session, "%s%s",
274 06f6b49b 2019-04-06 martijn session->headers[i + 1] == NULL ? "" : ":",
275 06f6b49b 2019-04-06 martijn session->headers[i]))
276 06f6b49b 2019-04-06 martijn return;
277 06f6b49b 2019-04-06 martijn tmp[0] = tmpchar;
278 06f6b49b 2019-04-06 martijn }
279 06f6b49b 2019-04-06 martijn dkim_signature_printf(session, "; b=");
280 06f6b49b 2019-04-06 martijn if (!dkim_signature_normalize(session))
281 06f6b49b 2019-04-06 martijn return;
282 06f6b49b 2019-04-06 martijn if ((tmp = strdup(session->signature.signature)) == NULL) {
283 06f6b49b 2019-04-06 martijn dkim_err(session, "Can't create DKIM signature");
284 06f6b49b 2019-04-06 martijn return;
285 06f6b49b 2019-04-06 martijn }
286 06f6b49b 2019-04-06 martijn dkim_parse_header(session, tmp, 1);
287 6b9e2bc6 2019-04-06 martijn if (EVP_DigestSignUpdate(session->b, tmp,
288 06f6b49b 2019-04-06 martijn strlen(tmp)) <= 0) {
289 06f6b49b 2019-04-06 martijn dkim_err(session, "Failed to update digest context");
290 06f6b49b 2019-04-06 martijn return;
291 06f6b49b 2019-04-06 martijn }
292 06f6b49b 2019-04-06 martijn free(tmp);
293 6b9e2bc6 2019-04-06 martijn if (EVP_DigestSignFinal(session->b, NULL, &linelen) <= 0) {
294 06f6b49b 2019-04-06 martijn dkim_err(session, "Failed to finalize digest");
295 06f6b49b 2019-04-06 martijn return;
296 06f6b49b 2019-04-06 martijn }
297 06f6b49b 2019-04-06 martijn if ((tmp = malloc(linelen)) == NULL) {
298 06f6b49b 2019-04-06 martijn dkim_err(session, "Can't allocate space for digest");
299 06f6b49b 2019-04-06 martijn return;
300 06f6b49b 2019-04-06 martijn }
301 6b9e2bc6 2019-04-06 martijn if (EVP_DigestSignFinal(session->b, tmp, &linelen) <= 0) {
302 06f6b49b 2019-04-06 martijn dkim_err(session, "Failed to finalize digest");
303 06f6b49b 2019-04-06 martijn return;
304 06f6b49b 2019-04-06 martijn }
305 14854bc8 2019-04-06 martijn /* Lines are unlikely to overflow */
306 14854bc8 2019-04-06 martijn b = malloc((((linelen + 2) / 3) * 4) + 1);
307 14854bc8 2019-04-06 martijn EVP_EncodeBlock(b, tmp, linelen);
308 06f6b49b 2019-04-06 martijn free(tmp);
309 14854bc8 2019-04-06 martijn dkim_signature_printf(session, "%s\r\n", b);
310 14854bc8 2019-04-06 martijn free(b);
311 06f6b49b 2019-04-06 martijn dkim_signature_normalize(session);
312 06f6b49b 2019-04-06 martijn tmp = session->signature.signature;
313 06f6b49b 2019-04-06 martijn while ((tmp2 = strchr(tmp, '\r')) != NULL) {
314 06f6b49b 2019-04-06 martijn tmp2[0] = '\0';
315 06f6b49b 2019-04-06 martijn smtp_filter_dataline(session->reqid, session->token,
316 06f6b49b 2019-04-06 martijn "%s", tmp);
317 06f6b49b 2019-04-06 martijn tmp = tmp2 + 2;
318 06f6b49b 2019-04-06 martijn }
319 06f6b49b 2019-04-06 martijn tmp = NULL;
320 06f6b49b 2019-04-06 martijn linelen = 0;
321 06f6b49b 2019-04-06 martijn rewind(session->origf);
322 06f6b49b 2019-04-06 martijn while ((i = getline(&tmp, &linelen, session->origf)) != -1) {
323 06f6b49b 2019-04-06 martijn tmp[i - 1] = '\0';
324 06f6b49b 2019-04-06 martijn smtp_filter_dataline(session->reqid, session->token,
325 06f6b49b 2019-04-06 martijn "%s", tmp);
326 06f6b49b 2019-04-06 martijn }
327 e768e91a 2019-04-08 martijn dkim_session_free(session);
328 f5286ea3 2019-04-08 martijn } else if (linelen != 0 && session->parsing_headers) {
329 f5286ea3 2019-04-08 martijn if (line[0] == '.')
330 f5286ea3 2019-04-08 martijn line++;
331 f5286ea3 2019-04-08 martijn dkim_parse_header(session, line, 0);
332 f5286ea3 2019-04-08 martijn } else if (linelen == 0 && session->parsing_headers) {
333 f5286ea3 2019-04-08 martijn session->parsing_headers = 0;
334 f5286ea3 2019-04-08 martijn } else {
335 f5286ea3 2019-04-08 martijn if (line[0] == '.')
336 f5286ea3 2019-04-08 martijn line++;
337 48c4bdc1 2019-04-04 martijn dkim_parse_body(session, line);
338 f5286ea3 2019-04-08 martijn }
339 48c4bdc1 2019-04-04 martijn }
340 48c4bdc1 2019-04-04 martijn
341 48c4bdc1 2019-04-04 martijn struct dkim_session *
342 48c4bdc1 2019-04-04 martijn dkim_session_new(uint64_t reqid)
343 48c4bdc1 2019-04-04 martijn {
344 48c4bdc1 2019-04-04 martijn struct dkim_session *session;
345 06f6b49b 2019-04-06 martijn struct dkim_signature *signature;
346 48c4bdc1 2019-04-04 martijn char origfile[] = "/tmp/filter-dkimXXXXXX";
347 48c4bdc1 2019-04-04 martijn int fd;
348 48c4bdc1 2019-04-04 martijn
349 48c4bdc1 2019-04-04 martijn if ((session = calloc(1, sizeof(*session))) == NULL)
350 48c4bdc1 2019-04-04 martijn fatal(NULL);
351 48c4bdc1 2019-04-04 martijn
352 48c4bdc1 2019-04-04 martijn session->reqid = reqid;
353 48c4bdc1 2019-04-04 martijn if ((fd = mkstemp(origfile)) == -1) {
354 48c4bdc1 2019-04-04 martijn dkim_err(session, "Can't open tempfile");
355 48c4bdc1 2019-04-04 martijn return NULL;
356 48c4bdc1 2019-04-04 martijn }
357 48c4bdc1 2019-04-04 martijn if (unlink(origfile) == -1)
358 48c4bdc1 2019-04-04 martijn log_warn("Failed to unlink tempfile %s", origfile);
359 48c4bdc1 2019-04-04 martijn if ((session->origf = fdopen(fd, "r+")) == NULL) {
360 48c4bdc1 2019-04-04 martijn dkim_err(session, "Can't open tempfile");
361 48c4bdc1 2019-04-04 martijn return NULL;
362 48c4bdc1 2019-04-04 martijn }
363 48c4bdc1 2019-04-04 martijn session->parsing_headers = 1;
364 48c4bdc1 2019-04-04 martijn
365 48c4bdc1 2019-04-04 martijn session->body_whitelines = 0;
366 48c4bdc1 2019-04-04 martijn session->headers = calloc(1, sizeof(*(session->headers)));
367 48c4bdc1 2019-04-04 martijn if (session->headers == NULL) {
368 48c4bdc1 2019-04-04 martijn dkim_err(session, "Can't save headers");
369 48c4bdc1 2019-04-04 martijn return NULL;
370 48c4bdc1 2019-04-04 martijn }
371 48c4bdc1 2019-04-04 martijn session->lastheader = 0;
372 06f6b49b 2019-04-06 martijn session->signature.signature = NULL;
373 06f6b49b 2019-04-06 martijn session->signature.size = 0;
374 06f6b49b 2019-04-06 martijn session->signature.len = 0;
375 48c4bdc1 2019-04-04 martijn
376 06f6b49b 2019-04-06 martijn if (!dkim_signature_printf(session,
377 fa10b9e8 2019-04-06 martijn "DKIM-signature: v=%s; a=%s-%s; c=%s/%s; d=%s; s=%s; ", "1",
378 fa10b9e8 2019-04-06 martijn cryptalg, hashalg,
379 06f6b49b 2019-04-06 martijn canonheader == CANON_SIMPLE ? "simple" : "relaxed",
380 06f6b49b 2019-04-06 martijn canonbody == CANON_SIMPLE ? "simple" : "relaxed",
381 06f6b49b 2019-04-06 martijn domain, selector))
382 06f6b49b 2019-04-06 martijn return NULL;
383 06f6b49b 2019-04-06 martijn
384 37df18b6 2019-04-06 martijn if ((session->b = EVP_MD_CTX_new()) == NULL ||
385 37df18b6 2019-04-06 martijn (session->bh = EVP_MD_CTX_new()) == NULL) {
386 06f6b49b 2019-04-06 martijn dkim_errx(session, "Can't create hash context");
387 06f6b49b 2019-04-06 martijn return NULL;
388 06f6b49b 2019-04-06 martijn }
389 37df18b6 2019-04-06 martijn if (EVP_DigestSignInit(session->b, NULL, hash_md, NULL, pkey) <= 0 ||
390 37df18b6 2019-04-06 martijn EVP_DigestInit_ex(session->bh, hash_md, NULL) == 0) {
391 37df18b6 2019-04-06 martijn dkim_errx(session, "Failed to initialize hash context");
392 37df18b6 2019-04-06 martijn return NULL;
393 37df18b6 2019-04-06 martijn }
394 48c4bdc1 2019-04-04 martijn if (RB_INSERT(dkim_sessions, &dkim_sessions, session) != NULL)
395 48c4bdc1 2019-04-04 martijn fatalx("session already registered");
396 48c4bdc1 2019-04-04 martijn return session;
397 48c4bdc1 2019-04-04 martijn }
398 48c4bdc1 2019-04-04 martijn
399 48c4bdc1 2019-04-04 martijn void
400 48c4bdc1 2019-04-04 martijn dkim_session_free(struct dkim_session *session)
401 48c4bdc1 2019-04-04 martijn {
402 48c4bdc1 2019-04-04 martijn size_t i;
403 48c4bdc1 2019-04-04 martijn
404 48c4bdc1 2019-04-04 martijn RB_REMOVE(dkim_sessions, &dkim_sessions, session);
405 48c4bdc1 2019-04-04 martijn fclose(session->origf);
406 6b9e2bc6 2019-04-06 martijn EVP_MD_CTX_free(session->b);
407 37df18b6 2019-04-06 martijn EVP_MD_CTX_free(session->bh);
408 14854bc8 2019-04-06 martijn free(session->signature.signature);
409 48c4bdc1 2019-04-04 martijn for (i = 0; session->headers[i] != NULL; i++)
410 48c4bdc1 2019-04-04 martijn free(session->headers[i]);
411 48c4bdc1 2019-04-04 martijn free(session->headers);
412 48c4bdc1 2019-04-04 martijn free(session);
413 48c4bdc1 2019-04-04 martijn }
414 48c4bdc1 2019-04-04 martijn
415 48c4bdc1 2019-04-04 martijn int
416 48c4bdc1 2019-04-04 martijn dkim_session_cmp(struct dkim_session *s1, struct dkim_session *s2)
417 48c4bdc1 2019-04-04 martijn {
418 48c4bdc1 2019-04-04 martijn return (s1->reqid < s2->reqid ? -1 : s1->reqid > s2->reqid);
419 48c4bdc1 2019-04-04 martijn }
420 48c4bdc1 2019-04-04 martijn
421 48c4bdc1 2019-04-04 martijn void
422 48c4bdc1 2019-04-04 martijn dkim_headers_set(char *headers)
423 48c4bdc1 2019-04-04 martijn {
424 48c4bdc1 2019-04-04 martijn size_t i;
425 48c4bdc1 2019-04-04 martijn int has_from = 0;
426 48c4bdc1 2019-04-04 martijn
427 48c4bdc1 2019-04-04 martijn nsign_headers = 1;
428 48c4bdc1 2019-04-04 martijn
429 48c4bdc1 2019-04-04 martijn for (i = 0; headers[i] != '\0'; i++) {
430 48c4bdc1 2019-04-04 martijn /* RFC 5322 field-name */
431 48c4bdc1 2019-04-04 martijn if (!(headers[i] >= 33 && headers[i] <= 126))
432 48c4bdc1 2019-04-04 martijn errx(1, "-h: invalid character");
433 48c4bdc1 2019-04-04 martijn if (headers[i] == ':') {
434 48c4bdc1 2019-04-04 martijn /* Test for empty headers */
435 48c4bdc1 2019-04-04 martijn if (i == 0 || headers[i - 1] == ':')
436 48c4bdc1 2019-04-04 martijn errx(1, "-h: header can't be empty");
437 48c4bdc1 2019-04-04 martijn nsign_headers++;
438 48c4bdc1 2019-04-04 martijn }
439 48c4bdc1 2019-04-04 martijn headers[i] = tolower(headers[i]);
440 48c4bdc1 2019-04-04 martijn }
441 48c4bdc1 2019-04-04 martijn if (headers[i - 1] == ':')
442 48c4bdc1 2019-04-04 martijn errx(1, "-h: header can't be empty");
443 48c4bdc1 2019-04-04 martijn
444 06f6b49b 2019-04-06 martijn sign_headers = reallocarray(NULL, nsign_headers + 1, sizeof(*sign_headers));
445 48c4bdc1 2019-04-04 martijn if (sign_headers == NULL)
446 48c4bdc1 2019-04-04 martijn errx(1, NULL);
447 48c4bdc1 2019-04-04 martijn
448 48c4bdc1 2019-04-04 martijn for (i = 0; i < nsign_headers; i++) {
449 48c4bdc1 2019-04-04 martijn sign_headers[i] = headers;
450 48c4bdc1 2019-04-04 martijn if (i != nsign_headers - 1) {
451 48c4bdc1 2019-04-04 martijn headers = strchr(headers, ':');
452 48c4bdc1 2019-04-04 martijn headers++[0] = '\0';
453 48c4bdc1 2019-04-04 martijn }
454 48c4bdc1 2019-04-04 martijn if (strcasecmp(sign_headers[i], "from") == 0)
455 48c4bdc1 2019-04-04 martijn has_from = 1;
456 48c4bdc1 2019-04-04 martijn }
457 48c4bdc1 2019-04-04 martijn if (!has_from)
458 48c4bdc1 2019-04-04 martijn errx(1, "From header must be included");
459 48c4bdc1 2019-04-04 martijn }
460 48c4bdc1 2019-04-04 martijn
461 48c4bdc1 2019-04-04 martijn void
462 48c4bdc1 2019-04-04 martijn dkim_err(struct dkim_session *session, char *msg)
463 48c4bdc1 2019-04-04 martijn {
464 48c4bdc1 2019-04-04 martijn smtp_filter_disconnect(session->reqid, session->token,
465 48c4bdc1 2019-04-04 martijn "Internal server error");
466 48c4bdc1 2019-04-04 martijn log_warn("%s", msg);
467 48c4bdc1 2019-04-04 martijn dkim_session_free(session);
468 48c4bdc1 2019-04-04 martijn }
469 48c4bdc1 2019-04-04 martijn
470 48c4bdc1 2019-04-04 martijn void
471 48c4bdc1 2019-04-04 martijn dkim_errx(struct dkim_session *session, char *msg)
472 48c4bdc1 2019-04-04 martijn {
473 48c4bdc1 2019-04-04 martijn smtp_filter_disconnect(session->reqid, session->token,
474 48c4bdc1 2019-04-04 martijn "Internal server error");
475 48c4bdc1 2019-04-04 martijn log_warnx("%s", msg);
476 48c4bdc1 2019-04-04 martijn dkim_session_free(session);
477 48c4bdc1 2019-04-04 martijn }
478 48c4bdc1 2019-04-04 martijn
479 48c4bdc1 2019-04-04 martijn void
480 06f6b49b 2019-04-06 martijn dkim_parse_header(struct dkim_session *session, char *line, int force)
481 48c4bdc1 2019-04-04 martijn {
482 48c4bdc1 2019-04-04 martijn size_t i;
483 48c4bdc1 2019-04-04 martijn size_t r, w;
484 48c4bdc1 2019-04-04 martijn size_t linelen;
485 48c4bdc1 2019-04-04 martijn size_t lastheader;
486 06f6b49b 2019-04-06 martijn size_t hlen;
487 b6e37a5e 2019-04-08 martijn int fieldname = 0;
488 48c4bdc1 2019-04-04 martijn char **mtmp;
489 48c4bdc1 2019-04-04 martijn char *htmp;
490 63ac36ce 2019-04-08 martijn char *tmp;
491 48c4bdc1 2019-04-04 martijn
492 48c4bdc1 2019-04-04 martijn if ((line[0] == ' ' || line[0] == '\t') && !session->lastheader)
493 48c4bdc1 2019-04-04 martijn return;
494 48c4bdc1 2019-04-04 martijn if ((line[0] != ' ' && line[0] != '\t')) {
495 06f6b49b 2019-04-06 martijn session->lastheader = 0;
496 48c4bdc1 2019-04-04 martijn for (i = 0; i < nsign_headers; i++) {
497 06f6b49b 2019-04-06 martijn hlen = strlen(sign_headers[i]);
498 06f6b49b 2019-04-06 martijn if (strncasecmp(line, sign_headers[i], hlen) == 0) {
499 06f6b49b 2019-04-06 martijn while (line[hlen] == ' ' || line[hlen] == '\t')
500 06f6b49b 2019-04-06 martijn hlen++;
501 06f6b49b 2019-04-06 martijn if (line[hlen] != ':')
502 06f6b49b 2019-04-06 martijn continue;
503 48c4bdc1 2019-04-04 martijn break;
504 48c4bdc1 2019-04-04 martijn }
505 48c4bdc1 2019-04-04 martijn }
506 06f6b49b 2019-04-06 martijn if (i == nsign_headers && !force)
507 48c4bdc1 2019-04-04 martijn return;
508 48c4bdc1 2019-04-04 martijn }
509 48c4bdc1 2019-04-04 martijn
510 48c4bdc1 2019-04-04 martijn if (canonheader == CANON_RELAXED) {
511 b6e37a5e 2019-04-08 martijn if (!session->lastheader)
512 b6e37a5e 2019-04-08 martijn fieldname = 1;
513 48c4bdc1 2019-04-04 martijn for (r = w = 0; line[r] != '\0'; r++) {
514 5d06ccad 2019-04-08 martijn if (line[r] == ':' && fieldname) {
515 48c4bdc1 2019-04-04 martijn if (line[w - 1] == ' ')
516 48c4bdc1 2019-04-04 martijn line[w - 1] = ':';
517 48c4bdc1 2019-04-04 martijn else
518 48c4bdc1 2019-04-04 martijn line[w++] = ':';
519 48c4bdc1 2019-04-04 martijn fieldname = 0;
520 48c4bdc1 2019-04-04 martijn while (line[r + 1] == ' ' ||
521 48c4bdc1 2019-04-04 martijn line[r + 1] == '\t')
522 48c4bdc1 2019-04-04 martijn r++;
523 48c4bdc1 2019-04-04 martijn continue;
524 48c4bdc1 2019-04-04 martijn }
525 06f6b49b 2019-04-06 martijn if (line[r] == ' ' || line[r] == '\t' ||
526 06f6b49b 2019-04-06 martijn line[r] == '\r' || line[r] == '\n') {
527 48c4bdc1 2019-04-04 martijn if (r != 0 && line[w - 1] == ' ')
528 48c4bdc1 2019-04-04 martijn continue;
529 48c4bdc1 2019-04-04 martijn else
530 48c4bdc1 2019-04-04 martijn line[w++] = ' ';
531 48c4bdc1 2019-04-04 martijn } else if (fieldname) {
532 48c4bdc1 2019-04-04 martijn line[w++] = tolower(line[r]);
533 48c4bdc1 2019-04-04 martijn continue;
534 48c4bdc1 2019-04-04 martijn } else
535 48c4bdc1 2019-04-04 martijn line[w++] = line[r];
536 48c4bdc1 2019-04-04 martijn }
537 48c4bdc1 2019-04-04 martijn linelen = line[w - 1] == ' ' ? w - 1 : w;
538 48c4bdc1 2019-04-04 martijn line[linelen] = '\0';
539 48c4bdc1 2019-04-04 martijn } else
540 48c4bdc1 2019-04-04 martijn linelen = strlen(line);
541 48c4bdc1 2019-04-04 martijn
542 48c4bdc1 2019-04-04 martijn for (lastheader = 0; session->headers[lastheader] != NULL; lastheader++)
543 48c4bdc1 2019-04-04 martijn continue;
544 48c4bdc1 2019-04-04 martijn if (!session->lastheader) {
545 a53d74c4 2019-04-08 martijn mtmp = recallocarray(session->headers, lastheader + 1,
546 a53d74c4 2019-04-08 martijn lastheader + 2, sizeof(*mtmp));
547 48c4bdc1 2019-04-04 martijn if (mtmp == NULL) {
548 48c4bdc1 2019-04-04 martijn dkim_err(session, "Can't store header");
549 48c4bdc1 2019-04-04 martijn return;
550 48c4bdc1 2019-04-04 martijn }
551 48c4bdc1 2019-04-04 martijn session->headers = mtmp;
552 48c4bdc1 2019-04-04 martijn
553 48c4bdc1 2019-04-04 martijn session->headers[lastheader] = strdup(line);
554 48c4bdc1 2019-04-04 martijn session->headers[lastheader + 1 ] = NULL;
555 48c4bdc1 2019-04-04 martijn session->lastheader = 1;
556 48c4bdc1 2019-04-04 martijn } else {
557 48c4bdc1 2019-04-04 martijn lastheader--;
558 48c4bdc1 2019-04-04 martijn linelen += strlen(session->headers[lastheader]);
559 48c4bdc1 2019-04-04 martijn if (canonheader == CANON_SIMPLE)
560 48c4bdc1 2019-04-04 martijn linelen += 2;
561 48c4bdc1 2019-04-04 martijn linelen++;
562 48c4bdc1 2019-04-04 martijn htmp = reallocarray(session->headers[lastheader], linelen,
563 48c4bdc1 2019-04-04 martijn sizeof(*htmp));
564 48c4bdc1 2019-04-04 martijn if (htmp == NULL) {
565 48c4bdc1 2019-04-04 martijn dkim_err(session, "Can't store header");
566 48c4bdc1 2019-04-04 martijn return;
567 48c4bdc1 2019-04-04 martijn }
568 48c4bdc1 2019-04-04 martijn session->headers[lastheader] = htmp;
569 48c4bdc1 2019-04-04 martijn if (canonheader == CANON_SIMPLE) {
570 48c4bdc1 2019-04-04 martijn if (strlcat(htmp, "\r\n", linelen) >= linelen)
571 48c4bdc1 2019-04-04 martijn fatalx("Missized header");
572 63ac36ce 2019-04-08 martijn } else if (canonheader == CANON_RELAXED &&
573 63ac36ce 2019-04-08 martijn (tmp = strchr(session->headers[lastheader], ':')) != NULL &&
574 63ac36ce 2019-04-08 martijn tmp[1] == '\0')
575 63ac36ce 2019-04-08 martijn line++;
576 63ac36ce 2019-04-08 martijn
577 48c4bdc1 2019-04-04 martijn if (strlcat(htmp, line, linelen) >= linelen)
578 48c4bdc1 2019-04-04 martijn fatalx("Missized header");
579 48c4bdc1 2019-04-04 martijn }
580 48c4bdc1 2019-04-04 martijn }
581 48c4bdc1 2019-04-04 martijn
582 48c4bdc1 2019-04-04 martijn void
583 48c4bdc1 2019-04-04 martijn dkim_parse_body(struct dkim_session *session, char *line)
584 48c4bdc1 2019-04-04 martijn {
585 48c4bdc1 2019-04-04 martijn size_t r, w;
586 48c4bdc1 2019-04-04 martijn size_t linelen;
587 1308710c 2019-04-09 martijn
588 1308710c 2019-04-09 martijn if (canonbody == CANON_RELAXED) {
589 1308710c 2019-04-09 martijn for (r = w = 0; line[r] != '\0'; r++) {
590 1308710c 2019-04-09 martijn if (line[r] == ' ' || line[r] == '\t') {
591 1308710c 2019-04-09 martijn if (r != 0 && line[w - 1] == ' ')
592 1308710c 2019-04-09 martijn continue;
593 1308710c 2019-04-09 martijn else
594 1308710c 2019-04-09 martijn line[w++] = ' ';
595 1308710c 2019-04-09 martijn } else
596 1308710c 2019-04-09 martijn line[w++] = line[r];
597 1308710c 2019-04-09 martijn }
598 1308710c 2019-04-09 martijn linelen = line[w - 1] == ' ' ? w - 1 : w;
599 1308710c 2019-04-09 martijn line[linelen] = '\0';
600 1308710c 2019-04-09 martijn } else
601 1308710c 2019-04-09 martijn linelen = strlen(line);
602 aba1824d 2019-04-10 martijn for (; line[linelen - 1] == '\r'; linelen--)
603 aba1824d 2019-04-10 martijn continue;
604 aba1824d 2019-04-10 martijn line[linelen] = '\0';
605 1308710c 2019-04-09 martijn
606 48c4bdc1 2019-04-04 martijn if (line[0] == '\0') {
607 48c4bdc1 2019-04-04 martijn session->body_whitelines++;
608 48c4bdc1 2019-04-04 martijn return;
609 48c4bdc1 2019-04-04 martijn }
610 48c4bdc1 2019-04-04 martijn
611 48c4bdc1 2019-04-04 martijn while (session->body_whitelines--) {
612 37df18b6 2019-04-06 martijn if (EVP_DigestUpdate(session->bh, "\r\n", 2) == 0) {
613 37df18b6 2019-04-06 martijn dkim_err(session, "Can't update hash context");
614 48c4bdc1 2019-04-04 martijn return;
615 37df18b6 2019-04-06 martijn }
616 48c4bdc1 2019-04-04 martijn }
617 48c4bdc1 2019-04-04 martijn session->body_whitelines = 0;
618 48c4bdc1 2019-04-04 martijn session->has_body = 1;
619 48c4bdc1 2019-04-04 martijn
620 37df18b6 2019-04-06 martijn if (EVP_DigestUpdate(session->bh, line, linelen) == 0 ||
621 37df18b6 2019-04-06 martijn EVP_DigestUpdate(session->bh, "\r\n", 2) == 0) {
622 37df18b6 2019-04-06 martijn dkim_err(session, "Can't update hash context");
623 48c4bdc1 2019-04-04 martijn return;
624 37df18b6 2019-04-06 martijn }
625 48c4bdc1 2019-04-04 martijn }
626 48c4bdc1 2019-04-04 martijn
627 06f6b49b 2019-04-06 martijn int
628 06f6b49b 2019-04-06 martijn dkim_signature_normalize(struct dkim_session *session)
629 48c4bdc1 2019-04-04 martijn {
630 06f6b49b 2019-04-06 martijn size_t i;
631 06f6b49b 2019-04-06 martijn size_t linelen;
632 06f6b49b 2019-04-06 martijn size_t checkpoint;
633 06f6b49b 2019-04-06 martijn size_t skip;
634 06f6b49b 2019-04-06 martijn size_t *headerlen = &(session->signature.len);
635 06f6b49b 2019-04-06 martijn int headername = 1;
636 06f6b49b 2019-04-06 martijn char tag = '\0';
637 06f6b49b 2019-04-06 martijn char *sig = session->signature.signature;
638 48c4bdc1 2019-04-04 martijn
639 06f6b49b 2019-04-06 martijn for (linelen = i = 0; sig[i] != '\0'; i++) {
640 ffa37923 2019-04-09 martijn if (sig[i] == '\r' && sig[i + 1] == '\n') {
641 ffa37923 2019-04-09 martijn i++;
642 06f6b49b 2019-04-06 martijn checkpoint = 0;
643 06f6b49b 2019-04-06 martijn linelen = 0;
644 06f6b49b 2019-04-06 martijn continue;
645 06f6b49b 2019-04-06 martijn }
646 06f6b49b 2019-04-06 martijn if (sig[i] == '\t')
647 06f6b49b 2019-04-06 martijn linelen = (linelen + 8) & ~7;
648 48c4bdc1 2019-04-04 martijn else
649 06f6b49b 2019-04-06 martijn linelen++;
650 06f6b49b 2019-04-06 martijn if (headername) {
651 06f6b49b 2019-04-06 martijn if (sig[i] == ':') {
652 06f6b49b 2019-04-06 martijn headername = 0;
653 06f6b49b 2019-04-06 martijn checkpoint = i;
654 06f6b49b 2019-04-06 martijn }
655 06f6b49b 2019-04-06 martijn continue;
656 06f6b49b 2019-04-06 martijn }
657 ffa37923 2019-04-09 martijn if (linelen > DKIM_SIGNATURE_LINELEN && checkpoint != 0) {
658 06f6b49b 2019-04-06 martijn for (skip = checkpoint + 1;
659 06f6b49b 2019-04-06 martijn sig[skip] == ' ' || sig[skip] == '\t';
660 06f6b49b 2019-04-06 martijn skip++)
661 06f6b49b 2019-04-06 martijn continue;
662 06f6b49b 2019-04-06 martijn skip -= checkpoint + 1;
663 06f6b49b 2019-04-06 martijn if (!dkim_signature_need(session,
664 226e5da8 2019-04-08 martijn skip > 3 ? 0 : 3 - skip + 1))
665 06f6b49b 2019-04-06 martijn return 0;
666 8a3a2cef 2019-04-08 martijn sig = session->signature.signature;
667 8a3a2cef 2019-04-08 martijn
668 06f6b49b 2019-04-06 martijn memmove(sig + checkpoint + 3,
669 06f6b49b 2019-04-06 martijn sig + checkpoint + skip,
670 540f552a 2019-04-06 martijn *headerlen - skip - checkpoint + 1);
671 06f6b49b 2019-04-06 martijn sig[checkpoint + 1] = '\r';
672 06f6b49b 2019-04-06 martijn sig[checkpoint + 2] = '\n';
673 06f6b49b 2019-04-06 martijn sig[checkpoint + 3] = '\t';
674 06f6b49b 2019-04-06 martijn linelen = 8;
675 06f6b49b 2019-04-06 martijn *headerlen = *headerlen + 3 - skip;
676 06f6b49b 2019-04-06 martijn i = checkpoint + 3;
677 06f6b49b 2019-04-06 martijn checkpoint = 0;
678 06f6b49b 2019-04-06 martijn }
679 06f6b49b 2019-04-06 martijn if (sig[i] == ';') {
680 06f6b49b 2019-04-06 martijn checkpoint = i;
681 06f6b49b 2019-04-06 martijn tag = '\0';
682 06f6b49b 2019-04-06 martijn continue;
683 06f6b49b 2019-04-06 martijn }
684 06f6b49b 2019-04-06 martijn switch (tag) {
685 06f6b49b 2019-04-06 martijn case 'B':
686 06f6b49b 2019-04-06 martijn case 'b':
687 06f6b49b 2019-04-06 martijn checkpoint = i;
688 06f6b49b 2019-04-06 martijn break;
689 06f6b49b 2019-04-06 martijn case 'h':
690 06f6b49b 2019-04-06 martijn if (sig[i] == ':')
691 06f6b49b 2019-04-06 martijn checkpoint = i;
692 06f6b49b 2019-04-06 martijn }
693 06f6b49b 2019-04-06 martijn if (tag == '\0' && sig[i] != ' ' && sig[i] != '\t') {
694 06f6b49b 2019-04-06 martijn if ((tag = sig[i]) == 'b' && sig[i + 1] == 'h' &&
695 06f6b49b 2019-04-06 martijn sig[i + 2] == '=') {
696 06f6b49b 2019-04-06 martijn tag = 'B';
697 06f6b49b 2019-04-06 martijn linelen += 2;
698 06f6b49b 2019-04-06 martijn i += 2;
699 06f6b49b 2019-04-06 martijn } else
700 06f6b49b 2019-04-06 martijn tag = sig[i];
701 06f6b49b 2019-04-06 martijn }
702 48c4bdc1 2019-04-04 martijn }
703 06f6b49b 2019-04-06 martijn return 1;
704 06f6b49b 2019-04-06 martijn }
705 48c4bdc1 2019-04-04 martijn
706 06f6b49b 2019-04-06 martijn int
707 06f6b49b 2019-04-06 martijn dkim_signature_printf(struct dkim_session *session, char *fmt, ...)
708 06f6b49b 2019-04-06 martijn {
709 06f6b49b 2019-04-06 martijn struct dkim_signature *sig = &(session->signature);
710 06f6b49b 2019-04-06 martijn va_list ap;
711 06f6b49b 2019-04-06 martijn size_t newlen;
712 06f6b49b 2019-04-06 martijn char *tmp;
713 06f6b49b 2019-04-06 martijn size_t len;
714 06f6b49b 2019-04-06 martijn
715 06f6b49b 2019-04-06 martijn va_start(ap, fmt);
716 06f6b49b 2019-04-06 martijn if ((len = vsnprintf(sig->signature + sig->len, sig->size - sig->len,
717 06f6b49b 2019-04-06 martijn fmt, ap)) >= sig->size - sig->len) {
718 06f6b49b 2019-04-06 martijn va_end(ap);
719 226e5da8 2019-04-08 martijn if (!dkim_signature_need(session, len + 1))
720 06f6b49b 2019-04-06 martijn return 0;
721 06f6b49b 2019-04-06 martijn va_start(ap, fmt);
722 06f6b49b 2019-04-06 martijn if ((len = vsnprintf(sig->signature + sig->len, sig->size - sig->len,
723 06f6b49b 2019-04-06 martijn fmt, ap)) >= sig->size - sig->len)
724 06f6b49b 2019-04-06 martijn fatalx("Miscalculated header size");
725 06f6b49b 2019-04-06 martijn }
726 06f6b49b 2019-04-06 martijn sig->len += len;
727 06f6b49b 2019-04-06 martijn va_end(ap);
728 06f6b49b 2019-04-06 martijn return 1;
729 06f6b49b 2019-04-06 martijn }
730 06f6b49b 2019-04-06 martijn
731 06f6b49b 2019-04-06 martijn int
732 06f6b49b 2019-04-06 martijn dkim_signature_need(struct dkim_session *session, size_t len)
733 06f6b49b 2019-04-06 martijn {
734 06f6b49b 2019-04-06 martijn struct dkim_signature *sig = &(session->signature);
735 06f6b49b 2019-04-06 martijn char *tmp;
736 06f6b49b 2019-04-06 martijn
737 a27b5ff7 2019-04-08 martijn if (sig->len + len < sig->size)
738 06f6b49b 2019-04-06 martijn return 1;
739 503597cc 2019-04-08 martijn sig->size = (((len + sig->len) / 512) + 1) * 512;
740 06f6b49b 2019-04-06 martijn if ((tmp = realloc(sig->signature, sig->size)) == NULL) {
741 06f6b49b 2019-04-06 martijn dkim_err(session, "No room for signature");
742 06f6b49b 2019-04-06 martijn return 0;
743 06f6b49b 2019-04-06 martijn }
744 06f6b49b 2019-04-06 martijn sig->signature = tmp;
745 06f6b49b 2019-04-06 martijn return 1;
746 06f6b49b 2019-04-06 martijn }
747 06f6b49b 2019-04-06 martijn
748 48c4bdc1 2019-04-04 martijn __dead void
749 48c4bdc1 2019-04-04 martijn usage(void)
750 48c4bdc1 2019-04-04 martijn {
751 a5698c0f 2019-04-06 martijn fprintf(stderr, "usage: %s [-a signalg] [-c canonicalization] [-h headerfields] -d domain -k keyfile "
752 a5698c0f 2019-04-06 martijn "-s selector\n", getprogname());
753 48c4bdc1 2019-04-04 martijn exit(1);
754 48c4bdc1 2019-04-04 martijn }
755 48c4bdc1 2019-04-04 martijn
756 48c4bdc1 2019-04-04 martijn RB_GENERATE(dkim_sessions, dkim_session, entry, dkim_session_cmp);