1 /* $Cambridge: exim/src/src/dk.c,v 1.5 2005/06/27 14:29:43 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, &dk_flags);
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);
168 /* TODO: This call should be removed with lib version >= 0.67 */
169 dk_flags = dk_policy(dk_context);
171 /* Grab domain policy */
172 if (dk_flags & DK_FLAG_SET) {
173 if (dk_flags & DK_FLAG_TESTING)
174 dk_verify_block->testing = TRUE;
175 if (dk_flags & DK_FLAG_SIGNSALL)
176 dk_verify_block->signsall = TRUE;
179 /* Set up main result. */
180 switch(dk_internal_status)
183 dk_verify_block->is_signed = FALSE;
184 dk_verify_block->result = DK_EXIM_RESULT_NO_SIGNATURE;
187 dk_verify_block->is_signed = TRUE;
188 dk_verify_block->result = DK_EXIM_RESULT_GOOD;
191 dk_verify_block->is_signed = TRUE;
192 dk_verify_block->result = DK_EXIM_RESULT_BAD;
194 case DK_STAT_REVOKED:
195 dk_verify_block->is_signed = TRUE;
196 dk_verify_block->result = DK_EXIM_RESULT_REVOKED;
200 dk_verify_block->is_signed = TRUE;
201 /* Syntax -> Bad format? */
202 dk_verify_block->result = DK_EXIM_RESULT_BAD_FORMAT;
205 dk_verify_block->is_signed = TRUE;
206 dk_verify_block->result = DK_EXIM_RESULT_NO_KEY;
208 case DK_STAT_NORESOURCE:
209 case DK_STAT_INTERNAL:
211 case DK_STAT_CANTVRFY:
212 dk_verify_block->result = DK_EXIM_RESULT_ERR;
214 /* This is missing DK_EXIM_RESULT_NON_PARTICIPANT. The lib does not
215 report such a status. */
218 /* Set up human readable result string. */
219 dk_verify_block->result_string = string_copy((uschar *)DK_STAT_to_string(dk_internal_status));
221 /* All done, reset dk_context. */
225 store_pool = old_pool;
228 uschar *dk_exim_sign(int dk_fd,
229 uschar *dk_private_key,
234 int dk_canon_int = DK_CANON_SIMPLE;
241 int old_pool = store_pool;
242 store_pool = POOL_PERM;
244 dk_lib = dk_init(&dk_internal_status);
245 if (dk_internal_status != DK_STAT_OK) {
246 debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status));
251 /* Figure out what canonicalization to use. Unfortunately
252 we must do this BEFORE knowing which domain we sign for. */
253 if ((dk_canon != NULL) && (Ustrcmp(dk_canon, "nofws") == 0)) dk_canon_int = DK_CANON_NOFWS;
254 else dk_canon = US "simple";
256 /* Initialize signing context. */
257 dk_context = dk_sign(dk_lib, &dk_internal_status, dk_canon_int);
258 if (dk_internal_status != DK_STAT_OK) {
259 debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status));
264 while((sread = read(dk_fd,&c,1)) > 0) {
266 if ((c == '.') && seen_lfdot) {
267 /* escaped dot, write "\n.", continue */
268 dk_message(dk_context, CUS "\n.", 2);
275 /* EOM, write "\n" and break */
276 dk_message(dk_context, CUS "\n", 1);
280 if ((c == '.') && seen_lf) {
286 /* normal lf, just send it */
287 dk_message(dk_context, CUS "\n", 1);
297 dk_message(dk_context, CUS &c, 1);
300 /* Handle failed read above. */
302 debug_printf("DK: Error reading -K file.\n");
308 /* Flag end-of-message. */
309 dk_internal_status = dk_end(dk_context, NULL);
310 /* TODO: check status */
313 /* Get domain to use, unless overridden. */
314 if (dk_domain == NULL) {
315 dk_domain = US dk_address(dk_context);
316 switch(dk_domain[0]) {
317 case 'N': dk_domain = NULL; break;
321 dk_domain = Ustrrchr(dk_domain,'@');
322 if (dk_domain != NULL) {
326 while (*p != 0) { *p = tolower(*p); p++; }
330 if (dk_domain == NULL) {
331 debug_printf("DK: Could not determine domain to use for signing from message headers.\n");
332 /* In this case, we return "OK" by sending up an empty string as the
333 DomainKey-Signature header. If there is no domain to sign for, we
334 can send the message anyway since the recipient has no policy to
341 dk_domain = expand_string(dk_domain);
342 if (dk_domain == NULL) {
343 /* expansion error, do not send message. */
344 debug_printf("DK: Error while expanding dk_domain option.\n");
350 /* Set up $dk_domain expansion variable. */
351 dk_signing_domain = dk_domain;
353 /* Get selector to use. */
354 dk_selector = expand_string(dk_selector);
355 if (dk_selector == NULL) {
356 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
357 "dk_selector: %s", expand_string_message);
362 /* Set up $dk_selector expansion variable. */
363 dk_signing_selector = dk_selector;
365 /* Get private key to use. */
366 dk_private_key = expand_string(dk_private_key);
367 if (dk_private_key == NULL) {
368 log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
369 "dk_private_key: %s", expand_string_message);
374 if ( (Ustrlen(dk_private_key) == 0) ||
375 (Ustrcmp(dk_private_key,"0") == 0) ||
376 (Ustrcmp(dk_private_key,"false") == 0) ) {
377 /* don't sign, but no error */
382 if (dk_private_key[0] == '/') {
384 /* Looks like a filename, load the private key. */
385 memset(big_buffer,0,big_buffer_size);
386 privkey_fd = open(CS dk_private_key,O_RDONLY);
387 (void)read(privkey_fd,big_buffer,16383);
388 (void)close(privkey_fd);
389 dk_private_key = big_buffer;
392 /* Get the signature. */
393 dk_internal_status = dk_getsig(dk_context, dk_private_key, sig, 8192);
395 /* Check for unuseable key */
396 if (dk_internal_status != DK_STAT_OK) {
397 debug_printf("DK: %s\n", DK_STAT_to_string(dk_internal_status));
402 rc = store_get(1024);
403 /* Build DomainKey-Signature header to return. */
404 snprintf(CS rc, 1024, "DomainKey-Signature: a=rsa-sha1; q=dns; c=%s;\r\n"
406 "\tb=%s;\r\n", dk_canon, dk_selector, dk_domain, sig);
408 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);
411 if (dk_context != NULL) {
415 store_pool = old_pool;