summaryrefslogtreecommitdiffabout
path: root/libetpan/src/engine/mailprivacy_smime.c
Unidiff
Diffstat (limited to 'libetpan/src/engine/mailprivacy_smime.c') (more/less context) (ignore whitespace changes)
-rw-r--r--libetpan/src/engine/mailprivacy_smime.c1755
1 files changed, 1755 insertions, 0 deletions
diff --git a/libetpan/src/engine/mailprivacy_smime.c b/libetpan/src/engine/mailprivacy_smime.c
new file mode 100644
index 0000000..43eb69f
--- a/dev/null
+++ b/libetpan/src/engine/mailprivacy_smime.c
@@ -0,0 +1,1755 @@
1/*
2 * libEtPan! -- a mail library
3 *
4 * Copyright (C) 2001, 2005 - DINH Viet Hoa
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the libEtPan! project nor the names of its
16 * contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32/*
33 * $Id$
34 */
35
36#include "mailprivacy_smime.h"
37#include <string.h>
38#include <sys/wait.h>
39#include <sys/types.h>
40#include <sys/stat.h>
41#include <fcntl.h>
42#include <unistd.h>
43#include <sys/mman.h>
44#include "mailprivacy_tools.h"
45#include "mailprivacy.h"
46#include <stdlib.h>
47#include <dirent.h>
48#include <stdio.h>
49#include <ctype.h>
50#include <libetpan/libetpan-config.h>
51
52/*
53 global variable
54
55 TODO : instance of privacy drivers
56*/
57
58static char cert_dir[PATH_MAX] = "";
59static chash * certificates = NULL;
60static chash * private_keys = NULL;
61static char CAcert_dir[PATH_MAX] = "";
62static char * CAfile = NULL;
63static int CA_check = 1;
64static int store_cert = 0;
65static char private_keys_dir[PATH_MAX] = "";
66
67static char * get_cert_file(char * email);
68
69static char * get_private_key_file(char * email);
70
71
72static int smime_is_signed(struct mailmime * mime)
73{
74 if (mime->mm_content_type != NULL) {
75 clistiter * cur;
76
77 for(cur = clist_begin(mime->mm_content_type->ct_parameters) ; cur != NULL ;
78 cur = clist_next(cur)) {
79 struct mailmime_parameter * param;
80
81 param = cur->data;
82
83 if ((strcasecmp(param->pa_name, "protocol") == 0) &&
84 ((strcasecmp(param->pa_value, "application/x-pkcs7-signature") == 0) ||
85 (strcasecmp(param->pa_value, "application/pkcs7-signature") == 0)))
86 return 1;
87 }
88 }
89
90 return 0;
91}
92
93static int smime_is_encrypted(struct mailmime * mime)
94{
95 if (mime->mm_content_type != NULL) {
96 if ((strcasecmp(mime->mm_content_type->ct_subtype, "x-pkcs7-mime") == 0) ||
97 (strcasecmp(mime->mm_content_type->ct_subtype, "pkcs7-mime") == 0))
98 return 1;
99 }
100
101 return 0;
102}
103
104#define BUF_SIZE 1024
105
106enum {
107 NO_ERROR_SMIME = 0,
108 ERROR_SMIME_CHECK,
109 ERROR_SMIME_COMMAND,
110 ERROR_SMIME_FILE,
111};
112
113/* write output to a file */
114
115static int get_smime_output(FILE * dest_f, char * command)
116{
117 FILE * p;
118 char buf[BUF_SIZE];
119 size_t size;
120 int res;
121 int status;
122 char command_redirected[PATH_MAX];
123
124 snprintf(command_redirected, sizeof(command_redirected), "%s 2>&1", command);
125
126 /*
127 flush buffer so that it is not flushed more than once when forking
128 */
129 fflush(dest_f);
130
131 p = popen(command_redirected, "r");
132 if (p == NULL) {
133 res = ERROR_SMIME_COMMAND;
134 goto err;
135 }
136
137 while ((size = fread(buf, 1, sizeof(buf), p)) != 0) {
138 size_t written;
139
140 written = fwrite(buf, 1, size, dest_f);
141 if (written != size) {
142 res = ERROR_SMIME_FILE;
143 goto close;
144 }
145 }
146 status = pclose(p);
147
148 if (WEXITSTATUS(status) != 0)
149 return ERROR_SMIME_CHECK;
150 else
151 return NO_ERROR_SMIME;
152
153 close:
154 pclose(p);
155 err:
156 return res;
157}
158
159static char * get_first_from_addr(struct mailmime * mime)
160{
161 clistiter * cur;
162 struct mailimf_single_fields single_fields;
163 struct mailimf_fields * fields;
164 struct mailimf_mailbox * mb;
165
166 while (mime->mm_parent != NULL)
167 mime = mime->mm_parent;
168
169 if (mime->mm_type != MAILMIME_MESSAGE)
170 return NULL;
171
172 fields = mime->mm_data.mm_message.mm_fields;
173 if (fields == NULL)
174 return NULL;
175
176 mailimf_single_fields_init(&single_fields, fields);
177
178 if (single_fields.fld_from == NULL)
179 return NULL;
180
181 cur = clist_begin(single_fields.fld_from->frm_mb_list->mb_list);
182 if (cur == NULL)
183 return NULL;
184
185 mb = clist_content(cur);
186
187 return mb->mb_addr_spec;
188}
189
190#define SMIME_DECRYPT_DESCRIPTION "S/MIME encrypted part\r\n"
191#define SMIME_DECRYPT_FAILED "S/MIME decryption FAILED\r\n"
192#define SMIME_DECRYPT_SUCCESS "S/MIME decryption success\r\n"
193
194static int smime_decrypt(struct mailprivacy * privacy,
195 mailmessage * msg,
196 struct mailmime * mime, struct mailmime ** result)
197{
198 char smime_filename[PATH_MAX];
199 char quoted_smime_filename[PATH_MAX];
200 char description_filename[PATH_MAX];
201 FILE * description_f;
202 char decrypted_filename[PATH_MAX];
203 FILE * decrypted_f;
204 char command[PATH_MAX];
205 struct mailmime * description_mime;
206 struct mailmime * decrypted_mime;
207 int r;
208 int res;
209 int sign_ok;
210 char quoted_decrypted_filename[PATH_MAX];
211 struct mailmime * multipart;
212 char * smime_cert;
213 char * smime_key;
214 char quoted_smime_cert[PATH_MAX];
215 char quoted_smime_key[PATH_MAX];
216 char * email;
217 chashiter * iter;
218
219 /* fetch the whole multipart and write it to a file */
220
221 r = mailprivacy_fetch_mime_body_to_file(privacy,
222 smime_filename, sizeof(smime_filename),
223 msg, mime);
224 if (r != MAIL_NO_ERROR) {
225 res = r;
226 goto err;
227 }
228
229 /* we are in a safe directory */
230
231 decrypted_f = mailprivacy_get_tmp_file(privacy,
232 decrypted_filename,
233 sizeof(decrypted_filename));
234 if (decrypted_f == NULL) {
235 res = MAIL_ERROR_FILE;
236 goto unlink_smime;
237 }
238 fclose(decrypted_f);
239
240 sign_ok = 0;
241 for(iter = chash_begin(private_keys) ; iter != NULL ;
242 iter = chash_next(private_keys, iter)) {
243 chashdatum key;
244 char email_buf[BUF_SIZE];
245
246 chash_key(iter, &key);
247
248 if (key.len >= sizeof(email_buf))
249 continue;
250
251 strncpy(email_buf, key.data, key.len);
252 email_buf[key.len] = '\0';
253 email = email_buf;
254
255 /* description */
256
257 description_f = mailprivacy_get_tmp_file(privacy,
258 description_filename,
259 sizeof(description_filename));
260 if (description_f == NULL) {
261 res = MAIL_ERROR_FILE;
262 goto unlink_decrypted;
263 }
264
265 fprintf(description_f, SMIME_DECRYPT_DESCRIPTION);
266
267 /* get encryption key */
268
269#if 0
270 email = get_first_from_addr(mime);
271 if (email == NULL) {
272 fclose(description_f);
273 res = MAIL_ERROR_INVAL;
274 goto unlink_description;
275 }
276#endif
277
278 smime_key = get_private_key_file(email);
279 smime_cert = get_cert_file(email);
280 if ((smime_cert == NULL) || (smime_key == NULL)) {
281 fclose(description_f);
282 res = MAIL_ERROR_INVAL;
283 goto unlink_description;
284 }
285
286 r = mail_quote_filename(quoted_smime_cert, sizeof(quoted_smime_cert),
287 smime_cert);
288 if (r < 0) {
289 fclose(description_f);
290 res = MAIL_ERROR_MEMORY;
291 goto unlink_description;
292 }
293
294 r = mail_quote_filename(quoted_smime_key, sizeof(quoted_smime_key),
295 smime_key);
296 if (r < 0) {
297 fclose(description_f);
298 res = MAIL_ERROR_MEMORY;
299 goto unlink_description;
300 }
301
302 /* run the command */
303
304 r = mail_quote_filename(quoted_smime_filename,
305 sizeof(quoted_smime_filename), smime_filename);
306 if (r < 0) {
307 fclose(description_f);
308 res = MAIL_ERROR_MEMORY;
309 goto unlink_description;
310 }
311
312 r = mail_quote_filename(quoted_decrypted_filename,
313 sizeof(quoted_decrypted_filename), decrypted_filename);
314 if (r < 0) {
315 fclose(description_f);
316 res = MAIL_ERROR_MEMORY;
317 goto unlink_description;
318 }
319
320 sign_ok = 0;
321 snprintf(command, PATH_MAX,
322 "openssl smime -decrypt -in %s -out %s -inkey %s -recip %s",
323 quoted_smime_filename, quoted_decrypted_filename,
324 quoted_smime_key, quoted_smime_cert);
325
326 r = get_smime_output(description_f, command);
327 switch (r) {
328 case NO_ERROR_SMIME:
329 sign_ok = 1;
330 break;
331 case ERROR_SMIME_CHECK:
332 sign_ok = 0;
333 break;
334 case ERROR_SMIME_COMMAND:
335 fclose(description_f);
336 res = MAIL_ERROR_COMMAND;
337 goto unlink_description;
338 case ERROR_SMIME_FILE:
339 fclose(description_f);
340 res = MAIL_ERROR_FILE;
341 goto unlink_description;
342 }
343
344 if (sign_ok) {
345 fprintf(description_f, SMIME_DECRYPT_SUCCESS);
346 fclose(description_f);
347 break;
348 }
349 else {
350 fclose(description_f);
351 unlink(description_filename);
352 }
353 }
354
355 if (!sign_ok) {
356 description_f = mailprivacy_get_tmp_file(privacy,
357 description_filename,
358 sizeof(description_filename));
359 if (description_f == NULL) {
360 res = MAIL_ERROR_FILE;
361 goto unlink_decrypted;
362 }
363
364 fprintf(description_f, SMIME_DECRYPT_DESCRIPTION);
365 fprintf(description_f, SMIME_DECRYPT_FAILED);
366 fclose(description_f);
367 }
368
369 /* building multipart */
370
371 r = mailmime_new_with_content("multipart/x-decrypted", NULL, &multipart);
372 if (r != MAILIMF_NO_ERROR) {
373 res = MAIL_ERROR_MEMORY;
374 goto unlink_description;
375 }
376
377 /* building the description part */
378
379 description_mime = mailprivacy_new_file_part(privacy,
380 description_filename,
381 "text/plain", MAILMIME_MECHANISM_8BIT);
382 if (description_mime == NULL) {
383 mailprivacy_mime_clear(multipart);
384 mailmime_free(multipart);
385 res = MAIL_ERROR_MEMORY;
386 goto unlink_description;
387 }
388
389 /* adds the description part */
390
391 r = mailmime_smart_add_part(multipart, description_mime);
392 if (r != MAIL_NO_ERROR) {
393 mailprivacy_mime_clear(description_mime);
394 mailmime_free(description_mime);
395 mailprivacy_mime_clear(multipart);
396 mailmime_free(multipart);
397 res = MAIL_ERROR_MEMORY;
398 goto unlink_description;
399 }
400
401 /* building the decrypted part */
402
403 r = mailprivacy_get_part_from_file(privacy, 1,
404 decrypted_filename, &decrypted_mime);
405 if (r == MAIL_NO_ERROR) {
406 /* adds the decrypted part */
407
408 r = mailmime_smart_add_part(multipart, decrypted_mime);
409 if (r != MAIL_NO_ERROR) {
410 mailprivacy_mime_clear(decrypted_mime);
411 mailmime_free(decrypted_mime);
412 mailprivacy_mime_clear(multipart);
413 mailmime_free(multipart);
414 res = MAIL_ERROR_MEMORY;
415 goto unlink_description;
416 }
417 }
418
419 unlink(description_filename);
420 unlink(decrypted_filename);
421 unlink(smime_filename);
422
423 * result = multipart;
424
425 return MAIL_NO_ERROR;
426
427 unlink_description:
428 unlink(description_filename);
429 unlink_decrypted:
430 unlink(decrypted_filename);
431 unlink_smime:
432 unlink(smime_filename);
433 err:
434 return res;
435}
436
437
438static int get_cert_from_sig(struct mailprivacy * privacy,
439 mailmessage * msg,
440 struct mailmime * mime);
441
442#define SMIME_VERIFY_DESCRIPTION "S/MIME verify signed message\r\n"
443#define SMIME_VERIFY_FAILED "S/MIME verification FAILED\r\n"
444#define SMIME_VERIFY_SUCCESS "S/MIME verification success\r\n"
445
446static int
447smime_verify(struct mailprivacy * privacy,
448 mailmessage * msg,
449 struct mailmime * mime, struct mailmime ** result)
450{
451 char smime_filename[PATH_MAX];
452 char quoted_smime_filename[PATH_MAX];
453 int res;
454 int r;
455 char command[PATH_MAX];
456 int sign_ok;
457 struct mailmime * description_mime;
458 FILE * description_f;
459 char description_filename[PATH_MAX];
460 struct mailmime * multipart;
461 char stripped_filename[PATH_MAX];
462 struct mailmime * stripped_mime;
463 char quoted_stripped_filename[PATH_MAX];
464 FILE * stripped_f;
465 char check_CA[PATH_MAX];
466 char quoted_CAfile[PATH_MAX];
467 char noverify[PATH_MAX];
468
469 if (store_cert)
470 get_cert_from_sig(privacy, msg, mime);
471
472 * check_CA = '\0';
473 if (CAfile != NULL) {
474 r = mail_quote_filename(quoted_CAfile, sizeof(quoted_CAfile), CAfile);
475 if (r < 0) {
476 res = MAIL_ERROR_MEMORY;
477 goto err;
478 }
479
480 snprintf(check_CA, sizeof(check_CA), "-CAfile %s", quoted_CAfile);
481 }
482
483 * noverify = '\0';
484 if (!CA_check) {
485 snprintf(noverify, sizeof(noverify), "-noverify");
486 }
487
488 /* fetch the whole multipart and write it to a file */
489
490 r = mailprivacy_fetch_mime_body_to_file(privacy,
491 smime_filename, sizeof(smime_filename),
492 msg, mime);
493 if (r != MAIL_NO_ERROR) {
494 res = r;
495 goto err;
496 }
497
498 stripped_f = mailprivacy_get_tmp_file(privacy,
499 stripped_filename,
500 sizeof(stripped_filename));
501 if (stripped_f == NULL) {
502 res = MAIL_ERROR_FILE;
503 goto unlink_smime;
504 }
505 fclose(stripped_f);
506
507 /* description */
508
509 description_f = mailprivacy_get_tmp_file(privacy,
510 description_filename,
511 sizeof(description_filename));
512 if (description_f == NULL) {
513 res = MAIL_ERROR_FILE;
514 goto unlink_stripped;
515 }
516
517 fprintf(description_f, SMIME_VERIFY_DESCRIPTION);
518
519 /* run the command */
520
521 r = mail_quote_filename(quoted_smime_filename,
522 sizeof(quoted_smime_filename), smime_filename);
523 if (r < 0) {
524 fclose(description_f);
525 res = MAIL_ERROR_MEMORY;
526 goto unlink_description;
527 }
528
529 r = mail_quote_filename(quoted_stripped_filename,
530 sizeof(quoted_stripped_filename), stripped_filename);
531 if (r < 0) {
532 fclose(description_f);
533 res = MAIL_ERROR_MEMORY;
534 goto unlink_description;
535 }
536
537 sign_ok = 0;
538 snprintf(command, PATH_MAX, "openssl smime -verify -in %s -out %s %s %s",
539 quoted_smime_filename, quoted_stripped_filename, check_CA, noverify);
540
541 r = get_smime_output(description_f, command);
542 switch (r) {
543 case NO_ERROR_SMIME:
544 sign_ok = 1;
545 break;
546 case ERROR_SMIME_CHECK:
547 sign_ok = 0;
548 break;
549 case ERROR_SMIME_COMMAND:
550 fclose(description_f);
551 res = MAIL_ERROR_COMMAND;
552 goto unlink_description;
553 case ERROR_SMIME_FILE:
554 fclose(description_f);
555 res = MAIL_ERROR_FILE;
556 goto unlink_description;
557 }
558 if (sign_ok)
559 fprintf(description_f, SMIME_VERIFY_SUCCESS);
560 else
561 fprintf(description_f, SMIME_VERIFY_FAILED);
562 fclose(description_f);
563
564 /* building multipart */
565
566 r = mailmime_new_with_content("multipart/x-verified", NULL, &multipart);
567 if (r != MAILIMF_NO_ERROR) {
568 res = MAIL_ERROR_MEMORY;
569 goto unlink_description;
570 }
571
572 /* building the description part */
573
574 description_mime = mailprivacy_new_file_part(privacy,
575 description_filename,
576 "text/plain", MAILMIME_MECHANISM_8BIT);
577 if (description_mime == NULL) {
578 mailprivacy_mime_clear(multipart);
579 mailmime_free(multipart);
580 res = MAIL_ERROR_MEMORY;
581 goto unlink_description;
582 }
583
584 /* adds the description part */
585
586 r = mailmime_smart_add_part(multipart, description_mime);
587 if (r != MAIL_NO_ERROR) {
588 mailprivacy_mime_clear(description_mime);
589 mailmime_free(description_mime);
590 mailprivacy_mime_clear(multipart);
591 mailmime_free(multipart);
592 res = MAIL_ERROR_MEMORY;
593 goto unlink_description;
594 }
595
596 r = mailprivacy_get_part_from_file(privacy, 1,
597 stripped_filename, &stripped_mime);
598 if (r != MAIL_NO_ERROR) {
599 mailprivacy_mime_clear(multipart);
600 mailmime_free(multipart);
601 res = r;
602 goto unlink_description;
603 }
604
605 r = mailmime_smart_add_part(multipart, stripped_mime);
606 if (r != MAIL_NO_ERROR) {
607 mailprivacy_mime_clear(stripped_mime);
608 mailmime_free(stripped_mime);
609 mailprivacy_mime_clear(multipart);
610 mailmime_free(multipart);
611 res = MAIL_ERROR_MEMORY;
612 goto unlink_description;
613 }
614
615 unlink(description_filename);
616 unlink(stripped_filename);
617 unlink(smime_filename);
618
619 * result = multipart;
620
621 return MAIL_NO_ERROR;
622
623 unlink_description:
624 unlink(description_filename);
625 unlink_stripped:
626 unlink(stripped_filename);
627 unlink_smime:
628 unlink(smime_filename);
629 err:
630 return res;
631}
632
633static int smime_test_encrypted(struct mailprivacy * privacy,
634 mailmessage * msg,
635 struct mailmime * mime)
636{
637 switch (mime->mm_type) {
638 case MAILMIME_MULTIPLE:
639 return smime_is_signed(mime);
640
641 case MAILMIME_SINGLE:
642 return smime_is_encrypted(mime);
643 }
644
645 return 0;
646}
647
648static int smime_handler(struct mailprivacy * privacy,
649 mailmessage * msg,
650 struct mailmime * mime, struct mailmime ** result)
651{
652 int r;
653 struct mailmime * alternative_mime;
654
655 alternative_mime = NULL;
656 switch (mime->mm_type) {
657 case MAILMIME_MULTIPLE:
658 r = MAIL_ERROR_INVAL;
659 if (smime_is_signed(mime))
660 r = smime_verify(privacy, msg, mime, &alternative_mime);
661
662 if (r != MAIL_NO_ERROR)
663 return r;
664
665 * result = alternative_mime;
666
667 return MAIL_NO_ERROR;
668
669 case MAILMIME_SINGLE:
670 r = MAIL_ERROR_INVAL;
671 if (smime_is_encrypted(mime))
672 r = smime_decrypt(privacy, msg, mime, &alternative_mime);
673
674 if (r != MAIL_NO_ERROR)
675 return r;
676
677 * result = alternative_mime;
678
679 return MAIL_NO_ERROR;
680 }
681
682 return MAIL_ERROR_INVAL;
683}
684
685
686
687
688static void strip_mime_headers(struct mailmime * mime)
689{
690 struct mailmime_fields * fields;
691 clistiter * cur;
692
693 fields = mime->mm_mime_fields;
694
695 if (fields == NULL)
696 return;
697
698 for(cur = clist_begin(fields->fld_list) ; cur != NULL ;
699 cur = clist_next(cur)) {
700 struct mailmime_field * field;
701
702 field = clist_content(cur);
703
704 if (field->fld_type == MAILMIME_FIELD_VERSION) {
705 mailmime_field_free(field);
706 clist_delete(fields->fld_list, cur);
707 break;
708 }
709 }
710}
711
712static int smime_sign(struct mailprivacy * privacy,
713 struct mailmime * mime, struct mailmime ** result)
714{
715 char signed_filename[PATH_MAX];
716 FILE * signed_f;
717 int res;
718 int r;
719 int col;
720 char signature_filename[PATH_MAX];
721 FILE * signature_f;
722 char command[PATH_MAX];
723 char quoted_signature_filename[PATH_MAX];
724 char quoted_signed_filename[PATH_MAX];
725 struct mailmime * signed_mime;
726 char * smime_cert;
727 char * smime_key;
728 char quoted_smime_cert[PATH_MAX];
729 char quoted_smime_key[PATH_MAX];
730 char * email;
731
732 /* get signing key */
733
734 email = get_first_from_addr(mime);
735 if (email == NULL) {
736 res = MAIL_ERROR_INVAL;
737 goto err;
738 }
739
740 smime_key = get_private_key_file(email);
741 smime_cert = get_cert_file(email);
742 if ((smime_cert == NULL) || (smime_key == NULL)) {
743 res = MAIL_ERROR_INVAL;
744 goto err;
745 }
746
747 /* part to sign */
748
749 /* encode quoted printable all text parts */
750
751 mailprivacy_prepare_mime(mime);
752
753 signed_f = mailprivacy_get_tmp_file(privacy,
754 signed_filename, sizeof(signed_filename));
755 if (signed_f == NULL) {
756 res = MAIL_ERROR_FILE;
757 goto err;
758 }
759
760 col = 0;
761 r = mailmime_write(signed_f, &col, mime);
762 if (r != MAILIMF_NO_ERROR) {
763 fclose(signed_f);
764 res = MAIL_ERROR_FILE;
765 goto unlink_signed;
766 }
767
768 fclose(signed_f);
769
770 /* prepare destination file for signature */
771
772 signature_f = mailprivacy_get_tmp_file(privacy,
773 signature_filename,
774 sizeof(signature_filename));
775 if (signature_f == NULL) {
776 res = MAIL_ERROR_FILE;
777 goto unlink_signed;
778 }
779 fclose(signature_f);
780
781 r = mail_quote_filename(quoted_signed_filename,
782 sizeof(quoted_signed_filename), signed_filename);
783 if (r < 0) {
784 res = MAIL_ERROR_MEMORY;
785 goto unlink_signature;
786 }
787
788 r = mail_quote_filename(quoted_signature_filename,
789 sizeof(quoted_signature_filename), signature_filename);
790 if (r < 0) {
791 res = MAIL_ERROR_MEMORY;
792 goto unlink_signature;
793 }
794
795 r = mail_quote_filename(quoted_smime_key,
796 sizeof(quoted_smime_key), smime_key);
797 if (r < 0) {
798 res = MAIL_ERROR_MEMORY;
799 goto unlink_signature;
800 }
801
802 r = mail_quote_filename(quoted_smime_cert,
803 sizeof(quoted_smime_cert), smime_cert);
804 if (r < 0) {
805 res = MAIL_ERROR_MEMORY;
806 goto unlink_signature;
807 }
808
809 snprintf(command, sizeof(command),
810 "openssl smime -sign -in %s -out %s -signer %s -inkey %s 2>/dev/null",
811 quoted_signed_filename, quoted_signature_filename,
812 quoted_smime_cert, quoted_smime_key);
813
814 r = system(command);
815 if (WEXITSTATUS(r) != 0) {
816 res = MAIL_ERROR_COMMAND;
817 goto unlink_signature;
818 }
819
820 /* signature part */
821
822 r = mailprivacy_get_part_from_file(privacy, 0,
823 signature_filename, &signed_mime);
824 if (r != MAIL_NO_ERROR) {
825 res = r;
826 goto unlink_signature;
827 }
828 strip_mime_headers(signed_mime);
829
830 unlink(signature_filename);
831 unlink(signed_filename);
832
833 * result = signed_mime;
834
835 return MAIL_NO_ERROR;
836
837 unlink_signature:
838 unlink(signature_filename);
839 unlink_signed:
840 unlink(signed_filename);
841 err:
842 return res;
843}
844
845
846/* ********************************************************************* */
847/* find S/MIME recipient */
848
849static int recipient_add_mb(char * recipient, size_t * len,
850 struct mailimf_mailbox * mb)
851{
852 char * filename;
853 char quoted_filename[PATH_MAX];
854 size_t buflen;
855 int r;
856
857 if (mb->mb_addr_spec == NULL)
858 return MAIL_NO_ERROR;
859
860 filename = get_cert_file(mb->mb_addr_spec);
861 if (filename == NULL)
862 return MAIL_ERROR_INVAL;
863
864 r = mail_quote_filename(quoted_filename, sizeof(quoted_filename),
865 filename);
866 if (r < 0)
867 return MAIL_ERROR_MEMORY;
868
869 buflen = strlen(quoted_filename + 1);
870 if (buflen >= * len)
871 return MAIL_ERROR_MEMORY;
872
873 strncat(recipient, quoted_filename, * len);
874 (* len) -= buflen;
875 strncat(recipient, " ", * len);
876 (* len) --;
877
878 return MAIL_NO_ERROR;
879}
880
881static int recipient_add_mb_list(char * recipient, size_t * len,
882 struct mailimf_mailbox_list * mb_list)
883{
884 clistiter * cur;
885 int r;
886
887 for(cur = clist_begin(mb_list->mb_list) ; cur != NULL ;
888 cur = clist_next(cur)) {
889 struct mailimf_mailbox * mb;
890
891 mb = clist_content(cur);
892
893 r = recipient_add_mb(recipient, len, mb);
894 if (r != MAIL_NO_ERROR)
895 return r;
896 }
897
898 return MAIL_NO_ERROR;
899}
900
901static int recipient_add_group(char * recipient, size_t * len,
902 struct mailimf_group * group)
903{
904 return recipient_add_mb_list(recipient, len, group->grp_mb_list);
905}
906
907static int recipient_add_addr(char * recipient, size_t * len,
908 struct mailimf_address * addr)
909{
910 int r;
911
912 switch (addr->ad_type) {
913 case MAILIMF_ADDRESS_MAILBOX:
914 r = recipient_add_mb(recipient, len, addr->ad_data.ad_mailbox);
915 break;
916 case MAILIMF_ADDRESS_GROUP:
917 r = recipient_add_group(recipient, len, addr->ad_data.ad_group);
918 break;
919 default:
920 r = MAIL_ERROR_INVAL;
921 }
922
923 return r;
924}
925
926static int recipient_add_addr_list(char * recipient, size_t * len,
927 struct mailimf_address_list * addr_list)
928{
929 clistiter * cur;
930 int r;
931
932 for(cur = clist_begin(addr_list->ad_list) ; cur != NULL ;
933 cur = clist_next(cur)) {
934 struct mailimf_address * addr;
935
936 addr = clist_content(cur);
937
938 r = recipient_add_addr(recipient, len, addr);
939 if (r != MAIL_NO_ERROR)
940 return r;
941 }
942
943 return MAIL_NO_ERROR;
944}
945
946static int collect_smime_cert(char * recipient, size_t size,
947 struct mailimf_fields * fields)
948{
949 struct mailimf_single_fields single_fields;
950 int r;
951 size_t remaining;
952 int res;
953
954 * recipient = '\0';
955 remaining = size;
956
957 if (fields != NULL)
958 mailimf_single_fields_init(&single_fields, fields);
959
960 if (single_fields.fld_to != NULL) {
961 r = recipient_add_addr_list(recipient, &remaining,
962 single_fields.fld_to->to_addr_list);
963 if (r != MAIL_NO_ERROR) {
964 res = r;
965 goto err;
966 }
967 }
968
969 if (single_fields.fld_cc != NULL) {
970 r = recipient_add_addr_list(recipient, &remaining,
971 single_fields.fld_cc->cc_addr_list);
972 if (r != MAIL_NO_ERROR) {
973 res = r;
974 goto err;
975 }
976 }
977
978 if (single_fields.fld_bcc != NULL) {
979 if (single_fields.fld_bcc->bcc_addr_list != NULL) {
980 r = recipient_add_addr_list(recipient, &remaining,
981 single_fields.fld_bcc->bcc_addr_list);
982 if (r < 0) {
983 res = r;
984 goto err;
985 }
986 }
987 }
988
989 return MAIL_NO_ERROR;
990
991 err:
992 return res;
993}
994
995
996
997static int smime_encrypt(struct mailprivacy * privacy,
998 struct mailmime * mime, struct mailmime ** result)
999{
1000 char encrypted_filename[PATH_MAX];
1001 FILE * encrypted_f;
1002 int res;
1003 int r;
1004 int col;
1005 char decrypted_filename[PATH_MAX];
1006 FILE * decrypted_f;
1007 char command[PATH_MAX];
1008 char quoted_decrypted_filename[PATH_MAX];
1009 char quoted_encrypted_filename[PATH_MAX];
1010 struct mailmime * encrypted_mime;
1011 struct mailmime * root;
1012 struct mailimf_fields * fields;
1013 char recipient[PATH_MAX];
1014
1015 root = mime;
1016 while (root->mm_parent != NULL)
1017 root = root->mm_parent;
1018
1019 fields = NULL;
1020 if (root->mm_type == MAILMIME_MESSAGE)
1021 fields = root->mm_data.mm_message.mm_fields;
1022
1023 /* recipient */
1024 r = collect_smime_cert(recipient, sizeof(recipient), fields);
1025 if (r != MAIL_NO_ERROR) {
1026 res = r;
1027 goto err;
1028 }
1029
1030 /* part to encrypt */
1031
1032 /* encode quoted printable all text parts */
1033
1034 mailprivacy_prepare_mime(mime);
1035
1036 decrypted_f = mailprivacy_get_tmp_file(privacy,
1037 decrypted_filename,
1038 sizeof(decrypted_filename));
1039 if (decrypted_f == NULL) {
1040 res = MAIL_ERROR_FILE;
1041 goto err;
1042 }
1043
1044 col = 0;
1045 r = mailmime_write(decrypted_f, &col, mime);
1046 if (r != MAILIMF_NO_ERROR) {
1047 fclose(decrypted_f);
1048 res = MAIL_ERROR_FILE;
1049 goto unlink_decrypted;
1050 }
1051
1052 fclose(decrypted_f);
1053
1054 /* prepare destination file for encryption */
1055
1056 encrypted_f = mailprivacy_get_tmp_file(privacy,
1057 encrypted_filename,
1058 sizeof(encrypted_filename));
1059 if (encrypted_f == NULL) {
1060 res = MAIL_ERROR_FILE;
1061 goto unlink_decrypted;
1062 }
1063 fclose(encrypted_f);
1064
1065 r = mail_quote_filename(quoted_decrypted_filename,
1066 sizeof(quoted_decrypted_filename), decrypted_filename);
1067 if (r < 0) {
1068 res = MAIL_ERROR_MEMORY;
1069 goto unlink_encrypted;
1070 }
1071
1072 r = mail_quote_filename(quoted_encrypted_filename,
1073 sizeof(quoted_encrypted_filename), encrypted_filename);
1074 if (r < 0) {
1075 res = MAIL_ERROR_MEMORY;
1076 goto unlink_encrypted;
1077 }
1078
1079 snprintf(command, sizeof(command),
1080 "openssl smime -encrypt -in %s -out %s %s 2>/dev/null",
1081 quoted_decrypted_filename, quoted_encrypted_filename, recipient);
1082
1083 r = system(command);
1084 if (WEXITSTATUS(r) != 0) {
1085 res = MAIL_ERROR_COMMAND;
1086 goto unlink_encrypted;
1087 }
1088
1089 /* encrypted part */
1090
1091 r = mailprivacy_get_part_from_file(privacy, 0,
1092 encrypted_filename, &encrypted_mime);
1093 if (r != MAIL_NO_ERROR) {
1094 res = r;
1095 goto unlink_encrypted;
1096 }
1097 strip_mime_headers(encrypted_mime);
1098
1099 unlink(encrypted_filename);
1100 unlink(decrypted_filename);
1101
1102 * result = encrypted_mime;
1103
1104 return MAIL_NO_ERROR;
1105
1106 unlink_encrypted:
1107 unlink(encrypted_filename);
1108 unlink_decrypted:
1109 unlink(decrypted_filename);
1110 err:
1111 return res;
1112}
1113
1114
1115static int smime_sign_encrypt(struct mailprivacy * privacy,
1116 struct mailmime * mime, struct mailmime ** result)
1117{
1118 char encrypted_filename[PATH_MAX];
1119 FILE * encrypted_f;
1120 int res;
1121 int r;
1122 int col;
1123 char signature_filename[PATH_MAX];
1124 FILE * signature_f;
1125 char decrypted_filename[PATH_MAX];
1126 FILE * decrypted_f;
1127 char command[PATH_MAX];
1128 char quoted_decrypted_filename[PATH_MAX];
1129 char quoted_encrypted_filename[PATH_MAX];
1130 char quoted_signature_filename[PATH_MAX];
1131 struct mailmime * encrypted_mime;
1132 struct mailmime * root;
1133 struct mailimf_fields * fields;
1134 char recipient[PATH_MAX];
1135 char * smime_cert;
1136 char * smime_key;
1137 char quoted_smime_cert[PATH_MAX];
1138 char quoted_smime_key[PATH_MAX];
1139 char * email;
1140
1141 root = mime;
1142 while (root->mm_parent != NULL)
1143 root = root->mm_parent;
1144
1145 fields = NULL;
1146 if (root->mm_type == MAILMIME_MESSAGE)
1147 fields = root->mm_data.mm_message.mm_fields;
1148
1149 /* recipient */
1150 r = collect_smime_cert(recipient, sizeof(recipient), fields);
1151 if (r != MAIL_NO_ERROR) {
1152 res = r;
1153 goto err;
1154 }
1155
1156 /* get signing key */
1157
1158 email = get_first_from_addr(mime);
1159 if (email == NULL) {
1160 res = MAIL_ERROR_INVAL;
1161 goto err;
1162 }
1163
1164 smime_key = get_private_key_file(email);
1165 smime_cert = get_cert_file(email);
1166 if ((smime_cert == NULL) || (smime_key == NULL)) {
1167 res = MAIL_ERROR_INVAL;
1168 goto err;
1169 }
1170
1171 /* part to encrypt */
1172
1173 /* encode quoted printable all text parts */
1174
1175 mailprivacy_prepare_mime(mime);
1176
1177 decrypted_f = mailprivacy_get_tmp_file(privacy,
1178 decrypted_filename, sizeof(decrypted_filename));
1179 if (decrypted_f == NULL) {
1180 res = MAIL_ERROR_FILE;
1181 goto err;
1182 }
1183
1184 col = 0;
1185 r = mailmime_write(decrypted_f, &col, mime);
1186 if (r != MAILIMF_NO_ERROR) {
1187 fclose(decrypted_f);
1188 res = MAIL_ERROR_FILE;
1189 goto unlink_decrypted;
1190 }
1191
1192 fclose(decrypted_f);
1193
1194 /* prepare destination file for signature */
1195
1196 signature_f = mailprivacy_get_tmp_file(privacy,
1197 signature_filename,
1198 sizeof(signature_filename));
1199 if (signature_f == NULL) {
1200 res = MAIL_ERROR_FILE;
1201 goto unlink_decrypted;
1202 }
1203 fclose(signature_f);
1204
1205 r = mail_quote_filename(quoted_decrypted_filename,
1206 sizeof(quoted_decrypted_filename), decrypted_filename);
1207 if (r < 0) {
1208 res = MAIL_ERROR_MEMORY;
1209 goto unlink_signature;
1210 }
1211
1212 r = mail_quote_filename(quoted_signature_filename,
1213 sizeof(quoted_signature_filename), signature_filename);
1214 if (r < 0) {
1215 res = MAIL_ERROR_MEMORY;
1216 goto unlink_signature;
1217 }
1218
1219 r = mail_quote_filename(quoted_smime_key,
1220 sizeof(quoted_smime_key), smime_key);
1221 if (r < 0) {
1222 res = MAIL_ERROR_MEMORY;
1223 goto unlink_signature;
1224 }
1225
1226 r = mail_quote_filename(quoted_smime_cert,
1227 sizeof(quoted_smime_cert), smime_cert);
1228 if (r < 0) {
1229 res = MAIL_ERROR_MEMORY;
1230 goto unlink_signature;
1231 }
1232
1233 snprintf(command, sizeof(command),
1234 "openssl smime -sign -in %s -out %s -signer %s -inkey %s 2>/dev/null",
1235 quoted_decrypted_filename, quoted_signature_filename,
1236 quoted_smime_cert, quoted_smime_key);
1237
1238 r = system(command);
1239 if (WEXITSTATUS(r) != 0) {
1240 res = MAIL_ERROR_COMMAND;
1241 goto unlink_signature;
1242 }
1243
1244
1245 /* prepare destination file for encryption */
1246
1247 encrypted_f = mailprivacy_get_tmp_file(privacy,
1248 encrypted_filename,
1249 sizeof(encrypted_filename));
1250 if (encrypted_f == NULL) {
1251 res = MAIL_ERROR_FILE;
1252 goto unlink_signature;
1253 }
1254 fclose(encrypted_f);
1255
1256 r = mail_quote_filename(quoted_encrypted_filename,
1257 sizeof(quoted_encrypted_filename), encrypted_filename);
1258 if (r < 0) {
1259 res = MAIL_ERROR_MEMORY;
1260 goto unlink_encrypted;
1261 }
1262
1263 snprintf(command, sizeof(command),
1264 "openssl smime -encrypt -in %s -out %s %s 2>/dev/null",
1265 quoted_signature_filename, quoted_encrypted_filename, recipient);
1266
1267 r = system(command);
1268 if (WEXITSTATUS(r) != 0) {
1269 res = MAIL_ERROR_COMMAND;
1270 goto unlink_encrypted;
1271 }
1272
1273 /* encrypted part */
1274
1275 r = mailprivacy_get_part_from_file(privacy, 0,
1276 encrypted_filename, &encrypted_mime);
1277 if (r != MAIL_NO_ERROR) {
1278 res = r;
1279 goto unlink_encrypted;
1280 }
1281 strip_mime_headers(encrypted_mime);
1282
1283 unlink(encrypted_filename);
1284 unlink(signature_filename);
1285 unlink(decrypted_filename);
1286
1287 * result = encrypted_mime;
1288
1289 return MAIL_NO_ERROR;
1290
1291 unlink_encrypted:
1292 unlink(encrypted_filename);
1293 unlink_signature:
1294 unlink(signature_filename);
1295 unlink_decrypted:
1296 unlink(decrypted_filename);
1297 err:
1298 return res;
1299}
1300
1301
1302
1303static struct mailprivacy_encryption smime_encryption_tab[] = {
1304 /* S/MIME signed part */
1305 {
1306 .name = "signed",
1307 .description = "S/MIME signed part",
1308 .encrypt = smime_sign,
1309 },
1310
1311 /* S/MIME encrypted part */
1312
1313 {
1314 .name = "encrypted",
1315 .description = "S/MIME encrypted part",
1316 .encrypt = smime_encrypt,
1317 },
1318
1319 /* S/MIME signed & encrypted part */
1320
1321 {
1322 .name = "signed-encrypted",
1323 .description = "S/MIME signed & encrypted part",
1324 .encrypt = smime_sign_encrypt,
1325 },
1326};
1327
1328static struct mailprivacy_protocol smime_protocol = {
1329 .name = "smime",
1330 .description = "S/MIME",
1331
1332 .is_encrypted = smime_test_encrypted,
1333 .decrypt = smime_handler,
1334
1335 .encryption_count =
1336 (sizeof(smime_encryption_tab) / sizeof(smime_encryption_tab[0])),
1337
1338 .encryption_tab = smime_encryption_tab,
1339};
1340
1341int mailprivacy_smime_init(struct mailprivacy * privacy)
1342{
1343 certificates = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYALL);
1344 if (certificates == NULL)
1345 goto err;
1346
1347 private_keys = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYALL);
1348 if (private_keys == NULL)
1349 goto free_cert;
1350
1351 CAcert_dir[0] = '\0';
1352
1353 return mailprivacy_register(privacy, &smime_protocol);
1354
1355 free_cert:
1356 chash_free(certificates);
1357 err:
1358 return MAIL_ERROR_MEMORY;
1359}
1360
1361void mailprivacy_smime_done(struct mailprivacy * privacy)
1362{
1363 mailprivacy_unregister(privacy, &smime_protocol);
1364 chash_free(private_keys);
1365 private_keys = NULL;
1366 chash_free(certificates);
1367 certificates = NULL;
1368 if (CAfile != NULL) {
1369 unlink(CAfile);
1370 free(CAfile);
1371 }
1372 CAfile = NULL;
1373 CAcert_dir[0] = '\0';
1374}
1375
1376
1377static void strip_string(char * str)
1378{
1379 char * p;
1380 size_t len;
1381
1382 p = strchr(str, '\r');
1383 if (p != NULL)
1384 * p = 0;
1385
1386 p = strchr(str, '\n');
1387 if (p != NULL)
1388 * p = 0;
1389
1390 p = str;
1391 while ((* p == ' ') || (* p == '\t')) {
1392 p ++;
1393 }
1394
1395 len = strlen(p);
1396 memmove(str, p, len);
1397 str[len] = 0;
1398
1399 if (len == 0)
1400 return;
1401
1402 p = str;
1403 len = len - 1;
1404 while ((p[len] == ' ') || (p[len] == '\t')) {
1405 p[len] = '\0';
1406
1407 if (len == 0)
1408 break;
1409
1410 len --;
1411 }
1412}
1413
1414
1415
1416#define MAX_EMAIL_SIZE 1024
1417
1418static void set_file(chash * hash, char * email, char * filename)
1419{
1420 char * n;
1421 char buf[MAX_EMAIL_SIZE];
1422 chashdatum key;
1423 chashdatum data;
1424
1425 strncpy(buf, email, sizeof(buf));
1426 buf[sizeof(buf) - 1] = '\0';
1427 for(n = buf ; * n != '\0' ; n ++)
1428 * n = toupper((unsigned char) * n);
1429 strip_string(buf);
1430
1431 key.data = buf;
1432 key.len = strlen(buf);
1433 data.data = filename;
1434 data.len = strlen(filename) + 1;
1435
1436 chash_set(hash, &key, &data, NULL);
1437}
1438
1439static char * get_file(chash * hash, char * email)
1440{
1441 chashdatum key;
1442 chashdatum data;
1443 char buf[MAX_EMAIL_SIZE];
1444 char * n;
1445 int r;
1446
1447 strncpy(buf, email, sizeof(buf));
1448 buf[sizeof(buf) - 1] = '\0';
1449 for(n = buf ; * n != '\0' ; n ++)
1450 * n = toupper((unsigned char) * n);
1451
1452 strip_string(buf);
1453 key.data = buf;
1454 key.len = strlen(buf);
1455 r = chash_get(hash, &key, &data);
1456 if (r < 0)
1457 return NULL;
1458
1459 return data.data;
1460}
1461
1462void mailprivacy_smime_set_cert_dir(struct mailprivacy * privacy,
1463 char * directory)
1464{
1465 DIR * dir;
1466 struct dirent * ent;
1467
1468 chash_clear(certificates);
1469
1470 if (directory == NULL)
1471 return;
1472
1473 if (* directory == '\0')
1474 return;
1475
1476 strncpy(cert_dir, directory, sizeof(cert_dir));
1477 cert_dir[sizeof(cert_dir) - 1] = '\0';
1478
1479 dir = opendir(directory);
1480 if (dir == NULL)
1481 return;
1482
1483 while ((ent = readdir(dir)) != NULL) {
1484 char filename[PATH_MAX];
1485 char command[PATH_MAX];
1486 char buf[MAX_EMAIL_SIZE];
1487 FILE * p;
1488
1489 snprintf(filename, sizeof(filename),
1490 "%s/%s", directory, ent->d_name);
1491
1492 snprintf(command, sizeof(command),
1493 "openssl x509 -email -noout -in %s 2>/dev/null", filename);
1494
1495 p = popen(command, "r");
1496 if (p == NULL)
1497 continue;
1498
1499 while (fgets(buf, sizeof(buf), p) != NULL)
1500 set_file(certificates, buf, filename);
1501
1502 pclose(p);
1503 }
1504 closedir(dir);
1505}
1506
1507static char * get_cert_file(char * email)
1508{
1509 return get_file(certificates, email);
1510}
1511
1512static char * get_private_key_file(char * email)
1513{
1514 return get_file(private_keys, email);
1515}
1516
1517void mail_private_smime_clear_private_keys(struct mailprivacy * privacy)
1518{
1519 chash_clear(private_keys);
1520}
1521
1522#define MAX_BUF 1024
1523
1524void mailprivacy_smime_set_CA_dir(struct mailprivacy * privacy,
1525 char * directory)
1526{
1527 DIR * dir;
1528 struct dirent * ent;
1529 FILE * f_CA;
1530 char CA_filename[PATH_MAX];
1531
1532 if (directory == NULL)
1533 return;
1534
1535 if (* directory == '\0')
1536 return;
1537
1538 /* make a temporary file that contains all the CAs */
1539
1540 if (CAfile != NULL) {
1541 unlink(CAfile);
1542 free(CAfile);
1543 CAfile = NULL;
1544 }
1545
1546 f_CA = mailprivacy_get_tmp_file(privacy, CA_filename, sizeof(CA_filename));
1547 if (f_CA == NULL)
1548 return;
1549
1550 strncpy(CAcert_dir, directory, sizeof(CAcert_dir));
1551 CAcert_dir[sizeof(CAcert_dir) - 1] = '\0';
1552
1553 dir = opendir(directory);
1554 if (dir == NULL) {
1555 fclose(f_CA);
1556 goto unlink_CA;
1557 }
1558
1559 while ((ent = readdir(dir)) != NULL) {
1560 char filename[PATH_MAX];
1561 char command[PATH_MAX];
1562 char buf[MAX_BUF];
1563 FILE * f;
1564
1565 snprintf(filename, sizeof(filename),
1566 "%s/%s", directory, ent->d_name);
1567
1568 f = fopen(filename, "r");
1569 if (f == NULL)
1570 continue;
1571
1572 while (fgets(buf, sizeof(buf), f) != NULL)
1573 fputs(buf, f_CA);
1574
1575 fclose(f);
1576 }
1577
1578 closedir(dir);
1579
1580 fclose(f_CA);
1581
1582 CAfile = strdup(CA_filename);
1583 if (CAfile == NULL)
1584 goto unlink_CA;
1585
1586 return;
1587
1588 unlink_CA:
1589 unlink(CA_filename);
1590}
1591
1592void mailprivacy_smime_set_CA_check(struct mailprivacy * privacy,
1593 int enabled)
1594{
1595 CA_check = enabled;
1596}
1597
1598void mailprivacy_smime_set_store_cert(struct mailprivacy * privacy,
1599 int enabled)
1600{
1601 store_cert = enabled;
1602}
1603
1604static int get_cert_from_sig(struct mailprivacy * privacy,
1605 mailmessage * msg,
1606 struct mailmime * mime)
1607{
1608 clistiter * cur;
1609 struct mailmime * signed_mime;
1610 struct mailmime * signature_mime;
1611 int res;
1612 char signature_filename[PATH_MAX];
1613 char quoted_signature_filename[PATH_MAX];
1614 char * email;
1615 char * cert_file;
1616 char store_cert_filename[PATH_MAX];
1617 char quoted_store_cert_filename[PATH_MAX];
1618 int r;
1619 char command[PATH_MAX];
1620
1621 if (* cert_dir == '\0')
1622 return MAIL_ERROR_INVAL;
1623
1624 if (mime->mm_type != MAILMIME_MULTIPLE)
1625 return MAIL_ERROR_INVAL;
1626
1627 email = get_first_from_addr(mime);
1628 if (email == NULL)
1629 return MAIL_ERROR_INVAL;
1630
1631 cert_file = get_cert_file(email);
1632 if (cert_file != NULL)
1633 return MAIL_NO_ERROR;
1634
1635 /* get the two parts of the S/MIME message */
1636
1637 cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list);
1638 if (cur == NULL) {
1639 res = MAIL_ERROR_INVAL;
1640 goto err;
1641 }
1642
1643 signed_mime = cur->data;
1644 cur = clist_next(cur);
1645 if (cur == NULL) {
1646 res = MAIL_ERROR_INVAL;
1647 goto err;
1648 }
1649
1650 signature_mime = cur->data;
1651
1652 r = mailprivacy_fetch_decoded_to_file(privacy,
1653 signature_filename, sizeof(signature_filename),
1654 msg, signature_mime);
1655 if (r != MAILIMF_NO_ERROR) {
1656 res = r;
1657 goto err;
1658 }
1659
1660 r = mail_quote_filename(quoted_signature_filename,
1661 sizeof(quoted_signature_filename), signature_filename);
1662 if (r < 0) {
1663 res = MAIL_ERROR_MEMORY;
1664 goto unlink_signature;
1665 }
1666
1667 snprintf(store_cert_filename, sizeof(store_cert_filename),
1668 "%s/%s-cert.pem", cert_dir, email);
1669
1670 r = mail_quote_filename(quoted_store_cert_filename,
1671 sizeof(quoted_store_cert_filename), store_cert_filename);
1672 if (r < 0) {
1673 res = MAIL_ERROR_MEMORY;
1674 goto unlink_signature;
1675 }
1676
1677 snprintf(command, sizeof(command),
1678 "openssl pkcs7 -inform DER -in %s -out %s -print_certs 2>/dev/null",
1679 quoted_signature_filename, quoted_store_cert_filename);
1680
1681 r = system(command);
1682 if (WEXITSTATUS(r) != 0) {
1683 res = MAIL_ERROR_COMMAND;
1684 goto unlink_signature;
1685 }
1686
1687 unlink(signature_filename);
1688
1689 set_file(certificates, email, store_cert_filename);
1690
1691 return MAIL_NO_ERROR;
1692
1693 unlink_signature:
1694 unlink(signature_filename);
1695 err:
1696 return res;
1697}
1698
1699
1700static void set_private_key(struct mailprivacy * privacy,
1701 char * email, char * file)
1702{
1703 set_file(private_keys, email, file);
1704}
1705
1706#define PRIVATE_KEY_SUFFIX "-private-key.pem"
1707
1708void mailprivacy_smime_set_private_keys_dir(struct mailprivacy * privacy,
1709 char * directory)
1710{
1711 DIR * dir;
1712 struct dirent * ent;
1713
1714 chash_clear(private_keys);
1715
1716 if (directory == NULL)
1717 return;
1718
1719 if (* directory == '\0')
1720 return;
1721
1722 strncpy(private_keys_dir, directory, sizeof(private_keys_dir));
1723 private_keys_dir[sizeof(private_keys_dir) - 1] = '\0';
1724
1725 dir = opendir(directory);
1726 if (dir == NULL)
1727 return;
1728
1729 while ((ent = readdir(dir)) != NULL) {
1730 char filename[PATH_MAX];
1731 char email[PATH_MAX];
1732 char * p;
1733
1734 snprintf(filename, sizeof(filename),
1735 "%s/%s", directory, ent->d_name);
1736
1737 strncpy(email, ent->d_name, sizeof(email));
1738 email[sizeof(email) - 1] = '\0';
1739
1740 p = strstr(email, PRIVATE_KEY_SUFFIX);
1741 if (p == NULL)
1742 continue;
1743
1744 if (strlen(p) != sizeof(PRIVATE_KEY_SUFFIX) - 1)
1745 continue;
1746
1747 * p = 0;
1748
1749 if (* email == '\0')
1750 continue;
1751
1752 set_private_key(privacy, email, filename);
1753 }
1754 closedir(dir);
1755}