Add verification glue code
[users/jgh/exim.git] / src / src / dkim.c
1 /* $Cambridge: exim/src/src/dkim.c,v 1.1.2.6 2009/04/09 13:57:21 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 len = (rr->data)[0];
38     //if (len > 511) len = 127; // ???
39     snprintf(answer, PDKIM_DNS_TXT_MAX_RECLEN, "%.*s", len, (char *)(rr->data+1));
40   }
41   else return 1;
42
43   return PDKIM_OK;
44 }
45
46
47 int dkim_exim_verify_init(void) {
48
49   /* Free previous context if there is one */
50   if (dkim_verify_ctx) pdkim_free_ctx(dkim_verify_ctx);
51
52   /* Create new context */
53   dkim_verify_ctx = pdkim_init_verify(PDKIM_INPUT_SMTP,
54                                       &dkim_exim_query_dns_txt
55                                      );
56
57   if (dkim_verify_ctx != NULL) {
58     dkim_collect_input = 1;
59     pdkim_set_debug_stream(dkim_verify_ctx,debug_file);
60     return 1;
61   }
62   else {
63     dkim_collect_input = 0;
64     return 0;
65   }
66 }
67
68
69 int dkim_exim_verify_feed(uschar *data, int len) {
70   if (pdkim_feed(dkim_verify_ctx,
71                  (char *)data,
72                  len) != PDKIM_OK) return 0;
73   return 1;
74 }
75
76
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;
81
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);
89       break;
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);
94       break;
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);
99       break;
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);
104       break;
105     }
106     /* Try next signature */
107     dkim_signatures = dkim_signatures->next;
108   }
109
110   return dkim_signatures?1:0;
111 }
112
113
114 int dkim_exim_verify_result(uschar *domain, uschar **result, uschar **error) {
115
116   if (dkim_verify_ctx) {
117
118   }
119
120   return OK;
121 }
122
123
124 uschar *dkim_exim_sign(int dkim_fd,
125                        uschar *dkim_private_key,
126                        uschar *dkim_domain,
127                        uschar *dkim_selector,
128                        uschar *dkim_canon,
129                        uschar *dkim_sign_headers) {
130   pdkim_ctx *ctx = NULL;
131   uschar *rc = NULL;
132   pdkim_signature *signature;
133   int pdkim_canon;
134   int sread;
135   char buf[4096];
136   int save_errno = 0;
137   int old_pool = store_pool;
138
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);
144     rc = NULL;
145     goto CLEANUP;
146   }
147   /* Set up $dkim_domain expansion variable. */
148   dkim_signing_domain = dkim_domain;
149
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);
155     rc = NULL;
156     goto CLEANUP;
157   }
158   /* Set up $dkim_selector expansion variable. */
159   dkim_signing_selector = dkim_selector;
160
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);
167     rc = NULL;
168     goto CLEANUP;
169   }
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;
174   else {
175     log_write(0, LOG_MAIN, "DKIM: unknown canonicalization method '%s', defaulting to 'relaxed'.\n",dkim_canon);
176     pdkim_canon = PDKIM_CANON_RELAXED;
177   }
178
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);
185       rc = NULL;
186       goto CLEANUP;
187     }
188   }
189
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);
195     rc = NULL;
196     goto CLEANUP;
197   }
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 */
202     rc = US"";
203     goto CLEANUP;
204   }
205
206   if (dkim_private_key[0] == '/') {
207     int privkey_fd = 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;
214   }
215
216   ctx = pdkim_init_sign(PDKIM_INPUT_SMTP,
217                         (char *)dkim_signing_domain,
218                         (char *)dkim_signing_selector,
219                         (char *)dkim_private_key
220                        );
221
222   pdkim_set_debug_stream(ctx,debug_file);
223
224   pdkim_set_optional(ctx,
225                      (char *)dkim_sign_headers,
226                      NULL,
227                      pdkim_canon,
228                      pdkim_canon,
229                      -1,
230                      PDKIM_ALGO_RSA_SHA256,
231                      0,
232                      0);
233
234   while((sread = read(dkim_fd,&buf,4096)) > 0) {
235     if (pdkim_feed(ctx,buf,sread) != PDKIM_OK) {
236       rc = NULL;
237       goto CLEANUP;
238     }
239   }
240   /* Handle failed read above. */
241   if (sread == -1) {
242     debug_printf("DKIM: Error reading -K file.\n");
243     save_errno = errno;
244     rc = NULL;
245     goto CLEANUP;
246   }
247
248   if (pdkim_feed_finish(ctx,&signature) != PDKIM_OK)
249     goto CLEANUP;
250
251   rc = store_get(strlen(signature->signature_header)+3);
252   Ustrcpy(rc,US signature->signature_header);
253   Ustrcat(rc,US"\r\n");
254
255   CLEANUP:
256   if (ctx != NULL) {
257     pdkim_free_ctx(ctx);
258   }
259   store_pool = old_pool;
260   errno = save_errno;
261   return rc;
262 };
263
264 #endif