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