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