* new version 2.11.8
[alpine.git] / pith / smime.c
blob532948bf24343baccc18f7ffd68a3feecf3c20b4
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: smime.c 1176 2008-09-29 21:16:42Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2008 University of Washington
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * ========================================================================
19 * This is based on a contribution from Jonathan Paisley
21 * File: smime.c
22 * Author: paisleyj@dcs.gla.ac.uk
23 * Date: 01/2001
27 #include "../pith/headers.h"
29 #ifdef SMIME
31 #include "../pith/osdep/canaccess.h"
32 #include "../pith/helptext.h"
33 #include "../pith/store.h"
34 #include "../pith/status.h"
35 #include "../pith/detach.h"
36 #include "../pith/conf.h"
37 #include "../pith/smkeys.h"
38 #include "../pith/smime.h"
39 #include "../pith/mailpart.h"
40 #include "../pith/reply.h"
41 #include "../pith/tempfile.h"
42 #include "../pith/readfile.h"
43 #include "../pith/remote.h"
45 #include <openssl/buffer.h>
48 typedef enum {Public, Private, CACert} WhichCerts;
51 /* internal prototypes */
52 static void forget_private_keys(void);
53 static int app_RAND_load_file(const char *file);
54 static void openssl_extra_randomness(void);
55 static int app_RAND_write_file(const char *file);
56 static void smime_init(void);
57 static const char *openssl_error_string(void);
58 static void create_local_cache(char *base, BODY *b);
59 static BIO *raw_part_to_bio(long msgno, const char *section);
60 static long rfc822_output_func(void *b, char *string);
61 static int load_private_key(PERSONAL_CERT *pcert);
62 static void setup_pkcs7_body_for_signature(BODY *b, char *description,
63 char *type, char *filename);
64 static BIO *body_to_bio(BODY *body);
65 static BIO *bio_from_store(STORE_S *store);
66 static STORE_S *get_part_contents(long msgno, const char *section);
67 static PKCS7 *get_pkcs7_from_part(long msgno, const char *section);
68 static int do_signature_verify(PKCS7 *p7, BIO *in, BIO *out);
69 static int do_detached_signature_verify(BODY *b, long msgno, char *section);
70 static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7);
71 static int do_decoding(BODY *b, long msgno, const char *section);
72 static void free_smime_struct(SMIME_STUFF_S **smime);
73 static void setup_storage_locations(void);
74 static int copy_dir_to_container(WhichCerts which);
75 static int copy_container_to_dir(WhichCerts which);
78 int (*pith_opt_smime_get_passphrase)(void);
81 static X509_STORE *s_cert_store;
83 /* State management for randomness functions below */
84 static int seeded = 0;
85 static int egdsocket = 0;
89 * Forget any cached private keys
91 static void
92 forget_private_keys(void)
94 PERSONAL_CERT *pcert;
95 size_t len;
96 volatile char *p;
98 dprint((9, "forget_private_keys()"));
99 if(ps_global->smime){
100 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
101 pcert;
102 pcert=pcert->next){
104 if(pcert->key){
105 EVP_PKEY_free(pcert->key);
106 pcert->key = NULL;
110 ps_global->smime->entered_passphrase = 0;
111 len = sizeof(ps_global->smime->passphrase);
112 p = ps_global->smime->passphrase;
114 while(len-- > 0)
115 *p++ = '\0';
121 * taken from openssl/apps/app_rand.c
123 static int
124 app_RAND_load_file(const char *file)
126 char buffer[200];
128 if(file == NULL)
129 file = RAND_file_name(buffer, sizeof buffer);
130 else if(RAND_egd(file) > 0){
131 /* we try if the given filename is an EGD socket.
132 if it is, we don't write anything back to the file. */
133 egdsocket = 1;
134 return 1;
137 if(file == NULL || !RAND_load_file(file, -1)){
138 if(RAND_status() == 0){
139 dprint((1, "unable to load 'random state'\n"));
140 dprint((1, "This means that the random number generator has not been seeded\n"));
141 dprint((1, "with much random data.\n"));
144 return 0;
147 seeded = 1;
148 return 1;
153 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
155 static void
156 openssl_extra_randomness(void)
158 #if !defined(WIN32)
159 int fd;
160 unsigned long i;
161 char *tf = NULL;
162 char tmp[MAXPATH];
163 struct stat sbuf;
164 /* if system doesn't have /dev/urandom */
165 if(stat ("/dev/urandom", &sbuf)){
166 tmp[0] = '0';
167 tf = temp_nam(NULL, NULL);
168 if(tf){
169 strncpy(tmp, tf, sizeof(tmp));
170 tmp[sizeof(tmp)-1] = '\0';
171 fs_give((void **) &tf);
174 if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
175 i = (unsigned long) tmp;
176 else{
177 unlink(tmp); /* don't need the file */
178 fstat(fd, &sbuf); /* get information about the file */
179 i = sbuf.st_ino; /* remember its inode */
180 close(fd); /* or its descriptor */
182 /* not great but it'll have to do */
183 snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
184 tcp_serverhost (),i,
185 (unsigned long) (time (0) ^ gethostid ()),
186 (unsigned long) getpid ());
187 RAND_seed(tmp, strlen(tmp));
189 #endif
193 /* taken from openssl/apps/app_rand.c */
194 static int
195 app_RAND_write_file(const char *file)
197 char buffer[200];
199 if(egdsocket || !seeded)
201 * If we did not manage to read the seed file,
202 * we should not write a low-entropy seed file back --
203 * it would suppress a crucial warning the next time
204 * we want to use it.
206 return 0;
208 if(file == NULL)
209 file = RAND_file_name(buffer, sizeof buffer);
211 if(file == NULL || !RAND_write_file(file)){
212 dprint((1, "unable to write 'random state'\n"));
213 return 0;
216 return 1;
220 /* Installed as an atexit() handler to save the random data */
221 void
222 smime_deinit(void)
224 dprint((9, "smime_deinit()"));
225 app_RAND_write_file(NULL);
226 free_smime_struct(&ps_global->smime);
230 /* Initialise openssl stuff if needed */
231 static void
232 smime_init(void)
234 if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
236 dprint((9, "smime_init()"));
237 if(!ps_global->smime)
238 ps_global->smime = new_smime_struct();
240 setup_storage_locations();
242 s_cert_store = get_ca_store();
244 OpenSSL_add_all_algorithms();
245 ERR_load_crypto_strings();
247 app_RAND_load_file(NULL);
249 openssl_extra_randomness();
250 ps_global->smime->inited = 1;
253 ERR_clear_error();
257 static void
258 setup_storage_locations(void)
260 int publiccertcontainer = 0, privatekeycontainer = 0, cacertcontainer = 0;
261 char path[MAXPATH+1], *contents;
263 if(!ps_global->smime)
264 return;
266 #ifdef APPLEKEYCHAIN
267 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
268 ps_global->smime->publictype = Keychain;
270 else{
271 #endif /* APPLEKEYCHAIN */
272 /* Public certificates in a container */
273 if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
275 publiccertcontainer = 1;
276 contents = NULL;
277 path[0] = '\0';
278 if(!signature_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
279 publiccertcontainer = 0;
281 if(publiccertcontainer && !IS_REMOTE(path)
282 && ps_global->VAR_OPER_DIR
283 && !in_dir(ps_global->VAR_OPER_DIR, path)){
284 q_status_message2(SM_ORDER | SM_DING, 3, 4,
285 /* TRANSLATORS: First arg is the directory name, second is
286 the file user wants to read but can't. */
287 _("Can't read file outside %s: %s"),
288 ps_global->VAR_OPER_DIR, path);
289 publiccertcontainer = 0;
292 if(publiccertcontainer
293 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
294 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
296 !(contents = read_file(path, READ_FROM_LOCALE)))
297 publiccertcontainer = 0;
300 if(publiccertcontainer && path[0]){
301 ps_global->smime->publictype = Container;
302 ps_global->smime->publicpath = cpystr(path);
304 if(contents){
305 ps_global->smime->publiccontent = contents;
306 ps_global->smime->publiccertlist = mem_to_certlist(contents);
311 /* Public certificates in a directory of files */
312 if(!publiccertcontainer){
313 ps_global->smime->publictype = Directory;
315 path[0] = '\0';
316 if(!(signature_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
317 && !IS_REMOTE(path)))
318 ps_global->smime->publictype = Nada;
319 else if(can_access(path, ACCESS_EXISTS)){
320 if(our_mkpath(path, 0700)){
321 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
322 ps_global->smime->publictype = Nada;
326 if(ps_global->smime->publictype == Directory)
327 ps_global->smime->publicpath = cpystr(path);
330 #ifdef APPLEKEYCHAIN
332 #endif /* APPLEKEYCHAIN */
334 /* private keys in a container */
335 if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
337 privatekeycontainer = 1;
338 contents = NULL;
339 path[0] = '\0';
340 if(!signature_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
341 privatekeycontainer = 0;
343 if(privatekeycontainer && !IS_REMOTE(path)
344 && ps_global->VAR_OPER_DIR
345 && !in_dir(ps_global->VAR_OPER_DIR, path)){
346 q_status_message2(SM_ORDER | SM_DING, 3, 4,
347 /* TRANSLATORS: First arg is the directory name, second is
348 the file user wants to read but can't. */
349 _("Can't read file outside %s: %s"),
350 ps_global->VAR_OPER_DIR, path);
351 privatekeycontainer = 0;
354 if(privatekeycontainer
355 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
356 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
358 !(contents = read_file(path, READ_FROM_LOCALE)))
359 privatekeycontainer = 0;
362 if(privatekeycontainer && path[0]){
363 ps_global->smime->privatetype = Container;
364 ps_global->smime->privatepath = cpystr(path);
366 if(contents){
367 ps_global->smime->privatecontent = contents;
368 ps_global->smime->personal_certs = mem_to_personal_certs(contents);
373 /* private keys in a directory of files */
374 if(!privatekeycontainer){
375 PERSONAL_CERT *result = NULL;
377 ps_global->smime->privatetype = Directory;
379 path[0] = '\0';
380 if(!(signature_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
381 && !IS_REMOTE(path)))
382 ps_global->smime->privatetype = Nada;
383 else if(can_access(path, ACCESS_EXISTS)){
384 if(our_mkpath(path, 0700)){
385 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
386 ps_global->smime->privatetype = Nada;
390 if(ps_global->smime->privatetype == Directory){
391 char buf2[MAXPATH];
392 struct dirent *d;
393 DIR *dirp;
395 ps_global->smime->privatepath = cpystr(path);
396 dirp = opendir(path);
397 if(dirp){
399 while((d=readdir(dirp)) != NULL){
400 X509 *cert;
401 size_t ll;
403 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, ".key")){
405 /* copy file name to temp buffer */
406 strncpy(buf2, d->d_name, sizeof(buf2)-1);
407 buf2[sizeof(buf2)-1] = '\0';
408 /* chop off ".key" trailier */
409 buf2[strlen(buf2)-4] = 0;
410 /* Look for certificate */
411 cert = get_cert_for(buf2);
413 if(cert){
414 PERSONAL_CERT *pc;
416 /* create a new PERSONAL_CERT, fill it in */
418 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
419 pc->cert = cert;
420 pc->name = cpystr(buf2);
422 /* Try to load the key with an empty password */
423 pc->key = load_key(pc, "");
425 pc->next = result;
426 result = pc;
431 closedir(dirp);
435 ps_global->smime->personal_certs = result;
438 /* extra cacerts in a container */
439 if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
441 cacertcontainer = 1;
442 contents = NULL;
443 path[0] = '\0';
444 if(!signature_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
445 cacertcontainer = 0;
447 if(cacertcontainer && !IS_REMOTE(path)
448 && ps_global->VAR_OPER_DIR
449 && !in_dir(ps_global->VAR_OPER_DIR, path)){
450 q_status_message2(SM_ORDER | SM_DING, 3, 4,
451 /* TRANSLATORS: First arg is the directory name, second is
452 the file user wants to read but can't. */
453 _("Can't read file outside %s: %s"),
454 ps_global->VAR_OPER_DIR, path);
455 cacertcontainer = 0;
458 if(cacertcontainer
459 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
460 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
462 !(contents = read_file(path, READ_FROM_LOCALE)))
463 cacertcontainer = 0;
466 if(cacertcontainer && path[0]){
467 ps_global->smime->catype = Container;
468 ps_global->smime->capath = cpystr(path);
469 ps_global->smime->cacontent = contents;
473 if(!cacertcontainer){
474 ps_global->smime->catype = Directory;
476 path[0] = '\0';
477 if(!(signature_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
478 && !IS_REMOTE(path)))
479 ps_global->smime->catype = Nada;
480 else if(can_access(path, ACCESS_EXISTS)){
481 if(our_mkpath(path, 0700)){
482 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
483 ps_global->smime->catype = Nada;
487 if(ps_global->smime->catype == Directory)
488 ps_global->smime->capath = cpystr(path);
494 copy_publiccert_dir_to_container(void)
496 return(copy_dir_to_container(Public));
501 copy_publiccert_container_to_dir(void)
503 return(copy_container_to_dir(Public));
508 copy_privatecert_dir_to_container(void)
510 return(copy_dir_to_container(Private));
515 copy_privatecert_container_to_dir(void)
517 return(copy_container_to_dir(Private));
522 copy_cacert_dir_to_container(void)
524 return(copy_dir_to_container(CACert));
529 copy_cacert_container_to_dir(void)
531 return(copy_container_to_dir(CACert));
536 * returns 0 on success, -1 on failure
539 copy_dir_to_container(WhichCerts which)
541 int ret = 0;
542 BIO *bio_out = NULL, *bio_in = NULL;
543 char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
544 char *tempfile = NULL;
545 DIR *dirp;
546 struct dirent *d;
547 REMDATA_S *rd = NULL;
548 char *configdir = NULL;
549 char *configpath = NULL;
550 char *filesuffix = NULL;
552 dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
553 smime_init();
555 srcpath[0] = '\0';
556 dstpath[0] = '\0';
557 file[0] = '\0';
558 emailaddr[0] = '\0';
560 if(which == Public){
561 configdir = ps_global->VAR_PUBLICCERT_DIR;
562 configpath = ps_global->smime->publicpath;
563 filesuffix = ".crt";
565 else if(which == Private){
566 configdir = ps_global->VAR_PRIVATEKEY_DIR;
567 configpath = ps_global->smime->privatepath;
568 filesuffix = ".key";
570 else if(which == CACert){
571 configdir = ps_global->VAR_CACERT_DIR;
572 configpath = ps_global->smime->capath;
573 filesuffix = ".crt";
576 if(!(configdir && configdir[0])){
577 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
578 return -1;
581 if(!(configpath && configpath[0])){
582 #ifdef APPLEKEYCHAIN
583 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
584 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
585 return -1;
587 #endif /* APPLEKEYCHAIN */
588 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
589 return -1;
592 if(!(filesuffix && strlen(filesuffix) == 4)){
593 return -1;
598 * If there is a legit directory to read from set up the
599 * container file to write to.
601 if(signature_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
603 if(IS_REMOTE(configpath)){
604 rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
605 NULL, "Error: ",
606 _("Can't access remote smime configuration."));
607 if(!rd)
608 return -1;
610 (void) rd_read_metadata(rd);
612 if(rd->access == MaybeRorW){
613 if(rd->read_status == 'R')
614 rd->access = ReadOnly;
615 else
616 rd->access = ReadWrite;
619 if(rd->access != NoExists){
621 rd_check_remvalid(rd, 1L);
624 * If the cached info says it is readonly but
625 * it looks like it's been fixed now, change it to readwrite.
627 if(rd->read_status == 'R'){
628 rd_check_readonly_access(rd);
629 if(rd->read_status == 'W'){
630 rd->access = ReadWrite;
631 rd->flags |= REM_OUTOFDATE;
633 else
634 rd->access = ReadOnly;
638 if(rd->flags & REM_OUTOFDATE){
639 if(rd_update_local(rd) != 0){
641 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
642 rd_close_remdata(&rd);
643 return -1;
646 else
647 rd_open_remote(rd);
649 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
650 rd_close_remdata(&rd);
651 return -1;
654 rd->flags |= DO_REMTRIM;
656 strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
657 dstpath[sizeof(dstpath)-1] = '\0';
659 else{
660 strncpy(dstpath, configpath, sizeof(dstpath)-1);
661 dstpath[sizeof(dstpath)-1] = '\0';
665 * dstpath is either the local Container file or the local cache file
666 * for the remote Container file.
668 tempfile = tempfile_in_same_dir(dstpath, "az", NULL);
672 * If there is a legit directory to read from and a tempfile
673 * to write to we continue.
675 if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
677 dirp = opendir(srcpath);
678 if(dirp){
680 while((d=readdir(dirp)) && !ret){
681 size_t ll;
683 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, filesuffix)){
685 /* copy file name to temp buffer */
686 strncpy(emailaddr, d->d_name, sizeof(emailaddr)-1);
687 emailaddr[sizeof(emailaddr)-1] = '\0';
688 /* chop off suffix trailier */
689 emailaddr[strlen(emailaddr)-4] = 0;
692 * This is the separator between the contents of
693 * different files.
695 if(which == CACert){
696 if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
697 && (BIO_puts(bio_out, emailaddr) > 0)
698 && (BIO_puts(bio_out, "\n") > 0)))
699 ret = -1;
701 else{
702 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
703 && (BIO_puts(bio_out, emailaddr) > 0)
704 && (BIO_puts(bio_out, "\n") > 0)))
705 ret = -1;
708 /* read then write contents of file */
709 build_path(file, srcpath, d->d_name, sizeof(file));
710 if(!(bio_in = BIO_new_file(file, "r")))
711 ret = -1;
713 if(!ret){
714 int good_stuff = 0;
716 while(BIO_gets(bio_in, line, sizeof(line)) > 0){
717 if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
718 good_stuff = 1;
720 if(good_stuff)
721 BIO_puts(bio_out, line);
723 if(strncmp("-----END", line, strlen("-----END")) == 0)
724 good_stuff = 0;
728 BIO_free(bio_in);
732 closedir(dirp);
735 BIO_free(bio_out);
737 if(!ret){
738 if(rename_file(tempfile, dstpath) < 0){
739 q_status_message2(SM_ORDER, 3, 3,
740 _("Can't rename %s to %s"), tempfile, dstpath);
741 ret = -1;
744 /* if the container is remote, copy it */
745 if(!ret && IS_REMOTE(configpath)){
746 int e;
747 char datebuf[200];
749 datebuf[0] = '\0';
751 if((e = rd_update_remote(rd, datebuf)) != 0){
752 if(e == -1){
753 q_status_message2(SM_ORDER | SM_DING, 3, 5,
754 _("Error opening temporary smime file %s: %s"),
755 rd->lf, error_description(errno));
756 dprint((1,
757 "write_remote_smime: error opening temp file %s\n",
758 rd->lf ? rd->lf : "?"));
760 else{
761 q_status_message2(SM_ORDER | SM_DING, 3, 5,
762 _("Error copying to %s: %s"),
763 rd->rn, error_description(errno));
764 dprint((1,
765 "write_remote_smime: error copying from %s to %s\n",
766 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
769 q_status_message(SM_ORDER | SM_DING, 5, 5,
770 _("Copy of smime key to remote folder failed, NOT saved remotely"));
772 else{
773 rd_update_metadata(rd, datebuf);
774 rd->read_status = 'W';
777 rd_close_remdata(&rd);
782 if(tempfile)
783 fs_give((void **) &tempfile);
785 return ret;
790 * returns 0 on success, -1 on failure
793 copy_container_to_dir(WhichCerts which)
795 char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
796 char iobuf[4096];
797 char *contents = NULL;
798 char *leader = NULL;
799 char *filesuffix = NULL;
800 char *configdir = NULL;
801 char *configpath = NULL;
802 char *tempfile = NULL;
803 char *p, *q, *line, *name, *certtext, *save_p;
804 int len;
805 BIO *in, *out;
807 dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
808 smime_init();
810 path[0] = '\0';
812 if(which == Public){
813 leader = EMAILADDRLEADER;
814 contents = ps_global->smime->publiccontent;
815 configdir = ps_global->VAR_PUBLICCERT_DIR;
816 configpath = ps_global->smime->publicpath;
817 filesuffix = ".crt";
818 if(!(configpath && configpath[0])){
819 #ifdef APPLEKEYCHAIN
820 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
821 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
822 return -1;
824 #endif /* APPLEKEYCHAIN */
825 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
826 return -1;
829 fs_give((void **) &ps_global->smime->publicpath);
831 path[0] = '\0';
832 if(!(signature_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
833 && !IS_REMOTE(path))){
834 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
835 return -1;
838 if(can_access(path, ACCESS_EXISTS)){
839 if(our_mkpath(path, 0700)){
840 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
841 return -1;
845 ps_global->smime->publicpath = cpystr(path);
846 configpath = ps_global->smime->publicpath;
848 else if(which == Private){
849 leader = EMAILADDRLEADER;
850 contents = ps_global->smime->privatecontent;
851 configdir = ps_global->VAR_PRIVATEKEY_DIR;
852 configpath = ps_global->smime->privatepath;
853 filesuffix = ".key";
854 if(!(configpath && configpath[0])){
855 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
856 return -1;
859 fs_give((void **) &ps_global->smime->privatepath);
861 path[0] = '\0';
862 if(!(signature_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
863 && !IS_REMOTE(path))){
864 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
865 return -1;
868 if(can_access(path, ACCESS_EXISTS)){
869 if(our_mkpath(path, 0700)){
870 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
871 return -1;
875 ps_global->smime->privatepath = cpystr(path);
876 configpath = ps_global->smime->privatepath;
878 else if(which == CACert){
879 leader = CACERTSTORELEADER;
880 contents = ps_global->smime->cacontent;
881 configdir = ps_global->VAR_CACERT_DIR;
882 configpath = ps_global->smime->capath;
883 filesuffix = ".crt";
884 if(!(configpath && configpath[0])){
885 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
886 return -1;
889 fs_give((void **) &ps_global->smime->capath);
891 path[0] = '\0';
892 if(!(signature_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
893 && !IS_REMOTE(path))){
894 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
895 return -1;
898 if(can_access(path, ACCESS_EXISTS)){
899 if(our_mkpath(path, 0700)){
900 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
901 return -1;
905 ps_global->smime->capath = cpystr(path);
906 configpath = ps_global->smime->capath;
909 if(!(configdir && configdir[0])){
910 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
911 return -1;
914 if(!(configpath && configpath[0])){
915 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
916 return -1;
919 if(!(filesuffix && strlen(filesuffix) == 4)){
920 return -1;
924 if(contents && *contents){
925 for(p = contents; *p != '\0';){
926 line = p;
928 while(*p && *p != '\n')
929 p++;
931 save_p = NULL;
932 if(*p == '\n'){
933 save_p = p;
934 *p++ = '\0';
937 if(strncmp(leader, line, strlen(leader)) == 0){
938 name = line + strlen(leader);
939 certtext = p;
940 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
941 if((q = strstr(certtext, leader)) != NULL){
942 p = q;
944 else{ /* end of file */
945 q = certtext + strlen(certtext);
946 p = q;
949 strncpy(buf, name, sizeof(buf)-5);
950 buf[sizeof(buf)-5] = '\0';
951 strncat(buf, filesuffix, 5);
952 build_path(file, configpath, buf, sizeof(file));
954 in = BIO_new_mem_buf(certtext, q-certtext);
955 if(in){
956 tempfile = tempfile_in_same_dir(file, "az", NULL);
957 out = NULL;
958 if(tempfile)
959 out = BIO_new_file(tempfile, "w");
961 if(out){
962 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
963 BIO_write(out, iobuf, len);
965 BIO_free(out);
967 if(rename_file(tempfile, file) < 0){
968 q_status_message2(SM_ORDER, 3, 3,
969 _("Can't rename %s to %s"),
970 tempfile, file);
971 return -1;
974 fs_give((void **) &tempfile);
977 BIO_free(in);
982 if(save_p)
983 *save_p = '\n';
987 return 0;
991 #ifdef APPLEKEYCHAIN
994 copy_publiccert_container_to_keychain(void)
996 /* NOT IMPLEMNTED */
997 return -1;
1001 copy_publiccert_keychain_to_container(void)
1003 /* NOT IMPLEMNTED */
1004 return -1;
1007 #endif /* APPLEKEYCHAIN */
1011 * Get a pointer to a string describing the most recent OpenSSL error.
1012 * It's statically allocated, so don't change or attempt to free it.
1014 static const char *
1015 openssl_error_string(void)
1017 char *errs;
1018 const char *data = NULL;
1019 long errn;
1021 errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
1022 errs = (char*) ERR_reason_error_string(errn);
1024 if(errs)
1025 return errs;
1026 else if(data)
1027 return data;
1029 return "unknown error";
1033 /* Return true if the body looks like a PKCS7 object */
1035 is_pkcs7_body(BODY *body)
1037 int result;
1039 result = body->type==TYPEAPPLICATION &&
1040 body->subtype &&
1041 (strucmp(body->subtype,"pkcs7-mime")==0 ||
1042 strucmp(body->subtype,"x-pkcs7-mime")==0 ||
1043 strucmp(body->subtype,"pkcs7-signature")==0 ||
1044 strucmp(body->subtype,"x-pkcs7-signature")==0);
1046 return result;
1050 #ifdef notdef
1052 * Somewhat useful debug utility to dump the contents of a BIO to a file.
1053 * Note that a memory BIO will have its contents eliminated after they
1054 * are read so this will break the next step.
1056 static void
1057 dump_bio_to_file(BIO *in, char *filename)
1059 char iobuf[4096];
1060 int len;
1061 BIO *out;
1063 out = BIO_new_file(filename, "w");
1065 if(out){
1066 if(BIO_method_type(in) != BIO_TYPE_MEM)
1067 BIO_reset(in);
1069 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1070 BIO_write(out, iobuf, len);
1072 BIO_free(out);
1075 BIO_reset(in);
1077 #endif
1081 * Recursively stash a pointer to the decrypted data in our
1082 * manufactured body.
1084 static void
1085 create_local_cache(char *base, BODY *b)
1087 if(b->type==TYPEMULTIPART){
1088 PART *p;
1090 #if 0
1091 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
1092 #else
1094 * We don't really want to copy the real body contents. It shouldn't be
1095 * used, and in the case of a message with attachments, we'll be
1096 * duplicating the files multiple times.
1098 cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
1099 #endif
1101 for(p=b->nested.part; p; p=p->next)
1102 create_local_cache(base, (BODY*) p);
1104 else{
1105 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
1110 static long
1111 rfc822_output_func(void *b, char *string)
1113 BIO *bio = (BIO *) b;
1115 return((BIO_puts(bio, string) > 0) ? 1L : 0L);
1120 * Attempt to load the private key for the given PERSONAL_CERT.
1121 * This sets the appropriate passphrase globals in order to
1122 * interact with the user correctly.
1124 static int
1125 load_private_key(PERSONAL_CERT *pcert)
1127 if(!pcert->key){
1129 /* Try empty password by default */
1130 char *password = "";
1132 if(ps_global->smime
1133 && (ps_global->smime->need_passphrase
1134 || ps_global->smime->entered_passphrase)){
1135 /* We've already been in here and discovered we need a different password */
1137 if(ps_global->smime->entered_passphrase)
1138 password = (char *) ps_global->smime->passphrase; /* already entered */
1139 else
1140 return 0;
1143 ERR_clear_error();
1145 if(!(pcert->key = load_key(pcert, password))){
1146 long err = ERR_get_error();
1148 /* Couldn't load key... */
1150 if(ps_global->smime && ps_global->smime->entered_passphrase){
1152 /* The user got the password wrong maybe? */
1154 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
1155 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
1156 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
1157 else
1158 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
1160 /* This passphrase is no good; forget it */
1161 ps_global->smime->entered_passphrase = 0;
1164 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
1165 if(ps_global->smime)
1166 ps_global->smime->need_passphrase = 1;
1168 if(ps_global->smime){
1169 if(ps_global->smime->passphrase_emailaddr){
1170 int i;
1171 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
1172 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
1173 fs_give((void **) ps_global->smime->passphrase_emailaddr);
1176 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
1179 return 0;
1181 else{
1182 /* This key will be cached, so we won't be called again */
1183 if(ps_global->smime){
1184 ps_global->smime->entered_passphrase = 0;
1185 ps_global->smime->need_passphrase = 0;
1189 return 1;
1192 return 0;
1196 static void
1197 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename)
1199 b->type = TYPEAPPLICATION;
1200 b->subtype = cpystr(type);
1201 b->encoding = ENCBINARY;
1202 b->description = cpystr(description);
1204 b->disposition.type = cpystr("attachment");
1205 set_parameter(&b->disposition.parameter, "filename", filename);
1207 set_parameter(&b->parameter, "name", filename);
1212 * Look for a personal certificate matching the
1213 * given address
1215 PERSONAL_CERT *
1216 match_personal_cert_to_email(ADDRESS *a)
1218 PERSONAL_CERT *pcert = NULL;
1219 char buf[MAXPATH];
1220 char **email;
1221 int i, done;
1223 if(!a || !a->mailbox || !a->host)
1224 return NULL;
1226 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
1228 if(ps_global->smime){
1229 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
1230 pcert;
1231 pcert=pcert->next){
1233 if(!pcert->cert)
1234 continue;
1236 email = get_x509_subject_email(pcert->cert);
1238 done = 0;
1239 if(email != NULL){
1240 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
1241 if(email[i] != NULL) done++;
1242 for(i = 0; email[i] != NULL; i++)
1243 fs_give((void **)&email[i]);
1244 fs_give((void **)email);
1247 if(done > 0)
1248 break;
1252 return pcert;
1257 * Look for a personal certificate matching the from
1258 * (or reply_to? in the given envelope)
1260 PERSONAL_CERT *
1261 match_personal_cert(ENVELOPE *env)
1263 PERSONAL_CERT *pcert;
1265 pcert = match_personal_cert_to_email(env->reply_to);
1266 if(!pcert)
1267 pcert = match_personal_cert_to_email(env->from);
1269 return pcert;
1274 * Flatten the given body into its MIME representation.
1275 * Return the result in a BIO.
1277 static BIO *
1278 body_to_bio(BODY *body)
1280 BIO *bio = NULL;
1281 int len;
1283 bio = BIO_new(BIO_s_mem());
1284 if(!bio)
1285 return NULL;
1287 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
1288 pine_write_body_header(body, rfc822_output_func, bio);
1289 pine_rfc822_output_body(body, rfc822_output_func, bio);
1292 * Now need to truncate by two characters since the above
1293 * appends CRLF.
1295 if((len=BIO_ctrl_pending(bio)) > 1){
1296 BUF_MEM *biobuf = NULL;
1298 BIO_get_mem_ptr(bio, &biobuf);
1299 if(biobuf){
1300 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
1304 return bio;
1308 static BIO *
1309 bio_from_store(STORE_S *store)
1311 BIO *ret = NULL;
1313 if(store && store->src == BioType && store->txt){
1314 ret = (BIO *) store->txt;
1317 return(ret);
1321 * Encrypt file; given a path (char *) fp, replace the file
1322 * by an encrypted version of it. If (char *) text is not null, then
1323 * replace the text of (char *) fp by the encrypted version of (char *) text.
1326 encrypt_file(char *fp, char *text)
1328 const EVP_CIPHER *cipher = NULL;
1329 STACK_OF(X509) *encerts = NULL;
1330 X509 *cert;
1331 PERSONAL_CERT *pcert;
1332 BIO *in;
1333 PKCS7 *p7 = NULL;
1334 FILE *fpp;
1335 int rv = 0;
1337 smime_init();
1338 if((pcert = ps_global->smime->personal_certs) == NULL)
1339 return 0;
1341 cipher = EVP_aes_256_cbc();
1342 encerts = sk_X509_new_null();
1344 if((cert = get_cert_for(pcert->name)) != NULL)
1345 sk_X509_push(encerts, cert);
1346 else
1347 goto end;
1349 if(text){
1350 in = BIO_new(BIO_s_mem());
1351 if(in == NULL)
1352 goto end;
1353 (void) BIO_reset(in);
1354 BIO_puts(in, text);
1356 else{
1357 if(!(in = BIO_new_file(fp, "rb")))
1358 goto end;
1360 BIO_read_filename(in, fp);
1363 if((p7 = PKCS7_encrypt(encerts, in, cipher, 0)) == NULL)
1364 goto end;
1365 BIO_set_close(in, BIO_CLOSE);
1366 BIO_free(in);
1367 if(!(in = BIO_new_file(fp, "w")))
1368 goto end;
1369 BIO_reset(in);
1370 rv = PEM_write_bio_PKCS7(in, p7);
1371 BIO_flush(in);
1373 end:
1374 BIO_free(in);
1375 PKCS7_free(p7);
1376 sk_X509_pop_free(encerts, X509_free);
1378 return rv;
1382 * Encrypt a message on the way out. Called from call_mailer in send.c
1383 * The body may be reallocated.
1386 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
1388 PKCS7 *p7 = NULL;
1389 BIO *in = NULL;
1390 BIO *out = NULL;
1391 const EVP_CIPHER *cipher = NULL;
1392 STACK_OF(X509) *encerts = NULL;
1393 STORE_S *outs = NULL;
1394 PINEFIELD *pf;
1395 ADDRESS *a;
1396 BODY *body = *bodyP;
1397 BODY *newBody = NULL;
1398 int result = 0;
1400 dprint((9, "encrypt_outgoing_message()"));
1401 smime_init();
1403 cipher = EVP_aes_256_cbc();
1405 encerts = sk_X509_new_null();
1407 /* Look for a certificate for each of the recipients */
1408 for(pf = header->local; pf && pf->name; pf = pf->next)
1409 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
1410 for(a=*pf->addr; a; a=a->next){
1411 X509 *cert;
1412 char buf[MAXPATH];
1414 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
1416 cert = get_cert_for(buf);
1417 if(cert)
1418 sk_X509_push(encerts,cert);
1419 else{
1420 q_status_message2(SM_ORDER, 1, 1,
1421 _("Unable to find certificate for <%s@%s>"),
1422 a->mailbox, a->host);
1423 goto end;
1429 in = body_to_bio(body);
1431 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
1433 outs = so_get(BioType, NULL, EDIT_ACCESS);
1434 out = bio_from_store(outs);
1436 i2d_PKCS7_bio(out, p7);
1437 (void) BIO_flush(out);
1439 so_seek(outs, 0, SEEK_SET);
1441 newBody = mail_newbody();
1443 newBody->type = TYPEAPPLICATION;
1444 newBody->subtype = cpystr("pkcs7-mime");
1445 newBody->encoding = ENCBINARY;
1447 newBody->disposition.type = cpystr("attachment");
1448 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
1450 newBody->description = cpystr("S/MIME Encrypted Message");
1451 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
1452 set_parameter(&newBody->parameter, "name", "smime.p7m");
1454 newBody->contents.text.data = (unsigned char *) outs;
1456 *bodyP = newBody;
1458 result = 1;
1460 end:
1462 BIO_free(in);
1463 PKCS7_free(p7);
1464 sk_X509_pop_free(encerts, X509_free);
1466 dprint((9, "encrypt_outgoing_message returns %d", result));
1467 return result;
1472 * Plonk the contents (mime headers and body) of the given
1473 * section of a message to a BIO_s_mem BIO object.
1475 static BIO *
1476 raw_part_to_bio(long msgno, const char *section)
1478 unsigned long len;
1479 char *text;
1480 BIO *bio;
1482 bio = BIO_new(BIO_s_mem());
1484 if(bio){
1486 (void) BIO_reset(bio);
1488 /* First grab headers of the chap */
1489 text = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) section, &len, 0);
1491 if(text){
1492 BIO_write(bio, text, len);
1494 /** Now grab actual body */
1495 text = mail_fetch_body (ps_global->mail_stream, msgno, (char*) section, &len, 0);
1496 if(text){
1497 BIO_write(bio, text, len);
1499 else{
1500 BIO_free(bio);
1501 bio = NULL;
1505 else{
1506 BIO_free(bio);
1507 bio = NULL;
1511 return bio;
1516 Get (and decode) the body of the given section of msg
1518 static STORE_S*
1519 get_part_contents(long msgno, const char *section)
1521 long len;
1522 gf_io_t pc;
1523 STORE_S *store = NULL;
1524 char *err;
1526 store = so_get(CharStar, NULL, EDIT_ACCESS);
1527 if(store){
1528 gf_set_so_writec(&pc,store);
1530 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
1532 gf_clear_so_writec(store);
1534 so_seek(store, 0, SEEK_SET);
1536 if(err)
1537 so_give(&store);
1540 return store;
1544 static PKCS7 *
1545 get_pkcs7_from_part(long msgno,const char *section)
1547 STORE_S *store = NULL;
1548 PKCS7 *p7 = NULL;
1549 BIO *in = NULL;
1551 store = get_part_contents(msgno, section);
1553 if(store){
1554 if(store->src == CharStar){
1555 int len;
1558 * We're reaching inside the STORE_S structure. We should
1559 * probably have a way to get the length, instead.
1561 len = (int) (store->eod - store->dp);
1562 in = BIO_new_mem_buf(store->txt, len);
1564 else{ /* just copy it */
1565 unsigned char c;
1567 in = BIO_new(BIO_s_mem());
1568 (void) BIO_reset(in);
1570 so_seek(store, 0L, 0);
1571 while(so_readc(&c, store)){
1572 BIO_write(in, &c, 1);
1576 if(in){
1577 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
1578 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
1579 /* error */
1582 BIO_free(in);
1585 so_give(&store);
1588 return p7;
1593 * Try to verify a signature.
1595 * p7 - the pkcs7 object to verify
1596 * in - the plain data to verify (NULL if not detached)
1597 * out - BIO to which to write the opaque data
1599 static int
1600 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out)
1602 STACK_OF(X509) *otherCerts = NULL;
1603 int result;
1604 const char *data;
1605 long err;
1607 #if 0
1608 dump_bio_to_file(in,"/tmp/verified-data");
1609 #endif
1611 if(!s_cert_store){
1612 q_status_message(SM_ORDER | SM_DING, 2, 2,
1613 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
1615 return -1;
1618 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, 0);
1620 if(result){
1621 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
1623 else{
1624 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
1626 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
1628 /* Retry verification so we can get the plain text */
1629 /* Might be better to reimplement PKCS7_verify here? */
1631 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY);
1634 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1635 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
1638 /* now try to extract the certificates of any signers */
1640 STACK_OF(X509) *signers;
1641 int i;
1643 signers = PKCS7_get0_signers(p7, NULL, 0);
1645 if(signers)
1646 for(i=0; i<sk_X509_num(signers); i++){
1647 char **email;
1648 X509 *x = sk_X509_value(signers,i);
1649 X509 *cert;
1651 if(!x)
1652 continue;
1654 email = get_x509_subject_email(x);
1656 if(email){
1657 int i;
1658 for(i = 0; email[i] != NULL; i++){
1659 cert = get_cert_for(email[i]);
1660 if(cert)
1661 X509_free(cert);
1662 else
1663 save_cert_for(email[i], x);
1664 fs_give((void **) &email[i]);
1666 fs_give((void **) email);
1670 sk_X509_free(signers);
1673 return result;
1677 void
1678 free_smime_body_sparep(void **sparep)
1680 if(sparep && *sparep){
1681 PKCS7_free((PKCS7 *) (*sparep));
1682 *sparep = NULL;
1688 * Given a multipart body of type multipart/signed, attempt to verify it.
1689 * Returns non-zero if the body was changed.
1691 static int
1692 do_detached_signature_verify(BODY *b, long msgno, char *section)
1694 PKCS7 *p7 = NULL;
1695 BIO *in = NULL;
1696 PART *p;
1697 int result, modified_the_body = 0;
1698 char newSec[100];
1699 char *what_we_did;
1701 dprint((9, "do_detached_signature_verify(msgno=%ld type=%d subtype=%s section=%s)", msgno, b->type, b->subtype ? b->subtype : "NULL", (section && *section) ? section : (section != NULL) ? "Top" : "NULL"));
1702 smime_init();
1704 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
1705 in = raw_part_to_bio(msgno, newSec);
1707 if(in){
1709 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
1710 p7 = get_pkcs7_from_part(msgno, newSec);
1712 if(!p7)
1713 goto end;
1715 result = do_signature_verify(p7, in, NULL);
1717 if(b->subtype)
1718 fs_give((void**) &b->subtype);
1720 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
1721 b->encoding = ENC8BIT;
1723 if(b->description)
1724 fs_give ((void**) &b->description);
1726 what_we_did = result ? _("This message was cryptographically signed.") :
1727 _("This message was cryptographically signed but the signature could not be verified.");
1729 b->description = cpystr(what_we_did);
1731 b->sparep = p7;
1732 p7 = NULL;
1734 p = b->nested.part;
1736 /* p is signed plaintext */
1737 if(p && p->next)
1738 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
1740 BIO_free(in);
1742 modified_the_body = 1;
1745 end:
1746 PKCS7_free(p7);
1748 return modified_the_body;
1752 PERSONAL_CERT *
1753 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
1755 PERSONAL_CERT *x = NULL;
1757 if(ps_global->smime){
1758 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
1759 X509 *mine;
1761 mine = x->cert;
1763 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,mine->cert_info->issuer) &&
1764 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,mine->cert_info->serialNumber)){
1765 break;
1770 return x;
1774 static PERSONAL_CERT *
1775 find_certificate_matching_pkcs7(PKCS7 *p7)
1777 int i;
1778 STACK_OF(PKCS7_RECIP_INFO) *recips;
1779 PERSONAL_CERT *x = NULL;
1781 recips = p7->d.enveloped->recipientinfo;
1783 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
1784 PKCS7_RECIP_INFO *ri;
1786 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
1788 if((x=find_certificate_matching_recip_info(ri))!=0){
1789 break;
1793 return x;
1796 /* decrypt an encrypted file.
1797 Args: fp - the path to the encrypted file.
1798 rv - a code that thells the caller what happened inside the function
1799 Returns the decoded text allocated in a char *, whose memory must be
1800 freed by caller
1803 char *
1804 decrypt_file(char *fp, int *rv)
1806 PKCS7 *p7 = NULL;
1807 char *text, *tmp;
1808 BIO *in = NULL, *out = NULL;
1809 EVP_PKEY *pkey = NULL, *key = NULL;
1810 PERSONAL_CERT *pcert = NULL;
1811 X509 *recip, *cert;
1812 STORE_S *outs = NULL, *store, *ins;
1813 int i, j;
1814 long unsigned int len;
1815 void *ret;
1817 smime_init();
1819 if((text = read_file(fp, 0)) == NULL)
1820 return NULL;
1822 tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1);
1823 for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0'
1824 && text[i] != '-'; j++, i++)
1825 tmp[j] = text[i];
1826 tmp[j] = '\0';
1828 ret = rfc822_base64(tmp, strlen(tmp), &len);
1830 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
1831 p7 = d2i_PKCS7_bio(in, NULL);
1832 BIO_free(in);
1835 if(text) fs_give((void **)&text);
1836 if(ret) fs_give((void **)&ret);
1838 if((pcert = ps_global->smime->personal_certs) == NULL)
1839 goto end;
1841 if((i = load_private_key(pcert)) == 0
1842 && ps_global->smime
1843 && ps_global->smime->need_passphrase
1844 && !ps_global->smime->already_auto_asked)
1845 for(; i == 0;){
1846 ps_global->smime->already_auto_asked = 1;
1847 if(pith_opt_smime_get_passphrase){
1848 switch((*pith_opt_smime_get_passphrase)()){
1849 case 0 : i = load_private_key(pcert);
1850 break;
1852 case 1 : i = -1;
1853 break;
1855 default: break; /* repeat until we cancel */
1858 else
1859 i = -2;
1862 if(rv) *rv = i;
1864 if((key = pcert->key) == NULL)
1865 goto end;
1867 recip = get_cert_for(pcert->name);
1868 out = BIO_new(BIO_s_mem());
1869 (void) BIO_reset(out);
1871 i = PKCS7_decrypt(p7, key, recip, out, 0);
1873 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
1874 forget_private_keys();
1876 if(i == 0){
1877 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
1878 (char*) openssl_error_string());
1879 goto end;
1882 BIO_get_mem_data(out, &tmp);
1884 text = cpystr(tmp);
1885 BIO_free(out);
1887 end:
1888 PKCS7_free(p7);
1890 return text;
1894 * Try to decode (decrypt or verify a signature) a PKCS7 body
1895 * Returns non-zero if something was changed.
1897 static int
1898 do_decoding(BODY *b, long msgno, const char *section)
1900 int modified_the_body = 0;
1901 BIO *out = NULL;
1902 PKCS7 *p7 = NULL;
1903 X509 *recip = NULL;
1904 EVP_PKEY *key = NULL;
1905 PERSONAL_CERT *pcert = NULL;
1906 char *what_we_did = "";
1907 char null[1];
1909 dprint((9, "do_decoding(msgno=%ld type=%d subtype=%s section=%s)", msgno, b->type, b->subtype ? b->subtype : "NULL", (section && *section) ? section : (section != NULL) ? "Top" : "NULL"));
1910 null[0] = '\0';
1911 smime_init();
1914 * Extract binary data from part to an in-memory store
1917 if(b->sparep){ /* already done */
1918 p7 = (PKCS7*) b->sparep;
1920 else{
1922 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
1923 if(!p7){
1924 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
1925 (char*) openssl_error_string());
1926 goto end;
1930 * Save the PKCS7 object for later dealings by the user interface.
1931 * It will be cleaned up when the body is garbage collected.
1933 b->sparep = p7;
1936 if(PKCS7_type_is_signed(p7)){
1937 int sigok;
1939 out = BIO_new(BIO_s_mem());
1940 (void) BIO_reset(out);
1941 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
1943 sigok = do_signature_verify(p7, NULL, out);
1945 /* shouldn't really duplicate these messages */
1946 what_we_did = sigok ? _("This message was cryptographically signed.") :
1947 _("This message was cryptographically signed but the signature could not be verified.");
1949 /* make sure it's null terminated */
1950 BIO_write(out, null, 1);
1952 else if(!PKCS7_type_is_enveloped(p7)){
1953 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
1954 goto end;
1956 else{ /* It *is* enveloped */
1957 int decrypt_result;
1959 what_we_did = _("This message was encrypted.");
1961 /* now need to find a cert that can decrypt this */
1962 pcert = find_certificate_matching_pkcs7(p7);
1964 if(!pcert){
1965 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
1966 goto end;
1969 recip = pcert->cert;
1971 if(!load_private_key(pcert)
1972 && ps_global->smime
1973 && ps_global->smime->need_passphrase
1974 && !ps_global->smime->already_auto_asked){
1975 /* Couldn't load key with blank password, ask user */
1976 ps_global->smime->already_auto_asked = 1;
1977 if(pith_opt_smime_get_passphrase){
1978 (*pith_opt_smime_get_passphrase)();
1979 load_private_key(pcert);
1983 key = pcert->key;
1984 if(!key)
1985 goto end;
1987 out = BIO_new(BIO_s_mem());
1988 (void) BIO_reset(out);
1989 BIO_puts(out, "MIME-Version: 1.0\r\n");
1991 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
1993 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
1994 forget_private_keys();
1996 if(!decrypt_result){
1997 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
1998 (char*) openssl_error_string());
1999 goto end;
2002 BIO_write(out, null, 1);
2006 * We've now produced a flattened MIME object in BIO out.
2007 * It needs to be turned back into a BODY.
2010 if(out){
2011 BODY *body;
2012 ENVELOPE *env;
2013 char *h = NULL;
2014 char *bstart;
2015 STRING s;
2016 BUF_MEM *bptr = NULL;
2018 BIO_get_mem_ptr(out, &bptr);
2019 if(bptr)
2020 h = bptr->data;
2022 /* look for start of body */
2023 bstart = strstr(h, "\r\n\r\n");
2025 if(!bstart){
2026 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
2028 else{
2029 bstart += 4; /* skip over CRLF*2 */
2031 INIT(&s, mail_string, bstart, strlen(bstart));
2032 rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
2033 mail_free_envelope(&env); /* Don't care about this */
2036 * Now convert original body (application/pkcs7-mime)
2037 * to a multipart body with one sub-part (the decrypted body).
2038 * Note that the sub-part may also be multipart!
2041 b->type = TYPEMULTIPART;
2042 if(b->subtype)
2043 fs_give((void**) &b->subtype);
2046 * This subtype is used in mailview.c to annotate the display of
2047 * encrypted or signed messages. We know for sure then that it's a PKCS7
2048 * part because the sparep field is set to the PKCS7 object (see above).
2050 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
2051 b->encoding = ENC8BIT;
2053 if(b->description)
2054 fs_give((void**) &b->description);
2056 b->description = cpystr(what_we_did);
2058 if(b->disposition.type)
2059 fs_give((void **) &b->disposition.type);
2061 if(b->contents.text.data)
2062 fs_give((void **) &b->contents.text.data);
2064 if(b->parameter)
2065 mail_free_body_parameter(&b->parameter);
2067 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
2068 b->nested.part = fs_get(sizeof(PART));
2069 b->nested.part->body = *body;
2070 b->nested.part->next = NULL;
2072 fs_give((void**) &body);
2075 * IMPORTANT BIT: set the body->contents.text.data elements to contain the decrypted
2076 * data. Otherwise, it'll try to load it from the original data. Eek.
2078 create_local_cache(bstart, &b->nested.part->body);
2080 modified_the_body = 1;
2084 end:
2085 if(out)
2086 BIO_free(out);
2088 return modified_the_body;
2093 * Recursively handle PKCS7 bodies in our message.
2095 * Returns non-zero if some fiddling was done.
2097 static int
2098 do_fiddle_smime_message(BODY *b, long msgno, char *section)
2100 int modified_the_body = 0;
2102 if(!b)
2103 return 0;
2105 dprint((9, "do_fiddle_smime_message(msgno=%ld type=%d subtype=%s section=%s)", msgno, b->type, b->subtype ? b->subtype : "NULL", (section && *section) ? section : (section != NULL) ? "Top" : "NULL"));
2107 if(is_pkcs7_body(b)){
2109 if(do_decoding(b, msgno, section)){
2111 * b should now be a multipart message:
2112 * fiddle it too in case it's been multiply-encrypted!
2115 /* fallthru */
2116 modified_the_body = 1;
2120 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
2122 PART *p;
2123 int partNum;
2124 char newSec[100];
2126 if(MIME_MULT_SIGNED(b->type, b->subtype)){
2130 * Ahah. We have a multipart signed entity.
2132 * Multipart/signed
2133 * part 1 (signed thing)
2134 * part 2 (the pkcs7 signature)
2136 * We're going to convert that to
2138 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
2139 * part 1 (signed thing)
2140 * part 2 has been freed
2142 * We also extract the signature from part 2 and save it
2143 * in the multipart body->sparep, and we add a description
2144 * in the multipart body->description.
2147 * The results of a decrypted message will be similar. It
2148 * will be
2150 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
2151 * part 1 (decrypted thing)
2154 modified_the_body += do_detached_signature_verify(b, msgno, section);
2156 else if(MIME_MSG(b->type, b->subtype)){
2157 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
2159 else{
2161 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
2163 /* Append part number to the section string */
2165 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
2167 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
2172 return modified_the_body;
2177 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
2178 * Returns non-zero if something was changed.
2181 fiddle_smime_message(BODY *b, long msgno)
2183 return do_fiddle_smime_message(b, msgno, "");
2187 /********************************************************************************/
2191 * Output a string in a distinctive style
2193 void
2194 gf_puts_uline(char *txt, gf_io_t pc)
2196 pc(TAG_EMBED); pc(TAG_BOLDON);
2197 gf_puts(txt, pc);
2198 pc(TAG_EMBED); pc(TAG_BOLDOFF);
2203 * Sign a message. Called from call_mailer in send.c.
2205 * This takes the header for the outgoing message as well as a pointer
2206 * to the current body (which may be reallocated).
2209 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach)
2211 STORE_S *outs = NULL;
2212 BODY *body = *bodyP;
2213 BODY *newBody = NULL;
2214 PART *p1 = NULL;
2215 PART *p2 = NULL;
2216 PERSONAL_CERT *pcert;
2217 BIO *in = NULL;
2218 BIO *out = NULL;
2219 PKCS7 *p7 = NULL;
2220 int result = 0;
2221 int flags = dont_detach ? 0 : PKCS7_DETACHED;
2223 dprint((9, "sign_outgoing_message()"));
2225 smime_init();
2227 /* Look for a private key matching the sender address... */
2229 pcert = match_personal_cert(header->env);
2231 if(!pcert){
2232 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
2233 goto end;
2236 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
2237 /* Couldn't load key with blank password, try again */
2238 if(pith_opt_smime_get_passphrase){
2239 (*pith_opt_smime_get_passphrase)();
2240 load_private_key(pcert);
2244 if(!pcert->key)
2245 goto end;
2247 in = body_to_bio(body);
2249 #ifdef notdef
2250 dump_bio_to_file(in,"/tmp/signed-data");
2251 #endif
2253 p7 = PKCS7_sign(pcert->cert, pcert->key, NULL, in, flags);
2255 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
2256 forget_private_keys();
2258 if(!p7){
2259 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
2260 goto end;
2263 outs = so_get(BioType, NULL, EDIT_ACCESS);
2264 out = bio_from_store(outs);
2266 i2d_PKCS7_bio(out, p7);
2267 (void) BIO_flush(out);
2269 so_seek(outs, 0, SEEK_SET);
2271 if((flags&PKCS7_DETACHED)==0){
2273 /* the simple case: the signed data is in the pkcs7 object */
2275 newBody = mail_newbody();
2277 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m");
2279 newBody->contents.text.data = (unsigned char *) outs;
2280 *bodyP = newBody;
2282 result = 1;
2284 else{
2287 * OK.
2288 * We have to create a new body as follows:
2290 * multipart/signed; blah blah blah
2291 * reference to existing body
2293 * pkcs7 object
2296 newBody = mail_newbody();
2298 newBody->type = TYPEMULTIPART;
2299 newBody->subtype = cpystr("signed");
2300 newBody->encoding = ENC7BIT;
2302 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
2303 set_parameter(&newBody->parameter, "micalg", "sha1");
2305 p1 = mail_newbody_part();
2306 p2 = mail_newbody_part();
2309 * This is nasty. We're just copying the body in here,
2310 * but since our newBody is freed at the end of call_mailer,
2311 * we mustn't let this body (the original one) be freed twice.
2313 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
2315 p1->next = p2;
2317 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s");
2318 p2->body.contents.text.data = (unsigned char *) outs;
2320 newBody->nested.part = p1;
2322 *bodyP = newBody;
2324 result = 1;
2327 end:
2329 PKCS7_free(p7);
2330 BIO_free(in);
2332 dprint((9, "sign_outgoing_message returns %d", result));
2333 return result;
2337 SMIME_STUFF_S *
2338 new_smime_struct(void)
2340 SMIME_STUFF_S *ret = NULL;
2342 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
2343 memset((void *) ret, 0, sizeof(*ret));
2344 ret->publictype = Nada;
2346 return ret;
2350 static void
2351 free_smime_struct(SMIME_STUFF_S **smime)
2353 if(smime && *smime){
2354 if((*smime)->passphrase_emailaddr){
2355 int i;
2356 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
2357 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
2358 fs_give((void **) (*smime)->passphrase_emailaddr);
2361 if((*smime)->publicpath)
2362 fs_give((void **) &(*smime)->publicpath);
2364 if((*smime)->publiccertlist)
2365 free_certlist(&(*smime)->publiccertlist);
2367 if((*smime)->publiccontent)
2368 fs_give((void **) &(*smime)->publiccontent);
2370 if((*smime)->privatepath)
2371 fs_give((void **) &(*smime)->privatepath);
2373 if((*smime)->personal_certs){
2374 PERSONAL_CERT *pc;
2376 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
2377 free_personal_certs(&pc);
2378 (*smime)->personal_certs = NULL;
2381 if((*smime)->privatecontent)
2382 fs_give((void **) &(*smime)->privatecontent);
2384 if((*smime)->capath)
2385 fs_give((void **) &(*smime)->capath);
2387 if((*smime)->cacontent)
2388 fs_give((void **) &(*smime)->cacontent);
2390 fs_give((void **) smime);
2394 #endif /* SMIME */