1 /* $Cambridge: exim/src/src/dkim.c,v 1.1.2.6 2009/04/09 13:57:21 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 1;
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 */
37 int len = (rr->data)[0];
38 //if (len > 511) len = 127; // ???
39 snprintf(answer, PDKIM_DNS_TXT_MAX_RECLEN, "%.*s", len, (char *)(rr->data+1));
47 int dkim_exim_verify_init(void) {
49 /* Free previous context if there is one */
50 if (dkim_verify_ctx) pdkim_free_ctx(dkim_verify_ctx);
52 /* Create new context */
53 dkim_verify_ctx = pdkim_init_verify(PDKIM_INPUT_SMTP,
54 &dkim_exim_query_dns_txt
57 if (dkim_verify_ctx != NULL) {
58 dkim_collect_input = 1;
59 pdkim_set_debug_stream(dkim_verify_ctx,debug_file);
63 dkim_collect_input = 0;
69 int dkim_exim_verify_feed(uschar *data, int len) {
70 if (pdkim_feed(dkim_verify_ctx,
72 len) != PDKIM_OK) return 0;
77 int dkim_exim_verify_finish(void) {
78 dkim_signatures = NULL;
79 dkim_collect_input = 0;
80 if (pdkim_feed_finish(dkim_verify_ctx,&dkim_signatures) != PDKIM_OK) return 0;
82 while (dkim_signatures != NULL) {
83 debug_printf("DKIM: Signature from domain '%s': ",dkim_signatures->domain);
84 switch(dkim_signatures->verify_status) {
85 case PDKIM_VERIFY_NONE:
86 debug_printf("not verified\n");
87 log_write(0, LOG_MAIN, "DKIM: Signature from domain '%s', selector '%s': "
88 "not verified", dkim_signatures->domain, dkim_signatures->selector);
90 case PDKIM_VERIFY_INVALID:
91 debug_printf("invalid\n");
92 log_write(0, LOG_MAIN, "DKIM: Signature from domain '%s', selector '%s': "
93 "invalid", dkim_signatures->domain, dkim_signatures->selector);
95 case PDKIM_VERIFY_FAIL:
96 debug_printf("verification failed\n");
97 log_write(0, LOG_MAIN, "DKIM: Signature from domain '%s', selector '%s': "
98 "verification failed", dkim_signatures->domain, dkim_signatures->selector);
100 case PDKIM_VERIFY_PASS:
101 debug_printf("verification succeeded\n");
102 log_write(0, LOG_MAIN, "DKIM: Signature from domain '%s', selector '%s': "
103 "verification succeeded", dkim_signatures->domain, dkim_signatures->selector);
106 /* Try next signature */
107 dkim_signatures = dkim_signatures->next;
110 return dkim_signatures?1:0;
114 int dkim_exim_verify_result(uschar *domain, uschar **result, uschar **error) {
116 if (dkim_verify_ctx) {
124 uschar *dkim_exim_sign(int dkim_fd,
125 uschar *dkim_private_key,
127 uschar *dkim_selector,
129 uschar *dkim_sign_headers) {
130 pdkim_ctx *ctx = NULL;
132 pdkim_signature *signature;
137 int old_pool = store_pool;
139 dkim_domain = expand_string(dkim_domain);
140 if (dkim_domain == NULL) {
141 /* expansion error, do not send message. */
142 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
143 "dkim_domain: %s", expand_string_message);
147 /* Set up $dkim_domain expansion variable. */
148 dkim_signing_domain = dkim_domain;
150 /* Get selector to use. */
151 dkim_selector = expand_string(dkim_selector);
152 if (dkim_selector == NULL) {
153 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
154 "dkim_selector: %s", expand_string_message);
158 /* Set up $dkim_selector expansion variable. */
159 dkim_signing_selector = dkim_selector;
161 /* Get canonicalization to use */
162 dkim_canon = expand_string(dkim_canon?dkim_canon:US"relaxed");
163 if (dkim_canon == NULL) {
164 /* expansion error, do not send message. */
165 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
166 "dkim_canon: %s", expand_string_message);
170 if (Ustrcmp(dkim_canon, "relaxed") == 0)
171 pdkim_canon = PDKIM_CANON_RELAXED;
172 else if (Ustrcmp(dkim_canon, "simple") == 0)
173 pdkim_canon = PDKIM_CANON_RELAXED;
175 log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon);
176 pdkim_canon = PDKIM_CANON_RELAXED;
179 /* Expand signing headers once */
180 if (dkim_sign_headers != NULL) {
181 dkim_sign_headers = expand_string(dkim_sign_headers);
182 if (dkim_sign_headers == NULL) {
183 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
184 "dkim_sign_headers: %s", expand_string_message);
190 /* Get private key to use. */
191 dkim_private_key = expand_string(dkim_private_key);
192 if (dkim_private_key == NULL) {
193 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
194 "dkim_private_key: %s", expand_string_message);
198 if ( (Ustrlen(dkim_private_key) == 0) ||
199 (Ustrcmp(dkim_private_key,"0") == 0) ||
200 (Ustrcmp(dkim_private_key,"false") == 0) ) {
201 /* don't sign, but no error */
206 if (dkim_private_key[0] == '/') {
208 /* Looks like a filename, load the private key. */
209 memset(big_buffer,0,big_buffer_size);
210 privkey_fd = open(CS dkim_private_key,O_RDONLY);
211 (void)read(privkey_fd,big_buffer,16383);
212 (void)close(privkey_fd);
213 dkim_private_key = big_buffer;
216 ctx = pdkim_init_sign(PDKIM_INPUT_SMTP,
217 (char *)dkim_signing_domain,
218 (char *)dkim_signing_selector,
219 (char *)dkim_private_key
222 pdkim_set_debug_stream(ctx,debug_file);
224 pdkim_set_optional(ctx,
225 (char *)dkim_sign_headers,
230 PDKIM_ALGO_RSA_SHA256,
234 while((sread = read(dkim_fd,&buf,4096)) > 0) {
235 if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) {
240 /* Handle failed read above. */
242 debug_printf("DKIM: Error reading -K file.\n");
248 if (pdkim_feed_finish(ctx,&signature) != PDKIM_OK)
251 rc = store_get(strlen(signature->signature_header)+3);
252 Ustrcpy(rc,US signature->signature_header);
253 Ustrcat(rc,US"\r\n");
259 store_pool = old_pool;