* New version 2.19.8
[alpine.git] / pith / smime.c
blob469a381e8cf42592afb23c971ddc7beb9060f4df
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 2013-2014 Eduardo Chappa
8 * Copyright 2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
20 * This is based on a contribution from Jonathan Paisley
22 * File: smime.c
23 * Author: paisleyj@dcs.gla.ac.uk
24 * Date: 01/2001
28 #include "../pith/headers.h"
30 #ifdef SMIME
32 #include "../pith/osdep/canaccess.h"
33 #include "../pith/helptext.h"
34 #include "../pith/store.h"
35 #include "../pith/status.h"
36 #include "../pith/detach.h"
37 #include "../pith/conf.h"
38 #include "../pith/smkeys.h"
39 #include "../pith/smime.h"
40 #include "../pith/mailpart.h"
41 #include "../pith/reply.h"
42 #include "../pith/tempfile.h"
43 #include "../pith/readfile.h"
44 #include "../pith/remote.h"
46 #include <openssl/buffer.h>
49 typedef enum {Public, Private, CACert} WhichCerts;
52 /* internal prototypes */
53 static void forget_private_keys(void);
54 static int app_RAND_load_file(const char *file);
55 static void openssl_extra_randomness(void);
56 static int app_RAND_write_file(const char *file);
57 static void smime_init(void);
58 static const char *openssl_error_string(void);
59 static void create_local_cache(char *h, char *base, BODY *b);
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, int silent);
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 *h, 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(h, base, (BODY*) p);
1104 else{
1105 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
1106 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
1111 static long
1112 rfc822_output_func(void *b, char *string)
1114 BIO *bio = (BIO *) b;
1116 return(string ? *string ? (BIO_puts(bio, string) > 0 ? 1L : 0L)
1117 : (BIO_puts(bio, string) >= 0 ? 1L : 0L)
1118 : 0L);
1123 * Attempt to load the private key for the given PERSONAL_CERT.
1124 * This sets the appropriate passphrase globals in order to
1125 * interact with the user correctly.
1127 static int
1128 load_private_key(PERSONAL_CERT *pcert)
1130 if(!pcert->key){
1132 /* Try empty password by default */
1133 char *password = "";
1135 if(ps_global->smime
1136 && (ps_global->smime->need_passphrase
1137 || ps_global->smime->entered_passphrase)){
1138 /* We've already been in here and discovered we need a different password */
1140 if(ps_global->smime->entered_passphrase)
1141 password = (char *) ps_global->smime->passphrase; /* already entered */
1142 else
1143 return 0;
1146 ERR_clear_error();
1148 if(!(pcert->key = load_key(pcert, password))){
1149 long err = ERR_get_error();
1151 /* Couldn't load key... */
1153 if(ps_global->smime && ps_global->smime->entered_passphrase){
1155 /* The user got the password wrong maybe? */
1157 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
1158 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
1159 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
1160 else
1161 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
1163 /* This passphrase is no good; forget it */
1164 ps_global->smime->entered_passphrase = 0;
1167 if(ps_global->smime){
1168 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
1169 ps_global->smime->need_passphrase = 1;
1170 if(ps_global->smime->passphrase_emailaddr){
1171 int i;
1172 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
1173 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
1174 fs_give((void **) ps_global->smime->passphrase_emailaddr);
1177 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
1180 return 0;
1182 else{
1183 /* This key will be cached, so we won't be called again */
1184 if(ps_global->smime){
1185 ps_global->smime->entered_passphrase = 0;
1186 ps_global->smime->need_passphrase = 0;
1190 return 1;
1193 return 0;
1197 static void
1198 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename)
1200 b->type = TYPEAPPLICATION;
1201 b->subtype = cpystr(type);
1202 b->encoding = ENCBINARY;
1203 b->description = cpystr(description);
1205 b->disposition.type = cpystr("attachment");
1206 set_parameter(&b->disposition.parameter, "filename", filename);
1208 set_parameter(&b->parameter, "name", filename);
1213 * Look for a personal certificate matching the
1214 * given address
1216 PERSONAL_CERT *
1217 match_personal_cert_to_email(ADDRESS *a)
1219 PERSONAL_CERT *pcert = NULL;
1220 char buf[MAXPATH];
1221 char **email;
1222 int i, done;
1224 if(!a || !a->mailbox || !a->host)
1225 return NULL;
1227 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
1229 if(ps_global->smime){
1230 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
1231 pcert;
1232 pcert=pcert->next){
1234 if(!pcert->cert)
1235 continue;
1237 email = get_x509_subject_email(pcert->cert);
1239 done = 0;
1240 if(email != NULL){
1241 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
1242 if(email[i] != NULL) done++;
1243 for(i = 0; email[i] != NULL; i++)
1244 fs_give((void **)&email[i]);
1245 fs_give((void **)email);
1248 if(done > 0)
1249 break;
1253 return pcert;
1258 * Look for a personal certificate matching the from
1259 * (or reply_to? in the given envelope)
1261 PERSONAL_CERT *
1262 match_personal_cert(ENVELOPE *env)
1264 PERSONAL_CERT *pcert;
1266 pcert = match_personal_cert_to_email(env->reply_to);
1267 if(!pcert)
1268 pcert = match_personal_cert_to_email(env->from);
1270 return pcert;
1275 * Flatten the given body into its MIME representation.
1276 * Return the result in a BIO.
1278 static BIO *
1279 body_to_bio(BODY *body)
1281 BIO *bio = NULL;
1282 int len;
1284 bio = BIO_new(BIO_s_mem());
1285 if(!bio)
1286 return NULL;
1288 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
1289 pine_write_body_header(body, rfc822_output_func, bio);
1290 pine_rfc822_output_body(body, rfc822_output_func, bio);
1293 * Now need to truncate by two characters since the above
1294 * appends CRLF.
1296 if((len=BIO_ctrl_pending(bio)) > 1){
1297 BUF_MEM *biobuf = NULL;
1299 BIO_get_mem_ptr(bio, &biobuf);
1300 if(biobuf){
1301 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
1305 return bio;
1309 static BIO *
1310 bio_from_store(STORE_S *store)
1312 BIO *ret = NULL;
1314 if(store && store->src == BioType && store->txt){
1315 ret = (BIO *) store->txt;
1318 return(ret);
1322 * Encrypt file; given a path (char *) fp, replace the file
1323 * by an encrypted version of it. If (char *) text is not null, then
1324 * replace the text of (char *) fp by the encrypted version of (char *) text.
1327 encrypt_file(char *fp, char *text)
1329 const EVP_CIPHER *cipher = NULL;
1330 STACK_OF(X509) *encerts = NULL;
1331 X509 *cert;
1332 PERSONAL_CERT *pcert;
1333 BIO *in;
1334 PKCS7 *p7 = NULL;
1335 FILE *fpp;
1336 int rv = 0;
1338 smime_init();
1339 if((pcert = ps_global->smime->personal_certs) == NULL)
1340 return 0;
1342 cipher = EVP_aes_256_cbc();
1343 encerts = sk_X509_new_null();
1345 if((cert = get_cert_for(pcert->name)) != NULL)
1346 sk_X509_push(encerts, cert);
1347 else
1348 goto end;
1350 if(text){
1351 in = BIO_new(BIO_s_mem());
1352 if(in == NULL)
1353 goto end;
1354 (void) BIO_reset(in);
1355 BIO_puts(in, text);
1357 else{
1358 if(!(in = BIO_new_file(fp, "rb")))
1359 goto end;
1361 BIO_read_filename(in, fp);
1364 if((p7 = PKCS7_encrypt(encerts, in, cipher, 0)) == NULL)
1365 goto end;
1366 BIO_set_close(in, BIO_CLOSE);
1367 BIO_free(in);
1368 if(!(in = BIO_new_file(fp, "w")))
1369 goto end;
1370 BIO_reset(in);
1371 rv = PEM_write_bio_PKCS7(in, p7);
1372 BIO_flush(in);
1374 end:
1375 BIO_free(in);
1376 PKCS7_free(p7);
1377 sk_X509_pop_free(encerts, X509_free);
1379 return rv;
1383 * Encrypt a message on the way out. Called from call_mailer in send.c
1384 * The body may be reallocated.
1387 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
1389 PKCS7 *p7 = NULL;
1390 BIO *in = NULL;
1391 BIO *out = NULL;
1392 const EVP_CIPHER *cipher = NULL;
1393 STACK_OF(X509) *encerts = NULL;
1394 STORE_S *outs = NULL;
1395 PINEFIELD *pf;
1396 ADDRESS *a;
1397 BODY *body = *bodyP;
1398 BODY *newBody = NULL;
1399 int result = 0;
1401 dprint((9, "encrypt_outgoing_message()"));
1402 smime_init();
1404 cipher = EVP_aes_256_cbc();
1406 encerts = sk_X509_new_null();
1408 /* Look for a certificate for each of the recipients */
1409 for(pf = header->local; pf && pf->name; pf = pf->next)
1410 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
1411 for(a=*pf->addr; a; a=a->next){
1412 X509 *cert;
1413 char buf[MAXPATH];
1415 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
1417 cert = get_cert_for(buf);
1418 if(cert)
1419 sk_X509_push(encerts,cert);
1420 else{
1421 q_status_message2(SM_ORDER, 1, 1,
1422 _("Unable to find certificate for <%s@%s>"),
1423 a->mailbox, a->host);
1424 goto end;
1430 in = body_to_bio(body);
1432 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
1434 outs = so_get(BioType, NULL, EDIT_ACCESS);
1435 out = bio_from_store(outs);
1437 i2d_PKCS7_bio(out, p7);
1438 (void) BIO_flush(out);
1440 so_seek(outs, 0, SEEK_SET);
1442 newBody = mail_newbody();
1444 newBody->type = TYPEAPPLICATION;
1445 newBody->subtype = cpystr("pkcs7-mime");
1446 newBody->encoding = ENCBINARY;
1448 newBody->disposition.type = cpystr("attachment");
1449 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
1451 newBody->description = cpystr("S/MIME Encrypted Message");
1452 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
1453 set_parameter(&newBody->parameter, "name", "smime.p7m");
1455 newBody->contents.text.data = (unsigned char *) outs;
1457 *bodyP = newBody;
1459 result = 1;
1461 end:
1463 BIO_free(in);
1464 PKCS7_free(p7);
1465 sk_X509_pop_free(encerts, X509_free);
1467 dprint((9, "encrypt_outgoing_message returns %d", result));
1468 return result;
1473 Get (and decode) the body of the given section of msg
1475 static STORE_S*
1476 get_part_contents(long msgno, const char *section)
1478 long len;
1479 gf_io_t pc;
1480 STORE_S *store = NULL;
1481 char *err;
1483 store = so_get(CharStar, NULL, EDIT_ACCESS);
1484 if(store){
1485 gf_set_so_writec(&pc,store);
1487 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
1489 gf_clear_so_writec(store);
1491 so_seek(store, 0, SEEK_SET);
1493 if(err)
1494 so_give(&store);
1497 return store;
1501 static PKCS7 *
1502 get_pkcs7_from_part(long msgno,const char *section)
1504 STORE_S *store = NULL;
1505 PKCS7 *p7 = NULL;
1506 BIO *in = NULL;
1508 store = get_part_contents(msgno, section);
1510 if(store){
1511 if(store->src == CharStar){
1512 int len;
1515 * We're reaching inside the STORE_S structure. We should
1516 * probably have a way to get the length, instead.
1518 len = (int) (store->eod - store->dp);
1519 in = BIO_new_mem_buf(store->txt, len);
1521 else{ /* just copy it */
1522 unsigned char c;
1524 in = BIO_new(BIO_s_mem());
1525 (void) BIO_reset(in);
1527 so_seek(store, 0L, 0);
1528 while(so_readc(&c, store)){
1529 BIO_write(in, &c, 1);
1533 if(in){
1534 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
1535 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
1536 /* error */
1539 BIO_free(in);
1542 so_give(&store);
1545 return p7;
1550 * Try to verify a signature.
1552 * p7 - the pkcs7 object to verify
1553 * in - the plain data to verify (NULL if not detached)
1554 * out - BIO to which to write the opaque data
1555 * silent - if non zero, do not print errors, only print success.
1557 static int
1558 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
1560 STACK_OF(X509) *otherCerts = NULL;
1561 int result;
1562 const char *data;
1563 long err;
1565 #if 0
1566 if (in)
1567 dump_bio_to_file(in,"/tmp/verified-data");
1568 #endif
1570 if(!s_cert_store){
1571 if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
1572 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
1574 return -1;
1577 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, 0);
1579 if(result){
1580 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
1582 else{
1583 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
1585 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
1587 /* Retry verification so we can get the plain text */
1588 /* Might be better to reimplement PKCS7_verify here? */
1590 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY);
1592 if (!silent) q_status_message1(SM_ORDER | SM_DING, 3, 3,
1593 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
1596 /* now try to extract the certificates of any signers */
1598 STACK_OF(X509) *signers;
1599 int i;
1601 if((signers = PKCS7_get0_signers(p7, NULL, 0)) != NULL)
1602 for(i=0; i<sk_X509_num(signers); i++){
1603 char **email;
1604 X509 *x, *cert;
1606 if((x = sk_X509_value(signers,i)) == NULL)
1607 continue;
1609 if((email = get_x509_subject_email(x)) != NULL){
1610 int i;
1611 for(i = 0; email[i] != NULL; i++){
1612 if((cert = get_cert_for(email[i])) != NULL)
1613 X509_free(cert);
1614 else
1615 save_cert_for(email[i], x);
1616 fs_give((void **) &email[i]);
1618 fs_give((void **) email);
1621 sk_X509_free(signers);
1624 return result;
1628 void
1629 free_smime_body_sparep(void **sparep)
1631 if(sparep && *sparep){
1632 PKCS7_free((PKCS7 *) (*sparep));
1633 *sparep = NULL;
1637 /* Big comment, explaining the mess that exists out there, and how we deal
1638 with it, and also how we solve the problems that are created this way.
1640 When Alpine sends a message, it constructs that message, computes the
1641 signature, but then it forgets the message it signed and reconstructs it
1642 again. Since it signs a message containing a notice about "mime aware
1643 tools", but it does not send that we do not include that in the part that
1644 is signed, and that takes care of much of the problems.
1646 Another problem is what is received from the servers. All servers tested
1647 seem to transmit the message that was signed intact and Alpine can check
1648 the signature correctly. That is not a problem. The problem arises when
1649 the message includes attachments. In this case different servers send
1650 different things, so it will be up to us to figure out what is the text
1651 that was actually signed. Confused? here is the story:
1653 When a message containing and attachment is sent by Alpine, UW-IMAP,
1654 Panda-IMAP, Gmail, and local reading of folders send exactly the message
1655 that was sent by Alpine, but GMX.com, Exchange, and probably other servers
1656 add a trailing \r\n in the message, so when validating the signature,
1657 these messages will not validate. There are several things that can be
1658 done.
1660 1. Add a trailing \r\n to any message that contains attachments, sign that
1661 and send that. In this way, all messages will validate with all
1662 servers.
1664 2. Compatibility mode: If a message has an attachment, contains a trailing
1665 \r\n and does not validate (sent by an earlier version of Alpine),
1666 remove the trailing \r\n and try to revalidate again.
1668 3. We do not add \r\n to validate a message that we sent, because that
1669 would only work in Alpine, and not in any other client. That would not
1670 be a good thing to do.
1672 PART II
1674 Now we have to deal with encrypted and signed messages. The problem is that
1675 c-client makes all its pointers point to "on disk" content, but since we
1676 decrypted the data earlier, we have to make sure of two things. One is that we
1677 saved that data (so we do not have to decrypt it again) and second that we can
1678 use it.
1680 In order to save the data we use create_local_cache, so that we do not
1681 have to redecrypt the message. Once this is saved, c-client functions will
1682 find it and send it to us in mail_fetch_mime and mail_fetch_body.
1687 * Given a multipart body of type multipart/signed, attempt to verify it.
1688 * Returns non-zero if the body was changed.
1690 static int
1691 do_detached_signature_verify(BODY *b, long msgno, char *section)
1693 PKCS7 *p7 = NULL;
1694 BIO *in = NULL;
1695 PART *p;
1696 int result, modified_the_body = 0;
1697 unsigned long mimelen, bodylen;
1698 char newSec[100], *mimetext, *bodytext;
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) ? "." : "");
1706 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
1708 if(mimetext)
1709 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
1711 if (mimetext == NULL || bodytext == NULL)
1712 return modified_the_body;
1714 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
1716 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
1717 || (in = BIO_new(BIO_s_mem())) == NULL)
1718 return modified_the_body;
1720 (void) BIO_reset(in);
1721 BIO_write(in, mimetext, mimelen);
1722 BIO_write(in, bodytext, bodylen);
1724 /* Try compatibility with the past and check if this message
1725 * validates when we remove the last two characters. Silence
1726 * any failures first.
1728 if(((result = do_signature_verify(p7, in, NULL, 1)) == 0)
1729 && bodylen > 2
1730 && (strncmp(bodytext+bodylen-2,"\r\n", 2) == 0)){
1731 BUF_MEM *biobuf = NULL;
1733 BIO_get_mem_ptr(in, &biobuf);
1734 if(biobuf)
1735 BUF_MEM_grow(biobuf, mimelen + bodylen - 2);
1736 result = do_signature_verify(p7, in, NULL, 0);
1739 BIO_free(in);
1740 if(b->subtype)
1741 fs_give((void**) &b->subtype);
1743 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
1744 b->encoding = ENC8BIT;
1746 if(b->description)
1747 fs_give ((void**) &b->description);
1749 what_we_did = result ? _("This message was cryptographically signed.") :
1750 _("This message was cryptographically signed but the signature could not be verified.");
1752 b->description = cpystr(what_we_did);
1754 b->sparep = p7;
1756 p = b->nested.part;
1758 /* p is signed plaintext */
1759 if(p && p->next)
1760 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
1762 modified_the_body = 1;
1764 return modified_the_body;
1768 PERSONAL_CERT *
1769 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
1771 PERSONAL_CERT *x = NULL;
1773 if(ps_global->smime){
1774 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
1775 X509 *mine;
1777 mine = x->cert;
1779 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,mine->cert_info->issuer) &&
1780 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,mine->cert_info->serialNumber)){
1781 break;
1786 return x;
1790 static PERSONAL_CERT *
1791 find_certificate_matching_pkcs7(PKCS7 *p7)
1793 int i;
1794 STACK_OF(PKCS7_RECIP_INFO) *recips;
1795 PERSONAL_CERT *x = NULL;
1797 recips = p7->d.enveloped->recipientinfo;
1799 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
1800 PKCS7_RECIP_INFO *ri;
1802 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
1804 if((x=find_certificate_matching_recip_info(ri))!=0){
1805 break;
1809 return x;
1812 /* decrypt an encrypted file.
1813 Args: fp - the path to the encrypted file.
1814 rv - a code that thells the caller what happened inside the function
1815 Returns the decoded text allocated in a char *, whose memory must be
1816 freed by caller
1819 char *
1820 decrypt_file(char *fp, int *rv)
1822 PKCS7 *p7 = NULL;
1823 char *text, *tmp;
1824 BIO *in = NULL, *out = NULL;
1825 EVP_PKEY *pkey = NULL, *key = NULL;
1826 PERSONAL_CERT *pcert = NULL;
1827 X509 *recip, *cert;
1828 STORE_S *outs = NULL, *store, *ins;
1829 int i, j;
1830 long unsigned int len;
1831 void *ret;
1833 smime_init();
1835 if((text = read_file(fp, 0)) == NULL)
1836 return NULL;
1838 tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1);
1839 for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0'
1840 && text[i] != '-'; j++, i++)
1841 tmp[j] = text[i];
1842 tmp[j] = '\0';
1844 ret = rfc822_base64(tmp, strlen(tmp), &len);
1846 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
1847 p7 = d2i_PKCS7_bio(in, NULL);
1848 BIO_free(in);
1851 if(text) fs_give((void **)&text);
1852 if(ret) fs_give((void **)&ret);
1854 if((pcert = ps_global->smime->personal_certs) == NULL)
1855 goto end;
1857 if((i = load_private_key(pcert)) == 0
1858 && ps_global->smime
1859 && ps_global->smime->need_passphrase
1860 && !ps_global->smime->already_auto_asked)
1861 for(; i == 0;){
1862 ps_global->smime->already_auto_asked = 1;
1863 if(pith_opt_smime_get_passphrase){
1864 switch((*pith_opt_smime_get_passphrase)()){
1865 case 0 : i = load_private_key(pcert);
1866 break;
1868 case 1 : i = -1;
1869 break;
1871 default: break; /* repeat until we cancel */
1874 else
1875 i = -2;
1878 if(rv) *rv = i;
1880 if((key = pcert->key) == NULL)
1881 goto end;
1883 recip = get_cert_for(pcert->name);
1884 out = BIO_new(BIO_s_mem());
1885 (void) BIO_reset(out);
1887 i = PKCS7_decrypt(p7, key, recip, out, 0);
1889 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
1890 forget_private_keys();
1892 if(i == 0){
1893 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
1894 (char*) openssl_error_string());
1895 goto end;
1898 BIO_get_mem_data(out, &tmp);
1900 text = cpystr(tmp);
1901 BIO_free(out);
1903 end:
1904 PKCS7_free(p7);
1906 return text;
1910 * Try to decode (decrypt or verify a signature) a PKCS7 body
1911 * Returns non-zero if something was changed.
1913 static int
1914 do_decoding(BODY *b, long msgno, const char *section)
1916 int modified_the_body = 0;
1917 BIO *out = NULL;
1918 PKCS7 *p7 = NULL;
1919 X509 *recip = NULL;
1920 EVP_PKEY *key = NULL;
1921 PERSONAL_CERT *pcert = NULL;
1922 char *what_we_did = "";
1923 char null[1];
1925 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"));
1926 null[0] = '\0';
1927 smime_init();
1930 * Extract binary data from part to an in-memory store
1933 if(b->sparep){ /* already done */
1934 p7 = (PKCS7*) b->sparep;
1936 else{
1938 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
1939 if(!p7){
1940 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
1941 (char*) openssl_error_string());
1942 goto end;
1946 * Save the PKCS7 object for later dealings by the user interface.
1947 * It will be cleaned up when the body is garbage collected.
1949 b->sparep = p7;
1952 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
1954 if(PKCS7_type_is_signed(p7)){
1955 int sigok;
1957 out = BIO_new(BIO_s_mem());
1958 (void) BIO_reset(out);
1959 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
1961 sigok = do_signature_verify(p7, NULL, out, 0);
1963 what_we_did = sigok ? _("This message was cryptographically signed.") :
1964 _("This message was cryptographically signed but the signature could not be verified.");
1966 /* make sure it's null terminated */
1967 BIO_write(out, null, 1);
1969 else if(!PKCS7_type_is_enveloped(p7)){
1970 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
1971 goto end;
1973 else{ /* It *is* enveloped */
1974 int decrypt_result;
1976 what_we_did = _("This message was encrypted.");
1978 /* now need to find a cert that can decrypt this */
1979 pcert = find_certificate_matching_pkcs7(p7);
1981 if(!pcert){
1982 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
1983 goto end;
1986 recip = pcert->cert;
1988 if(!load_private_key(pcert)
1989 && ps_global->smime
1990 && ps_global->smime->need_passphrase
1991 && !ps_global->smime->already_auto_asked){
1992 /* Couldn't load key with blank password, ask user */
1993 ps_global->smime->already_auto_asked = 1;
1994 if(pith_opt_smime_get_passphrase){
1995 (*pith_opt_smime_get_passphrase)();
1996 load_private_key(pcert);
2000 key = pcert->key;
2001 if(!key)
2002 goto end;
2004 out = BIO_new(BIO_s_mem());
2005 (void) BIO_reset(out);
2006 BIO_puts(out, "MIME-Version: 1.0\r\n");
2008 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
2010 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
2011 forget_private_keys();
2013 if(!decrypt_result){
2014 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
2015 (char*) openssl_error_string());
2016 goto end;
2019 BIO_write(out, null, 1);
2023 * We've now produced a flattened MIME object in BIO out.
2024 * It needs to be turned back into a BODY.
2027 if(out){
2028 BODY *body;
2029 ENVELOPE *env;
2030 char *h = NULL;
2031 char *bstart;
2032 STRING s;
2033 BUF_MEM *bptr = NULL;
2035 BIO_get_mem_ptr(out, &bptr);
2036 if(bptr)
2037 h = bptr->data;
2039 /* look for start of body */
2040 bstart = strstr(h, "\r\n\r\n");
2042 if(!bstart){
2043 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
2045 else{
2046 bstart += 4; /* skip over CRLF*2 */
2048 INIT(&s, mail_string, bstart, strlen(bstart));
2049 rfc822_parse_msg_full(&env, &body, h, (bstart-h)-2, &s, BADHOST, 0, 0);
2050 mail_free_envelope(&env); /* Don't care about this */
2052 body->mime.offset = 0;
2053 body->mime.text.size = 0;
2055 * Now convert original body (application/pkcs7-mime)
2056 * to a multipart body with one sub-part (the decrypted body).
2057 * Note that the sub-part may also be multipart!
2060 b->type = TYPEMULTIPART;
2061 if(b->subtype)
2062 fs_give((void**) &b->subtype);
2065 * This subtype is used in mailview.c to annotate the display of
2066 * encrypted or signed messages. We know for sure then that it's a PKCS7
2067 * part because the sparep field is set to the PKCS7 object (see above).
2069 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
2070 b->encoding = ENC8BIT;
2072 if(b->description)
2073 fs_give((void**) &b->description);
2075 b->description = cpystr(what_we_did);
2077 if(b->disposition.type)
2078 fs_give((void **) &b->disposition.type);
2080 if(b->contents.text.data)
2081 fs_give((void **) &b->contents.text.data);
2083 if(b->parameter)
2084 mail_free_body_parameter(&b->parameter);
2086 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
2087 b->nested.part = fs_get(sizeof(PART));
2088 b->nested.part->body = *body;
2089 b->nested.part->next = NULL;
2091 fs_give((void**) &body);
2094 * IMPORTANT BIT: set the body->contents.text.data elements to contain the decrypted
2095 * data. Otherwise, it'll try to load it from the original data. Eek.
2097 create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body);
2099 modified_the_body = 1;
2103 end:
2104 if(out)
2105 BIO_free(out);
2107 return modified_the_body;
2112 * Recursively handle PKCS7 bodies in our message.
2114 * Returns non-zero if some fiddling was done.
2116 static int
2117 do_fiddle_smime_message(BODY *b, long msgno, char *section)
2119 int modified_the_body = 0;
2121 if(!b)
2122 return 0;
2124 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"));
2126 if(is_pkcs7_body(b)){
2128 if(do_decoding(b, msgno, section)){
2130 * b should now be a multipart message:
2131 * fiddle it too in case it's been multiply-encrypted!
2134 /* fallthru */
2135 modified_the_body = 1;
2139 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
2141 PART *p;
2142 int partNum;
2143 char newSec[100];
2145 if(MIME_MULT_SIGNED(b->type, b->subtype)){
2149 * Ahah. We have a multipart signed entity.
2151 * Multipart/signed
2152 * part 1 (signed thing)
2153 * part 2 (the pkcs7 signature)
2155 * We're going to convert that to
2157 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
2158 * part 1 (signed thing)
2159 * part 2 has been freed
2161 * We also extract the signature from part 2 and save it
2162 * in the multipart body->sparep, and we add a description
2163 * in the multipart body->description.
2166 * The results of a decrypted message will be similar. It
2167 * will be
2169 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
2170 * part 1 (decrypted thing)
2173 modified_the_body += do_detached_signature_verify(b, msgno, section);
2175 else if(MIME_MSG(b->type, b->subtype)){
2176 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
2178 else{
2180 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
2182 /* Append part number to the section string */
2184 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
2186 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
2191 return modified_the_body;
2196 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
2197 * Returns non-zero if something was changed.
2200 fiddle_smime_message(BODY *b, long msgno)
2202 return do_fiddle_smime_message(b, msgno, "");
2206 /********************************************************************************/
2210 * Output a string in a distinctive style
2212 void
2213 gf_puts_uline(char *txt, gf_io_t pc)
2215 pc(TAG_EMBED); pc(TAG_BOLDON);
2216 gf_puts(txt, pc);
2217 pc(TAG_EMBED); pc(TAG_BOLDOFF);
2222 * Sign a message. Called from call_mailer in send.c.
2224 * This takes the header for the outgoing message as well as a pointer
2225 * to the current body (which may be reallocated).
2228 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach)
2230 STORE_S *outs = NULL;
2231 BODY *body = *bodyP;
2232 BODY *newBody = NULL;
2233 PART *p1 = NULL;
2234 PART *p2 = NULL;
2235 PERSONAL_CERT *pcert;
2236 BIO *in = NULL;
2237 BIO *out = NULL;
2238 PKCS7 *p7 = NULL;
2239 int result = 0;
2240 int flags = dont_detach ? 0 : PKCS7_DETACHED;
2242 dprint((9, "sign_outgoing_message()"));
2244 smime_init();
2246 /* Look for a private key matching the sender address... */
2248 pcert = match_personal_cert(header->env);
2250 if(!pcert){
2251 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
2252 goto end;
2255 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
2256 /* Couldn't load key with blank password, try again */
2257 if(pith_opt_smime_get_passphrase){
2258 (*pith_opt_smime_get_passphrase)();
2259 load_private_key(pcert);
2263 if(!pcert->key)
2264 goto end;
2266 in = body_to_bio(body);
2268 #ifdef notdef
2269 dump_bio_to_file(in,"/tmp/signed-data");
2270 #endif
2272 p7 = PKCS7_sign(pcert->cert, pcert->key, NULL, in, flags);
2274 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
2275 forget_private_keys();
2277 if(!p7){
2278 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
2279 goto end;
2282 outs = so_get(BioType, NULL, EDIT_ACCESS);
2283 out = bio_from_store(outs);
2285 i2d_PKCS7_bio(out, p7);
2286 (void) BIO_flush(out);
2288 so_seek(outs, 0, SEEK_SET);
2290 if((flags&PKCS7_DETACHED)==0){
2292 /* the simple case: the signed data is in the pkcs7 object */
2294 newBody = mail_newbody();
2296 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m");
2298 newBody->contents.text.data = (unsigned char *) outs;
2299 *bodyP = newBody;
2301 result = 1;
2303 else{
2306 * OK.
2307 * We have to create a new body as follows:
2309 * multipart/signed; blah blah blah
2310 * reference to existing body
2312 * pkcs7 object
2315 newBody = mail_newbody();
2317 newBody->type = TYPEMULTIPART;
2318 newBody->subtype = cpystr("signed");
2319 newBody->encoding = ENC7BIT;
2321 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
2322 set_parameter(&newBody->parameter, "micalg", "sha1");
2324 p1 = mail_newbody_part();
2325 p2 = mail_newbody_part();
2328 * This is nasty. We're just copying the body in here,
2329 * but since our newBody is freed at the end of call_mailer,
2330 * we mustn't let this body (the original one) be freed twice.
2332 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
2334 p1->next = p2;
2336 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s");
2337 p2->body.contents.text.data = (unsigned char *) outs;
2339 newBody->nested.part = p1;
2341 *bodyP = newBody;
2343 result = 1;
2346 end:
2348 PKCS7_free(p7);
2349 BIO_free(in);
2351 dprint((9, "sign_outgoing_message returns %d", result));
2352 return result;
2356 SMIME_STUFF_S *
2357 new_smime_struct(void)
2359 SMIME_STUFF_S *ret = NULL;
2361 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
2362 memset((void *) ret, 0, sizeof(*ret));
2363 ret->publictype = Nada;
2365 return ret;
2369 static void
2370 free_smime_struct(SMIME_STUFF_S **smime)
2372 if(smime && *smime){
2373 if((*smime)->passphrase_emailaddr){
2374 int i;
2375 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
2376 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
2377 fs_give((void **) (*smime)->passphrase_emailaddr);
2380 if((*smime)->publicpath)
2381 fs_give((void **) &(*smime)->publicpath);
2383 if((*smime)->publiccertlist)
2384 free_certlist(&(*smime)->publiccertlist);
2386 if((*smime)->publiccontent)
2387 fs_give((void **) &(*smime)->publiccontent);
2389 if((*smime)->privatepath)
2390 fs_give((void **) &(*smime)->privatepath);
2392 if((*smime)->personal_certs){
2393 PERSONAL_CERT *pc;
2395 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
2396 free_personal_certs(&pc);
2397 (*smime)->personal_certs = NULL;
2400 if((*smime)->privatecontent)
2401 fs_give((void **) &(*smime)->privatecontent);
2403 if((*smime)->capath)
2404 fs_give((void **) &(*smime)->capath);
2406 if((*smime)->cacontent)
2407 fs_give((void **) &(*smime)->cacontent);
2409 fs_give((void **) smime);
2413 #endif /* SMIME */