Merge 4.80.1 security fix in.
[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   if (dns_lookup(&dnsa, (uschar *)name, T_TXT, NULL) != DNS_SUCCEED) return PDKIM_FAIL;
27
28   /* Search for TXT record */
29   for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
30        rr != NULL;
31        rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
32     if (rr->type == T_TXT) break;
33
34   /* Copy record content to the answer buffer */
35   if (rr != NULL) {
36     int rr_offset = 0;
37     int answer_offset = 0;
38     while (rr_offset < rr->size) {
39       uschar len = (rr->data)[rr_offset++];
40       snprintf(answer+(answer_offset),
41                PDKIM_DNS_TXT_MAX_RECLEN-(answer_offset),
42                "%.*s", (int)len, (char *)((rr->data)+rr_offset));
43       rr_offset+=len;
44       answer_offset+=len;
45       if (answer_offset >= PDKIM_DNS_TXT_MAX_RECLEN) {
46         return PDKIM_FAIL;
47       }
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, "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( "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, "DKIM: %s", 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   /* NULL-terminate and chop the last colon from the domain list */
206   if (dkim_signers != NULL) {
207     dkim_signers[dkim_signers_ptr] = '\0';
208     if (Ustrlen(dkim_signers) > 0)
209       dkim_signers[Ustrlen(dkim_signers)-1] = '\0';
210   }
211 }
212
213
214 void dkim_exim_acl_setup(uschar *id) {
215   pdkim_signature *sig = dkim_signatures;
216   dkim_cur_sig = NULL;
217   dkim_cur_signer = id;
218   if (dkim_disable_verify ||
219       !id || !dkim_verify_ctx) return;
220   /* Find signature to run ACL on */
221   while (sig != NULL) {
222     uschar *cmp_val = NULL;
223     if (Ustrchr(id,'@') != NULL) cmp_val = (uschar *)sig->identity;
224                             else cmp_val = (uschar *)sig->domain;
225     if (cmp_val && (strcmpic(cmp_val,id) == 0)) {
226       dkim_cur_sig = sig;
227       /* The "dkim_domain" and "dkim_selector" expansion variables have
228          related globals, since they are used in the signing code too.
229          Instead of inventing separate names for verification, we set
230          them here. This is easy since a domain and selector is guaranteed
231          to be in a signature. The other dkim_* expansion items are
232          dynamically fetched from dkim_cur_sig at expansion time (see
233          function below). */
234       dkim_signing_domain   = (uschar *)sig->domain;
235       dkim_signing_selector = (uschar *)sig->selector;
236       return;
237     }
238     sig = sig->next;
239   }
240 }
241
242
243 uschar *dkim_exim_expand_query(int what) {
244
245   if (!dkim_verify_ctx ||
246       dkim_disable_verify ||
247       !dkim_cur_sig) return dkim_exim_expand_defaults(what);
248
249   switch(what) {
250     case DKIM_ALGO:
251       switch(dkim_cur_sig->algo) {
252         case PDKIM_ALGO_RSA_SHA1:
253           return US"rsa-sha1";
254         case PDKIM_ALGO_RSA_SHA256:
255         default:
256           return US"rsa-sha256";
257       }
258     case DKIM_BODYLENGTH:
259       return (dkim_cur_sig->bodylength >= 0)?
260               (uschar *)string_sprintf(OFF_T_FMT,(LONGLONG_T)dkim_cur_sig->bodylength)
261               :dkim_exim_expand_defaults(what);
262     case DKIM_CANON_BODY:
263       switch(dkim_cur_sig->canon_body) {
264         case PDKIM_CANON_RELAXED:
265           return US"relaxed";
266         case PDKIM_CANON_SIMPLE:
267         default:
268           return US"simple";
269       }
270     case DKIM_CANON_HEADERS:
271       switch(dkim_cur_sig->canon_headers) {
272         case PDKIM_CANON_RELAXED:
273           return US"relaxed";
274         case PDKIM_CANON_SIMPLE:
275         default:
276           return US"simple";
277       }
278     case DKIM_COPIEDHEADERS:
279       return dkim_cur_sig->copiedheaders?
280               (uschar *)(dkim_cur_sig->copiedheaders)
281               :dkim_exim_expand_defaults(what);
282     case DKIM_CREATED:
283       return (dkim_cur_sig->created > 0)?
284               (uschar *)string_sprintf("%llu",dkim_cur_sig->created)
285               :dkim_exim_expand_defaults(what);
286     case DKIM_EXPIRES:
287       return (dkim_cur_sig->expires > 0)?
288               (uschar *)string_sprintf("%llu",dkim_cur_sig->expires)
289               :dkim_exim_expand_defaults(what);
290     case DKIM_HEADERNAMES:
291       return dkim_cur_sig->headernames?
292               (uschar *)(dkim_cur_sig->headernames)
293               :dkim_exim_expand_defaults(what);
294     case DKIM_IDENTITY:
295       return dkim_cur_sig->identity?
296               (uschar *)(dkim_cur_sig->identity)
297               :dkim_exim_expand_defaults(what);
298     case DKIM_KEY_GRANULARITY:
299       return dkim_cur_sig->pubkey?
300               (dkim_cur_sig->pubkey->granularity?
301                 (uschar *)(dkim_cur_sig->pubkey->granularity)
302                 :dkim_exim_expand_defaults(what)
303               )
304               :dkim_exim_expand_defaults(what);
305     case DKIM_KEY_SRVTYPE:
306       return dkim_cur_sig->pubkey?
307               (dkim_cur_sig->pubkey->srvtype?
308                 (uschar *)(dkim_cur_sig->pubkey->srvtype)
309                 :dkim_exim_expand_defaults(what)
310               )
311               :dkim_exim_expand_defaults(what);
312     case DKIM_KEY_NOTES:
313       return dkim_cur_sig->pubkey?
314               (dkim_cur_sig->pubkey->notes?
315                 (uschar *)(dkim_cur_sig->pubkey->notes)
316                 :dkim_exim_expand_defaults(what)
317               )
318               :dkim_exim_expand_defaults(what);
319     case DKIM_KEY_TESTING:
320       return dkim_cur_sig->pubkey?
321               (dkim_cur_sig->pubkey->testing?
322                 US"1"
323                 :dkim_exim_expand_defaults(what)
324               )
325               :dkim_exim_expand_defaults(what);
326     case DKIM_NOSUBDOMAINS:
327       return dkim_cur_sig->pubkey?
328               (dkim_cur_sig->pubkey->no_subdomaining?
329                 US"1"
330                 :dkim_exim_expand_defaults(what)
331               )
332               :dkim_exim_expand_defaults(what);
333     case DKIM_VERIFY_STATUS:
334       switch(dkim_cur_sig->verify_status) {
335         case PDKIM_VERIFY_INVALID:
336           return US"invalid";
337         case PDKIM_VERIFY_FAIL:
338           return US"fail";
339         case PDKIM_VERIFY_PASS:
340           return US"pass";
341         case PDKIM_VERIFY_NONE:
342         default:
343           return US"none";
344       }
345     case DKIM_VERIFY_REASON:
346       switch (dkim_cur_sig->verify_ext_status) {
347         case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
348           return US"pubkey_unavailable";
349         case PDKIM_VERIFY_INVALID_PUBKEY_PARSING:
350           return US"pubkey_syntax";
351         case PDKIM_VERIFY_FAIL_BODY:
352           return US"bodyhash_mismatch";
353         case PDKIM_VERIFY_FAIL_MESSAGE:
354           return US"signature_incorrect";
355       }
356     default:
357       return US"";
358   }
359 }
360
361
362 uschar *dkim_exim_expand_defaults(int what) {
363   switch(what) {
364     case DKIM_ALGO:               return US"";
365     case DKIM_BODYLENGTH:         return US"9999999999999";
366     case DKIM_CANON_BODY:         return US"";
367     case DKIM_CANON_HEADERS:      return US"";
368     case DKIM_COPIEDHEADERS:      return US"";
369     case DKIM_CREATED:            return US"0";
370     case DKIM_EXPIRES:            return US"9999999999999";
371     case DKIM_HEADERNAMES:        return US"";
372     case DKIM_IDENTITY:           return US"";
373     case DKIM_KEY_GRANULARITY:    return US"*";
374     case DKIM_KEY_SRVTYPE:        return US"*";
375     case DKIM_KEY_NOTES:          return US"";
376     case DKIM_KEY_TESTING:        return US"0";
377     case DKIM_NOSUBDOMAINS:       return US"0";
378     case DKIM_VERIFY_STATUS:      return US"none";
379     case DKIM_VERIFY_REASON:      return US"";
380     default:                      return US"";
381   }
382 }
383
384
385 uschar *dkim_exim_sign(int dkim_fd,
386                        uschar *dkim_private_key,
387                        uschar *dkim_domain,
388                        uschar *dkim_selector,
389                        uschar *dkim_canon,
390                        uschar *dkim_sign_headers) {
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_string(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       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       (void)read(privkey_fd,big_buffer,(big_buffer_size-2));
508       (void)close(privkey_fd);
509       dkim_private_key_expanded = big_buffer;
510     }
511
512     ctx = pdkim_init_sign(PDKIM_INPUT_SMTP,
513                           (char *)dkim_signing_domain,
514                           (char *)dkim_signing_selector,
515                           (char *)dkim_private_key_expanded
516                          );
517
518     pdkim_set_debug_stream(ctx,debug_file);
519
520     pdkim_set_optional(ctx,
521                        (char *)dkim_sign_headers_expanded,
522                        NULL,
523                        pdkim_canon,
524                        pdkim_canon,
525                        -1,
526                        PDKIM_ALGO_RSA_SHA256,
527                        0,
528                        0);
529
530     lseek(dkim_fd, 0, SEEK_SET);
531     while((sread = read(dkim_fd,&buf,4096)) > 0) {
532       if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) {
533         rc = NULL;
534         goto CLEANUP;
535       }
536     }
537     /* Handle failed read above. */
538     if (sread == -1) {
539       debug_printf("DKIM: Error reading -K file.\n");
540       save_errno = errno;
541       rc = NULL;
542       goto CLEANUP;
543     }
544
545     pdkim_rc = pdkim_feed_finish(ctx,&signature);
546     if (pdkim_rc != PDKIM_OK) {
547       log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: signing failed (RC %d)", pdkim_rc);
548       rc = NULL;
549       goto CLEANUP;
550     }
551
552     sigbuf = string_append(sigbuf, &sigsize, &sigptr, 2,
553                            US signature->signature_header,
554                            US"\r\n");
555
556     pdkim_free_ctx(ctx);
557     ctx = NULL;
558   }
559
560   if (sigbuf != NULL) {
561     sigbuf[sigptr] = '\0';
562     rc = sigbuf;
563   } else
564     rc = US"";
565
566   CLEANUP:
567   if (ctx != NULL)
568     pdkim_free_ctx(ctx);
569   store_pool = old_pool;
570   errno = save_errno;
571   return rc;
572 }
573
574 #endif