1 /* $Cambridge: exim/src/src/dkim.c,v 1.2 2009/06/10 07:34:04 tom Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) University of Cambridge 2009 */
8 /* See the file NOTICE for conditions of use and distribution. */
10 /* Code for DKIM support. Other DKIM relevant code is in
11 receive.c, transport.c and transports/smtp.c */
17 #include "pdkim/pdkim.h"
19 pdkim_ctx *dkim_verify_ctx = NULL;
20 pdkim_signature *dkim_signatures = NULL;
21 pdkim_signature *dkim_cur_sig = NULL;
23 int dkim_exim_query_dns_txt(char *name, char *answer) {
28 if (dns_lookup(&dnsa, (uschar *)name, T_TXT, NULL) != DNS_SUCCEED) return PDKIM_FAIL;
30 /* Search for TXT record */
31 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
33 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
34 if (rr->type == T_TXT) break;
36 /* Copy record content to the answer buffer */
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));
49 else return PDKIM_FAIL;
55 void dkim_exim_verify_init(void) {
57 /* Free previous context if there is one */
58 if (dkim_verify_ctx) pdkim_free_ctx(dkim_verify_ctx);
60 /* Create new context */
61 dkim_verify_ctx = pdkim_init_verify(PDKIM_INPUT_SMTP,
62 &dkim_exim_query_dns_txt
65 if (dkim_verify_ctx != NULL) {
66 dkim_collect_input = TRUE;
67 pdkim_set_debug_stream(dkim_verify_ctx,debug_file);
69 else dkim_collect_input = FALSE;
74 void dkim_exim_verify_feed(uschar *data, int len) {
75 if (dkim_collect_input &&
76 pdkim_feed(dkim_verify_ctx,
78 len) != PDKIM_OK) dkim_collect_input = FALSE;
82 void dkim_exim_verify_finish(void) {
83 pdkim_signature *sig = NULL;
84 int dkim_signing_domains_size = 0;
85 int dkim_signing_domains_ptr = 0;
86 dkim_signing_domains = NULL;
88 /* Delete eventual previous signature chain */
89 dkim_signatures = NULL;
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;
99 dkim_collect_input = FALSE;
101 /* Finish DKIM operation and fetch link to signatures chain */
102 if (pdkim_feed_finish(dkim_verify_ctx,&dkim_signatures) != PDKIM_OK) return;
104 sig = dkim_signatures;
105 while (sig != NULL) {
108 /* Log a line for each signature */
109 uschar *logmsg = string_append(NULL, &size, &ptr, 5,
111 string_sprintf( "DKIM: d=%s s=%s c=%s/%s a=%s ",
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"
118 ((sig->identity != NULL)?
119 string_sprintf("i=%s ", sig->identity)
124 string_sprintf("t=%lu ", sig->created)
129 string_sprintf("x=%lu ", sig->expires)
133 ((sig->bodylength > -1)?
134 string_sprintf("l=%lu ", sig->bodylength)
140 switch(sig->verify_status) {
141 case PDKIM_VERIFY_NONE:
142 logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]");
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]");
150 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
151 logmsg = string_append(logmsg, &size, &ptr, 1, "overlong public key record]");
153 case PDKIM_VERIFY_INVALID_PUBKEY_PARSING:
154 logmsg = string_append(logmsg, &size, &ptr, 1, "syntax error in public key record]");
157 logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified problem]");
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)]");
166 case PDKIM_VERIFY_FAIL_MESSAGE:
167 logmsg = string_append(logmsg, &size, &ptr, 1, "signature did not verify (headers probably modified in transit)]");
170 logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]");
173 case PDKIM_VERIFY_PASS:
174 logmsg = string_append(logmsg, &size, &ptr, 1, "[verification succeeded]");
179 log_write(0, LOG_MAIN, (char *)logmsg);
181 /* Build a colon-separated list of signing domains in dkim_signing_domains */
182 dkim_signing_domains = string_append(dkim_signing_domains,
183 &dkim_signing_domains_size,
184 &dkim_signing_domains_ptr,
190 /* Process next signature */
194 /* Chop the last colon from the domain list */
195 if ((dkim_signing_domains != NULL) &&
196 (Ustrlen(dkim_signing_domains) > 0))
197 dkim_signing_domains[Ustrlen(dkim_signing_domains)-1] = '\0';
201 void dkim_exim_acl_setup(uschar *id) {
202 pdkim_signature *sig = dkim_signatures;
204 if (dkim_disable_verify ||
206 !dkim_verify_ctx) return;
207 /* Find signature to run ACL on */
208 while (sig != NULL) {
209 uschar *cmp_val = NULL;
210 if (Ustrchr(id,'@') != NULL) cmp_val = (uschar *)sig->identity;
211 else cmp_val = (uschar *)sig->domain;
212 if (cmp_val && (strcmpic(cmp_val,id) == 0)) {
214 /* The "dkim_domain" and "dkim_selector" expansion variables have
215 related globals, since they are used in the signing code too.
216 Instead of inventing separate names for verification, we set
217 them here. This is easy since a domain and selector is guaranteed
218 to be in a signature. The other dkim_* expansion items are
219 dynamically fetched from dkim_cur_sig at expansion time (see
221 dkim_signing_domain = (uschar *)sig->domain;
222 dkim_signing_selector = (uschar *)sig->selector;
230 uschar *dkim_exim_expand_query(int what) {
232 if (!dkim_verify_ctx ||
233 dkim_disable_verify ||
234 !dkim_cur_sig) return dkim_exim_expand_defaults(what);
238 return dkim_cur_sig->algo?
239 (uschar *)(dkim_cur_sig->algo)
240 :dkim_exim_expand_defaults(what);
241 case DKIM_BODYLENGTH:
242 return (dkim_cur_sig->bodylength >= 0)?
243 (uschar *)string_sprintf(OFF_T_FMT,(LONGLONG_T)dkim_cur_sig->bodylength)
244 :dkim_exim_expand_defaults(what);
245 case DKIM_CANON_BODY:
246 return dkim_cur_sig->canon_body?
247 (uschar *)(dkim_cur_sig->canon_body)
248 :dkim_exim_expand_defaults(what);
249 case DKIM_CANON_HEADERS:
250 return dkim_cur_sig->canon_headers?
251 (uschar *)(dkim_cur_sig->canon_headers)
252 :dkim_exim_expand_defaults(what);
253 case DKIM_COPIEDHEADERS:
254 return dkim_cur_sig->copiedheaders?
255 (uschar *)(dkim_cur_sig->copiedheaders)
256 :dkim_exim_expand_defaults(what);
258 return (dkim_cur_sig->created > 0)?
259 (uschar *)string_sprintf("%llu",dkim_cur_sig->created)
260 :dkim_exim_expand_defaults(what);
262 return (dkim_cur_sig->expires > 0)?
263 (uschar *)string_sprintf("%llu",dkim_cur_sig->expires)
264 :dkim_exim_expand_defaults(what);
265 case DKIM_HEADERNAMES:
266 return dkim_cur_sig->headernames?
267 (uschar *)(dkim_cur_sig->headernames)
268 :dkim_exim_expand_defaults(what);
270 return dkim_cur_sig->identity?
271 (uschar *)(dkim_cur_sig->identity)
272 :dkim_exim_expand_defaults(what);
273 case DKIM_KEY_GRANULARITY:
274 return dkim_cur_sig->pubkey?
275 (dkim_cur_sig->pubkey->granularity?
276 (uschar *)(dkim_cur_sig->pubkey->granularity)
277 :dkim_exim_expand_defaults(what)
279 :dkim_exim_expand_defaults(what);
280 case DKIM_KEY_SRVTYPE:
281 return dkim_cur_sig->pubkey?
282 (dkim_cur_sig->pubkey->srvtype?
283 (uschar *)(dkim_cur_sig->pubkey->srvtype)
284 :dkim_exim_expand_defaults(what)
286 :dkim_exim_expand_defaults(what);
288 return dkim_cur_sig->pubkey?
289 (dkim_cur_sig->pubkey->notes?
290 (uschar *)(dkim_cur_sig->pubkey->notes)
291 :dkim_exim_expand_defaults(what)
293 :dkim_exim_expand_defaults(what);
294 case DKIM_KEY_TESTING:
295 return dkim_cur_sig->pubkey?
296 (dkim_cur_sig->pubkey->testing?
298 :dkim_exim_expand_defaults(what)
300 :dkim_exim_expand_defaults(what);
301 case DKIM_NOSUBDOMAINS:
302 return dkim_cur_sig->pubkey?
303 (dkim_cur_sig->pubkey->no_subdomaining?
305 :dkim_exim_expand_defaults(what)
307 :dkim_exim_expand_defaults(what);
308 case DKIM_VERIFY_STATUS:
309 switch(dkim_cur_sig->verify_status) {
310 case PDKIM_VERIFY_INVALID:
312 case PDKIM_VERIFY_FAIL:
314 case PDKIM_VERIFY_PASS:
316 case PDKIM_VERIFY_NONE:
320 case DKIM_VERIFY_REASON:
321 switch (dkim_cur_sig->verify_ext_status) {
322 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
323 return US"pubkey_unavailable";
324 case PDKIM_VERIFY_INVALID_PUBKEY_PARSING:
325 return US"pubkey_syntax";
326 case PDKIM_VERIFY_FAIL_BODY:
327 return US"bodyhash_mismatch";
328 case PDKIM_VERIFY_FAIL_MESSAGE:
329 return US"signature_incorrect";
337 uschar *dkim_exim_expand_defaults(int what) {
339 case DKIM_ALGO: return US"";
340 case DKIM_BODYLENGTH: return US"9999999999999";
341 case DKIM_CANON_BODY: return US"";
342 case DKIM_CANON_HEADERS: return US"";
343 case DKIM_COPIEDHEADERS: return US"";
344 case DKIM_CREATED: return US"0";
345 case DKIM_EXPIRES: return US"9999999999999";
346 case DKIM_HEADERNAMES: return US"";
347 case DKIM_IDENTITY: return US"";
348 case DKIM_KEY_GRANULARITY: return US"*";
349 case DKIM_KEY_SRVTYPE: return US"*";
350 case DKIM_KEY_NOTES: return US"";
351 case DKIM_KEY_TESTING: return US"0";
352 case DKIM_NOSUBDOMAINS: return US"0";
353 case DKIM_VERIFY_STATUS: return US"none";
354 case DKIM_VERIFY_REASON: return US"";
355 default: return US"";
360 uschar *dkim_exim_sign(int dkim_fd,
361 uschar *dkim_private_key,
363 uschar *dkim_selector,
365 uschar *dkim_sign_headers) {
366 pdkim_ctx *ctx = NULL;
368 pdkim_signature *signature;
373 int old_pool = store_pool;
375 dkim_domain = expand_string(dkim_domain);
376 if (dkim_domain == NULL) {
377 /* expansion error, do not send message. */
378 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
379 "dkim_domain: %s", expand_string_message);
383 /* Set up $dkim_domain expansion variable. */
384 dkim_signing_domain = dkim_domain;
386 /* Get selector to use. */
387 dkim_selector = expand_string(dkim_selector);
388 if (dkim_selector == NULL) {
389 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
390 "dkim_selector: %s", expand_string_message);
394 /* Set up $dkim_selector expansion variable. */
395 dkim_signing_selector = dkim_selector;
397 /* Get canonicalization to use */
398 dkim_canon = expand_string(dkim_canon?dkim_canon:US"relaxed");
399 if (dkim_canon == NULL) {
400 /* expansion error, do not send message. */
401 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
402 "dkim_canon: %s", expand_string_message);
406 if (Ustrcmp(dkim_canon, "relaxed") == 0)
407 pdkim_canon = PDKIM_CANON_RELAXED;
408 else if (Ustrcmp(dkim_canon, "simple") == 0)
409 pdkim_canon = PDKIM_CANON_RELAXED;
411 log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon);
412 pdkim_canon = PDKIM_CANON_RELAXED;
415 /* Expand signing headers once */
416 if (dkim_sign_headers != NULL) {
417 dkim_sign_headers = expand_string(dkim_sign_headers);
418 if (dkim_sign_headers == NULL) {
419 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
420 "dkim_sign_headers: %s", expand_string_message);
426 /* Get private key to use. */
427 dkim_private_key = expand_string(dkim_private_key);
428 if (dkim_private_key == NULL) {
429 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
430 "dkim_private_key: %s", expand_string_message);
434 if ( (Ustrlen(dkim_private_key) == 0) ||
435 (Ustrcmp(dkim_private_key,"0") == 0) ||
436 (Ustrcmp(dkim_private_key,"false") == 0) ) {
437 /* don't sign, but no error */
442 if (dkim_private_key[0] == '/') {
444 /* Looks like a filename, load the private key. */
445 memset(big_buffer,0,big_buffer_size);
446 privkey_fd = open(CS dkim_private_key,O_RDONLY);
447 (void)read(privkey_fd,big_buffer,16383);
448 (void)close(privkey_fd);
449 dkim_private_key = big_buffer;
452 ctx = pdkim_init_sign(PDKIM_INPUT_SMTP,
453 (char *)dkim_signing_domain,
454 (char *)dkim_signing_selector,
455 (char *)dkim_private_key
458 pdkim_set_debug_stream(ctx,debug_file);
460 pdkim_set_optional(ctx,
461 (char *)dkim_sign_headers,
466 PDKIM_ALGO_RSA_SHA256,
470 while((sread = read(dkim_fd,&buf,4096)) > 0) {
471 if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) {
476 /* Handle failed read above. */
478 debug_printf("DKIM: Error reading -K file.\n");
484 if (pdkim_feed_finish(ctx,&signature) != PDKIM_OK)
487 rc = store_get(strlen(signature->signature_header)+3);
488 Ustrcpy(rc,US signature->signature_header);
489 Ustrcat(rc,US"\r\n");
495 store_pool = old_pool;