Improve log output when DKIM signing operation fails.
[exim.git] / src / src / dkim.c
1 /* $Cambridge: exim/src/src/dkim.c,v 1.11 2009/12/15 08:23:15 tom Exp $ */
2
3 /*************************************************
4 *     Exim - an Internet mail transport agent    *
5 *************************************************/
6
7 /* Copyright (c) University of Cambridge, 1995 - 2007 */
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_signers_size = 0;
85   int dkim_signers_ptr = 0;
86   dkim_signers = 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 (and identities, if present) in dkim_signers */
182     dkim_signers = string_append(dkim_signers,
183                                  &dkim_signers_size,
184                                  &dkim_signers_ptr,
185                                  2,
186                                  sig->domain,
187                                  ":"
188                                 );
189
190     if (sig->identity != NULL) {
191       dkim_signers = string_append(dkim_signers,
192                                    &dkim_signers_size,
193                                    &dkim_signers_ptr,
194                                    2,
195                                    sig->identity,
196                                    ":"
197                                   );
198     }
199
200     /* Process next signature */
201     sig = sig->next;
202   }
203
204   /* NULL-terminate and chop the last colon from the domain list */
205   if (dkim_signers != NULL) {
206     dkim_signers[dkim_signers_ptr] = '\0';
207     if (Ustrlen(dkim_signers) > 0)
208       dkim_signers[Ustrlen(dkim_signers)-1] = '\0';
209   }
210 }
211
212
213 void dkim_exim_acl_setup(uschar *id) {
214   pdkim_signature *sig = dkim_signatures;
215   dkim_cur_sig = NULL;
216   dkim_cur_signer = id;
217   if (dkim_disable_verify ||
218       !id || !dkim_verify_ctx) return;
219   /* Find signature to run ACL on */
220   while (sig != NULL) {
221     uschar *cmp_val = NULL;
222     if (Ustrchr(id,'@') != NULL) cmp_val = (uschar *)sig->identity;
223                             else cmp_val = (uschar *)sig->domain;
224     if (cmp_val && (strcmpic(cmp_val,id) == 0)) {
225       dkim_cur_sig = sig;
226       /* The "dkim_domain" and "dkim_selector" expansion variables have
227          related globals, since they are used in the signing code too.
228          Instead of inventing separate names for verification, we set
229          them here. This is easy since a domain and selector is guaranteed
230          to be in a signature. The other dkim_* expansion items are
231          dynamically fetched from dkim_cur_sig at expansion time (see
232          function below). */
233       dkim_signing_domain   = (uschar *)sig->domain;
234       dkim_signing_selector = (uschar *)sig->selector;
235       return;
236     }
237     sig = sig->next;
238   }
239 }
240
241
242 uschar *dkim_exim_expand_query(int what) {
243
244   if (!dkim_verify_ctx ||
245       dkim_disable_verify ||
246       !dkim_cur_sig) return dkim_exim_expand_defaults(what);
247
248   switch(what) {
249     case DKIM_ALGO:
250       switch(dkim_cur_sig->algo) {
251         case PDKIM_ALGO_RSA_SHA1:
252           return US"rsa-sha1";
253         case PDKIM_ALGO_RSA_SHA256:
254         default:
255           return US"rsa-sha256";
256       }
257     case DKIM_BODYLENGTH:
258       return (dkim_cur_sig->bodylength >= 0)?
259               (uschar *)string_sprintf(OFF_T_FMT,(LONGLONG_T)dkim_cur_sig->bodylength)
260               :dkim_exim_expand_defaults(what);
261     case DKIM_CANON_BODY:
262       switch(dkim_cur_sig->canon_body) {
263         case PDKIM_CANON_RELAXED:
264           return US"relaxed";
265         case PDKIM_CANON_SIMPLE:
266         default:
267           return US"simple";
268       }
269     case DKIM_CANON_HEADERS:
270       switch(dkim_cur_sig->canon_headers) {
271         case PDKIM_CANON_RELAXED:
272           return US"relaxed";
273         case PDKIM_CANON_SIMPLE:
274         default:
275           return US"simple";
276       }
277     case DKIM_COPIEDHEADERS:
278       return dkim_cur_sig->copiedheaders?
279               (uschar *)(dkim_cur_sig->copiedheaders)
280               :dkim_exim_expand_defaults(what);
281     case DKIM_CREATED:
282       return (dkim_cur_sig->created > 0)?
283               (uschar *)string_sprintf("%llu",dkim_cur_sig->created)
284               :dkim_exim_expand_defaults(what);
285     case DKIM_EXPIRES:
286       return (dkim_cur_sig->expires > 0)?
287               (uschar *)string_sprintf("%llu",dkim_cur_sig->expires)
288               :dkim_exim_expand_defaults(what);
289     case DKIM_HEADERNAMES:
290       return dkim_cur_sig->headernames?
291               (uschar *)(dkim_cur_sig->headernames)
292               :dkim_exim_expand_defaults(what);
293     case DKIM_IDENTITY:
294       return dkim_cur_sig->identity?
295               (uschar *)(dkim_cur_sig->identity)
296               :dkim_exim_expand_defaults(what);
297     case DKIM_KEY_GRANULARITY:
298       return dkim_cur_sig->pubkey?
299               (dkim_cur_sig->pubkey->granularity?
300                 (uschar *)(dkim_cur_sig->pubkey->granularity)
301                 :dkim_exim_expand_defaults(what)
302               )
303               :dkim_exim_expand_defaults(what);
304     case DKIM_KEY_SRVTYPE:
305       return dkim_cur_sig->pubkey?
306               (dkim_cur_sig->pubkey->srvtype?
307                 (uschar *)(dkim_cur_sig->pubkey->srvtype)
308                 :dkim_exim_expand_defaults(what)
309               )
310               :dkim_exim_expand_defaults(what);
311     case DKIM_KEY_NOTES:
312       return dkim_cur_sig->pubkey?
313               (dkim_cur_sig->pubkey->notes?
314                 (uschar *)(dkim_cur_sig->pubkey->notes)
315                 :dkim_exim_expand_defaults(what)
316               )
317               :dkim_exim_expand_defaults(what);
318     case DKIM_KEY_TESTING:
319       return dkim_cur_sig->pubkey?
320               (dkim_cur_sig->pubkey->testing?
321                 US"1"
322                 :dkim_exim_expand_defaults(what)
323               )
324               :dkim_exim_expand_defaults(what);
325     case DKIM_NOSUBDOMAINS:
326       return dkim_cur_sig->pubkey?
327               (dkim_cur_sig->pubkey->no_subdomaining?
328                 US"1"
329                 :dkim_exim_expand_defaults(what)
330               )
331               :dkim_exim_expand_defaults(what);
332     case DKIM_VERIFY_STATUS:
333       switch(dkim_cur_sig->verify_status) {
334         case PDKIM_VERIFY_INVALID:
335           return US"invalid";
336         case PDKIM_VERIFY_FAIL:
337           return US"fail";
338         case PDKIM_VERIFY_PASS:
339           return US"pass";
340         case PDKIM_VERIFY_NONE:
341         default:
342           return US"none";
343       }
344     case DKIM_VERIFY_REASON:
345       switch (dkim_cur_sig->verify_ext_status) {
346         case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
347           return US"pubkey_unavailable";
348         case PDKIM_VERIFY_INVALID_PUBKEY_PARSING:
349           return US"pubkey_syntax";
350         case PDKIM_VERIFY_FAIL_BODY:
351           return US"bodyhash_mismatch";
352         case PDKIM_VERIFY_FAIL_MESSAGE:
353           return US"signature_incorrect";
354       }
355     default:
356       return US"";
357   }
358 }
359
360
361 uschar *dkim_exim_expand_defaults(int what) {
362   switch(what) {
363     case DKIM_ALGO:               return US"";
364     case DKIM_BODYLENGTH:         return US"9999999999999";
365     case DKIM_CANON_BODY:         return US"";
366     case DKIM_CANON_HEADERS:      return US"";
367     case DKIM_COPIEDHEADERS:      return US"";
368     case DKIM_CREATED:            return US"0";
369     case DKIM_EXPIRES:            return US"9999999999999";
370     case DKIM_HEADERNAMES:        return US"";
371     case DKIM_IDENTITY:           return US"";
372     case DKIM_KEY_GRANULARITY:    return US"*";
373     case DKIM_KEY_SRVTYPE:        return US"*";
374     case DKIM_KEY_NOTES:          return US"";
375     case DKIM_KEY_TESTING:        return US"0";
376     case DKIM_NOSUBDOMAINS:       return US"0";
377     case DKIM_VERIFY_STATUS:      return US"none";
378     case DKIM_VERIFY_REASON:      return US"";
379     default:                      return US"";
380   }
381 }
382
383
384 uschar *dkim_exim_sign(int dkim_fd,
385                        uschar *dkim_private_key,
386                        uschar *dkim_domain,
387                        uschar *dkim_selector,
388                        uschar *dkim_canon,
389                        uschar *dkim_sign_headers) {
390   pdkim_ctx *ctx = NULL;
391   uschar *rc = NULL;
392   pdkim_signature *signature;
393   int pdkim_canon;
394   int pdkim_rc;
395   int sread;
396   char buf[4096];
397   int save_errno = 0;
398   int old_pool = store_pool;
399
400   dkim_domain = expand_string(dkim_domain);
401   if (dkim_domain == NULL) {
402     /* expansion error, do not send message. */
403     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
404           "dkim_domain: %s", expand_string_message);
405     rc = NULL;
406     goto CLEANUP;
407   }
408   /* Set up $dkim_domain expansion variable. */
409   dkim_signing_domain = dkim_domain;
410
411   /* Get selector to use. */
412   dkim_selector = expand_string(dkim_selector);
413   if (dkim_selector == NULL) {
414     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
415       "dkim_selector: %s", expand_string_message);
416     rc = NULL;
417     goto CLEANUP;
418   }
419   /* Set up $dkim_selector expansion variable. */
420   dkim_signing_selector = dkim_selector;
421
422   /* Get canonicalization to use */
423   dkim_canon = expand_string(dkim_canon?dkim_canon:US"relaxed");
424   if (dkim_canon == NULL) {
425     /* expansion error, do not send message. */
426     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
427           "dkim_canon: %s", expand_string_message);
428     rc = NULL;
429     goto CLEANUP;
430   }
431   if (Ustrcmp(dkim_canon, "relaxed") == 0)
432     pdkim_canon = PDKIM_CANON_RELAXED;
433   else if (Ustrcmp(dkim_canon, "simple") == 0)
434     pdkim_canon = PDKIM_CANON_SIMPLE;
435   else {
436     log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon);
437     pdkim_canon = PDKIM_CANON_RELAXED;
438   }
439
440   /* Expand signing headers once */
441   if (dkim_sign_headers != NULL) {
442     dkim_sign_headers = expand_string(dkim_sign_headers);
443     if (dkim_sign_headers == NULL) {
444       log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
445         "dkim_sign_headers: %s", expand_string_message);
446       rc = NULL;
447       goto CLEANUP;
448     }
449   }
450
451   /* Get private key to use. */
452   dkim_private_key = expand_string(dkim_private_key);
453   if (dkim_private_key == NULL) {
454     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
455       "dkim_private_key: %s", expand_string_message);
456     rc = NULL;
457     goto CLEANUP;
458   }
459   if ( (Ustrlen(dkim_private_key) == 0) ||
460        (Ustrcmp(dkim_private_key,"0") == 0) ||
461        (Ustrcmp(dkim_private_key,"false") == 0) ) {
462     /* don't sign, but no error */
463     rc = US"";
464     goto CLEANUP;
465   }
466
467   if (dkim_private_key[0] == '/') {
468     int privkey_fd = 0;
469     /* Looks like a filename, load the private key. */
470     memset(big_buffer,0,big_buffer_size);
471     privkey_fd = open(CS dkim_private_key,O_RDONLY);
472     if (privkey_fd < 0) {
473       log_write(0, LOG_MAIN|LOG_PANIC, "unable to open "
474         "private key file for reading: %s", dkim_private_key);
475       rc = NULL;
476       goto CLEANUP;
477     }
478     (void)read(privkey_fd,big_buffer,(big_buffer_size-2));
479     (void)close(privkey_fd);
480     dkim_private_key = big_buffer;
481   }
482
483   ctx = pdkim_init_sign(PDKIM_INPUT_SMTP,
484                         (char *)dkim_signing_domain,
485                         (char *)dkim_signing_selector,
486                         (char *)dkim_private_key
487                        );
488
489   pdkim_set_debug_stream(ctx,debug_file);
490
491   pdkim_set_optional(ctx,
492                      (char *)dkim_sign_headers,
493                      NULL,
494                      pdkim_canon,
495                      pdkim_canon,
496                      -1,
497                      PDKIM_ALGO_RSA_SHA256,
498                      0,
499                      0);
500
501   while((sread = read(dkim_fd,&buf,4096)) > 0) {
502     if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) {
503       rc = NULL;
504       goto CLEANUP;
505     }
506   }
507   /* Handle failed read above. */
508   if (sread == -1) {
509     debug_printf("DKIM: Error reading -K file.\n");
510     save_errno = errno;
511     rc = NULL;
512     goto CLEANUP;
513   }
514
515   pdkim_rc = pdkim_feed_finish(ctx,&signature);
516   if (pdkim_rc != PDKIM_OK) {
517     log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: signing failed (RC %d)", pdkim_rc);
518     goto CLEANUP;
519   }
520
521   rc = store_get(strlen(signature->signature_header)+3);
522   Ustrcpy(rc,US signature->signature_header);
523   Ustrcat(rc,US"\r\n");
524
525   CLEANUP:
526   if (ctx != NULL) {
527     pdkim_free_ctx(ctx);
528   }
529   store_pool = old_pool;
530   errno = save_errno;
531   return rc;
532 }
533
534 #endif