1 /* $Cambridge: exim/src/src/dk.c,v 1.3 2005/06/20 11:20:41 ph10 Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) University of Cambridge 1995 - 2005 */
8 /* See the file NOTICE for conditions of use and distribution. */
10 /* Code for DomainKeys support. Other DK relevant code is in
11 receive.c, transport.c and transports/smtp.c */
15 #ifdef EXPERIMENTAL_DOMAINKEYS
17 /* Globals related to the DK reference library. */
18 DK *dk_context = NULL;
19 DK_LIB *dk_lib = NULL;
21 DK_STAT dk_internal_status;
23 /* Globals related to Exim DK implementation. */
24 dk_exim_verify_block *dk_verify_block = NULL;
26 /* Global char buffer for getc/ungetc functions. We need
27 to accumulate some chars to be able to match EOD and
28 doubled SMTP dots. Those must not be fed to the validation
30 int dkbuff[6] = {256,256,256,256,256,256};
32 /* receive_getc() wrapper that feeds DK while Exim reads
34 int dk_receive_getc(void) {
36 int c = receive_getc();
38 if (dk_context != NULL) {
39 /* Send oldest byte */
40 if ((dkbuff[0] < 256) && (dk_internal_status == DK_STAT_OK)) {
41 dk_internal_status = dk_message(dk_context, CUS &dkbuff[0], 1);
42 if (dk_internal_status != DK_STAT_OK)
43 DEBUG(D_receive) debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status));
46 for (i=0;i<5;i++) dkbuff[i]=dkbuff[i+1];
48 /* look for our candidate patterns */
49 if ( (dkbuff[1] == '\r') &&
50 (dkbuff[2] == '\n') &&
52 (dkbuff[4] == '\r') &&
53 (dkbuff[5] == '\n') ) {
59 if ( (dkbuff[2] == '\r') &&
60 (dkbuff[3] == '\n') &&
62 (dkbuff[5] == '.') ) {
63 /* doubled dot, skip this char */
70 /* When exim puts a char back in the fd, we
71 must rotate our buffer back. */
72 int dk_receive_ungetc(int c) {
74 if (dk_context != NULL) {
75 /* rotate buffer back */
76 for (i=5;i>0;i--) dkbuff[i]=dkbuff[i-1];
79 return receive_ungetc(c);
83 void dk_exim_verify_init(void) {
84 int old_pool = store_pool;
85 store_pool = POOL_PERM;
87 /* Reset DK state in any case. */
90 dk_verify_block = NULL;
92 /* Set up DK context if DK was requested and input is SMTP. */
93 if (smtp_input && !smtp_batched_input && dk_do_verify) {
94 /* initialize library */
95 dk_lib = dk_init(&dk_internal_status);
96 if (dk_internal_status != DK_STAT_OK)
97 debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status));
99 /* initialize verification context */
100 dk_context = dk_verify(dk_lib, &dk_internal_status);
101 if (dk_internal_status != DK_STAT_OK) {
102 debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status));
106 /* Reserve some space for the verify block. */
107 dk_verify_block = store_get(sizeof(dk_exim_verify_block));
108 if (dk_verify_block == NULL) {
109 debug_printf("DK: Can't allocate %d bytes.\n",sizeof(dk_exim_verify_block));
113 memset(dk_verify_block, 0, sizeof(dk_exim_verify_block));
118 store_pool = old_pool;
122 void dk_exim_verify_finish(void) {
125 int old_pool = store_pool;
127 /* Bail out if context could not be set up earlier. */
128 if (dk_context == NULL)
131 store_pool = POOL_PERM;
133 /* Send remaining bytes from input which are still in the buffer. */
136 dk_internal_status = dk_message(dk_context, CUS &dkbuff[i], 1);
138 /* Flag end-of-message. */
139 dk_internal_status = dk_end(dk_context, NULL);
141 /* Grab address/domain information. */
142 p = dk_address(dk_context);
146 dk_verify_block->address_source = DK_EXIM_ADDRESS_NONE;
149 dk_verify_block->address_source = DK_EXIM_ADDRESS_FROM_SENDER;
152 dk_verify_block->address_source = DK_EXIM_ADDRESS_FROM_FROM;
157 dk_verify_block->address = string_copy((uschar *)p);
159 if ((q != NULL) && (*(q+1) != '\0')) {
160 dk_verify_block->domain = string_copy((uschar *)(q+1));
162 dk_verify_block->local_part = string_copy((uschar *)p);
167 dk_flags = dk_policy(dk_context);
169 /* Grab domain policy */
170 if (dk_flags & DK_FLAG_SET) {
171 if (dk_flags & DK_FLAG_TESTING)
172 dk_verify_block->testing = TRUE;
173 if (dk_flags & DK_FLAG_SIGNSALL)
174 dk_verify_block->signsall = TRUE;
177 /* Set up main result. */
178 switch(dk_internal_status)
181 dk_verify_block->is_signed = FALSE;
182 dk_verify_block->result = DK_EXIM_RESULT_NO_SIGNATURE;
185 dk_verify_block->is_signed = TRUE;
186 dk_verify_block->result = DK_EXIM_RESULT_GOOD;
189 dk_verify_block->is_signed = TRUE;
190 dk_verify_block->result = DK_EXIM_RESULT_BAD;
192 case DK_STAT_REVOKED:
193 dk_verify_block->is_signed = TRUE;
194 dk_verify_block->result = DK_EXIM_RESULT_REVOKED;
198 dk_verify_block->is_signed = TRUE;
199 /* Syntax -> Bad format? */
200 dk_verify_block->result = DK_EXIM_RESULT_BAD_FORMAT;
203 dk_verify_block->is_signed = TRUE;
204 dk_verify_block->result = DK_EXIM_RESULT_NO_KEY;
206 case DK_STAT_NORESOURCE:
207 case DK_STAT_INTERNAL:
209 case DK_STAT_CANTVRFY:
210 dk_verify_block->result = DK_EXIM_RESULT_ERR;
212 /* This is missing DK_EXIM_RESULT_NON_PARTICIPANT. The lib does not
213 report such a status. */
216 /* Set up human readable result string. */
217 dk_verify_block->result_string = string_copy((uschar *)DK_STAT_to_string(dk_internal_status));
219 /* All done, reset dk_context. */
223 store_pool = old_pool;
226 uschar *dk_exim_sign(int dk_fd,
227 uschar *dk_private_key,
232 int dk_canon_int = DK_CANON_SIMPLE;
239 int old_pool = store_pool;
240 store_pool = POOL_PERM;
242 dk_lib = dk_init(&dk_internal_status);
243 if (dk_internal_status != DK_STAT_OK) {
244 debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status));
249 /* Figure out what canonicalization to use. Unfortunately
250 we must do this BEFORE knowing which domain we sign for. */
251 if ((dk_canon != NULL) && (Ustrcmp(dk_canon, "nofws") == 0)) dk_canon_int = DK_CANON_NOFWS;
252 else dk_canon = US "simple";
254 /* Initialize signing context. */
255 dk_context = dk_sign(dk_lib, &dk_internal_status, dk_canon_int);
256 if (dk_internal_status != DK_STAT_OK) {
257 debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status));
262 while((sread = read(dk_fd,&c,1)) > 0) {
264 if ((c == '.') && seen_lfdot) {
265 /* escaped dot, write "\n.", continue */
266 dk_message(dk_context, CUS "\n.", 2);
273 /* EOM, write "\n" and break */
274 dk_message(dk_context, CUS "\n", 1);
278 if ((c == '.') && seen_lf) {
284 /* normal lf, just send it */
285 dk_message(dk_context, CUS "\n", 1);
295 dk_message(dk_context, CUS &c, 1);
298 /* Handle failed read above. */
300 debug_printf("DK: Error reading -K file.\n");
306 /* Flag end-of-message. */
307 dk_internal_status = dk_end(dk_context, NULL);
308 /* TODO: check status */
311 /* Get domain to use, unless overridden. */
312 if (dk_domain == NULL) {
313 dk_domain = US dk_address(dk_context);
314 switch(dk_domain[0]) {
315 case 'N': dk_domain = NULL; break;
319 dk_domain = Ustrrchr(dk_domain,'@');
320 if (dk_domain != NULL) {
324 while (*p != 0) { *p = tolower(*p); p++; }
328 if (dk_domain == NULL) {
329 debug_printf("DK: Could not determine domain to use for signing from message headers.\n");
330 /* In this case, we return "OK" by sending up an empty string as the
331 DomainKey-Signature header. If there is no domain to sign for, we
332 can send the message anyway since the recipient has no policy to
339 dk_domain = expand_string(dk_domain);
340 if (dk_domain == NULL) {
341 /* expansion error, do not send message. */
342 debug_printf("DK: Error while expanding dk_domain option.\n");
348 /* Set up $dk_domain expansion variable. */
349 dk_signing_domain = dk_domain;
351 /* Get selector to use. */
352 dk_selector = expand_string(dk_selector);
353 if (dk_selector == NULL) {
354 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
355 "dk_selector: %s", expand_string_message);
360 /* Set up $dk_selector expansion variable. */
361 dk_signing_selector = dk_selector;
363 /* Get private key to use. */
364 dk_private_key = expand_string(dk_private_key);
365 if (dk_private_key == NULL) {
366 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
367 "dk_private_key: %s", expand_string_message);
372 if ( (Ustrlen(dk_private_key) == 0) ||
373 (Ustrcmp(dk_private_key,"0") == 0) ||
374 (Ustrcmp(dk_private_key,"false") == 0) ) {
375 /* don't sign, but no error */
380 if (dk_private_key[0] == '/') {
382 /* Looks like a filename, load the private key. */
383 memset(big_buffer,0,big_buffer_size);
384 privkey_fd = open(CS dk_private_key,O_RDONLY);
385 read(privkey_fd,big_buffer,16383);
387 dk_private_key = big_buffer;
390 /* Get the signature. */
391 dk_internal_status = dk_getsig(dk_context, dk_private_key, sig, 8192);
393 /* Check for unuseable key */
394 if (dk_internal_status != DK_STAT_OK) {
395 debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status));
400 rc = store_get(1024);
401 /* Build DomainKey-Signature header to return. */
402 snprintf(CS rc, 1024, "DomainKey-Signature: a=rsa-sha1; q=dns; c=%s;\r\n"
404 "\tb=%s;\r\n", dk_canon, dk_selector, dk_domain, sig);
406 log_write(0, LOG_MAIN, "DK: message signed using a=rsa-sha1; q=dns; c=%s; s=%s; d=%s;", dk_canon, dk_selector, dk_domain);
409 if (dk_context != NULL) {
413 store_pool = old_pool;