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