Promote dkim_cur_signer to expansion variable
[exim.git] / src / src / dkim.c
1 /* $Cambridge: exim/src/src/dkim.c,v 1.6 2009/10/15 08:27:37 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
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   /* Chop the last colon from the domain list */
205   if ((dkim_signers != NULL) &&
206       (Ustrlen(dkim_signers) > 0))
207     dkim_signers[Ustrlen(dkim_signers)-1] = '\0';
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   pdkim_ctx *ctx = NULL;
389   uschar *rc = NULL;
390   pdkim_signature *signature;
391   int pdkim_canon;
392   int sread;
393   char buf[4096];
394   int save_errno = 0;
395   int old_pool = store_pool;
396
397   dkim_domain = expand_string(dkim_domain);
398   if (dkim_domain == NULL) {
399     /* expansion error, do not send message. */
400     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
401           "dkim_domain: %s", expand_string_message);
402     rc = NULL;
403     goto CLEANUP;
404   }
405   /* Set up $dkim_domain expansion variable. */
406   dkim_signing_domain = dkim_domain;
407
408   /* Get selector to use. */
409   dkim_selector = expand_string(dkim_selector);
410   if (dkim_selector == NULL) {
411     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
412       "dkim_selector: %s", expand_string_message);
413     rc = NULL;
414     goto CLEANUP;
415   }
416   /* Set up $dkim_selector expansion variable. */
417   dkim_signing_selector = dkim_selector;
418
419   /* Get canonicalization to use */
420   dkim_canon = expand_string(dkim_canon?dkim_canon:US"relaxed");
421   if (dkim_canon == NULL) {
422     /* expansion error, do not send message. */
423     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
424           "dkim_canon: %s", expand_string_message);
425     rc = NULL;
426     goto CLEANUP;
427   }
428   if (Ustrcmp(dkim_canon, "relaxed") == 0)
429     pdkim_canon = PDKIM_CANON_RELAXED;
430   else if (Ustrcmp(dkim_canon, "simple") == 0)
431     pdkim_canon = PDKIM_CANON_RELAXED;
432   else {
433     log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon);
434     pdkim_canon = PDKIM_CANON_RELAXED;
435   }
436
437   /* Expand signing headers once */
438   if (dkim_sign_headers != NULL) {
439     dkim_sign_headers = expand_string(dkim_sign_headers);
440     if (dkim_sign_headers == NULL) {
441       log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
442         "dkim_sign_headers: %s", expand_string_message);
443       rc = NULL;
444       goto CLEANUP;
445     }
446   }
447
448   /* Get private key to use. */
449   dkim_private_key = expand_string(dkim_private_key);
450   if (dkim_private_key == NULL) {
451     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
452       "dkim_private_key: %s", expand_string_message);
453     rc = NULL;
454     goto CLEANUP;
455   }
456   if ( (Ustrlen(dkim_private_key) == 0) ||
457        (Ustrcmp(dkim_private_key,"0") == 0) ||
458        (Ustrcmp(dkim_private_key,"false") == 0) ) {
459     /* don't sign, but no error */
460     rc = US"";
461     goto CLEANUP;
462   }
463
464   if (dkim_private_key[0] == '/') {
465     int privkey_fd = 0;
466     /* Looks like a filename, load the private key. */
467     memset(big_buffer,0,big_buffer_size);
468     privkey_fd = open(CS dkim_private_key,O_RDONLY);
469     if (privkey_fd < 0) {
470       log_write(0, LOG_MAIN|LOG_PANIC, "unable to open "
471         "private key file for reading: %s", dkim_private_key);
472       rc = NULL;
473       goto CLEANUP;
474     }
475     (void)read(privkey_fd,big_buffer,(big_buffer_size-2));
476     (void)close(privkey_fd);
477     dkim_private_key = big_buffer;
478   }
479
480   ctx = pdkim_init_sign(PDKIM_INPUT_SMTP,
481                         (char *)dkim_signing_domain,
482                         (char *)dkim_signing_selector,
483                         (char *)dkim_private_key
484                        );
485
486   pdkim_set_debug_stream(ctx,debug_file);
487
488   pdkim_set_optional(ctx,
489                      (char *)dkim_sign_headers,
490                      NULL,
491                      pdkim_canon,
492                      pdkim_canon,
493                      -1,
494                      PDKIM_ALGO_RSA_SHA256,
495                      0,
496                      0);
497
498   while((sread = read(dkim_fd,&buf,4096)) > 0) {
499     if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) {
500       rc = NULL;
501       goto CLEANUP;
502     }
503   }
504   /* Handle failed read above. */
505   if (sread == -1) {
506     debug_printf("DKIM: Error reading -K file.\n");
507     save_errno = errno;
508     rc = NULL;
509     goto CLEANUP;
510   }
511
512   if (pdkim_feed_finish(ctx,&signature) != PDKIM_OK)
513     goto CLEANUP;
514
515   rc = store_get(strlen(signature->signature_header)+3);
516   Ustrcpy(rc,US signature->signature_header);
517   Ustrcat(rc,US"\r\n");
518
519   CLEANUP:
520   if (ctx != NULL) {
521     pdkim_free_ctx(ctx);
522   }
523   store_pool = old_pool;
524   errno = save_errno;
525   return rc;
526 };
527
528 #endif