1 /* $Cambridge: exim/src/src/dkim.c,v 1.1.2.13 2009/05/27 17:26:54 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;
22 int dkim_exim_query_dns_txt(char *name, char *answer) {
27 if (dns_lookup(&dnsa, (uschar *)name, T_TXT, NULL) != DNS_SUCCEED) return PDKIM_FAIL;
29 /* Search for TXT record */
30 for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
32 rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
33 if (rr->type == T_TXT) break;
35 /* Copy record content to the answer buffer */
38 int answer_offset = 0;
39 while (rr_offset < rr->size) {
40 uschar len = (rr->data)[rr_offset++];
41 snprintf(answer+(answer_offset),
42 PDKIM_DNS_TXT_MAX_RECLEN-(answer_offset),
43 "%.*s", (int)len, (char *)((rr->data)+rr_offset));
48 else return PDKIM_FAIL;
54 void dkim_exim_verify_init(void) {
56 /* Free previous context if there is one */
57 if (dkim_verify_ctx) pdkim_free_ctx(dkim_verify_ctx);
59 /* Create new context */
60 dkim_verify_ctx = pdkim_init_verify(PDKIM_INPUT_SMTP,
61 &dkim_exim_query_dns_txt
64 if (dkim_verify_ctx != NULL) {
65 dkim_collect_input = TRUE;
66 pdkim_set_debug_stream(dkim_verify_ctx,debug_file);
68 else dkim_collect_input = FALSE;
73 void dkim_exim_verify_feed(uschar *data, int len) {
74 if (dkim_collect_input &&
75 pdkim_feed(dkim_verify_ctx,
77 len) != PDKIM_OK) dkim_collect_input = FALSE;
81 void dkim_exim_verify_finish(void) {
82 int dkim_signing_domains_size = 0;
83 int dkim_signing_domains_ptr = 0;
84 dkim_signing_domains = NULL;
86 /* Delete eventual previous signature chain */
87 dkim_signatures = NULL;
89 /* If we have arrived here with dkim_collect_input == FALSE, it
90 means there was a processing error somewhere along the way.
91 Log the incident and disable futher verification. */
92 if (!dkim_collect_input) {
93 log_write(0, LOG_MAIN|LOG_PANIC, "DKIM: Error while running this message through validation, disabling signature verification.");
94 dkim_disable_verify = TRUE;
97 dkim_collect_input = FALSE;
99 /* Finish DKIM operation and fetch link to signatures chain */
100 if (pdkim_feed_finish(dkim_verify_ctx,&dkim_signatures) != PDKIM_OK) return;
103 while (dkim_signatures != NULL) {
106 /* Log a line for each signature */
107 uschar *logmsg = string_append(NULL, &size, &ptr, 5,
109 string_sprintf( "DKIM: d=%s s=%s c=%s/%s a=%s ",
110 dkim_signatures->domain,
111 dkim_signatures->selector,
112 (dkim_signatures->canon_headers == PDKIM_CANON_SIMPLE)?"simple":"relaxed",
113 (dkim_signatures->canon_body == PDKIM_CANON_SIMPLE)?"simple":"relaxed",
114 (dkim_signatures->algo == PDKIM_ALGO_RSA_SHA256)?"rsa-sha256":"rsa-sha1"
116 ((dkim_signatures->identity != NULL)?
117 string_sprintf("i=%s ", dkim_signatures->identity)
121 ((dkim_signatures->created > 0)?
122 string_sprintf("t=%lu ", dkim_signatures->created)
126 ((dkim_signatures->expires > 0)?
127 string_sprintf("x=%lu ", dkim_signatures->expires)
131 ((dkim_signatures->bodylength > -1)?
132 string_sprintf("l=%lu ", dkim_signatures->bodylength)
138 switch(dkim_signatures->verify_status) {
139 case PDKIM_VERIFY_NONE:
140 logmsg = string_append(logmsg, &size, &ptr, 1, "[not verified]");
142 case PDKIM_VERIFY_INVALID:
143 logmsg = string_append(logmsg, &size, &ptr, 1, "[invalid - ");
144 switch (dkim_signatures->verify_ext_status) {
145 case PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE:
146 logmsg = string_append(logmsg, &size, &ptr, 1, "public key record (currently?) unavailable]");
148 case PDKIM_VERIFY_INVALID_BUFFER_SIZE:
149 logmsg = string_append(logmsg, &size, &ptr, 1, "overlong public key record]");
151 case PDKIM_VERIFY_INVALID_PUBKEY_PARSING:
152 logmsg = string_append(logmsg, &size, &ptr, 1, "syntax error in public key record]");
155 logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified problem]");
158 case PDKIM_VERIFY_FAIL:
159 logmsg = string_append(logmsg, &size, &ptr, 1, "[verification failed - ");
160 switch (dkim_signatures->verify_ext_status) {
161 case PDKIM_VERIFY_FAIL_BODY:
162 logmsg = string_append(logmsg, &size, &ptr, 1, "body hash mismatch (body probably modified in transit)]");
164 case PDKIM_VERIFY_FAIL_MESSAGE:
165 logmsg = string_append(logmsg, &size, &ptr, 1, "signature did not verify (headers probably modified in transit)]");
168 logmsg = string_append(logmsg, &size, &ptr, 1, "unspecified reason]");
171 case PDKIM_VERIFY_PASS:
172 logmsg = string_append(logmsg, &size, &ptr, 1, "[verification succeeded]");
177 log_write(0, LOG_MAIN, (char *)logmsg);
179 /* Build a colon-separated list of signing domains in dkim_signing_domains */
180 dkim_signing_domains = string_append(dkim_signing_domains,
181 &dkim_signing_domains_size,
182 &dkim_signing_domains_ptr,
184 dkim_signatures->domain,
188 /* Process next signature */
189 dkim_signatures = dkim_signatures->next;
192 /* Chop the last colon from the domain list */
193 if ((dkim_signing_domains != NULL) &&
194 (Ustrlen(dkim_signing_domains) > 0))
195 dkim_signing_domains[strlen(dkim_signing_domains)-1] = '\0';
199 void dkim_exim_verify_result(uschar *domain, uschar **result, uschar **error) {
200 if (dkim_verify_ctx) {
206 uschar *dkim_exim_sign(int dkim_fd,
207 uschar *dkim_private_key,
209 uschar *dkim_selector,
211 uschar *dkim_sign_headers) {
212 pdkim_ctx *ctx = NULL;
214 pdkim_signature *signature;
219 int old_pool = store_pool;
221 dkim_domain = expand_string(dkim_domain);
222 if (dkim_domain == NULL) {
223 /* expansion error, do not send message. */
224 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
225 "dkim_domain: %s", expand_string_message);
229 /* Set up $dkim_domain expansion variable. */
230 dkim_signing_domain = dkim_domain;
232 /* Get selector to use. */
233 dkim_selector = expand_string(dkim_selector);
234 if (dkim_selector == NULL) {
235 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
236 "dkim_selector: %s", expand_string_message);
240 /* Set up $dkim_selector expansion variable. */
241 dkim_signing_selector = dkim_selector;
243 /* Get canonicalization to use */
244 dkim_canon = expand_string(dkim_canon?dkim_canon:US"relaxed");
245 if (dkim_canon == NULL) {
246 /* expansion error, do not send message. */
247 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
248 "dkim_canon: %s", expand_string_message);
252 if (Ustrcmp(dkim_canon, "relaxed") == 0)
253 pdkim_canon = PDKIM_CANON_RELAXED;
254 else if (Ustrcmp(dkim_canon, "simple") == 0)
255 pdkim_canon = PDKIM_CANON_RELAXED;
257 log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon);
258 pdkim_canon = PDKIM_CANON_RELAXED;
261 /* Expand signing headers once */
262 if (dkim_sign_headers != NULL) {
263 dkim_sign_headers = expand_string(dkim_sign_headers);
264 if (dkim_sign_headers == NULL) {
265 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
266 "dkim_sign_headers: %s", expand_string_message);
272 /* Get private key to use. */
273 dkim_private_key = expand_string(dkim_private_key);
274 if (dkim_private_key == NULL) {
275 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
276 "dkim_private_key: %s", expand_string_message);
280 if ( (Ustrlen(dkim_private_key) == 0) ||
281 (Ustrcmp(dkim_private_key,"0") == 0) ||
282 (Ustrcmp(dkim_private_key,"false") == 0) ) {
283 /* don't sign, but no error */
288 if (dkim_private_key[0] == '/') {
290 /* Looks like a filename, load the private key. */
291 memset(big_buffer,0,big_buffer_size);
292 privkey_fd = open(CS dkim_private_key,O_RDONLY);
293 (void)read(privkey_fd,big_buffer,16383);
294 (void)close(privkey_fd);
295 dkim_private_key = big_buffer;
298 ctx = pdkim_init_sign(PDKIM_INPUT_SMTP,
299 (char *)dkim_signing_domain,
300 (char *)dkim_signing_selector,
301 (char *)dkim_private_key
304 pdkim_set_debug_stream(ctx,debug_file);
306 pdkim_set_optional(ctx,
307 (char *)dkim_sign_headers,
312 PDKIM_ALGO_RSA_SHA256,
316 while((sread = read(dkim_fd,&buf,4096)) > 0) {
317 if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) {
322 /* Handle failed read above. */
324 debug_printf("DKIM: Error reading -K file.\n");
330 if (pdkim_feed_finish(ctx,&signature) != PDKIM_OK)
333 rc = store_get(strlen(signature->signature_header)+3);
334 Ustrcpy(rc,US signature->signature_header);
335 Ustrcat(rc,US"\r\n");
341 store_pool = old_pool;