Bugzilla #890
[exim.git] / src / src / dkim.c
1 /* $Cambridge: exim/src/src/dkim.c,v 1.4 2009/10/13 18:32:05 tom Exp $ */
2
3 /*************************************************
4 *     Exim - an Internet mail transport agent    *
5 *************************************************/
6
7 /* Copyright (c) University of Cambridge 2009 */
8 /* See the file NOTICE for conditions of use and distribution. */
9
10 /* Code for DKIM support. Other DKIM relevant code is in
11    receive.c, transport.c and transports/smtp.c */
12
13 #include "exim.h"
14
15 #ifndef DISABLE_DKIM
16
17 #include "pdkim/pdkim.h"
18
19 pdkim_ctx       *dkim_verify_ctx = NULL;
20 pdkim_signature *dkim_signatures = NULL;
21 pdkim_signature *dkim_cur_sig    = NULL;
22
23 int dkim_exim_query_dns_txt(char *name, char *answer) {
24   dns_answer dnsa;
25   dns_scan   dnss;
26   dns_record *rr;
27
28   if (dns_lookup(&dnsa, (uschar *)name, T_TXT, NULL) != DNS_SUCCEED) return PDKIM_FAIL;
29
30   /* Search for TXT record */
31   for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
32        rr != NULL;
33        rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
34     if (rr->type == T_TXT) break;
35
36   /* Copy record content to the answer buffer */
37   if (rr != NULL) {
38     int rr_offset = 0;
39     int answer_offset = 0;
40     while (rr_offset < rr->size) {
41       uschar len = (rr->data)[rr_offset++];
42       snprintf(answer+(answer_offset),
43                PDKIM_DNS_TXT_MAX_RECLEN-(answer_offset),
44                "%.*s", (int)len, (char *)((rr->data)+rr_offset));
45       rr_offset+=len;
46       answer_offset+=len;
47     }
48   }
49   else return PDKIM_FAIL;
50
51   return PDKIM_OK;
52 }
53
54
55 void dkim_exim_verify_init(void) {
56
57   /* Free previous context if there is one */
58   if (dkim_verify_ctx) pdkim_free_ctx(dkim_verify_ctx);
59
60   /* Create new context */
61   dkim_verify_ctx = pdkim_init_verify(PDKIM_INPUT_SMTP,
62                                       &dkim_exim_query_dns_txt
63                                      );
64
65   if (dkim_verify_ctx != NULL) {
66     dkim_collect_input = TRUE;
67     pdkim_set_debug_stream(dkim_verify_ctx,debug_file);
68   }
69   else dkim_collect_input = FALSE;
70
71 }
72
73
74 void dkim_exim_verify_feed(uschar *data, int len) {
75   if (dkim_collect_input &&
76       pdkim_feed(dkim_verify_ctx,
77                  (char *)data,
78                  len) != PDKIM_OK) dkim_collect_input = FALSE;
79 }
80
81
82 void dkim_exim_verify_finish(void) {
83   pdkim_signature *sig = NULL;
84   int dkim_signing_domains_size = 0;
85   int dkim_signing_domains_ptr = 0;
86   dkim_signing_domains = NULL;
87
88   /* Delete eventual previous signature chain */
89   dkim_signatures = NULL;
90
91   /* If we have arrived here with dkim_collect_input == FALSE, it
92      means there was a processing error somewhere along the way.
93      Log the incident and disable futher verification. */
94   if (!dkim_collect_input) {
95     log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: Error while running this message through validation, disabling signature verification.");
96     dkim_disable_verify = TRUE;
97     return;
98   }
99   dkim_collect_input = FALSE;
100
101   /* Finish DKIM operation and fetch link to signatures chain */
102   if (pdkim_feed_finish(dkim_verify_ctx,&dkim_signatures) != PDKIM_OK) return;
103
104   sig = dkim_signatures;
105   while (sig != NULL) {
106     int size = 0;
107     int ptr = 0;
108     /* Log a line for each signature */
109     uschar *logmsg = string_append(NULL, &size, &ptr, 5,
110
111       string_sprintf( "DKIM: d=%s s=%s c=%s/%s a=%s ",
112                       sig->domain,
113                       sig->selector,
114                       (sig->canon_headers == PDKIM_CANON_SIMPLE)?"simple":"relaxed",
115                       (sig->canon_body    == PDKIM_CANON_SIMPLE)?"simple":"relaxed",
116                       (sig->algo          == PDKIM_ALGO_RSA_SHA256)?"rsa-sha256":"rsa-sha1"
117                     ),
118       ((sig->identity != NULL)?
119         string_sprintf("i=%s ", sig->identity)
120         :
121         US""
122       ),
123       ((sig->created > 0)?
124         string_sprintf("t=%lu ", sig->created)
125         :
126         US""
127       ),
128       ((sig->expires > 0)?
129         string_sprintf("x=%lu ", sig->expires)
130         :
131         US""
132       ),
133       ((sig->bodylength > -1)?
134         string_sprintf("l=%lu ", sig->bodylength)
135         :
136         US""
137       )
138     );
139
140     switch(sig->verify_status) {
141       case PDKIM_VERIFY_NONE:
142         logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]");
143       break;
144       case PDKIM_VERIFY_INVALID:
145         logmsg = string_append(logmsg, &size, &ptr, 1, "[invalid - ");
146         switch (sig->verify_ext_status) {
147           case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
148             logmsg = string_append(logmsg, &size, &ptr, 1, "public key record (currently?) unavailable]");
149           break;
150           case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
151             logmsg = string_append(logmsg, &size, &ptr, 1, "overlong public key record]");
152           break;
153           case PDKIM_VERIFY_INVALID_PUBKEY_PARSING:
154             logmsg = string_append(logmsg, &size, &ptr, 1, "syntax error in public key record]");
155           break;
156           default:
157             logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified problem]");
158         }
159       break;
160       case PDKIM_VERIFY_FAIL:
161         logmsg = string_append(logmsg, &size, &ptr, 1, "[verification failed - ");
162         switch (sig->verify_ext_status) {
163           case PDKIM_VERIFY_FAIL_BODY:
164             logmsg = string_append(logmsg, &size, &ptr, 1, "body hash mismatch (body probably modified in transit)]");
165           break;
166           case PDKIM_VERIFY_FAIL_MESSAGE:
167             logmsg = string_append(logmsg, &size, &ptr, 1, "signature did not verify (headers probably modified in transit)]");
168           break;
169           default:
170             logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]");
171         }
172       break;
173       case PDKIM_VERIFY_PASS:
174         logmsg = string_append(logmsg, &size, &ptr, 1, "[verification succeeded]");
175       break;
176     }
177
178     logmsg[ptr] = '\0';
179     log_write(0, LOG_MAIN, (char *)logmsg);
180
181     /* Build a colon-separated list of signing domains in dkim_signing_domains */
182     dkim_signing_domains = string_append(dkim_signing_domains,
183                                          &dkim_signing_domains_size,
184                                          &dkim_signing_domains_ptr,
185                                          2,
186                                          sig->domain,
187                                          ":"
188                                         );
189
190     /* Process next signature */
191     sig = sig->next;
192   }
193
194   /* Chop the last colon from the domain list */
195   if ((dkim_signing_domains != NULL) &&
196       (Ustrlen(dkim_signing_domains) > 0))
197     dkim_signing_domains[Ustrlen(dkim_signing_domains)-1] = '\0';
198 }
199
200
201 void dkim_exim_acl_setup(uschar *id) {
202   pdkim_signature *sig = dkim_signatures;
203   dkim_cur_sig = NULL;
204   if (dkim_disable_verify ||
205       !id || !sig ||
206       !dkim_verify_ctx) return;
207   /* Find signature to run ACL on */
208   while (sig != NULL) {
209     uschar *cmp_val = NULL;
210     if (Ustrchr(id,'@') != NULL) cmp_val = (uschar *)sig->identity;
211                             else cmp_val = (uschar *)sig->domain;
212     if (cmp_val && (strcmpic(cmp_val,id) == 0)) {
213       dkim_cur_sig = sig;
214       /* The "dkim_domain" and "dkim_selector" expansion variables have
215          related globals, since they are used in the signing code too.
216          Instead of inventing separate names for verification, we set
217          them here. This is easy since a domain and selector is guaranteed
218          to be in a signature. The other dkim_* expansion items are
219          dynamically fetched from dkim_cur_sig at expansion time (see
220          function below). */
221       dkim_signing_domain   = (uschar *)sig->domain;
222       dkim_signing_selector = (uschar *)sig->selector;
223       return;
224     }
225     sig = sig->next;
226   }
227 }
228
229
230 uschar *dkim_exim_expand_query(int what) {
231
232   if (!dkim_verify_ctx ||
233       dkim_disable_verify ||
234       !dkim_cur_sig) return dkim_exim_expand_defaults(what);
235
236   switch(what) {
237     case DKIM_ALGO:
238       switch(dkim_cur_sig->algo) {
239         case PDKIM_ALGO_RSA_SHA1:
240           return US"rsa-sha1";
241         case PDKIM_ALGO_RSA_SHA256:
242         default:
243           return US"rsa-sha256";
244       }
245     case DKIM_BODYLENGTH:
246       return (dkim_cur_sig->bodylength >= 0)?
247               (uschar *)string_sprintf(OFF_T_FMT,(LONGLONG_T)dkim_cur_sig->bodylength)
248               :dkim_exim_expand_defaults(what);
249     case DKIM_CANON_BODY:
250       switch(dkim_cur_sig->canon_body) {
251         case PDKIM_CANON_RELAXED:
252           return US"relaxed";
253         case PDKIM_CANON_SIMPLE:
254         default:
255           return US"simple";
256       }
257     case DKIM_CANON_HEADERS:
258       switch(dkim_cur_sig->canon_headers) {
259         case PDKIM_CANON_RELAXED:
260           return US"relaxed";
261         case PDKIM_CANON_SIMPLE:
262         default:
263           return US"simple";
264       }
265     case DKIM_COPIEDHEADERS:
266       return dkim_cur_sig->copiedheaders?
267               (uschar *)(dkim_cur_sig->copiedheaders)
268               :dkim_exim_expand_defaults(what);
269     case DKIM_CREATED:
270       return (dkim_cur_sig->created > 0)?
271               (uschar *)string_sprintf("%llu",dkim_cur_sig->created)
272               :dkim_exim_expand_defaults(what);
273     case DKIM_EXPIRES:
274       return (dkim_cur_sig->expires > 0)?
275               (uschar *)string_sprintf("%llu",dkim_cur_sig->expires)
276               :dkim_exim_expand_defaults(what);
277     case DKIM_HEADERNAMES:
278       return dkim_cur_sig->headernames?
279               (uschar *)(dkim_cur_sig->headernames)
280               :dkim_exim_expand_defaults(what);
281     case DKIM_IDENTITY:
282       return dkim_cur_sig->identity?
283               (uschar *)(dkim_cur_sig->identity)
284               :dkim_exim_expand_defaults(what);
285     case DKIM_KEY_GRANULARITY:
286       return dkim_cur_sig->pubkey?
287               (dkim_cur_sig->pubkey->granularity?
288                 (uschar *)(dkim_cur_sig->pubkey->granularity)
289                 :dkim_exim_expand_defaults(what)
290               )
291               :dkim_exim_expand_defaults(what);
292     case DKIM_KEY_SRVTYPE:
293       return dkim_cur_sig->pubkey?
294               (dkim_cur_sig->pubkey->srvtype?
295                 (uschar *)(dkim_cur_sig->pubkey->srvtype)
296                 :dkim_exim_expand_defaults(what)
297               )
298               :dkim_exim_expand_defaults(what);
299     case DKIM_KEY_NOTES:
300       return dkim_cur_sig->pubkey?
301               (dkim_cur_sig->pubkey->notes?
302                 (uschar *)(dkim_cur_sig->pubkey->notes)
303                 :dkim_exim_expand_defaults(what)
304               )
305               :dkim_exim_expand_defaults(what);
306     case DKIM_KEY_TESTING:
307       return dkim_cur_sig->pubkey?
308               (dkim_cur_sig->pubkey->testing?
309                 US"1"
310                 :dkim_exim_expand_defaults(what)
311               )
312               :dkim_exim_expand_defaults(what);
313     case DKIM_NOSUBDOMAINS:
314       return dkim_cur_sig->pubkey?
315               (dkim_cur_sig->pubkey->no_subdomaining?
316                 US"1"
317                 :dkim_exim_expand_defaults(what)
318               )
319               :dkim_exim_expand_defaults(what);
320     case DKIM_VERIFY_STATUS:
321       switch(dkim_cur_sig->verify_status) {
322         case PDKIM_VERIFY_INVALID:
323           return US"invalid";
324         case PDKIM_VERIFY_FAIL:
325           return US"fail";
326         case PDKIM_VERIFY_PASS:
327           return US"pass";
328         case PDKIM_VERIFY_NONE:
329         default:
330           return US"none";
331       }
332     case DKIM_VERIFY_REASON:
333       switch (dkim_cur_sig->verify_ext_status) {
334         case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
335           return US"pubkey_unavailable";
336         case PDKIM_VERIFY_INVALID_PUBKEY_PARSING:
337           return US"pubkey_syntax";
338         case PDKIM_VERIFY_FAIL_BODY:
339           return US"bodyhash_mismatch";
340         case PDKIM_VERIFY_FAIL_MESSAGE:
341           return US"signature_incorrect";
342       }
343     default:
344       return US"";
345   }
346 }
347
348
349 uschar *dkim_exim_expand_defaults(int what) {
350   switch(what) {
351     case DKIM_ALGO:               return US"";
352     case DKIM_BODYLENGTH:         return US"9999999999999";
353     case DKIM_CANON_BODY:         return US"";
354     case DKIM_CANON_HEADERS:      return US"";
355     case DKIM_COPIEDHEADERS:      return US"";
356     case DKIM_CREATED:            return US"0";
357     case DKIM_EXPIRES:            return US"9999999999999";
358     case DKIM_HEADERNAMES:        return US"";
359     case DKIM_IDENTITY:           return US"";
360     case DKIM_KEY_GRANULARITY:    return US"*";
361     case DKIM_KEY_SRVTYPE:        return US"*";
362     case DKIM_KEY_NOTES:          return US"";
363     case DKIM_KEY_TESTING:        return US"0";
364     case DKIM_NOSUBDOMAINS:       return US"0";
365     case DKIM_VERIFY_STATUS:      return US"none";
366     case DKIM_VERIFY_REASON:      return US"";
367     default:                      return US"";
368   }
369 }
370
371
372 uschar *dkim_exim_sign(int dkim_fd,
373                        uschar *dkim_private_key,
374                        uschar *dkim_domain,
375                        uschar *dkim_selector,
376                        uschar *dkim_canon,
377                        uschar *dkim_sign_headers) {
378   pdkim_ctx *ctx = NULL;
379   uschar *rc = NULL;
380   pdkim_signature *signature;
381   int pdkim_canon;
382   int sread;
383   char buf[4096];
384   int save_errno = 0;
385   int old_pool = store_pool;
386
387   dkim_domain = expand_string(dkim_domain);
388   if (dkim_domain == NULL) {
389     /* expansion error, do not send message. */
390     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
391           "dkim_domain: %s", expand_string_message);
392     rc = NULL;
393     goto CLEANUP;
394   }
395   /* Set up $dkim_domain expansion variable. */
396   dkim_signing_domain = dkim_domain;
397
398   /* Get selector to use. */
399   dkim_selector = expand_string(dkim_selector);
400   if (dkim_selector == NULL) {
401     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
402       "dkim_selector: %s", expand_string_message);
403     rc = NULL;
404     goto CLEANUP;
405   }
406   /* Set up $dkim_selector expansion variable. */
407   dkim_signing_selector = dkim_selector;
408
409   /* Get canonicalization to use */
410   dkim_canon = expand_string(dkim_canon?dkim_canon:US"relaxed");
411   if (dkim_canon == NULL) {
412     /* expansion error, do not send message. */
413     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
414           "dkim_canon: %s", expand_string_message);
415     rc = NULL;
416     goto CLEANUP;
417   }
418   if (Ustrcmp(dkim_canon, "relaxed") == 0)
419     pdkim_canon = PDKIM_CANON_RELAXED;
420   else if (Ustrcmp(dkim_canon, "simple") == 0)
421     pdkim_canon = PDKIM_CANON_RELAXED;
422   else {
423     log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon);
424     pdkim_canon = PDKIM_CANON_RELAXED;
425   }
426
427   /* Expand signing headers once */
428   if (dkim_sign_headers != NULL) {
429     dkim_sign_headers = expand_string(dkim_sign_headers);
430     if (dkim_sign_headers == NULL) {
431       log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
432         "dkim_sign_headers: %s", expand_string_message);
433       rc = NULL;
434       goto CLEANUP;
435     }
436   }
437
438   /* Get private key to use. */
439   dkim_private_key = expand_string(dkim_private_key);
440   if (dkim_private_key == NULL) {
441     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
442       "dkim_private_key: %s", expand_string_message);
443     rc = NULL;
444     goto CLEANUP;
445   }
446   if ( (Ustrlen(dkim_private_key) == 0) ||
447        (Ustrcmp(dkim_private_key,"0") == 0) ||
448        (Ustrcmp(dkim_private_key,"false") == 0) ) {
449     /* don't sign, but no error */
450     rc = US"";
451     goto CLEANUP;
452   }
453
454   if (dkim_private_key[0] == '/') {
455     int privkey_fd = 0;
456     /* Looks like a filename, load the private key. */
457     memset(big_buffer,0,big_buffer_size);
458     privkey_fd = open(CS dkim_private_key,O_RDONLY);
459     if (privkey_fd < 0) {
460       log_write(0, LOG_MAIN|LOG_PANIC, "unable to open "
461         "private key file for reading: %s", dkim_private_key);
462       rc = NULL;
463       goto CLEANUP;
464     }
465     (void)read(privkey_fd,big_buffer,(big_buffer_size-2));
466     (void)close(privkey_fd);
467     dkim_private_key = big_buffer;
468   }
469
470   ctx = pdkim_init_sign(PDKIM_INPUT_SMTP,
471                         (char *)dkim_signing_domain,
472                         (char *)dkim_signing_selector,
473                         (char *)dkim_private_key
474                        );
475
476   pdkim_set_debug_stream(ctx,debug_file);
477
478   pdkim_set_optional(ctx,
479                      (char *)dkim_sign_headers,
480                      NULL,
481                      pdkim_canon,
482                      pdkim_canon,
483                      -1,
484                      PDKIM_ALGO_RSA_SHA256,
485                      0,
486                      0);
487
488   while((sread = read(dkim_fd,&buf,4096)) > 0) {
489     if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) {
490       rc = NULL;
491       goto CLEANUP;
492     }
493   }
494   /* Handle failed read above. */
495   if (sread == -1) {
496     debug_printf("DKIM: Error reading -K file.\n");
497     save_errno = errno;
498     rc = NULL;
499     goto CLEANUP;
500   }
501
502   if (pdkim_feed_finish(ctx,&signature) != PDKIM_OK)
503     goto CLEANUP;
504
505   rc = store_get(strlen(signature->signature_header)+3);
506   Ustrcpy(rc,US signature->signature_header);
507   Ustrcat(rc,US"\r\n");
508
509   CLEANUP:
510   if (ctx != NULL) {
511     pdkim_free_ctx(ctx);
512   }
513   store_pool = old_pool;
514   errno = save_errno;
515   return rc;
516 };
517
518 #endif