171fcccdbe2088d26ab52829df132ae508fba1f2
[users/jgh/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 *dkim_exim_sign(int dkim_fd,
387                        uschar *dkim_private_key,
388                        uschar *dkim_domain,
389                        uschar *dkim_selector,
390                        uschar *dkim_canon,
391                        uschar *dkim_sign_headers) {
392   int sep = 0;
393   uschar *seen_items = NULL;
394   int seen_items_size = 0;
395   int seen_items_offset = 0;
396   uschar itembuf[256];
397   uschar *dkim_canon_expanded;
398   uschar *dkim_sign_headers_expanded;
399   uschar *dkim_private_key_expanded;
400   pdkim_ctx *ctx = NULL;
401   uschar *rc = NULL;
402   uschar *sigbuf = NULL;
403   int sigsize = 0;
404   int sigptr = 0;
405   pdkim_signature *signature;
406   int pdkim_canon;
407   int pdkim_rc;
408   int sread;
409   char buf[4096];
410   int save_errno = 0;
411   int old_pool = store_pool;
412
413   store_pool = POOL_MAIN;
414
415   dkim_domain = expand_string(dkim_domain);
416   if (dkim_domain == NULL) {
417     /* expansion error, do not send message. */
418     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
419           "dkim_domain: %s", expand_string_message);
420     rc = NULL;
421     goto CLEANUP;
422   }
423
424   /* Set $dkim_domain expansion variable to each unique domain in list. */
425   while ((dkim_signing_domain = string_nextinlist(&dkim_domain, &sep,
426                                                   itembuf,
427                                                   sizeof(itembuf))) != NULL) {
428     if (!dkim_signing_domain || (dkim_signing_domain[0] == '\0')) continue;
429     /* Only sign once for each domain, no matter how often it
430        appears in the expanded list. */
431     if (seen_items != NULL) {
432       uschar *seen_items_list = seen_items;
433       if (match_isinlist(dkim_signing_domain,
434                          &seen_items_list,0,NULL,NULL,MCL_STRING,TRUE,NULL) == OK)
435         continue;
436       seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,":");
437     }
438     seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,dkim_signing_domain);
439     seen_items[seen_items_offset] = '\0';
440
441     /* Set up $dkim_selector expansion variable. */
442     dkim_signing_selector = expand_string(dkim_selector);
443     if (dkim_signing_selector == NULL) {
444       log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
445                 "dkim_selector: %s", expand_string_message);
446       rc = NULL;
447       goto CLEANUP;
448     }
449
450     /* Get canonicalization to use */
451     dkim_canon_expanded = expand_string(dkim_canon?dkim_canon:US"relaxed");
452     if (dkim_canon_expanded == NULL) {
453       /* expansion error, do not send message. */
454       log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
455                 "dkim_canon: %s", expand_string_message);
456       rc = NULL;
457       goto CLEANUP;
458     }
459     if (Ustrcmp(dkim_canon_expanded, "relaxed") == 0)
460       pdkim_canon = PDKIM_CANON_RELAXED;
461     else if (Ustrcmp(dkim_canon_expanded, "simple") == 0)
462       pdkim_canon = PDKIM_CANON_SIMPLE;
463     else {
464       log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon_expanded);
465       pdkim_canon = PDKIM_CANON_RELAXED;
466     }
467
468     if (dkim_sign_headers) {
469       dkim_sign_headers_expanded = expand_string(dkim_sign_headers);
470       if (dkim_sign_headers_expanded == NULL) {
471         log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
472                   "dkim_sign_headers: %s", expand_string_message);
473         rc = NULL;
474         goto CLEANUP;
475       }
476     }
477     else {
478       /* pass NULL, which means default header list */
479       dkim_sign_headers_expanded = NULL;
480     }
481
482     /* Get private key to use. */
483     dkim_private_key_expanded = expand_string(dkim_private_key);
484     if (dkim_private_key_expanded == NULL) {
485       log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
486                 "dkim_private_key: %s", expand_string_message);
487       rc = NULL;
488       goto CLEANUP;
489     }
490     if ( (Ustrlen(dkim_private_key_expanded) == 0) ||
491          (Ustrcmp(dkim_private_key_expanded,"0") == 0) ||
492          (Ustrcmp(dkim_private_key_expanded,"false") == 0) ) {
493       /* don't sign, but no error */
494       continue;
495     }
496
497     if (dkim_private_key_expanded[0] == '/') {
498       int privkey_fd = 0;
499       /* Looks like a filename, load the private key. */
500       memset(big_buffer,0,big_buffer_size);
501       privkey_fd = open(CS dkim_private_key_expanded,O_RDONLY);
502       if (privkey_fd < 0) {
503         log_write(0, LOG_MAIN|LOG_PANIC, "unable to open "
504                   "private key file for reading: %s", dkim_private_key_expanded);
505         rc = NULL;
506         goto CLEANUP;
507       }
508       if (read(privkey_fd,big_buffer,(big_buffer_size-2)) < 0) {
509         log_write(0, LOG_MAIN|LOG_PANIC, "unable to read private key file: %s",
510           dkim_private_key_expanded);
511         rc = NULL;
512         goto CLEANUP;
513       }
514       (void)close(privkey_fd);
515       dkim_private_key_expanded = big_buffer;
516     }
517
518     ctx = pdkim_init_sign(PDKIM_INPUT_SMTP,
519                           (char *)dkim_signing_domain,
520                           (char *)dkim_signing_selector,
521                           (char *)dkim_private_key_expanded
522                          );
523
524     pdkim_set_debug_stream(ctx,debug_file);
525
526     pdkim_set_optional(ctx,
527                        (char *)dkim_sign_headers_expanded,
528                        NULL,
529                        pdkim_canon,
530                        pdkim_canon,
531                        -1,
532                        PDKIM_ALGO_RSA_SHA256,
533                        0,
534                        0);
535
536     lseek(dkim_fd, 0, SEEK_SET);
537     while((sread = read(dkim_fd,&buf,4096)) > 0) {
538       if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) {
539         rc = NULL;
540         goto CLEANUP;
541       }
542     }
543     /* Handle failed read above. */
544     if (sread == -1) {
545       debug_printf("DKIM: Error reading -K file.\n");
546       save_errno = errno;
547       rc = NULL;
548       goto CLEANUP;
549     }
550
551     pdkim_rc = pdkim_feed_finish(ctx,&signature);
552     if (pdkim_rc != PDKIM_OK) {
553       log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: signing failed (RC %d)", pdkim_rc);
554       rc = NULL;
555       goto CLEANUP;
556     }
557
558     sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
559                            US signature->signature_header,
560                            US"\r\n");
561
562     pdkim_free_ctx(ctx);
563     ctx = NULL;
564   }
565
566   if (sigbuf != NULL) {
567     sigbuf[sigptr] = '\0';
568     rc = sigbuf;
569   } else
570     rc = US"";
571
572   CLEANUP:
573   if (ctx != NULL)
574     pdkim_free_ctx(ctx);
575   store_pool = old_pool;
576   errno = save_errno;
577   return rc;
578 }
579
580 #endif