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