2 48c4bdc1 2019-04-04 martijn * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
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.
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.
16 48c4bdc1 2019-04-04 martijn #include <sys/tree.h>
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>
22 48c4bdc1 2019-04-04 martijn #include <ctype.h>
23 48c4bdc1 2019-04-04 martijn #include <err.h>
24 c826412d 2019-05-02 martijn #include <errno.h>
25 1308710c 2019-04-09 martijn #include <fcntl.h>
26 48c4bdc1 2019-04-04 martijn #include <stdio.h>
27 48c4bdc1 2019-04-04 martijn #include <stdlib.h>
28 48c4bdc1 2019-04-04 martijn #include <string.h>
29 48c4bdc1 2019-04-04 martijn #include <syslog.h>
30 48c4bdc1 2019-04-04 martijn #include <time.h>
31 48c4bdc1 2019-04-04 martijn #include <unistd.h>
33 48c4bdc1 2019-04-04 martijn #include "smtp_proc.h"
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;
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;
52 6b9e2bc6 2019-04-06 martijn EVP_MD_CTX *b;
53 37df18b6 2019-04-06 martijn EVP_MD_CTX *bh;
54 48c4bdc1 2019-04-04 martijn RB_ENTRY(dkim_session) entry;
57 48c4bdc1 2019-04-04 martijn RB_HEAD(dkim_sessions, dkim_session) dkim_sessions = RB_INITIALIZER(NULL);
58 48c4bdc1 2019-04-04 martijn RB_PROTOTYPE(dkim_sessions, dkim_session, entry, dkim_session_cmp);
60 37c6191b 2019-04-06 martijn /* RFC 6376 section 5.4.1 */
61 37c6191b 2019-04-06 martijn static char *dsign_headers[] = {
63 37c6191b 2019-04-06 martijn "reply-to",
64 37c6191b 2019-04-06 martijn "subject",
68 37c6191b 2019-04-06 martijn "resent-date",
69 37c6191b 2019-04-06 martijn "resent-from",
70 37c6191b 2019-04-06 martijn "resent-to",
71 37c6191b 2019-04-06 martijn "resent-cc",
72 37c6191b 2019-04-06 martijn "in-reply-to",
73 37c6191b 2019-04-06 martijn "references",
74 37c6191b 2019-04-06 martijn "list-id",
75 37c6191b 2019-04-06 martijn "list-help",
76 37c6191b 2019-04-06 martijn "list-unsubscribe",
77 37c6191b 2019-04-06 martijn "list-subscribe",
78 37c6191b 2019-04-06 martijn "list-post",
79 37c6191b 2019-04-06 martijn "list-owner",
80 37c6191b 2019-04-06 martijn "list-archive"
82 37c6191b 2019-04-06 martijn static char **sign_headers = dsign_headers;
83 37c6191b 2019-04-06 martijn static size_t nsign_headers = sizeof(dsign_headers) / sizeof(*dsign_headers);
85 fddd7e1e 2019-04-06 martijn static char *hashalg = "sha256";
86 fddd7e1e 2019-04-06 martijn static char *cryptalg = "rsa";
88 48c4bdc1 2019-04-04 martijn #define CANON_SIMPLE 0
89 48c4bdc1 2019-04-04 martijn #define CANON_RELAXED 1
90 48c4bdc1 2019-04-04 martijn static int canonheader = CANON_SIMPLE;
91 48c4bdc1 2019-04-04 martijn static int canonbody = CANON_SIMPLE;
93 e7076cef 2019-05-02 martijn static int addtime = 0;
94 e974922c 2019-05-02 martijn static long long addexpire = 0;
95 c826412d 2019-05-02 martijn static int addheaders = 0;
97 48c4bdc1 2019-04-04 martijn static char *domain = NULL;
98 06f6b49b 2019-04-06 martijn static char *selector = NULL;
100 06f6b49b 2019-04-06 martijn static EVP_PKEY *pkey;
101 06f6b49b 2019-04-06 martijn static const EVP_MD *hash_md;
103 06f6b49b 2019-04-06 martijn #define DKIM_SIGNATURE_LINELEN 78
105 48c4bdc1 2019-04-04 martijn void usage(void);
106 48c4bdc1 2019-04-04 martijn void dkim_err(struct dkim_session *, char *);
107 48c4bdc1 2019-04-04 martijn void dkim_errx(struct dkim_session *, char *);
108 48c4bdc1 2019-04-04 martijn void dkim_headers_set(char *);
109 48c4bdc1 2019-04-04 martijn void dkim_dataline(char *, int, struct timespec *, char *, char *, uint64_t,
110 48c4bdc1 2019-04-04 martijn uint64_t, char *);
111 fa9e6602 2019-05-01 martijn void dkim_commit(char *, int, struct timespec *, char *, char *, uint64_t,
112 fa9e6602 2019-05-01 martijn uint64_t);
113 48c4bdc1 2019-04-04 martijn void dkim_disconnect(char *, int, struct timespec *, char *, char *, uint64_t);
114 48c4bdc1 2019-04-04 martijn struct dkim_session *dkim_session_new(uint64_t);
115 48c4bdc1 2019-04-04 martijn void dkim_session_free(struct dkim_session *);
116 48c4bdc1 2019-04-04 martijn int dkim_session_cmp(struct dkim_session *, struct dkim_session *);
117 06f6b49b 2019-04-06 martijn void dkim_parse_header(struct dkim_session *, char *, int);
118 48c4bdc1 2019-04-04 martijn void dkim_parse_body(struct dkim_session *, char *);
119 365862b1 2019-05-02 martijn void dkim_sign(struct dkim_session *);
120 c826412d 2019-05-02 martijn int dkim_signature_printheader(struct dkim_session *, char *);
121 06f6b49b 2019-04-06 martijn int dkim_signature_printf(struct dkim_session *, char *, ...)
122 06f6b49b 2019-04-06 martijn __attribute__((__format__ (printf, 2, 3)));
123 06f6b49b 2019-04-06 martijn int dkim_signature_normalize(struct dkim_session *);
124 06f6b49b 2019-04-06 martijn int dkim_signature_need(struct dkim_session *, size_t);
125 06f6b49b 2019-04-06 martijn int dkim_sign_init(struct dkim_session *);
128 48c4bdc1 2019-04-04 martijn main(int argc, char *argv[])
132 48c4bdc1 2019-04-04 martijn int debug = 0;
133 06f6b49b 2019-04-06 martijn FILE *keyfile;
134 e974922c 2019-05-02 martijn const char *errstr;
136 c826412d 2019-05-02 martijn while ((ch = getopt(argc, argv, "a:c:Dd:h:k:s:tx:zZ")) != -1) {
137 48c4bdc1 2019-04-04 martijn switch (ch) {
138 48c4bdc1 2019-04-04 martijn case 'a':
139 fddd7e1e 2019-04-06 martijn if (strncmp(optarg, "rsa-", 4) != 0)
140 48c4bdc1 2019-04-04 martijn err(1, "invalid algorithm");
141 fddd7e1e 2019-04-06 martijn hashalg = optarg + 4;
143 48c4bdc1 2019-04-04 martijn case 'c':
144 48c4bdc1 2019-04-04 martijn if (strncmp(optarg, "simple", 6) == 0) {
145 48c4bdc1 2019-04-04 martijn canonheader = CANON_SIMPLE;
146 48c4bdc1 2019-04-04 martijn optarg += 6;
147 48c4bdc1 2019-04-04 martijn } else if (strncmp(optarg, "relaxed", 7) == 0) {
148 48c4bdc1 2019-04-04 martijn canonheader = CANON_RELAXED;
149 48c4bdc1 2019-04-04 martijn optarg += 7;
151 48c4bdc1 2019-04-04 martijn err(1, "Invalid canonicalization");
152 48c4bdc1 2019-04-04 martijn if (optarg[0] == '/') {
153 48c4bdc1 2019-04-04 martijn if (strcmp(optarg + 1, "simple") == 0)
154 48c4bdc1 2019-04-04 martijn canonbody = CANON_SIMPLE;
155 48c4bdc1 2019-04-04 martijn else if (strcmp(optarg + 1, "relaxed") == 0)
156 48c4bdc1 2019-04-04 martijn canonbody = CANON_RELAXED;
158 48c4bdc1 2019-04-04 martijn err(1, "Invalid canonicalization");
159 48c4bdc1 2019-04-04 martijn } else if (optarg[0] == '\0')
160 48c4bdc1 2019-04-04 martijn canonbody = CANON_SIMPLE;
162 48c4bdc1 2019-04-04 martijn err(1, "Invalid canonicalization");
164 48c4bdc1 2019-04-04 martijn case 'd':
165 48c4bdc1 2019-04-04 martijn domain = optarg;
167 06f6b49b 2019-04-06 martijn case 'h':
168 06f6b49b 2019-04-06 martijn dkim_headers_set(optarg);
170 06f6b49b 2019-04-06 martijn case 'k':
171 06f6b49b 2019-04-06 martijn if ((keyfile = fopen(optarg, "r")) == NULL)
172 06f6b49b 2019-04-06 martijn err(1, "Can't open key file");
173 06f6b49b 2019-04-06 martijn pkey = PEM_read_PrivateKey(keyfile, NULL, NULL, NULL);
174 06f6b49b 2019-04-06 martijn if (pkey == NULL)
175 06f6b49b 2019-04-06 martijn errx(1, "Can't read key file");
176 06f6b49b 2019-04-06 martijn if (EVP_PKEY_get0_RSA(pkey) == NULL)
177 06f6b49b 2019-04-06 martijn err(1, "Key is not of type rsa");
178 06f6b49b 2019-04-06 martijn fclose(keyfile);
180 06f6b49b 2019-04-06 martijn case 's':
181 06f6b49b 2019-04-06 martijn selector = optarg;
183 e7076cef 2019-05-02 martijn case 't':
184 e7076cef 2019-05-02 martijn addtime = 1;
186 e974922c 2019-05-02 martijn case 'x':
187 e974922c 2019-05-02 martijn addexpire = strtonum(optarg, 1, INT64_MAX, &errstr);
188 e974922c 2019-05-02 martijn if (addexpire == 0)
189 e974922c 2019-05-02 martijn errx(1, "Expire offset is %s", errstr);
191 c826412d 2019-05-02 martijn case 'z':
192 c826412d 2019-05-02 martijn addheaders = 1;
194 c826412d 2019-05-02 martijn case 'Z':
195 c826412d 2019-05-02 martijn addheaders = 2;
197 48c4bdc1 2019-04-04 martijn case 'D':
198 48c4bdc1 2019-04-04 martijn debug = 1;
200 48c4bdc1 2019-04-04 martijn default:
201 48c4bdc1 2019-04-04 martijn usage();
205 fddd7e1e 2019-04-06 martijn OpenSSL_add_all_digests();
206 fddd7e1e 2019-04-06 martijn if ((hash_md = EVP_get_digestbyname(hashalg)) == NULL)
207 27541da8 2019-06-30 martijn errx(1, "Can't find hash: %s", hashalg);
210 0f26145c 2019-04-25 martijn * fattr required for tmpfile.
211 0f26145c 2019-04-25 martijn * Can hopefully be removed in the future
213 0f26145c 2019-04-25 martijn if (pledge("fattr tmppath stdio", NULL) == -1)
214 27541da8 2019-06-30 martijn err(1, "pledge");
216 06f6b49b 2019-04-06 martijn if (domain == NULL || selector == NULL || pkey == NULL)
217 48c4bdc1 2019-04-04 martijn usage();
219 48c4bdc1 2019-04-04 martijn smtp_register_filter_dataline(dkim_dataline);
220 fa9e6602 2019-05-01 martijn smtp_register_filter_commit(dkim_commit);
221 48c4bdc1 2019-04-04 martijn smtp_in_register_report_disconnect(dkim_disconnect);
222 48c4bdc1 2019-04-04 martijn smtp_run(debug);
224 48c4bdc1 2019-04-04 martijn return 0;
228 48c4bdc1 2019-04-04 martijn dkim_disconnect(char *type, int version, struct timespec *tm, char *direction,
229 48c4bdc1 2019-04-04 martijn char *phase, uint64_t reqid)
231 48c4bdc1 2019-04-04 martijn struct dkim_session *session, search;
233 48c4bdc1 2019-04-04 martijn search.reqid = reqid;
234 48c4bdc1 2019-04-04 martijn if ((session = RB_FIND(dkim_sessions, &dkim_sessions, &search)) != NULL)
235 48c4bdc1 2019-04-04 martijn dkim_session_free(session);
239 48c4bdc1 2019-04-04 martijn dkim_dataline(char *type, int version, struct timespec *tm, char *direction,
240 48c4bdc1 2019-04-04 martijn char *phase, uint64_t reqid, uint64_t token, char *line)
242 48c4bdc1 2019-04-04 martijn struct dkim_session *session, search;
243 48c4bdc1 2019-04-04 martijn size_t linelen;
245 48c4bdc1 2019-04-04 martijn search.reqid = reqid;
246 48c4bdc1 2019-04-04 martijn session = RB_FIND(dkim_sessions, &dkim_sessions, &search);
247 48c4bdc1 2019-04-04 martijn if (session == NULL) {
248 06f6b49b 2019-04-06 martijn if ((session = dkim_session_new(reqid)) == NULL)
250 48c4bdc1 2019-04-04 martijn session->token = token;
251 48c4bdc1 2019-04-04 martijn } else if (session->token != token)
252 27541da8 2019-06-30 martijn errx(1, "Token incorrect");
253 fa9e6602 2019-05-01 martijn if (session->err)
256 48c4bdc1 2019-04-04 martijn linelen = strlen(line);
257 a883a4eb 2019-04-09 martijn if (fprintf(session->origf, "%s\n", line) < linelen)
258 48c4bdc1 2019-04-04 martijn dkim_err(session, "Couldn't write to tempfile");
260 7ecf4491 2019-05-01 martijn if (line[0] == '.' && line[1] =='\0') {
261 365862b1 2019-05-02 martijn dkim_sign(session);
262 f5286ea3 2019-04-08 martijn } else if (linelen != 0 && session->parsing_headers) {
263 f5286ea3 2019-04-08 martijn if (line[0] == '.')
265 f5286ea3 2019-04-08 martijn dkim_parse_header(session, line, 0);
266 f5286ea3 2019-04-08 martijn } else if (linelen == 0 && session->parsing_headers) {
267 c826412d 2019-05-02 martijn if (addheaders > 0 && !dkim_signature_printf(session, "; "))
269 f5286ea3 2019-04-08 martijn session->parsing_headers = 0;
270 f5286ea3 2019-04-08 martijn } else {
271 f5286ea3 2019-04-08 martijn if (line[0] == '.')
273 48c4bdc1 2019-04-04 martijn dkim_parse_body(session, line);
278 fa9e6602 2019-05-01 martijn dkim_commit(char *type, int version, struct timespec *tm, char *direction,
279 fa9e6602 2019-05-01 martijn char *phase, uint64_t reqid, uint64_t token)
281 fa9e6602 2019-05-01 martijn struct dkim_session *session, search;
283 fa9e6602 2019-05-01 martijn search.reqid = reqid;
284 fa9e6602 2019-05-01 martijn if ((session = RB_FIND(dkim_sessions, &dkim_sessions, &search)) == NULL)
285 27541da8 2019-06-30 martijn errx(1, "Commit on undefined session");
287 fa9e6602 2019-05-01 martijn if (session->err)
288 fa9e6602 2019-05-01 martijn smtp_filter_disconnect(session->reqid, session->token,
289 fa9e6602 2019-05-01 martijn "Internal server error");
291 fa9e6602 2019-05-01 martijn smtp_filter_proceed(reqid, token);
293 fa9e6602 2019-05-01 martijn dkim_session_free(session);
296 48c4bdc1 2019-04-04 martijn struct dkim_session *
297 48c4bdc1 2019-04-04 martijn dkim_session_new(uint64_t reqid)
299 48c4bdc1 2019-04-04 martijn struct dkim_session *session;
300 06f6b49b 2019-04-06 martijn struct dkim_signature *signature;
302 48c4bdc1 2019-04-04 martijn if ((session = calloc(1, sizeof(*session))) == NULL)
303 27541da8 2019-06-30 martijn err(1, NULL);
305 48c4bdc1 2019-04-04 martijn session->reqid = reqid;
306 0f26145c 2019-04-25 martijn if ((session->origf = tmpfile()) == NULL) {
307 48c4bdc1 2019-04-04 martijn dkim_err(session, "Can't open tempfile");
308 48c4bdc1 2019-04-04 martijn return NULL;
310 48c4bdc1 2019-04-04 martijn session->parsing_headers = 1;
312 48c4bdc1 2019-04-04 martijn session->body_whitelines = 0;
313 48c4bdc1 2019-04-04 martijn session->headers = calloc(1, sizeof(*(session->headers)));
314 48c4bdc1 2019-04-04 martijn if (session->headers == NULL) {
315 48c4bdc1 2019-04-04 martijn dkim_err(session, "Can't save headers");
316 48c4bdc1 2019-04-04 martijn return NULL;
318 48c4bdc1 2019-04-04 martijn session->lastheader = 0;
319 06f6b49b 2019-04-06 martijn session->signature.signature = NULL;
320 06f6b49b 2019-04-06 martijn session->signature.size = 0;
321 06f6b49b 2019-04-06 martijn session->signature.len = 0;
322 fa9e6602 2019-05-01 martijn session->err = 0;
324 06f6b49b 2019-04-06 martijn if (!dkim_signature_printf(session,
325 fa10b9e8 2019-04-06 martijn "DKIM-signature: v=%s; a=%s-%s; c=%s/%s; d=%s; s=%s; ", "1",
326 fa10b9e8 2019-04-06 martijn cryptalg, hashalg,
327 06f6b49b 2019-04-06 martijn canonheader == CANON_SIMPLE ? "simple" : "relaxed",
328 06f6b49b 2019-04-06 martijn canonbody == CANON_SIMPLE ? "simple" : "relaxed",
329 06f6b49b 2019-04-06 martijn domain, selector))
330 06f6b49b 2019-04-06 martijn return NULL;
331 c826412d 2019-05-02 martijn if (addheaders > 0 && !dkim_signature_printf(session, "z="))
332 c826412d 2019-05-02 martijn return NULL;
334 37df18b6 2019-04-06 martijn if ((session->b = EVP_MD_CTX_new()) == NULL ||
335 37df18b6 2019-04-06 martijn (session->bh = EVP_MD_CTX_new()) == NULL) {
336 06f6b49b 2019-04-06 martijn dkim_errx(session, "Can't create hash context");
337 06f6b49b 2019-04-06 martijn return NULL;
339 37df18b6 2019-04-06 martijn if (EVP_DigestSignInit(session->b, NULL, hash_md, NULL, pkey) <= 0 ||
340 37df18b6 2019-04-06 martijn EVP_DigestInit_ex(session->bh, hash_md, NULL) == 0) {
341 37df18b6 2019-04-06 martijn dkim_errx(session, "Failed to initialize hash context");
342 37df18b6 2019-04-06 martijn return NULL;
344 48c4bdc1 2019-04-04 martijn if (RB_INSERT(dkim_sessions, &dkim_sessions, session) != NULL)
345 27541da8 2019-06-30 martijn errx(1, "session already registered");
346 48c4bdc1 2019-04-04 martijn return session;
350 48c4bdc1 2019-04-04 martijn dkim_session_free(struct dkim_session *session)
352 48c4bdc1 2019-04-04 martijn size_t i;
354 48c4bdc1 2019-04-04 martijn RB_REMOVE(dkim_sessions, &dkim_sessions, session);
355 48c4bdc1 2019-04-04 martijn fclose(session->origf);
356 6b9e2bc6 2019-04-06 martijn EVP_MD_CTX_free(session->b);
357 37df18b6 2019-04-06 martijn EVP_MD_CTX_free(session->bh);
358 14854bc8 2019-04-06 martijn free(session->signature.signature);
359 48c4bdc1 2019-04-04 martijn for (i = 0; session->headers[i] != NULL; i++)
360 48c4bdc1 2019-04-04 martijn free(session->headers[i]);
361 48c4bdc1 2019-04-04 martijn free(session->headers);
362 48c4bdc1 2019-04-04 martijn free(session);
366 48c4bdc1 2019-04-04 martijn dkim_session_cmp(struct dkim_session *s1, struct dkim_session *s2)
368 48c4bdc1 2019-04-04 martijn return (s1->reqid < s2->reqid ? -1 : s1->reqid > s2->reqid);
372 48c4bdc1 2019-04-04 martijn dkim_headers_set(char *headers)
374 48c4bdc1 2019-04-04 martijn size_t i;
375 48c4bdc1 2019-04-04 martijn int has_from = 0;
377 48c4bdc1 2019-04-04 martijn nsign_headers = 1;
379 48c4bdc1 2019-04-04 martijn for (i = 0; headers[i] != '\0'; i++) {
380 48c4bdc1 2019-04-04 martijn /* RFC 5322 field-name */
381 48c4bdc1 2019-04-04 martijn if (!(headers[i] >= 33 && headers[i] <= 126))
382 48c4bdc1 2019-04-04 martijn errx(1, "-h: invalid character");
383 48c4bdc1 2019-04-04 martijn if (headers[i] == ':') {
384 48c4bdc1 2019-04-04 martijn /* Test for empty headers */
385 48c4bdc1 2019-04-04 martijn if (i == 0 || headers[i - 1] == ':')
386 48c4bdc1 2019-04-04 martijn errx(1, "-h: header can't be empty");
387 48c4bdc1 2019-04-04 martijn nsign_headers++;
389 48c4bdc1 2019-04-04 martijn headers[i] = tolower(headers[i]);
391 48c4bdc1 2019-04-04 martijn if (headers[i - 1] == ':')
392 48c4bdc1 2019-04-04 martijn errx(1, "-h: header can't be empty");
394 06f6b49b 2019-04-06 martijn sign_headers = reallocarray(NULL, nsign_headers + 1, sizeof(*sign_headers));
395 48c4bdc1 2019-04-04 martijn if (sign_headers == NULL)
396 48c4bdc1 2019-04-04 martijn errx(1, NULL);
398 48c4bdc1 2019-04-04 martijn for (i = 0; i < nsign_headers; i++) {
399 48c4bdc1 2019-04-04 martijn sign_headers[i] = headers;
400 48c4bdc1 2019-04-04 martijn if (i != nsign_headers - 1) {
401 48c4bdc1 2019-04-04 martijn headers = strchr(headers, ':');
402 48c4bdc1 2019-04-04 martijn headers++[0] = '\0';
404 48c4bdc1 2019-04-04 martijn if (strcasecmp(sign_headers[i], "from") == 0)
405 48c4bdc1 2019-04-04 martijn has_from = 1;
407 48c4bdc1 2019-04-04 martijn if (!has_from)
408 48c4bdc1 2019-04-04 martijn errx(1, "From header must be included");
412 48c4bdc1 2019-04-04 martijn dkim_err(struct dkim_session *session, char *msg)
414 fa9e6602 2019-05-01 martijn session->err = 1;
415 27541da8 2019-06-30 martijn warn("%s", msg);
419 48c4bdc1 2019-04-04 martijn dkim_errx(struct dkim_session *session, char *msg)
421 fa9e6602 2019-05-01 martijn session->err = 1;
422 27541da8 2019-06-30 martijn warnx("%s", msg);
426 06f6b49b 2019-04-06 martijn dkim_parse_header(struct dkim_session *session, char *line, int force)
428 48c4bdc1 2019-04-04 martijn size_t i;
429 48c4bdc1 2019-04-04 martijn size_t r, w;
430 48c4bdc1 2019-04-04 martijn size_t linelen;
431 48c4bdc1 2019-04-04 martijn size_t lastheader;
432 06f6b49b 2019-04-06 martijn size_t hlen;
433 b6e37a5e 2019-04-08 martijn int fieldname = 0;
434 48c4bdc1 2019-04-04 martijn char **mtmp;
435 48c4bdc1 2019-04-04 martijn char *htmp;
436 63ac36ce 2019-04-08 martijn char *tmp;
438 c826412d 2019-05-02 martijn if (addheaders == 2 && !force &&
439 c826412d 2019-05-02 martijn !dkim_signature_printheader(session, line))
442 48c4bdc1 2019-04-04 martijn if ((line[0] == ' ' || line[0] == '\t') && !session->lastheader)
444 48c4bdc1 2019-04-04 martijn if ((line[0] != ' ' && line[0] != '\t')) {
445 06f6b49b 2019-04-06 martijn session->lastheader = 0;
446 48c4bdc1 2019-04-04 martijn for (i = 0; i < nsign_headers; i++) {
447 06f6b49b 2019-04-06 martijn hlen = strlen(sign_headers[i]);
448 06f6b49b 2019-04-06 martijn if (strncasecmp(line, sign_headers[i], hlen) == 0) {
449 06f6b49b 2019-04-06 martijn while (line[hlen] == ' ' || line[hlen] == '\t')
451 06f6b49b 2019-04-06 martijn if (line[hlen] != ':')
452 06f6b49b 2019-04-06 martijn continue;
456 06f6b49b 2019-04-06 martijn if (i == nsign_headers && !force)
460 c826412d 2019-05-02 martijn if (addheaders == 1 && !force &&
461 c826412d 2019-05-02 martijn !dkim_signature_printheader(session, line))
464 48c4bdc1 2019-04-04 martijn if (canonheader == CANON_RELAXED) {
465 b6e37a5e 2019-04-08 martijn if (!session->lastheader)
466 b6e37a5e 2019-04-08 martijn fieldname = 1;
467 48c4bdc1 2019-04-04 martijn for (r = w = 0; line[r] != '\0'; r++) {
468 5d06ccad 2019-04-08 martijn if (line[r] == ':' && fieldname) {
469 48c4bdc1 2019-04-04 martijn if (line[w - 1] == ' ')
470 48c4bdc1 2019-04-04 martijn line[w - 1] = ':';
472 48c4bdc1 2019-04-04 martijn line[w++] = ':';
473 48c4bdc1 2019-04-04 martijn fieldname = 0;
474 48c4bdc1 2019-04-04 martijn while (line[r + 1] == ' ' ||
475 48c4bdc1 2019-04-04 martijn line[r + 1] == '\t')
477 48c4bdc1 2019-04-04 martijn continue;
479 06f6b49b 2019-04-06 martijn if (line[r] == ' ' || line[r] == '\t' ||
480 06f6b49b 2019-04-06 martijn line[r] == '\r' || line[r] == '\n') {
481 48c4bdc1 2019-04-04 martijn if (r != 0 && line[w - 1] == ' ')
482 48c4bdc1 2019-04-04 martijn continue;
484 48c4bdc1 2019-04-04 martijn line[w++] = ' ';
485 48c4bdc1 2019-04-04 martijn } else if (fieldname) {
486 48c4bdc1 2019-04-04 martijn line[w++] = tolower(line[r]);
487 48c4bdc1 2019-04-04 martijn continue;
489 48c4bdc1 2019-04-04 martijn line[w++] = line[r];
491 48c4bdc1 2019-04-04 martijn linelen = line[w - 1] == ' ' ? w - 1 : w;
492 48c4bdc1 2019-04-04 martijn line[linelen] = '\0';
494 48c4bdc1 2019-04-04 martijn linelen = strlen(line);
496 48c4bdc1 2019-04-04 martijn for (lastheader = 0; session->headers[lastheader] != NULL; lastheader++)
497 48c4bdc1 2019-04-04 martijn continue;
498 48c4bdc1 2019-04-04 martijn if (!session->lastheader) {
499 a53d74c4 2019-04-08 martijn mtmp = recallocarray(session->headers, lastheader + 1,
500 a53d74c4 2019-04-08 martijn lastheader + 2, sizeof(*mtmp));
501 48c4bdc1 2019-04-04 martijn if (mtmp == NULL) {
502 48c4bdc1 2019-04-04 martijn dkim_err(session, "Can't store header");
505 48c4bdc1 2019-04-04 martijn session->headers = mtmp;
507 48c4bdc1 2019-04-04 martijn session->headers[lastheader] = strdup(line);
508 48c4bdc1 2019-04-04 martijn session->headers[lastheader + 1 ] = NULL;
509 48c4bdc1 2019-04-04 martijn session->lastheader = 1;
510 48c4bdc1 2019-04-04 martijn } else {
511 48c4bdc1 2019-04-04 martijn lastheader--;
512 48c4bdc1 2019-04-04 martijn linelen += strlen(session->headers[lastheader]);
513 48c4bdc1 2019-04-04 martijn if (canonheader == CANON_SIMPLE)
514 48c4bdc1 2019-04-04 martijn linelen += 2;
515 48c4bdc1 2019-04-04 martijn linelen++;
516 48c4bdc1 2019-04-04 martijn htmp = reallocarray(session->headers[lastheader], linelen,
517 48c4bdc1 2019-04-04 martijn sizeof(*htmp));
518 48c4bdc1 2019-04-04 martijn if (htmp == NULL) {
519 48c4bdc1 2019-04-04 martijn dkim_err(session, "Can't store header");
522 48c4bdc1 2019-04-04 martijn session->headers[lastheader] = htmp;
523 48c4bdc1 2019-04-04 martijn if (canonheader == CANON_SIMPLE) {
524 48c4bdc1 2019-04-04 martijn if (strlcat(htmp, "\r\n", linelen) >= linelen)
525 27541da8 2019-06-30 martijn errx(1, "Missized header");
526 63ac36ce 2019-04-08 martijn } else if (canonheader == CANON_RELAXED &&
527 63ac36ce 2019-04-08 martijn (tmp = strchr(session->headers[lastheader], ':')) != NULL &&
528 63ac36ce 2019-04-08 martijn tmp[1] == '\0')
531 48c4bdc1 2019-04-04 martijn if (strlcat(htmp, line, linelen) >= linelen)
532 27541da8 2019-06-30 martijn errx(1, "Missized header");
537 48c4bdc1 2019-04-04 martijn dkim_parse_body(struct dkim_session *session, char *line)
539 48c4bdc1 2019-04-04 martijn size_t r, w;
540 48c4bdc1 2019-04-04 martijn size_t linelen;
542 1308710c 2019-04-09 martijn if (canonbody == CANON_RELAXED) {
543 1308710c 2019-04-09 martijn for (r = w = 0; line[r] != '\0'; r++) {
544 1308710c 2019-04-09 martijn if (line[r] == ' ' || line[r] == '\t') {
545 1308710c 2019-04-09 martijn if (r != 0 && line[w - 1] == ' ')
546 1308710c 2019-04-09 martijn continue;
548 1308710c 2019-04-09 martijn line[w++] = ' ';
550 1308710c 2019-04-09 martijn line[w++] = line[r];
552 1308710c 2019-04-09 martijn linelen = line[w - 1] == ' ' ? w - 1 : w;
553 1308710c 2019-04-09 martijn line[linelen] = '\0';
555 1308710c 2019-04-09 martijn linelen = strlen(line);
557 48c4bdc1 2019-04-04 martijn if (line[0] == '\0') {
558 48c4bdc1 2019-04-04 martijn session->body_whitelines++;
562 48c4bdc1 2019-04-04 martijn while (session->body_whitelines--) {
563 37df18b6 2019-04-06 martijn if (EVP_DigestUpdate(session->bh, "\r\n", 2) == 0) {
564 37df18b6 2019-04-06 martijn dkim_err(session, "Can't update hash context");
568 48c4bdc1 2019-04-04 martijn session->body_whitelines = 0;
569 48c4bdc1 2019-04-04 martijn session->has_body = 1;
571 37df18b6 2019-04-06 martijn if (EVP_DigestUpdate(session->bh, line, linelen) == 0 ||
572 37df18b6 2019-04-06 martijn EVP_DigestUpdate(session->bh, "\r\n", 2) == 0) {
573 37df18b6 2019-04-06 martijn dkim_err(session, "Can't update hash context");
579 365862b1 2019-05-02 martijn dkim_sign(struct dkim_session *session)
581 365862b1 2019-05-02 martijn /* Use largest hash size here */
582 365862b1 2019-05-02 martijn char bbh[EVP_MAX_MD_SIZE];
583 365862b1 2019-05-02 martijn char bh[(((sizeof(bbh) + 2) / 3) * 4) + 1];
584 365862b1 2019-05-02 martijn char *b;
585 e974922c 2019-05-02 martijn time_t now;
586 365862b1 2019-05-02 martijn ssize_t i, j;
587 365862b1 2019-05-02 martijn size_t linelen;
588 365862b1 2019-05-02 martijn char *tmp, *tmp2;
589 365862b1 2019-05-02 martijn char tmpchar;
591 e974922c 2019-05-02 martijn if (addtime || addexpire)
592 e974922c 2019-05-02 martijn now = time(NULL);
593 e974922c 2019-05-02 martijn if (addtime && !dkim_signature_printf(session, "t=%lld; ", now))
595 e974922c 2019-05-02 martijn if (addexpire != 0 && !dkim_signature_printf(session, "x=%lld; ",
596 e974922c 2019-05-02 martijn now + addexpire < now ? INT64_MAX : now + addexpire))
599 365862b1 2019-05-02 martijn if (canonbody == CANON_SIMPLE && !session->has_body) {
600 365862b1 2019-05-02 martijn if (EVP_DigestUpdate(session->bh, "\r\n", 2) <= 0) {
601 365862b1 2019-05-02 martijn dkim_err(session, "Can't update hash context");
605 365862b1 2019-05-02 martijn if (EVP_DigestFinal_ex(session->bh, bbh, NULL) == 0) {
606 365862b1 2019-05-02 martijn dkim_err(session, "Can't finalize hash context");
609 365862b1 2019-05-02 martijn EVP_EncodeBlock(bh, bbh, EVP_MD_CTX_size(session->bh));
610 365862b1 2019-05-02 martijn if (!dkim_signature_printf(session, "bh=%s; h=", bh))
612 365862b1 2019-05-02 martijn /* Reverse order for ease of use of RFC6367 section 5.4.2 */
613 365862b1 2019-05-02 martijn for (i = 0; session->headers[i] != NULL; i++)
614 365862b1 2019-05-02 martijn continue;
615 365862b1 2019-05-02 martijn for (i--; i >= 0; i--) {
616 365862b1 2019-05-02 martijn if (EVP_DigestSignUpdate(session->b,
617 365862b1 2019-05-02 martijn session->headers[i],
618 365862b1 2019-05-02 martijn strlen(session->headers[i])) <= 0 ||
619 365862b1 2019-05-02 martijn EVP_DigestSignUpdate(session->b, "\r\n", 2) <= 0) {
620 365862b1 2019-05-02 martijn dkim_errx(session, "Failed to update digest context");
623 365862b1 2019-05-02 martijn /* We're done with the cached header after hashing */
624 365862b1 2019-05-02 martijn for (tmp = session->headers[i]; tmp[0] != ':'; tmp++) {
625 365862b1 2019-05-02 martijn if (tmp[0] == ' ' || tmp[0] == '\t')
627 365862b1 2019-05-02 martijn tmp[0] = tolower(tmp[0]);
629 365862b1 2019-05-02 martijn tmp[0] = '\0';
630 365862b1 2019-05-02 martijn if (!dkim_signature_printf(session, "%s%s",
631 365862b1 2019-05-02 martijn session->headers[i + 1] == NULL ? "" : ":",
632 365862b1 2019-05-02 martijn session->headers[i]))
634 365862b1 2019-05-02 martijn tmp[0] = tmpchar;
636 365862b1 2019-05-02 martijn dkim_signature_printf(session, "; b=");
637 365862b1 2019-05-02 martijn if (!dkim_signature_normalize(session))
639 365862b1 2019-05-02 martijn if ((tmp = strdup(session->signature.signature)) == NULL) {
640 365862b1 2019-05-02 martijn dkim_err(session, "Can't create DKIM signature");
643 365862b1 2019-05-02 martijn dkim_parse_header(session, tmp, 1);
644 365862b1 2019-05-02 martijn if (EVP_DigestSignUpdate(session->b, tmp, strlen(tmp)) <= 0) {
645 365862b1 2019-05-02 martijn dkim_err(session, "Failed to update digest context");
648 365862b1 2019-05-02 martijn free(tmp);
649 365862b1 2019-05-02 martijn if (EVP_DigestSignFinal(session->b, NULL, &linelen) <= 0) {
650 365862b1 2019-05-02 martijn dkim_err(session, "Failed to finalize digest");
653 365862b1 2019-05-02 martijn if ((tmp = malloc(linelen)) == NULL) {
654 365862b1 2019-05-02 martijn dkim_err(session, "Can't allocate space for digest");
657 365862b1 2019-05-02 martijn if (EVP_DigestSignFinal(session->b, tmp, &linelen) <= 0) {
658 365862b1 2019-05-02 martijn dkim_err(session, "Failed to finalize digest");
661 365862b1 2019-05-02 martijn if ((b = malloc((((linelen + 2) / 3) * 4) + 1)) == NULL) {
662 365862b1 2019-05-02 martijn dkim_err(session, "Can't create DKIM signature");
665 365862b1 2019-05-02 martijn EVP_EncodeBlock(b, tmp, linelen);
666 365862b1 2019-05-02 martijn free(tmp);
667 365862b1 2019-05-02 martijn dkim_signature_printf(session, "%s\r\n", b);
668 365862b1 2019-05-02 martijn free(b);
669 365862b1 2019-05-02 martijn dkim_signature_normalize(session);
670 365862b1 2019-05-02 martijn tmp = session->signature.signature;
671 365862b1 2019-05-02 martijn while ((tmp2 = strchr(tmp, '\r')) != NULL) {
672 365862b1 2019-05-02 martijn tmp2[0] = '\0';
673 365862b1 2019-05-02 martijn smtp_filter_dataline(session->reqid, session->token,
674 365862b1 2019-05-02 martijn "%s", tmp);
675 365862b1 2019-05-02 martijn tmp = tmp2 + 2;
677 365862b1 2019-05-02 martijn tmp = NULL;
678 365862b1 2019-05-02 martijn linelen = 0;
679 365862b1 2019-05-02 martijn rewind(session->origf);
680 365862b1 2019-05-02 martijn while ((i = getline(&tmp, &linelen, session->origf)) != -1) {
681 365862b1 2019-05-02 martijn tmp[i - 1] = '\0';
682 365862b1 2019-05-02 martijn smtp_filter_dataline(session->reqid, session->token, "%s", tmp);
687 06f6b49b 2019-04-06 martijn dkim_signature_normalize(struct dkim_session *session)
689 06f6b49b 2019-04-06 martijn size_t i;
690 06f6b49b 2019-04-06 martijn size_t linelen;
691 06f6b49b 2019-04-06 martijn size_t checkpoint;
692 06f6b49b 2019-04-06 martijn size_t skip;
693 06f6b49b 2019-04-06 martijn size_t *headerlen = &(session->signature.len);
694 06f6b49b 2019-04-06 martijn int headername = 1;
695 06f6b49b 2019-04-06 martijn char tag = '\0';
696 06f6b49b 2019-04-06 martijn char *sig = session->signature.signature;
698 06f6b49b 2019-04-06 martijn for (linelen = i = 0; sig[i] != '\0'; i++) {
699 ffa37923 2019-04-09 martijn if (sig[i] == '\r' && sig[i + 1] == '\n') {
701 06f6b49b 2019-04-06 martijn checkpoint = 0;
702 06f6b49b 2019-04-06 martijn linelen = 0;
703 06f6b49b 2019-04-06 martijn continue;
705 06f6b49b 2019-04-06 martijn if (sig[i] == '\t')
706 06f6b49b 2019-04-06 martijn linelen = (linelen + 8) & ~7;
708 06f6b49b 2019-04-06 martijn linelen++;
709 06f6b49b 2019-04-06 martijn if (headername) {
710 06f6b49b 2019-04-06 martijn if (sig[i] == ':') {
711 06f6b49b 2019-04-06 martijn headername = 0;
712 06f6b49b 2019-04-06 martijn checkpoint = i;
714 06f6b49b 2019-04-06 martijn continue;
716 ffa37923 2019-04-09 martijn if (linelen > DKIM_SIGNATURE_LINELEN && checkpoint != 0) {
717 06f6b49b 2019-04-06 martijn for (skip = checkpoint + 1;
718 06f6b49b 2019-04-06 martijn sig[skip] == ' ' || sig[skip] == '\t';
720 06f6b49b 2019-04-06 martijn continue;
721 06f6b49b 2019-04-06 martijn skip -= checkpoint + 1;
722 06f6b49b 2019-04-06 martijn if (!dkim_signature_need(session,
723 226e5da8 2019-04-08 martijn skip > 3 ? 0 : 3 - skip + 1))
724 06f6b49b 2019-04-06 martijn return 0;
725 8a3a2cef 2019-04-08 martijn sig = session->signature.signature;
727 06f6b49b 2019-04-06 martijn memmove(sig + checkpoint + 3,
728 06f6b49b 2019-04-06 martijn sig + checkpoint + skip,
729 540f552a 2019-04-06 martijn *headerlen - skip - checkpoint + 1);
730 06f6b49b 2019-04-06 martijn sig[checkpoint + 1] = '\r';
731 06f6b49b 2019-04-06 martijn sig[checkpoint + 2] = '\n';
732 06f6b49b 2019-04-06 martijn sig[checkpoint + 3] = '\t';
733 06f6b49b 2019-04-06 martijn linelen = 8;
734 06f6b49b 2019-04-06 martijn *headerlen = *headerlen + 3 - skip;
735 06f6b49b 2019-04-06 martijn i = checkpoint + 3;
736 06f6b49b 2019-04-06 martijn checkpoint = 0;
738 06f6b49b 2019-04-06 martijn if (sig[i] == ';') {
739 06f6b49b 2019-04-06 martijn checkpoint = i;
740 06f6b49b 2019-04-06 martijn tag = '\0';
741 06f6b49b 2019-04-06 martijn continue;
743 06f6b49b 2019-04-06 martijn switch (tag) {
744 06f6b49b 2019-04-06 martijn case 'B':
745 06f6b49b 2019-04-06 martijn case 'b':
746 c826412d 2019-05-02 martijn case 'z':
747 06f6b49b 2019-04-06 martijn checkpoint = i;
749 06f6b49b 2019-04-06 martijn case 'h':
750 06f6b49b 2019-04-06 martijn if (sig[i] == ':')
751 06f6b49b 2019-04-06 martijn checkpoint = i;
754 06f6b49b 2019-04-06 martijn if (tag == '\0' && sig[i] != ' ' && sig[i] != '\t') {
755 06f6b49b 2019-04-06 martijn if ((tag = sig[i]) == 'b' && sig[i + 1] == 'h' &&
756 06f6b49b 2019-04-06 martijn sig[i + 2] == '=') {
757 06f6b49b 2019-04-06 martijn tag = 'B';
758 06f6b49b 2019-04-06 martijn linelen += 2;
761 06f6b49b 2019-04-06 martijn tag = sig[i];
764 06f6b49b 2019-04-06 martijn return 1;
768 c826412d 2019-05-02 martijn dkim_signature_printheader(struct dkim_session *session, char *header)
770 c826412d 2019-05-02 martijn size_t i, j, len;
771 c826412d 2019-05-02 martijn static char *fmtheader = NULL;
772 c826412d 2019-05-02 martijn char *tmp;
773 c826412d 2019-05-02 martijn static size_t size = 0;
774 c826412d 2019-05-02 martijn int first;
776 c826412d 2019-05-02 martijn len = strlen(header);
777 c826412d 2019-05-02 martijn if ((len + 3) * 3 < len) {
778 c826412d 2019-05-02 martijn errno = EOVERFLOW;
779 c826412d 2019-05-02 martijn dkim_err(session, "Can't add z-component to header");
780 c826412d 2019-05-02 martijn return 0;
782 c826412d 2019-05-02 martijn if ((len + 3) * 3 > size) {
783 c826412d 2019-05-02 martijn if ((tmp = reallocarray(fmtheader, 3, len + 3)) == NULL) {
784 c826412d 2019-05-02 martijn dkim_err(session, "Can't add z-component to header");
785 c826412d 2019-05-02 martijn return 0;
787 c826412d 2019-05-02 martijn fmtheader = tmp;
788 c826412d 2019-05-02 martijn size = (len + 1) * 3;
791 c826412d 2019-05-02 martijn first = session->signature.signature[session->signature.len - 1] == '=';
792 c826412d 2019-05-02 martijn for (j = i = 0; header[i] != '\0'; i++, j++) {
793 c826412d 2019-05-02 martijn if (i == 0 && header[i] != ' ' && header[i] != '\t' && !first)
794 c826412d 2019-05-02 martijn fmtheader[j++] = '|';
795 c826412d 2019-05-02 martijn if ((header[i] >= 0x21 && header[i] <= 0x3A) ||
796 c826412d 2019-05-02 martijn header[i] == 0x3C ||
797 c826412d 2019-05-02 martijn (header[i] >= 0x3E && header[i] <= 0x7B) ||
798 c826412d 2019-05-02 martijn (header[i] >= 0x7D && header[i] <= 0x7E))
799 c826412d 2019-05-02 martijn fmtheader[j] = header[i];
801 c826412d 2019-05-02 martijn fmtheader[j++] = '=';
802 c826412d 2019-05-02 martijn (void) sprintf(fmtheader + j, "%02hhX", header[i]);
806 c826412d 2019-05-02 martijn (void) sprintf(fmtheader + j, "=%02hhX=%02hhX", (unsigned char) '\r',
807 c826412d 2019-05-02 martijn (unsigned char) '\n');
809 c826412d 2019-05-02 martijn return dkim_signature_printf(session, "%s", fmtheader);
813 06f6b49b 2019-04-06 martijn dkim_signature_printf(struct dkim_session *session, char *fmt, ...)
815 06f6b49b 2019-04-06 martijn struct dkim_signature *sig = &(session->signature);
816 06f6b49b 2019-04-06 martijn va_list ap;
817 06f6b49b 2019-04-06 martijn size_t newlen;
818 06f6b49b 2019-04-06 martijn char *tmp;
819 06f6b49b 2019-04-06 martijn size_t len;
821 06f6b49b 2019-04-06 martijn va_start(ap, fmt);
822 06f6b49b 2019-04-06 martijn if ((len = vsnprintf(sig->signature + sig->len, sig->size - sig->len,
823 06f6b49b 2019-04-06 martijn fmt, ap)) >= sig->size - sig->len) {
824 06f6b49b 2019-04-06 martijn va_end(ap);
825 226e5da8 2019-04-08 martijn if (!dkim_signature_need(session, len + 1))
826 06f6b49b 2019-04-06 martijn return 0;
827 06f6b49b 2019-04-06 martijn va_start(ap, fmt);
828 06f6b49b 2019-04-06 martijn if ((len = vsnprintf(sig->signature + sig->len, sig->size - sig->len,
829 06f6b49b 2019-04-06 martijn fmt, ap)) >= sig->size - sig->len)
830 27541da8 2019-06-30 martijn errx(1, "Miscalculated header size");
832 06f6b49b 2019-04-06 martijn sig->len += len;
833 06f6b49b 2019-04-06 martijn va_end(ap);
834 06f6b49b 2019-04-06 martijn return 1;
838 06f6b49b 2019-04-06 martijn dkim_signature_need(struct dkim_session *session, size_t len)
840 06f6b49b 2019-04-06 martijn struct dkim_signature *sig = &(session->signature);
841 06f6b49b 2019-04-06 martijn char *tmp;
843 a27b5ff7 2019-04-08 martijn if (sig->len + len < sig->size)
844 06f6b49b 2019-04-06 martijn return 1;
845 503597cc 2019-04-08 martijn sig->size = (((len + sig->len) / 512) + 1) * 512;
846 06f6b49b 2019-04-06 martijn if ((tmp = realloc(sig->signature, sig->size)) == NULL) {
847 06f6b49b 2019-04-06 martijn dkim_err(session, "No room for signature");
848 06f6b49b 2019-04-06 martijn return 0;
850 06f6b49b 2019-04-06 martijn sig->signature = tmp;
851 06f6b49b 2019-04-06 martijn return 1;
854 48c4bdc1 2019-04-04 martijn __dead void
855 48c4bdc1 2019-04-04 martijn usage(void)
857 a5698c0f 2019-04-06 martijn fprintf(stderr, "usage: %s [-a signalg] [-c canonicalization] [-h headerfields] -d domain -k keyfile "
858 a5698c0f 2019-04-06 martijn "-s selector\n", getprogname());
859 48c4bdc1 2019-04-04 martijn exit(1);
862 48c4bdc1 2019-04-04 martijn RB_GENERATE(dkim_sessions, dkim_session, entry, dkim_session_cmp);