fe536d9caa0dbf00c40bf9ee73258141fd24214e
[users/jgh/exim.git] / src / src / dkim.c
1 /* $Cambridge: exim/src/src/dkim.c,v 1.1.2.7 2009/04/30 08:21:30 tom Exp $ */
2
3 /*************************************************
4 *     Exim - an Internet mail transport agent    *
5 *************************************************/
6
7 /* Copyright (c) University of Cambridge 2009 */
8 /* See the file NOTICE for conditions of use and distribution. */
9
10 /* Code for DKIM support. Other DKIM relevant code is in
11    receive.c, transport.c and transports/smtp.c */
12
13 #include "exim.h"
14
15 #ifndef DISABLE_DKIM
16
17 #include "pdkim/pdkim.h"
18
19 pdkim_ctx       *dkim_verify_ctx = NULL;
20 pdkim_signature *dkim_signatures = NULL;
21
22 int dkim_exim_query_dns_txt(char *name, char *answer) {
23   dns_answer dnsa;
24   dns_scan   dnss;
25   dns_record *rr;
26
27   if (dns_lookup(&dnsa, (uschar *)name, T_TXT, NULL) != DNS_SUCCEED) return 1;
28
29   /* Search for TXT record */
30   for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
31        rr != NULL;
32        rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
33     if (rr->type == T_TXT) break;
34
35   /* Copy record content to the answer buffer */
36   if (rr != NULL) {
37     int rr_offset = 0;
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));
44       rr_offset+=len;
45       answer_offset+=len;
46     }
47   }
48   else return 1;
49
50   return PDKIM_OK;
51 }
52
53
54 int dkim_exim_verify_init(void) {
55
56   /* Free previous context if there is one */
57   if (dkim_verify_ctx) pdkim_free_ctx(dkim_verify_ctx);
58
59   /* Create new context */
60   dkim_verify_ctx = pdkim_init_verify(PDKIM_INPUT_SMTP,
61                                       &dkim_exim_query_dns_txt
62                                      );
63
64   if (dkim_verify_ctx != NULL) {
65     dkim_collect_input = 1;
66     pdkim_set_debug_stream(dkim_verify_ctx,debug_file);
67     return 1;
68   }
69   else {
70     dkim_collect_input = 0;
71     return 0;
72   }
73 }
74
75
76 int dkim_exim_verify_feed(uschar *data, int len) {
77   if (pdkim_feed(dkim_verify_ctx,
78                  (char *)data,
79                  len) != PDKIM_OK) return 0;
80   return 1;
81 }
82
83
84 int dkim_exim_verify_finish(void) {
85   dkim_signatures = NULL;
86   dkim_collect_input = 0;
87   if (pdkim_feed_finish(dkim_verify_ctx,&dkim_signatures) != PDKIM_OK) return 0;
88
89   while (dkim_signatures != NULL) {
90     debug_printf("DKIM: Signature from domain '%s': ",dkim_signatures->domain);
91     switch(dkim_signatures->verify_status) {
92       case PDKIM_VERIFY_NONE:
93         debug_printf("not verified\n");
94         log_write(0, LOG_MAIN, "DKIM: Signature from domain '%s', selector '%s': "
95                   "not verified", dkim_signatures->domain, dkim_signatures->selector);
96       break;
97       case PDKIM_VERIFY_INVALID:
98         debug_printf("invalid\n");
99         log_write(0, LOG_MAIN, "DKIM: Signature from domain '%s', selector '%s': "
100                   "invalid", dkim_signatures->domain, dkim_signatures->selector);
101       break;
102       case PDKIM_VERIFY_FAIL:
103         debug_printf("verification failed\n");
104         log_write(0, LOG_MAIN, "DKIM: Signature from domain '%s', selector '%s': "
105                   "verification failed", dkim_signatures->domain, dkim_signatures->selector);
106       break;
107       case PDKIM_VERIFY_PASS:
108         debug_printf("verification succeeded\n");
109         log_write(0, LOG_MAIN, "DKIM: Signature from domain '%s', selector '%s': "
110                   "verification succeeded", dkim_signatures->domain, dkim_signatures->selector);
111       break;
112     }
113     /* Try next signature */
114     dkim_signatures = dkim_signatures->next;
115   }
116
117   return dkim_signatures?1:0;
118 }
119
120
121 int dkim_exim_verify_result(uschar *domain, uschar **result, uschar **error) {
122
123   if (dkim_verify_ctx) {
124
125   }
126
127   return OK;
128 }
129
130
131 uschar *dkim_exim_sign(int dkim_fd,
132                        uschar *dkim_private_key,
133                        uschar *dkim_domain,
134                        uschar *dkim_selector,
135                        uschar *dkim_canon,
136                        uschar *dkim_sign_headers) {
137   pdkim_ctx *ctx = NULL;
138   uschar *rc = NULL;
139   pdkim_signature *signature;
140   int pdkim_canon;
141   int sread;
142   char buf[4096];
143   int save_errno = 0;
144   int old_pool = store_pool;
145
146   dkim_domain = expand_string(dkim_domain);
147   if (dkim_domain == NULL) {
148     /* expansion error, do not send message. */
149     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
150           "dkim_domain: %s", expand_string_message);
151     rc = NULL;
152     goto CLEANUP;
153   }
154   /* Set up $dkim_domain expansion variable. */
155   dkim_signing_domain = dkim_domain;
156
157   /* Get selector to use. */
158   dkim_selector = expand_string(dkim_selector);
159   if (dkim_selector == NULL) {
160     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
161       "dkim_selector: %s", expand_string_message);
162     rc = NULL;
163     goto CLEANUP;
164   }
165   /* Set up $dkim_selector expansion variable. */
166   dkim_signing_selector = dkim_selector;
167
168   /* Get canonicalization to use */
169   dkim_canon = expand_string(dkim_canon?dkim_canon:US"relaxed");
170   if (dkim_canon == NULL) {
171     /* expansion error, do not send message. */
172     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
173           "dkim_canon: %s", expand_string_message);
174     rc = NULL;
175     goto CLEANUP;
176   }
177   if (Ustrcmp(dkim_canon, "relaxed") == 0)
178     pdkim_canon = PDKIM_CANON_RELAXED;
179   else if (Ustrcmp(dkim_canon, "simple") == 0)
180     pdkim_canon = PDKIM_CANON_RELAXED;
181   else {
182     log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon);
183     pdkim_canon = PDKIM_CANON_RELAXED;
184   }
185
186   /* Expand signing headers once */
187   if (dkim_sign_headers != NULL) {
188     dkim_sign_headers = expand_string(dkim_sign_headers);
189     if (dkim_sign_headers == NULL) {
190       log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
191         "dkim_sign_headers: %s", expand_string_message);
192       rc = NULL;
193       goto CLEANUP;
194     }
195   }
196
197   /* Get private key to use. */
198   dkim_private_key = expand_string(dkim_private_key);
199   if (dkim_private_key == NULL) {
200     log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
201       "dkim_private_key: %s", expand_string_message);
202     rc = NULL;
203     goto CLEANUP;
204   }
205   if ( (Ustrlen(dkim_private_key) == 0) ||
206        (Ustrcmp(dkim_private_key,"0") == 0) ||
207        (Ustrcmp(dkim_private_key,"false") == 0) ) {
208     /* don't sign, but no error */
209     rc = US"";
210     goto CLEANUP;
211   }
212
213   if (dkim_private_key[0] == '/') {
214     int privkey_fd = 0;
215     /* Looks like a filename, load the private key. */
216     memset(big_buffer,0,big_buffer_size);
217     privkey_fd = open(CS dkim_private_key,O_RDONLY);
218     (void)read(privkey_fd,big_buffer,16383);
219     (void)close(privkey_fd);
220     dkim_private_key = big_buffer;
221   }
222
223   ctx = pdkim_init_sign(PDKIM_INPUT_SMTP,
224                         (char *)dkim_signing_domain,
225                         (char *)dkim_signing_selector,
226                         (char *)dkim_private_key
227                        );
228
229   pdkim_set_debug_stream(ctx,debug_file);
230
231   pdkim_set_optional(ctx,
232                      (char *)dkim_sign_headers,
233                      NULL,
234                      pdkim_canon,
235                      pdkim_canon,
236                      -1,
237                      PDKIM_ALGO_RSA_SHA256,
238                      0,
239                      0);
240
241   while((sread = read(dkim_fd,&buf,4096)) > 0) {
242     if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) {
243       rc = NULL;
244       goto CLEANUP;
245     }
246   }
247   /* Handle failed read above. */
248   if (sread == -1) {
249     debug_printf("DKIM: Error reading -K file.\n");
250     save_errno = errno;
251     rc = NULL;
252     goto CLEANUP;
253   }
254
255   if (pdkim_feed_finish(ctx,&signature) != PDKIM_OK)
256     goto CLEANUP;
257
258   rc = store_get(strlen(signature->signature_header)+3);
259   Ustrcpy(rc,US signature->signature_header);
260   Ustrcat(rc,US"\r\n");
261
262   CLEANUP:
263   if (ctx != NULL) {
264     pdkim_free_ctx(ctx);
265   }
266   store_pool = old_pool;
267   errno = save_errno;
268   return rc;
269 };
270
271 #endif