Blob


1 /*
2 * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16 #include <openssl/evp.h>
17 #include <openssl/pem.h>
18 #include <openssl/sha.h>
20 #include <ctype.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <syslog.h>
27 #include <time.h>
28 #include <unistd.h>
30 #include "opensmtpd.h"
32 struct dkim_signature {
33 char *signature;
34 size_t size;
35 size_t len;
36 };
38 struct dkim_message {
39 FILE *origf;
40 int parsing_headers;
41 char **headers;
42 int lastheader;
43 size_t body_whitelines;
44 int has_body;
45 struct dkim_signature signature;
46 int err;
47 EVP_MD_CTX *b;
48 EVP_MD_CTX *bh;
49 };
51 /* RFC 6376 section 5.4.1 */
52 static char *dsign_headers[] = {
53 "from",
54 "reply-to",
55 "subject",
56 "date",
57 "to",
58 "cc",
59 "resent-date",
60 "resent-from",
61 "resent-to",
62 "resent-cc",
63 "in-reply-to",
64 "references",
65 "list-id",
66 "list-help",
67 "list-unsubscribe",
68 "list-subscribe",
69 "list-post",
70 "list-owner",
71 "list-archive"
72 };
73 static char **sign_headers = dsign_headers;
74 static size_t nsign_headers = sizeof(dsign_headers) / sizeof(*dsign_headers);
76 static char *hashalg = "sha256";
77 static char *cryptalg = "rsa";
79 #define CANON_SIMPLE 0
80 #define CANON_RELAXED 1
81 static int canonheader = CANON_SIMPLE;
82 static int canonbody = CANON_SIMPLE;
84 static int addtime = 0;
85 static long long addexpire = 0;
86 static int addheaders = 0;
88 static char *domain = NULL;
89 static char *selector = NULL;
91 static EVP_PKEY *pkey;
92 static const EVP_MD *hash_md;
94 #define DKIM_SIGNATURE_LINELEN 78
96 void usage(void);
97 void dkim_err(struct dkim_message *, char *);
98 void dkim_errx(struct dkim_message *, char *);
99 void dkim_headers_set(char *);
100 void dkim_dataline(struct osmtpd_ctx *, const char *);
101 void dkim_commit(struct osmtpd_ctx *);
102 void *dkim_message_new(struct osmtpd_ctx *);
103 void dkim_message_free(struct osmtpd_ctx *, void *);
104 void dkim_parse_header(struct dkim_message *, char *, int);
105 void dkim_parse_body(struct dkim_message *, char *);
106 void dkim_sign(struct osmtpd_ctx *);
107 int dkim_signature_printheader(struct dkim_message *, const char *);
108 int dkim_signature_printf(struct dkim_message *, char *, ...)
109 __attribute__((__format__ (printf, 2, 3)));
110 int dkim_signature_normalize(struct dkim_message *);
111 int dkim_signature_need(struct dkim_message *, size_t);
112 int dkim_sign_init(struct dkim_message *);
114 int
115 main(int argc, char *argv[])
117 int ch;
118 FILE *keyfile;
119 const char *errstr;
121 while ((ch = getopt(argc, argv, "a:c:d:h:k:s:tx:z")) != -1) {
122 switch (ch) {
123 case 'a':
124 if (strncmp(optarg, "rsa-", 4) != 0)
125 osmtpd_err(1, "invalid algorithm");
126 hashalg = optarg + 4;
127 break;
128 case 'c':
129 if (strncmp(optarg, "simple", 6) == 0) {
130 canonheader = CANON_SIMPLE;
131 optarg += 6;
132 } else if (strncmp(optarg, "relaxed", 7) == 0) {
133 canonheader = CANON_RELAXED;
134 optarg += 7;
135 } else
136 osmtpd_err(1, "Invalid canonicalization");
137 if (optarg[0] == '/') {
138 if (strcmp(optarg + 1, "simple") == 0)
139 canonbody = CANON_SIMPLE;
140 else if (strcmp(optarg + 1, "relaxed") == 0)
141 canonbody = CANON_RELAXED;
142 else
143 osmtpd_err(1,
144 "Invalid canonicalization");
145 } else if (optarg[0] == '\0')
146 canonbody = CANON_SIMPLE;
147 else
148 osmtpd_err(1, "Invalid canonicalization");
149 break;
150 case 'd':
151 domain = optarg;
152 break;
153 case 'h':
154 dkim_headers_set(optarg);
155 break;
156 case 'k':
157 if ((keyfile = fopen(optarg, "r")) == NULL)
158 osmtpd_err(1, "Can't open key file (%s)",
159 optarg);
160 pkey = PEM_read_PrivateKey(keyfile, NULL, NULL, NULL);
161 if (pkey == NULL)
162 osmtpd_errx(1, "Can't read key file");
163 if (EVP_PKEY_get0_RSA(pkey) == NULL)
164 osmtpd_err(1, "Key is not of type rsa");
165 fclose(keyfile);
166 break;
167 case 's':
168 selector = optarg;
169 break;
170 case 't':
171 addtime = 1;
172 break;
173 case 'x':
174 addexpire = strtonum(optarg, 1, INT64_MAX, &errstr);
175 if (addexpire == 0)
176 osmtpd_errx(1, "Expire offset is %s", errstr);
177 break;
178 case 'z':
179 addheaders++;
180 break;
181 default:
182 usage();
186 OpenSSL_add_all_digests();
187 if ((hash_md = EVP_get_digestbyname(hashalg)) == NULL)
188 osmtpd_errx(1, "Can't find hash: %s", hashalg);
190 if (pledge("tmppath stdio", NULL) == -1)
191 osmtpd_err(1, "pledge");
193 if (domain == NULL || selector == NULL || pkey == NULL)
194 usage();
196 osmtpd_register_filter_dataline(dkim_dataline);
197 osmtpd_register_filter_commit(dkim_commit);
198 osmtpd_local_message(dkim_message_new, dkim_message_free);
199 osmtpd_run();
201 return 0;
204 void
205 dkim_dataline(struct osmtpd_ctx *ctx, const char *line)
207 struct dkim_message *message = ctx->local_message;
208 char *linedup;
209 size_t linelen;
211 if (message->err)
212 return;
214 linelen = strlen(line);
215 if (fprintf(message->origf, "%s\n", line) < (int) linelen)
216 dkim_err(message, "Couldn't write to tempfile");
218 if (line[0] == '.' && line[1] =='\0') {
219 dkim_sign(ctx);
220 } else if (linelen != 0 && message->parsing_headers) {
221 if (line[0] == '.')
222 line++;
223 if ((linedup = strdup(line)) == NULL)
224 osmtpd_err(1, "strdup");
225 dkim_parse_header(message, linedup, 0);
226 free(linedup);
227 } else if (linelen == 0 && message->parsing_headers) {
228 if (addheaders > 0 && !dkim_signature_printf(message, "; "))
229 return;
230 message->parsing_headers = 0;
231 } else {
232 if (line[0] == '.')
233 line++;
234 if ((linedup = strdup(line)) == NULL)
235 osmtpd_err(1, "strdup");
236 dkim_parse_body(message, linedup);
237 free(linedup);
241 void
242 dkim_commit(struct osmtpd_ctx *ctx)
244 struct dkim_message *message = ctx->local_message;
246 if (message->err)
247 osmtpd_filter_disconnect(ctx, "Internal server error");
248 else
249 osmtpd_filter_proceed(ctx);
252 void *
253 dkim_message_new(struct osmtpd_ctx *ctx)
255 struct dkim_message *message;
257 if ((message = calloc(1, sizeof(*message))) == NULL)
258 osmtpd_err(1, NULL);
260 if ((message->origf = tmpfile()) == NULL) {
261 dkim_err(message, "Can't open tempfile");
262 return NULL;
264 message->parsing_headers = 1;
266 message->body_whitelines = 0;
267 message->headers = calloc(1, sizeof(*(message->headers)));
268 if (message->headers == NULL) {
269 dkim_err(message, "Can't save headers");
270 return NULL;
272 message->lastheader = 0;
273 message->signature.signature = NULL;
274 message->signature.size = 0;
275 message->signature.len = 0;
276 message->err = 0;
278 if (!dkim_signature_printf(message,
279 "DKIM-Signature: v=%s; a=%s-%s; c=%s/%s; d=%s; s=%s; ", "1",
280 cryptalg, hashalg,
281 canonheader == CANON_SIMPLE ? "simple" : "relaxed",
282 canonbody == CANON_SIMPLE ? "simple" : "relaxed",
283 domain, selector))
284 return NULL;
285 if (addheaders > 0 && !dkim_signature_printf(message, "z="))
286 return NULL;
288 if ((message->b = EVP_MD_CTX_new()) == NULL ||
289 (message->bh = EVP_MD_CTX_new()) == NULL) {
290 dkim_errx(message, "Can't create hash context");
291 return NULL;
293 if (EVP_DigestSignInit(message->b, NULL, hash_md, NULL, pkey) <= 0 ||
294 EVP_DigestInit_ex(message->bh, hash_md, NULL) == 0) {
295 dkim_errx(message, "Failed to initialize hash context");
296 return NULL;
298 return message;
301 void
302 dkim_message_free(struct osmtpd_ctx *ctx, void *data)
304 struct dkim_message *message = data;
305 size_t i;
307 fclose(message->origf);
308 EVP_MD_CTX_free(message->b);
309 EVP_MD_CTX_free(message->bh);
310 free(message->signature.signature);
311 for (i = 0; message->headers[i] != NULL; i++)
312 free(message->headers[i]);
313 free(message->headers);
314 free(message);
317 void
318 dkim_headers_set(char *headers)
320 size_t i;
321 int has_from = 0;
323 nsign_headers = 1;
325 for (i = 0; headers[i] != '\0'; i++) {
326 /* RFC 5322 field-name */
327 if (!(headers[i] >= 33 && headers[i] <= 126))
328 osmtpd_errx(1, "-h: invalid character");
329 if (headers[i] == ':') {
330 /* Test for empty headers */
331 if (i == 0 || headers[i - 1] == ':')
332 osmtpd_errx(1, "-h: header can't be empty");
333 nsign_headers++;
335 headers[i] = tolower(headers[i]);
337 if (headers[i - 1] == ':')
338 osmtpd_errx(1, "-h: header can't be empty");
340 if ((sign_headers = reallocarray(NULL, nsign_headers + 1,
341 sizeof(*sign_headers))) == NULL)
342 osmtpd_errx(1, NULL);
344 for (i = 0; i < nsign_headers; i++) {
345 sign_headers[i] = headers;
346 if (i != nsign_headers - 1) {
347 headers = strchr(headers, ':');
348 headers++[0] = '\0';
350 if (strcasecmp(sign_headers[i], "from") == 0)
351 has_from = 1;
353 if (!has_from)
354 osmtpd_errx(1, "From header must be included");
357 void
358 dkim_err(struct dkim_message *message, char *msg)
360 message->err = 1;
361 fprintf(stderr, "%s\n", msg);
364 void
365 dkim_errx(struct dkim_message *message, char *msg)
367 message->err = 1;
368 fprintf(stderr, "%s\n", msg);
371 void
372 dkim_parse_header(struct dkim_message *message, char *line, int force)
374 size_t i;
375 size_t r, w;
376 size_t linelen;
377 size_t lastheader;
378 size_t hlen;
379 int fieldname = 0;
380 char **mtmp;
381 char *htmp;
382 char *tmp;
384 if (addheaders == 2 && !force &&
385 !dkim_signature_printheader(message, line))
386 return;
388 if ((line[0] == ' ' || line[0] == '\t') && !message->lastheader)
389 return;
390 if ((line[0] != ' ' && line[0] != '\t')) {
391 message->lastheader = 0;
392 for (i = 0; i < nsign_headers; i++) {
393 hlen = strlen(sign_headers[i]);
394 if (strncasecmp(line, sign_headers[i], hlen) == 0) {
395 while (line[hlen] == ' ' || line[hlen] == '\t')
396 hlen++;
397 if (line[hlen] != ':')
398 continue;
399 break;
402 if (i == nsign_headers && !force)
403 return;
406 if (addheaders == 1 && !force &&
407 !dkim_signature_printheader(message, line))
408 return;
410 if (canonheader == CANON_RELAXED) {
411 if (!message->lastheader)
412 fieldname = 1;
413 for (r = w = 0; line[r] != '\0'; r++) {
414 if (line[r] == ':' && fieldname) {
415 if (w > 0 && line[w - 1] == ' ')
416 line[w - 1] = ':';
417 else
418 line[w++] = ':';
419 fieldname = 0;
420 while (line[r + 1] == ' ' ||
421 line[r + 1] == '\t')
422 r++;
423 continue;
425 if (line[r] == ' ' || line[r] == '\t' ||
426 line[r] == '\r' || line[r] == '\n') {
427 if (r != 0 && w != 0 && line[w - 1] == ' ')
428 continue;
429 else
430 line[w++] = ' ';
431 } else if (fieldname) {
432 line[w++] = tolower(line[r]);
433 continue;
434 } else
435 line[w++] = line[r];
437 linelen = (w != 0 && line[w - 1] == ' ') ? w - 1 : w;
438 line[linelen] = '\0';
439 } else
440 linelen = strlen(line);
442 for (lastheader = 0; message->headers[lastheader] != NULL; lastheader++)
443 continue;
444 if (!message->lastheader) {
445 mtmp = recallocarray(message->headers, lastheader + 1,
446 lastheader + 2, sizeof(*mtmp));
447 if (mtmp == NULL) {
448 dkim_err(message, "Can't store header");
449 return;
451 message->headers = mtmp;
453 message->headers[lastheader] = strdup(line);
454 message->headers[lastheader + 1 ] = NULL;
455 message->lastheader = 1;
456 } else {
457 lastheader--;
458 linelen += strlen(message->headers[lastheader]);
459 if (canonheader == CANON_SIMPLE)
460 linelen += 2;
461 linelen++;
462 htmp = reallocarray(message->headers[lastheader], linelen,
463 sizeof(*htmp));
464 if (htmp == NULL) {
465 dkim_err(message, "Can't store header");
466 return;
468 message->headers[lastheader] = htmp;
469 if (canonheader == CANON_SIMPLE) {
470 if (strlcat(htmp, "\r\n", linelen) >= linelen)
471 osmtpd_errx(1, "Missized header");
472 } else if (canonheader == CANON_RELAXED &&
473 (tmp = strchr(message->headers[lastheader], ':')) != NULL &&
474 tmp[1] == '\0')
475 line++;
477 if (strlcat(htmp, line, linelen) >= linelen)
478 osmtpd_errx(1, "Missized header");
482 void
483 dkim_parse_body(struct dkim_message *message, char *line)
485 size_t r, w;
486 size_t linelen;
488 if (canonbody == CANON_RELAXED) {
489 for (r = w = 0; line[r] != '\0'; r++) {
490 if (line[r] == ' ' || line[r] == '\t') {
491 if (r != 0 && line[w - 1] == ' ')
492 continue;
493 else
494 line[w++] = ' ';
495 } else
496 line[w++] = line[r];
498 linelen = (w != 0 && line[w - 1] == ' ') ? w - 1 : w;
499 line[linelen] = '\0';
500 } else
501 linelen = strlen(line);
503 if (line[0] == '\0') {
504 message->body_whitelines++;
505 return;
508 while (message->body_whitelines--) {
509 if (EVP_DigestUpdate(message->bh, "\r\n", 2) == 0) {
510 dkim_err(message, "Can't update hash context");
511 return;
514 message->body_whitelines = 0;
515 message->has_body = 1;
517 if (EVP_DigestUpdate(message->bh, line, linelen) == 0 ||
518 EVP_DigestUpdate(message->bh, "\r\n", 2) == 0) {
519 dkim_err(message, "Can't update hash context");
520 return;
524 void
525 dkim_sign(struct osmtpd_ctx *ctx)
527 struct dkim_message *message = ctx->local_message;
528 /* Use largest hash size here */
529 char bbh[EVP_MAX_MD_SIZE];
530 char bh[(((sizeof(bbh) + 2) / 3) * 4) + 1];
531 char *b;
532 time_t now;
533 ssize_t i;
534 size_t linelen;
535 char *tmp, *tmp2;
537 if (addtime || addexpire)
538 now = time(NULL);
539 if (addtime && !dkim_signature_printf(message, "t=%lld; ", now))
540 return;
541 if (addexpire != 0 && !dkim_signature_printf(message, "x=%lld; ",
542 now + addexpire < now ? INT64_MAX : now + addexpire))
543 return;
545 if (canonbody == CANON_SIMPLE && !message->has_body) {
546 if (EVP_DigestUpdate(message->bh, "\r\n", 2) <= 0) {
547 dkim_err(message, "Can't update hash context");
548 return;
551 if (EVP_DigestFinal_ex(message->bh, bbh, NULL) == 0) {
552 dkim_err(message, "Can't finalize hash context");
553 return;
555 EVP_EncodeBlock(bh, bbh, EVP_MD_CTX_size(message->bh));
556 if (!dkim_signature_printf(message, "bh=%s; h=", bh))
557 return;
558 /* Reverse order for ease of use of RFC6367 section 5.4.2 */
559 for (i = 0; message->headers[i] != NULL; i++)
560 continue;
561 for (i--; i >= 0; i--) {
562 if (EVP_DigestSignUpdate(message->b,
563 message->headers[i],
564 strlen(message->headers[i])) <= 0 ||
565 EVP_DigestSignUpdate(message->b, "\r\n", 2) <= 0) {
566 dkim_errx(message, "Failed to update digest context");
567 return;
569 /* We're done with the cached header after hashing */
570 for (tmp = message->headers[i]; tmp[0] != ':'; tmp++) {
571 if (tmp[0] == ' ' || tmp[0] == '\t')
572 break;
573 tmp[0] = tolower(tmp[0]);
575 tmp[0] = '\0';
576 if (!dkim_signature_printf(message, "%s%s",
577 message->headers[i + 1] == NULL ? "" : ":",
578 message->headers[i]))
579 return;
581 dkim_signature_printf(message, "; b=");
582 if (!dkim_signature_normalize(message))
583 return;
584 if ((tmp = strdup(message->signature.signature)) == NULL) {
585 dkim_err(message, "Can't create DKIM signature");
586 return;
588 dkim_parse_header(message, tmp, 1);
589 if (EVP_DigestSignUpdate(message->b, tmp, strlen(tmp)) <= 0) {
590 dkim_err(message, "Failed to update digest context");
591 return;
593 free(tmp);
594 if (EVP_DigestSignFinal(message->b, NULL, &linelen) <= 0) {
595 dkim_err(message, "Failed to finalize digest");
596 return;
598 if ((tmp = malloc(linelen)) == NULL) {
599 dkim_err(message, "Can't allocate space for digest");
600 return;
602 if (EVP_DigestSignFinal(message->b, tmp, &linelen) <= 0) {
603 dkim_err(message, "Failed to finalize digest");
604 return;
606 if ((b = malloc((((linelen + 2) / 3) * 4) + 1)) == NULL) {
607 dkim_err(message, "Can't create DKIM signature");
608 return;
610 EVP_EncodeBlock(b, tmp, linelen);
611 free(tmp);
612 dkim_signature_printf(message, "%s\r\n", b);
613 free(b);
614 dkim_signature_normalize(message);
615 tmp = message->signature.signature;
616 while ((tmp2 = strchr(tmp, '\r')) != NULL) {
617 tmp2[0] = '\0';
618 osmtpd_filter_dataline(ctx, "%s", tmp);
619 tmp = tmp2 + 2;
621 tmp = NULL;
622 linelen = 0;
623 rewind(message->origf);
624 while ((i = getline(&tmp, &linelen, message->origf)) != -1) {
625 tmp[i - 1] = '\0';
626 osmtpd_filter_dataline(ctx, "%s", tmp);
630 int
631 dkim_signature_normalize(struct dkim_message *message)
633 size_t i;
634 size_t linelen;
635 size_t checkpoint;
636 size_t skip;
637 size_t *headerlen = &(message->signature.len);
638 int headername = 1;
639 char tag = '\0';
640 char *sig = message->signature.signature;
642 for (linelen = i = 0; sig[i] != '\0'; i++) {
643 if (sig[i] == '\r' && sig[i + 1] == '\n') {
644 i++;
645 checkpoint = 0;
646 linelen = 0;
647 continue;
649 if (sig[i] == '\t')
650 linelen = (linelen + 8) & ~7;
651 else
652 linelen++;
653 if (headername) {
654 if (sig[i] == ':') {
655 headername = 0;
656 checkpoint = i;
658 continue;
660 if (linelen > DKIM_SIGNATURE_LINELEN && checkpoint != 0) {
661 for (skip = checkpoint + 1;
662 sig[skip] == ' ' || sig[skip] == '\t';
663 skip++)
664 continue;
665 skip -= checkpoint + 1;
666 if (!dkim_signature_need(message,
667 skip > 3 ? 0 : 3 - skip + 1))
668 return 0;
669 sig = message->signature.signature;
671 memmove(sig + checkpoint + 3,
672 sig + checkpoint + skip,
673 *headerlen - skip - checkpoint + 1);
674 sig[checkpoint + 1] = '\r';
675 sig[checkpoint + 2] = '\n';
676 sig[checkpoint + 3] = '\t';
677 linelen = 8;
678 *headerlen = *headerlen + 3 - skip;
679 i = checkpoint + 3;
680 checkpoint = 0;
682 if (sig[i] == ';') {
683 checkpoint = i;
684 tag = '\0';
685 continue;
687 switch (tag) {
688 case 'B':
689 case 'b':
690 case 'z':
691 checkpoint = i;
692 break;
693 case 'h':
694 if (sig[i] == ':')
695 checkpoint = i;
696 break;
698 if (tag == '\0' && sig[i] != ' ' && sig[i] != '\t') {
699 if ((tag = sig[i]) == 'b' && sig[i + 1] == 'h' &&
700 sig[i + 2] == '=') {
701 tag = 'B';
702 linelen += 2;
703 i += 2;
704 } else
705 tag = sig[i];
708 return 1;
711 int
712 dkim_signature_printheader(struct dkim_message *message, const char *header)
714 size_t i, j, len;
715 static char *fmtheader = NULL;
716 char *tmp;
717 static size_t size = 0;
718 int first;
720 len = strlen(header);
721 if ((len + 3) * 3 < len) {
722 errno = EOVERFLOW;
723 dkim_err(message, "Can't add z-component to header");
724 return 0;
726 if ((len + 3) * 3 > size) {
727 if ((tmp = reallocarray(fmtheader, 3, len + 3)) == NULL) {
728 dkim_err(message, "Can't add z-component to header");
729 return 0;
731 fmtheader = tmp;
732 size = (len + 1) * 3;
735 first = message->signature.signature[message->signature.len - 1] == '=';
736 for (j = i = 0; header[i] != '\0'; i++, j++) {
737 if (i == 0 && header[i] != ' ' && header[i] != '\t' && !first)
738 fmtheader[j++] = '|';
739 if ((header[i] >= 0x21 && header[i] <= 0x3A) ||
740 (header[i] == 0x3C) ||
741 (header[i] >= 0x3E && header[i] <= 0x7B) ||
742 (header[i] >= 0x7D && header[i] <= 0x7E))
743 fmtheader[j] = header[i];
744 else {
745 fmtheader[j++] = '=';
746 (void) sprintf(fmtheader + j, "%02hhX", header[i]);
747 j++;
750 (void) sprintf(fmtheader + j, "=%02hhX=%02hhX", (unsigned char) '\r',
751 (unsigned char) '\n');
753 return dkim_signature_printf(message, "%s", fmtheader);
756 int
757 dkim_signature_printf(struct dkim_message *message, char *fmt, ...)
759 struct dkim_signature *sig = &(message->signature);
760 va_list ap;
761 size_t len;
763 va_start(ap, fmt);
764 if ((len = vsnprintf(sig->signature + sig->len, sig->size - sig->len,
765 fmt, ap)) >= sig->size - sig->len) {
766 va_end(ap);
767 if (!dkim_signature_need(message, len + 1))
768 return 0;
769 va_start(ap, fmt);
770 if ((len = vsnprintf(sig->signature + sig->len,
771 sig->size - sig->len, fmt, ap)) >= sig->size - sig->len)
772 osmtpd_errx(1, "Miscalculated header size");
774 sig->len += len;
775 va_end(ap);
776 return 1;
779 int
780 dkim_signature_need(struct dkim_message *message, size_t len)
782 struct dkim_signature *sig = &(message->signature);
783 char *tmp;
785 if (sig->len + len < sig->size)
786 return 1;
787 sig->size = (((len + sig->len) / 512) + 1) * 512;
788 if ((tmp = realloc(sig->signature, sig->size)) == NULL) {
789 dkim_err(message, "No room for signature");
790 return 0;
792 sig->signature = tmp;
793 return 1;
796 __dead void
797 usage(void)
799 fprintf(stderr, "usage: filter-dkimsign [-tz] [-a signalg] "
800 "[-c canonicalization] \n [-h headerfields]"
801 "[-x seconds] -d domain -k keyfile -s selector\n");
802 exit(1);