1 /* $Cambridge: exim/src/src/dkim.c,v 1.3 2009/08/31 21:14:50 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 switch(dkim_cur_sig->algo) {
239 case PDKIM_ALGO_RSA_SHA1:
241 case PDKIM_ALGO_RSA_SHA256:
243 return US"rsa-sha256";
245 case DKIM_BODYLENGTH:
246 return (dkim_cur_sig->bodylength >= 0)?
247 (uschar *)string_sprintf(OFF_T_FMT,(LONGLONG_T)dkim_cur_sig->bodylength)
248 :dkim_exim_expand_defaults(what);
249 case DKIM_CANON_BODY:
250 switch(dkim_cur_sig->canon_body) {
251 case PDKIM_CANON_RELAXED:
253 case PDKIM_CANON_SIMPLE:
257 case DKIM_CANON_HEADERS:
258 switch(dkim_cur_sig->canon_headers) {
259 case PDKIM_CANON_RELAXED:
261 case PDKIM_CANON_SIMPLE:
265 case DKIM_COPIEDHEADERS:
266 return dkim_cur_sig->copiedheaders?
267 (uschar *)(dkim_cur_sig->copiedheaders)
268 :dkim_exim_expand_defaults(what);
270 return (dkim_cur_sig->created > 0)?
271 (uschar *)string_sprintf("%llu",dkim_cur_sig->created)
272 :dkim_exim_expand_defaults(what);
274 return (dkim_cur_sig->expires > 0)?
275 (uschar *)string_sprintf("%llu",dkim_cur_sig->expires)
276 :dkim_exim_expand_defaults(what);
277 case DKIM_HEADERNAMES:
278 return dkim_cur_sig->headernames?
279 (uschar *)(dkim_cur_sig->headernames)
280 :dkim_exim_expand_defaults(what);
282 return dkim_cur_sig->identity?
283 (uschar *)(dkim_cur_sig->identity)
284 :dkim_exim_expand_defaults(what);
285 case DKIM_KEY_GRANULARITY:
286 return dkim_cur_sig->pubkey?
287 (dkim_cur_sig->pubkey->granularity?
288 (uschar *)(dkim_cur_sig->pubkey->granularity)
289 :dkim_exim_expand_defaults(what)
291 :dkim_exim_expand_defaults(what);
292 case DKIM_KEY_SRVTYPE:
293 return dkim_cur_sig->pubkey?
294 (dkim_cur_sig->pubkey->srvtype?
295 (uschar *)(dkim_cur_sig->pubkey->srvtype)
296 :dkim_exim_expand_defaults(what)
298 :dkim_exim_expand_defaults(what);
300 return dkim_cur_sig->pubkey?
301 (dkim_cur_sig->pubkey->notes?
302 (uschar *)(dkim_cur_sig->pubkey->notes)
303 :dkim_exim_expand_defaults(what)
305 :dkim_exim_expand_defaults(what);
306 case DKIM_KEY_TESTING:
307 return dkim_cur_sig->pubkey?
308 (dkim_cur_sig->pubkey->testing?
310 :dkim_exim_expand_defaults(what)
312 :dkim_exim_expand_defaults(what);
313 case DKIM_NOSUBDOMAINS:
314 return dkim_cur_sig->pubkey?
315 (dkim_cur_sig->pubkey->no_subdomaining?
317 :dkim_exim_expand_defaults(what)
319 :dkim_exim_expand_defaults(what);
320 case DKIM_VERIFY_STATUS:
321 switch(dkim_cur_sig->verify_status) {
322 case PDKIM_VERIFY_INVALID:
324 case PDKIM_VERIFY_FAIL:
326 case PDKIM_VERIFY_PASS:
328 case PDKIM_VERIFY_NONE:
332 case DKIM_VERIFY_REASON:
333 switch (dkim_cur_sig->verify_ext_status) {
334 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
335 return US"pubkey_unavailable";
336 case PDKIM_VERIFY_INVALID_PUBKEY_PARSING:
337 return US"pubkey_syntax";
338 case PDKIM_VERIFY_FAIL_BODY:
339 return US"bodyhash_mismatch";
340 case PDKIM_VERIFY_FAIL_MESSAGE:
341 return US"signature_incorrect";
349 uschar *dkim_exim_expand_defaults(int what) {
351 case DKIM_ALGO: return US"";
352 case DKIM_BODYLENGTH: return US"9999999999999";
353 case DKIM_CANON_BODY: return US"";
354 case DKIM_CANON_HEADERS: return US"";
355 case DKIM_COPIEDHEADERS: return US"";
356 case DKIM_CREATED: return US"0";
357 case DKIM_EXPIRES: return US"9999999999999";
358 case DKIM_HEADERNAMES: return US"";
359 case DKIM_IDENTITY: return US"";
360 case DKIM_KEY_GRANULARITY: return US"*";
361 case DKIM_KEY_SRVTYPE: return US"*";
362 case DKIM_KEY_NOTES: return US"";
363 case DKIM_KEY_TESTING: return US"0";
364 case DKIM_NOSUBDOMAINS: return US"0";
365 case DKIM_VERIFY_STATUS: return US"none";
366 case DKIM_VERIFY_REASON: return US"";
367 default: return US"";
372 uschar *dkim_exim_sign(int dkim_fd,
373 uschar *dkim_private_key,
375 uschar *dkim_selector,
377 uschar *dkim_sign_headers) {
378 pdkim_ctx *ctx = NULL;
380 pdkim_signature *signature;
385 int old_pool = store_pool;
387 dkim_domain = expand_string(dkim_domain);
388 if (dkim_domain == NULL) {
389 /* expansion error, do not send message. */
390 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
391 "dkim_domain: %s", expand_string_message);
395 /* Set up $dkim_domain expansion variable. */
396 dkim_signing_domain = dkim_domain;
398 /* Get selector to use. */
399 dkim_selector = expand_string(dkim_selector);
400 if (dkim_selector == NULL) {
401 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
402 "dkim_selector: %s", expand_string_message);
406 /* Set up $dkim_selector expansion variable. */
407 dkim_signing_selector = dkim_selector;
409 /* Get canonicalization to use */
410 dkim_canon = expand_string(dkim_canon?dkim_canon:US"relaxed");
411 if (dkim_canon == NULL) {
412 /* expansion error, do not send message. */
413 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
414 "dkim_canon: %s", expand_string_message);
418 if (Ustrcmp(dkim_canon, "relaxed") == 0)
419 pdkim_canon = PDKIM_CANON_RELAXED;
420 else if (Ustrcmp(dkim_canon, "simple") == 0)
421 pdkim_canon = PDKIM_CANON_RELAXED;
423 log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon);
424 pdkim_canon = PDKIM_CANON_RELAXED;
427 /* Expand signing headers once */
428 if (dkim_sign_headers != NULL) {
429 dkim_sign_headers = expand_string(dkim_sign_headers);
430 if (dkim_sign_headers == NULL) {
431 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
432 "dkim_sign_headers: %s", expand_string_message);
438 /* Get private key to use. */
439 dkim_private_key = expand_string(dkim_private_key);
440 if (dkim_private_key == NULL) {
441 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
442 "dkim_private_key: %s", expand_string_message);
446 if ( (Ustrlen(dkim_private_key) == 0) ||
447 (Ustrcmp(dkim_private_key,"0") == 0) ||
448 (Ustrcmp(dkim_private_key,"false") == 0) ) {
449 /* don't sign, but no error */
454 if (dkim_private_key[0] == '/') {
456 /* Looks like a filename, load the private key. */
457 memset(big_buffer,0,big_buffer_size);
458 privkey_fd = open(CS dkim_private_key,O_RDONLY);
459 (void)read(privkey_fd,big_buffer,16383);
460 (void)close(privkey_fd);
461 dkim_private_key = big_buffer;
464 ctx = pdkim_init_sign(PDKIM_INPUT_SMTP,
465 (char *)dkim_signing_domain,
466 (char *)dkim_signing_selector,
467 (char *)dkim_private_key
470 pdkim_set_debug_stream(ctx,debug_file);
472 pdkim_set_optional(ctx,
473 (char *)dkim_sign_headers,
478 PDKIM_ALGO_RSA_SHA256,
482 while((sread = read(dkim_fd,&buf,4096)) > 0) {
483 if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) {
488 /* Handle failed read above. */
490 debug_printf("DKIM: Error reading -K file.\n");
496 if (pdkim_feed_finish(ctx,&signature) != PDKIM_OK)
499 rc = store_get(strlen(signature->signature_header)+3);
500 Ustrcpy(rc,US signature->signature_header);
501 Ustrcat(rc,US"\r\n");
507 store_pool = old_pool;