* Fixes to documentation to update old washington.edu/alpine site
[alpine.git] / pith / smime.c
blob8d7bbd27ae5c1a00a140ad65926a345174e168be
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 long rfc822_output_func(void *b, char *string);
60 static int load_private_key(PERSONAL_CERT *pcert);
61 static void setup_pkcs7_body_for_signature(BODY *b, char *description,
62 char *type, char *filename);
63 static BIO *body_to_bio(BODY *body);
64 static BIO *bio_from_store(STORE_S *store);
65 static STORE_S *get_part_contents(long msgno, const char *section);
66 static PKCS7 *get_pkcs7_from_part(long msgno, const char *section);
67 static int do_signature_verify(PKCS7 *p7, BIO *in, BIO *out);
68 static int do_detached_signature_verify(BODY *b, long msgno, char *section);
69 static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7);
70 static int do_decoding(BODY *b, long msgno, const char *section);
71 static void free_smime_struct(SMIME_STUFF_S **smime);
72 static void setup_storage_locations(void);
73 static int copy_dir_to_container(WhichCerts which);
74 static int copy_container_to_dir(WhichCerts which);
77 int (*pith_opt_smime_get_passphrase)(void);
80 static X509_STORE *s_cert_store;
82 /* State management for randomness functions below */
83 static int seeded = 0;
84 static int egdsocket = 0;
88 * Forget any cached private keys
90 static void
91 forget_private_keys(void)
93 PERSONAL_CERT *pcert;
94 size_t len;
95 volatile char *p;
97 dprint((9, "forget_private_keys()"));
98 if(ps_global->smime){
99 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
100 pcert;
101 pcert=pcert->next){
103 if(pcert->key){
104 EVP_PKEY_free(pcert->key);
105 pcert->key = NULL;
109 ps_global->smime->entered_passphrase = 0;
110 len = sizeof(ps_global->smime->passphrase);
111 p = ps_global->smime->passphrase;
113 while(len-- > 0)
114 *p++ = '\0';
120 * taken from openssl/apps/app_rand.c
122 static int
123 app_RAND_load_file(const char *file)
125 char buffer[200];
127 if(file == NULL)
128 file = RAND_file_name(buffer, sizeof buffer);
129 else if(RAND_egd(file) > 0){
130 /* we try if the given filename is an EGD socket.
131 if it is, we don't write anything back to the file. */
132 egdsocket = 1;
133 return 1;
136 if(file == NULL || !RAND_load_file(file, -1)){
137 if(RAND_status() == 0){
138 dprint((1, "unable to load 'random state'\n"));
139 dprint((1, "This means that the random number generator has not been seeded\n"));
140 dprint((1, "with much random data.\n"));
143 return 0;
146 seeded = 1;
147 return 1;
152 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
154 static void
155 openssl_extra_randomness(void)
157 #if !defined(WIN32)
158 int fd;
159 unsigned long i;
160 char *tf = NULL;
161 char tmp[MAXPATH];
162 struct stat sbuf;
163 /* if system doesn't have /dev/urandom */
164 if(stat ("/dev/urandom", &sbuf)){
165 tmp[0] = '0';
166 tf = temp_nam(NULL, NULL);
167 if(tf){
168 strncpy(tmp, tf, sizeof(tmp));
169 tmp[sizeof(tmp)-1] = '\0';
170 fs_give((void **) &tf);
173 if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
174 i = (unsigned long) tmp;
175 else{
176 unlink(tmp); /* don't need the file */
177 fstat(fd, &sbuf); /* get information about the file */
178 i = sbuf.st_ino; /* remember its inode */
179 close(fd); /* or its descriptor */
181 /* not great but it'll have to do */
182 snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
183 tcp_serverhost (),i,
184 (unsigned long) (time (0) ^ gethostid ()),
185 (unsigned long) getpid ());
186 RAND_seed(tmp, strlen(tmp));
188 #endif
192 /* taken from openssl/apps/app_rand.c */
193 static int
194 app_RAND_write_file(const char *file)
196 char buffer[200];
198 if(egdsocket || !seeded)
200 * If we did not manage to read the seed file,
201 * we should not write a low-entropy seed file back --
202 * it would suppress a crucial warning the next time
203 * we want to use it.
205 return 0;
207 if(file == NULL)
208 file = RAND_file_name(buffer, sizeof buffer);
210 if(file == NULL || !RAND_write_file(file)){
211 dprint((1, "unable to write 'random state'\n"));
212 return 0;
215 return 1;
219 /* Installed as an atexit() handler to save the random data */
220 void
221 smime_deinit(void)
223 dprint((9, "smime_deinit()"));
224 app_RAND_write_file(NULL);
225 free_smime_struct(&ps_global->smime);
229 /* Initialise openssl stuff if needed */
230 static void
231 smime_init(void)
233 if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
235 dprint((9, "smime_init()"));
236 if(!ps_global->smime)
237 ps_global->smime = new_smime_struct();
239 setup_storage_locations();
241 s_cert_store = get_ca_store();
243 OpenSSL_add_all_algorithms();
244 ERR_load_crypto_strings();
246 app_RAND_load_file(NULL);
248 openssl_extra_randomness();
249 ps_global->smime->inited = 1;
252 ERR_clear_error();
256 static void
257 setup_storage_locations(void)
259 int publiccertcontainer = 0, privatekeycontainer = 0, cacertcontainer = 0;
260 char path[MAXPATH+1], *contents;
262 if(!ps_global->smime)
263 return;
265 #ifdef APPLEKEYCHAIN
266 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
267 ps_global->smime->publictype = Keychain;
269 else{
270 #endif /* APPLEKEYCHAIN */
271 /* Public certificates in a container */
272 if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
274 publiccertcontainer = 1;
275 contents = NULL;
276 path[0] = '\0';
277 if(!signature_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
278 publiccertcontainer = 0;
280 if(publiccertcontainer && !IS_REMOTE(path)
281 && ps_global->VAR_OPER_DIR
282 && !in_dir(ps_global->VAR_OPER_DIR, path)){
283 q_status_message2(SM_ORDER | SM_DING, 3, 4,
284 /* TRANSLATORS: First arg is the directory name, second is
285 the file user wants to read but can't. */
286 _("Can't read file outside %s: %s"),
287 ps_global->VAR_OPER_DIR, path);
288 publiccertcontainer = 0;
291 if(publiccertcontainer
292 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
293 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
295 !(contents = read_file(path, READ_FROM_LOCALE)))
296 publiccertcontainer = 0;
299 if(publiccertcontainer && path[0]){
300 ps_global->smime->publictype = Container;
301 ps_global->smime->publicpath = cpystr(path);
303 if(contents){
304 ps_global->smime->publiccontent = contents;
305 ps_global->smime->publiccertlist = mem_to_certlist(contents);
310 /* Public certificates in a directory of files */
311 if(!publiccertcontainer){
312 ps_global->smime->publictype = Directory;
314 path[0] = '\0';
315 if(!(signature_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
316 && !IS_REMOTE(path)))
317 ps_global->smime->publictype = Nada;
318 else if(can_access(path, ACCESS_EXISTS)){
319 if(our_mkpath(path, 0700)){
320 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
321 ps_global->smime->publictype = Nada;
325 if(ps_global->smime->publictype == Directory)
326 ps_global->smime->publicpath = cpystr(path);
329 #ifdef APPLEKEYCHAIN
331 #endif /* APPLEKEYCHAIN */
333 /* private keys in a container */
334 if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
336 privatekeycontainer = 1;
337 contents = NULL;
338 path[0] = '\0';
339 if(!signature_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
340 privatekeycontainer = 0;
342 if(privatekeycontainer && !IS_REMOTE(path)
343 && ps_global->VAR_OPER_DIR
344 && !in_dir(ps_global->VAR_OPER_DIR, path)){
345 q_status_message2(SM_ORDER | SM_DING, 3, 4,
346 /* TRANSLATORS: First arg is the directory name, second is
347 the file user wants to read but can't. */
348 _("Can't read file outside %s: %s"),
349 ps_global->VAR_OPER_DIR, path);
350 privatekeycontainer = 0;
353 if(privatekeycontainer
354 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
355 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
357 !(contents = read_file(path, READ_FROM_LOCALE)))
358 privatekeycontainer = 0;
361 if(privatekeycontainer && path[0]){
362 ps_global->smime->privatetype = Container;
363 ps_global->smime->privatepath = cpystr(path);
365 if(contents){
366 ps_global->smime->privatecontent = contents;
367 ps_global->smime->personal_certs = mem_to_personal_certs(contents);
372 /* private keys in a directory of files */
373 if(!privatekeycontainer){
374 PERSONAL_CERT *result = NULL;
376 ps_global->smime->privatetype = Directory;
378 path[0] = '\0';
379 if(!(signature_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
380 && !IS_REMOTE(path)))
381 ps_global->smime->privatetype = Nada;
382 else if(can_access(path, ACCESS_EXISTS)){
383 if(our_mkpath(path, 0700)){
384 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
385 ps_global->smime->privatetype = Nada;
389 if(ps_global->smime->privatetype == Directory){
390 char buf2[MAXPATH];
391 struct dirent *d;
392 DIR *dirp;
394 ps_global->smime->privatepath = cpystr(path);
395 dirp = opendir(path);
396 if(dirp){
398 while((d=readdir(dirp)) != NULL){
399 X509 *cert;
400 size_t ll;
402 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, ".key")){
404 /* copy file name to temp buffer */
405 strncpy(buf2, d->d_name, sizeof(buf2)-1);
406 buf2[sizeof(buf2)-1] = '\0';
407 /* chop off ".key" trailier */
408 buf2[strlen(buf2)-4] = 0;
409 /* Look for certificate */
410 cert = get_cert_for(buf2);
412 if(cert){
413 PERSONAL_CERT *pc;
415 /* create a new PERSONAL_CERT, fill it in */
417 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
418 pc->cert = cert;
419 pc->name = cpystr(buf2);
421 /* Try to load the key with an empty password */
422 pc->key = load_key(pc, "");
424 pc->next = result;
425 result = pc;
430 closedir(dirp);
434 ps_global->smime->personal_certs = result;
437 /* extra cacerts in a container */
438 if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
440 cacertcontainer = 1;
441 contents = NULL;
442 path[0] = '\0';
443 if(!signature_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
444 cacertcontainer = 0;
446 if(cacertcontainer && !IS_REMOTE(path)
447 && ps_global->VAR_OPER_DIR
448 && !in_dir(ps_global->VAR_OPER_DIR, path)){
449 q_status_message2(SM_ORDER | SM_DING, 3, 4,
450 /* TRANSLATORS: First arg is the directory name, second is
451 the file user wants to read but can't. */
452 _("Can't read file outside %s: %s"),
453 ps_global->VAR_OPER_DIR, path);
454 cacertcontainer = 0;
457 if(cacertcontainer
458 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
459 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
461 !(contents = read_file(path, READ_FROM_LOCALE)))
462 cacertcontainer = 0;
465 if(cacertcontainer && path[0]){
466 ps_global->smime->catype = Container;
467 ps_global->smime->capath = cpystr(path);
468 ps_global->smime->cacontent = contents;
472 if(!cacertcontainer){
473 ps_global->smime->catype = Directory;
475 path[0] = '\0';
476 if(!(signature_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
477 && !IS_REMOTE(path)))
478 ps_global->smime->catype = Nada;
479 else if(can_access(path, ACCESS_EXISTS)){
480 if(our_mkpath(path, 0700)){
481 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
482 ps_global->smime->catype = Nada;
486 if(ps_global->smime->catype == Directory)
487 ps_global->smime->capath = cpystr(path);
493 copy_publiccert_dir_to_container(void)
495 return(copy_dir_to_container(Public));
500 copy_publiccert_container_to_dir(void)
502 return(copy_container_to_dir(Public));
507 copy_privatecert_dir_to_container(void)
509 return(copy_dir_to_container(Private));
514 copy_privatecert_container_to_dir(void)
516 return(copy_container_to_dir(Private));
521 copy_cacert_dir_to_container(void)
523 return(copy_dir_to_container(CACert));
528 copy_cacert_container_to_dir(void)
530 return(copy_container_to_dir(CACert));
535 * returns 0 on success, -1 on failure
538 copy_dir_to_container(WhichCerts which)
540 int ret = 0;
541 BIO *bio_out = NULL, *bio_in = NULL;
542 char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
543 char *tempfile = NULL;
544 DIR *dirp;
545 struct dirent *d;
546 REMDATA_S *rd = NULL;
547 char *configdir = NULL;
548 char *configpath = NULL;
549 char *filesuffix = NULL;
551 dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
552 smime_init();
554 srcpath[0] = '\0';
555 dstpath[0] = '\0';
556 file[0] = '\0';
557 emailaddr[0] = '\0';
559 if(which == Public){
560 configdir = ps_global->VAR_PUBLICCERT_DIR;
561 configpath = ps_global->smime->publicpath;
562 filesuffix = ".crt";
564 else if(which == Private){
565 configdir = ps_global->VAR_PRIVATEKEY_DIR;
566 configpath = ps_global->smime->privatepath;
567 filesuffix = ".key";
569 else if(which == CACert){
570 configdir = ps_global->VAR_CACERT_DIR;
571 configpath = ps_global->smime->capath;
572 filesuffix = ".crt";
575 if(!(configdir && configdir[0])){
576 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
577 return -1;
580 if(!(configpath && configpath[0])){
581 #ifdef APPLEKEYCHAIN
582 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
583 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
584 return -1;
586 #endif /* APPLEKEYCHAIN */
587 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
588 return -1;
591 if(!(filesuffix && strlen(filesuffix) == 4)){
592 return -1;
597 * If there is a legit directory to read from set up the
598 * container file to write to.
600 if(signature_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
602 if(IS_REMOTE(configpath)){
603 rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
604 NULL, "Error: ",
605 _("Can't access remote smime configuration."));
606 if(!rd)
607 return -1;
609 (void) rd_read_metadata(rd);
611 if(rd->access == MaybeRorW){
612 if(rd->read_status == 'R')
613 rd->access = ReadOnly;
614 else
615 rd->access = ReadWrite;
618 if(rd->access != NoExists){
620 rd_check_remvalid(rd, 1L);
623 * If the cached info says it is readonly but
624 * it looks like it's been fixed now, change it to readwrite.
626 if(rd->read_status == 'R'){
627 rd_check_readonly_access(rd);
628 if(rd->read_status == 'W'){
629 rd->access = ReadWrite;
630 rd->flags |= REM_OUTOFDATE;
632 else
633 rd->access = ReadOnly;
637 if(rd->flags & REM_OUTOFDATE){
638 if(rd_update_local(rd) != 0){
640 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
641 rd_close_remdata(&rd);
642 return -1;
645 else
646 rd_open_remote(rd);
648 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
649 rd_close_remdata(&rd);
650 return -1;
653 rd->flags |= DO_REMTRIM;
655 strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
656 dstpath[sizeof(dstpath)-1] = '\0';
658 else{
659 strncpy(dstpath, configpath, sizeof(dstpath)-1);
660 dstpath[sizeof(dstpath)-1] = '\0';
664 * dstpath is either the local Container file or the local cache file
665 * for the remote Container file.
667 tempfile = tempfile_in_same_dir(dstpath, "az", NULL);
671 * If there is a legit directory to read from and a tempfile
672 * to write to we continue.
674 if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
676 dirp = opendir(srcpath);
677 if(dirp){
679 while((d=readdir(dirp)) && !ret){
680 size_t ll;
682 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, filesuffix)){
684 /* copy file name to temp buffer */
685 strncpy(emailaddr, d->d_name, sizeof(emailaddr)-1);
686 emailaddr[sizeof(emailaddr)-1] = '\0';
687 /* chop off suffix trailier */
688 emailaddr[strlen(emailaddr)-4] = 0;
691 * This is the separator between the contents of
692 * different files.
694 if(which == CACert){
695 if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
696 && (BIO_puts(bio_out, emailaddr) > 0)
697 && (BIO_puts(bio_out, "\n") > 0)))
698 ret = -1;
700 else{
701 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
702 && (BIO_puts(bio_out, emailaddr) > 0)
703 && (BIO_puts(bio_out, "\n") > 0)))
704 ret = -1;
707 /* read then write contents of file */
708 build_path(file, srcpath, d->d_name, sizeof(file));
709 if(!(bio_in = BIO_new_file(file, "r")))
710 ret = -1;
712 if(!ret){
713 int good_stuff = 0;
715 while(BIO_gets(bio_in, line, sizeof(line)) > 0){
716 if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
717 good_stuff = 1;
719 if(good_stuff)
720 BIO_puts(bio_out, line);
722 if(strncmp("-----END", line, strlen("-----END")) == 0)
723 good_stuff = 0;
727 BIO_free(bio_in);
731 closedir(dirp);
734 BIO_free(bio_out);
736 if(!ret){
737 if(rename_file(tempfile, dstpath) < 0){
738 q_status_message2(SM_ORDER, 3, 3,
739 _("Can't rename %s to %s"), tempfile, dstpath);
740 ret = -1;
743 /* if the container is remote, copy it */
744 if(!ret && IS_REMOTE(configpath)){
745 int e;
746 char datebuf[200];
748 datebuf[0] = '\0';
750 if((e = rd_update_remote(rd, datebuf)) != 0){
751 if(e == -1){
752 q_status_message2(SM_ORDER | SM_DING, 3, 5,
753 _("Error opening temporary smime file %s: %s"),
754 rd->lf, error_description(errno));
755 dprint((1,
756 "write_remote_smime: error opening temp file %s\n",
757 rd->lf ? rd->lf : "?"));
759 else{
760 q_status_message2(SM_ORDER | SM_DING, 3, 5,
761 _("Error copying to %s: %s"),
762 rd->rn, error_description(errno));
763 dprint((1,
764 "write_remote_smime: error copying from %s to %s\n",
765 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
768 q_status_message(SM_ORDER | SM_DING, 5, 5,
769 _("Copy of smime key to remote folder failed, NOT saved remotely"));
771 else{
772 rd_update_metadata(rd, datebuf);
773 rd->read_status = 'W';
776 rd_close_remdata(&rd);
781 if(tempfile)
782 fs_give((void **) &tempfile);
784 return ret;
789 * returns 0 on success, -1 on failure
792 copy_container_to_dir(WhichCerts which)
794 char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
795 char iobuf[4096];
796 char *contents = NULL;
797 char *leader = NULL;
798 char *filesuffix = NULL;
799 char *configdir = NULL;
800 char *configpath = NULL;
801 char *tempfile = NULL;
802 char *p, *q, *line, *name, *certtext, *save_p;
803 int len;
804 BIO *in, *out;
806 dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
807 smime_init();
809 path[0] = '\0';
811 if(which == Public){
812 leader = EMAILADDRLEADER;
813 contents = ps_global->smime->publiccontent;
814 configdir = ps_global->VAR_PUBLICCERT_DIR;
815 configpath = ps_global->smime->publicpath;
816 filesuffix = ".crt";
817 if(!(configpath && configpath[0])){
818 #ifdef APPLEKEYCHAIN
819 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
820 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
821 return -1;
823 #endif /* APPLEKEYCHAIN */
824 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
825 return -1;
828 fs_give((void **) &ps_global->smime->publicpath);
830 path[0] = '\0';
831 if(!(signature_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
832 && !IS_REMOTE(path))){
833 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
834 return -1;
837 if(can_access(path, ACCESS_EXISTS)){
838 if(our_mkpath(path, 0700)){
839 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
840 return -1;
844 ps_global->smime->publicpath = cpystr(path);
845 configpath = ps_global->smime->publicpath;
847 else if(which == Private){
848 leader = EMAILADDRLEADER;
849 contents = ps_global->smime->privatecontent;
850 configdir = ps_global->VAR_PRIVATEKEY_DIR;
851 configpath = ps_global->smime->privatepath;
852 filesuffix = ".key";
853 if(!(configpath && configpath[0])){
854 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
855 return -1;
858 fs_give((void **) &ps_global->smime->privatepath);
860 path[0] = '\0';
861 if(!(signature_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
862 && !IS_REMOTE(path))){
863 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
864 return -1;
867 if(can_access(path, ACCESS_EXISTS)){
868 if(our_mkpath(path, 0700)){
869 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
870 return -1;
874 ps_global->smime->privatepath = cpystr(path);
875 configpath = ps_global->smime->privatepath;
877 else if(which == CACert){
878 leader = CACERTSTORELEADER;
879 contents = ps_global->smime->cacontent;
880 configdir = ps_global->VAR_CACERT_DIR;
881 configpath = ps_global->smime->capath;
882 filesuffix = ".crt";
883 if(!(configpath && configpath[0])){
884 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
885 return -1;
888 fs_give((void **) &ps_global->smime->capath);
890 path[0] = '\0';
891 if(!(signature_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
892 && !IS_REMOTE(path))){
893 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
894 return -1;
897 if(can_access(path, ACCESS_EXISTS)){
898 if(our_mkpath(path, 0700)){
899 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
900 return -1;
904 ps_global->smime->capath = cpystr(path);
905 configpath = ps_global->smime->capath;
908 if(!(configdir && configdir[0])){
909 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
910 return -1;
913 if(!(configpath && configpath[0])){
914 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
915 return -1;
918 if(!(filesuffix && strlen(filesuffix) == 4)){
919 return -1;
923 if(contents && *contents){
924 for(p = contents; *p != '\0';){
925 line = p;
927 while(*p && *p != '\n')
928 p++;
930 save_p = NULL;
931 if(*p == '\n'){
932 save_p = p;
933 *p++ = '\0';
936 if(strncmp(leader, line, strlen(leader)) == 0){
937 name = line + strlen(leader);
938 certtext = p;
939 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
940 if((q = strstr(certtext, leader)) != NULL){
941 p = q;
943 else{ /* end of file */
944 q = certtext + strlen(certtext);
945 p = q;
948 strncpy(buf, name, sizeof(buf)-5);
949 buf[sizeof(buf)-5] = '\0';
950 strncat(buf, filesuffix, 5);
951 build_path(file, configpath, buf, sizeof(file));
953 in = BIO_new_mem_buf(certtext, q-certtext);
954 if(in){
955 tempfile = tempfile_in_same_dir(file, "az", NULL);
956 out = NULL;
957 if(tempfile)
958 out = BIO_new_file(tempfile, "w");
960 if(out){
961 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
962 BIO_write(out, iobuf, len);
964 BIO_free(out);
966 if(rename_file(tempfile, file) < 0){
967 q_status_message2(SM_ORDER, 3, 3,
968 _("Can't rename %s to %s"),
969 tempfile, file);
970 return -1;
973 fs_give((void **) &tempfile);
976 BIO_free(in);
981 if(save_p)
982 *save_p = '\n';
986 return 0;
990 #ifdef APPLEKEYCHAIN
993 copy_publiccert_container_to_keychain(void)
995 /* NOT IMPLEMNTED */
996 return -1;
1000 copy_publiccert_keychain_to_container(void)
1002 /* NOT IMPLEMNTED */
1003 return -1;
1006 #endif /* APPLEKEYCHAIN */
1010 * Get a pointer to a string describing the most recent OpenSSL error.
1011 * It's statically allocated, so don't change or attempt to free it.
1013 static const char *
1014 openssl_error_string(void)
1016 char *errs;
1017 const char *data = NULL;
1018 long errn;
1020 errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
1021 errs = (char*) ERR_reason_error_string(errn);
1023 if(errs)
1024 return errs;
1025 else if(data)
1026 return data;
1028 return "unknown error";
1032 /* Return true if the body looks like a PKCS7 object */
1034 is_pkcs7_body(BODY *body)
1036 int result;
1038 result = body->type==TYPEAPPLICATION &&
1039 body->subtype &&
1040 (strucmp(body->subtype,"pkcs7-mime")==0 ||
1041 strucmp(body->subtype,"x-pkcs7-mime")==0 ||
1042 strucmp(body->subtype,"pkcs7-signature")==0 ||
1043 strucmp(body->subtype,"x-pkcs7-signature")==0);
1045 return result;
1049 #ifdef notdef
1051 * Somewhat useful debug utility to dump the contents of a BIO to a file.
1052 * Note that a memory BIO will have its contents eliminated after they
1053 * are read so this will break the next step.
1055 static void
1056 dump_bio_to_file(BIO *in, char *filename)
1058 char iobuf[4096];
1059 int len;
1060 BIO *out;
1062 out = BIO_new_file(filename, "w");
1064 if(out){
1065 if(BIO_method_type(in) != BIO_TYPE_MEM)
1066 BIO_reset(in);
1068 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1069 BIO_write(out, iobuf, len);
1071 BIO_free(out);
1074 BIO_reset(in);
1076 #endif
1080 * Recursively stash a pointer to the decrypted data in our
1081 * manufactured body.
1083 static void
1084 create_local_cache(char *base, BODY *b)
1086 if(b->type==TYPEMULTIPART){
1087 PART *p;
1089 #if 0
1090 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
1091 #else
1093 * We don't really want to copy the real body contents. It shouldn't be
1094 * used, and in the case of a message with attachments, we'll be
1095 * duplicating the files multiple times.
1097 cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
1098 #endif
1100 for(p=b->nested.part; p; p=p->next)
1101 create_local_cache(base, (BODY*) p);
1103 else{
1104 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
1109 static long
1110 rfc822_output_func(void *b, char *string)
1112 BIO *bio = (BIO *) b;
1114 return((BIO_puts(bio, string) > 0) ? 1L : 0L);
1119 * Attempt to load the private key for the given PERSONAL_CERT.
1120 * This sets the appropriate passphrase globals in order to
1121 * interact with the user correctly.
1123 static int
1124 load_private_key(PERSONAL_CERT *pcert)
1126 if(!pcert->key){
1128 /* Try empty password by default */
1129 char *password = "";
1131 if(ps_global->smime
1132 && (ps_global->smime->need_passphrase
1133 || ps_global->smime->entered_passphrase)){
1134 /* We've already been in here and discovered we need a different password */
1136 if(ps_global->smime->entered_passphrase)
1137 password = (char *) ps_global->smime->passphrase; /* already entered */
1138 else
1139 return 0;
1142 ERR_clear_error();
1144 if(!(pcert->key = load_key(pcert, password))){
1145 long err = ERR_get_error();
1147 /* Couldn't load key... */
1149 if(ps_global->smime && ps_global->smime->entered_passphrase){
1151 /* The user got the password wrong maybe? */
1153 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
1154 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
1155 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
1156 else
1157 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
1159 /* This passphrase is no good; forget it */
1160 ps_global->smime->entered_passphrase = 0;
1163 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
1164 if(ps_global->smime)
1165 ps_global->smime->need_passphrase = 1;
1167 if(ps_global->smime){
1168 if(ps_global->smime->passphrase_emailaddr){
1169 int i;
1170 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
1171 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
1172 fs_give((void **) ps_global->smime->passphrase_emailaddr);
1175 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
1178 return 0;
1180 else{
1181 /* This key will be cached, so we won't be called again */
1182 if(ps_global->smime){
1183 ps_global->smime->entered_passphrase = 0;
1184 ps_global->smime->need_passphrase = 0;
1188 return 1;
1191 return 0;
1195 static void
1196 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename)
1198 b->type = TYPEAPPLICATION;
1199 b->subtype = cpystr(type);
1200 b->encoding = ENCBINARY;
1201 b->description = cpystr(description);
1203 b->disposition.type = cpystr("attachment");
1204 set_parameter(&b->disposition.parameter, "filename", filename);
1206 set_parameter(&b->parameter, "name", filename);
1211 * Look for a personal certificate matching the
1212 * given address
1214 PERSONAL_CERT *
1215 match_personal_cert_to_email(ADDRESS *a)
1217 PERSONAL_CERT *pcert = NULL;
1218 char buf[MAXPATH];
1219 char **email;
1220 int i, done;
1222 if(!a || !a->mailbox || !a->host)
1223 return NULL;
1225 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
1227 if(ps_global->smime){
1228 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
1229 pcert;
1230 pcert=pcert->next){
1232 if(!pcert->cert)
1233 continue;
1235 email = get_x509_subject_email(pcert->cert);
1237 done = 0;
1238 if(email != NULL){
1239 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
1240 if(email[i] != NULL) done++;
1241 for(i = 0; email[i] != NULL; i++)
1242 fs_give((void **)&email[i]);
1243 fs_give((void **)email);
1246 if(done > 0)
1247 break;
1251 return pcert;
1256 * Look for a personal certificate matching the from
1257 * (or reply_to? in the given envelope)
1259 PERSONAL_CERT *
1260 match_personal_cert(ENVELOPE *env)
1262 PERSONAL_CERT *pcert;
1264 pcert = match_personal_cert_to_email(env->reply_to);
1265 if(!pcert)
1266 pcert = match_personal_cert_to_email(env->from);
1268 return pcert;
1273 * Flatten the given body into its MIME representation.
1274 * Return the result in a BIO.
1276 static BIO *
1277 body_to_bio(BODY *body)
1279 BIO *bio = NULL;
1280 int len;
1282 bio = BIO_new(BIO_s_mem());
1283 if(!bio)
1284 return NULL;
1286 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
1287 pine_write_body_header(body, rfc822_output_func, bio);
1288 pine_rfc822_output_body(body, rfc822_output_func, bio);
1291 * Now need to truncate by two characters since the above
1292 * appends CRLF.
1294 if((len=BIO_ctrl_pending(bio)) > 1){
1295 BUF_MEM *biobuf = NULL;
1297 BIO_get_mem_ptr(bio, &biobuf);
1298 if(biobuf){
1299 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
1303 return bio;
1307 static BIO *
1308 bio_from_store(STORE_S *store)
1310 BIO *ret = NULL;
1312 if(store && store->src == BioType && store->txt){
1313 ret = (BIO *) store->txt;
1316 return(ret);
1320 * Encrypt file; given a path (char *) fp, replace the file
1321 * by an encrypted version of it. If (char *) text is not null, then
1322 * replace the text of (char *) fp by the encrypted version of (char *) text.
1325 encrypt_file(char *fp, char *text)
1327 const EVP_CIPHER *cipher = NULL;
1328 STACK_OF(X509) *encerts = NULL;
1329 X509 *cert;
1330 PERSONAL_CERT *pcert;
1331 BIO *in;
1332 PKCS7 *p7 = NULL;
1333 FILE *fpp;
1334 int rv = 0;
1336 smime_init();
1337 if((pcert = ps_global->smime->personal_certs) == NULL)
1338 return 0;
1340 cipher = EVP_aes_256_cbc();
1341 encerts = sk_X509_new_null();
1343 if((cert = get_cert_for(pcert->name)) != NULL)
1344 sk_X509_push(encerts, cert);
1345 else
1346 goto end;
1348 if(text){
1349 in = BIO_new(BIO_s_mem());
1350 if(in == NULL)
1351 goto end;
1352 (void) BIO_reset(in);
1353 BIO_puts(in, text);
1355 else{
1356 if(!(in = BIO_new_file(fp, "rb")))
1357 goto end;
1359 BIO_read_filename(in, fp);
1362 if((p7 = PKCS7_encrypt(encerts, in, cipher, 0)) == NULL)
1363 goto end;
1364 BIO_set_close(in, BIO_CLOSE);
1365 BIO_free(in);
1366 if(!(in = BIO_new_file(fp, "w")))
1367 goto end;
1368 BIO_reset(in);
1369 rv = PEM_write_bio_PKCS7(in, p7);
1370 BIO_flush(in);
1372 end:
1373 BIO_free(in);
1374 PKCS7_free(p7);
1375 sk_X509_pop_free(encerts, X509_free);
1377 return rv;
1381 * Encrypt a message on the way out. Called from call_mailer in send.c
1382 * The body may be reallocated.
1385 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
1387 PKCS7 *p7 = NULL;
1388 BIO *in = NULL;
1389 BIO *out = NULL;
1390 const EVP_CIPHER *cipher = NULL;
1391 STACK_OF(X509) *encerts = NULL;
1392 STORE_S *outs = NULL;
1393 PINEFIELD *pf;
1394 ADDRESS *a;
1395 BODY *body = *bodyP;
1396 BODY *newBody = NULL;
1397 int result = 0;
1399 dprint((9, "encrypt_outgoing_message()"));
1400 smime_init();
1402 cipher = EVP_aes_256_cbc();
1404 encerts = sk_X509_new_null();
1406 /* Look for a certificate for each of the recipients */
1407 for(pf = header->local; pf && pf->name; pf = pf->next)
1408 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
1409 for(a=*pf->addr; a; a=a->next){
1410 X509 *cert;
1411 char buf[MAXPATH];
1413 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
1415 cert = get_cert_for(buf);
1416 if(cert)
1417 sk_X509_push(encerts,cert);
1418 else{
1419 q_status_message2(SM_ORDER, 1, 1,
1420 _("Unable to find certificate for <%s@%s>"),
1421 a->mailbox, a->host);
1422 goto end;
1428 in = body_to_bio(body);
1430 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
1432 outs = so_get(BioType, NULL, EDIT_ACCESS);
1433 out = bio_from_store(outs);
1435 i2d_PKCS7_bio(out, p7);
1436 (void) BIO_flush(out);
1438 so_seek(outs, 0, SEEK_SET);
1440 newBody = mail_newbody();
1442 newBody->type = TYPEAPPLICATION;
1443 newBody->subtype = cpystr("pkcs7-mime");
1444 newBody->encoding = ENCBINARY;
1446 newBody->disposition.type = cpystr("attachment");
1447 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
1449 newBody->description = cpystr("S/MIME Encrypted Message");
1450 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
1451 set_parameter(&newBody->parameter, "name", "smime.p7m");
1453 newBody->contents.text.data = (unsigned char *) outs;
1455 *bodyP = newBody;
1457 result = 1;
1459 end:
1461 BIO_free(in);
1462 PKCS7_free(p7);
1463 sk_X509_pop_free(encerts, X509_free);
1465 dprint((9, "encrypt_outgoing_message returns %d", result));
1466 return result;
1471 Get (and decode) the body of the given section of msg
1473 static STORE_S*
1474 get_part_contents(long msgno, const char *section)
1476 long len;
1477 gf_io_t pc;
1478 STORE_S *store = NULL;
1479 char *err;
1481 store = so_get(CharStar, NULL, EDIT_ACCESS);
1482 if(store){
1483 gf_set_so_writec(&pc,store);
1485 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
1487 gf_clear_so_writec(store);
1489 so_seek(store, 0, SEEK_SET);
1491 if(err)
1492 so_give(&store);
1495 return store;
1499 static PKCS7 *
1500 get_pkcs7_from_part(long msgno,const char *section)
1502 STORE_S *store = NULL;
1503 PKCS7 *p7 = NULL;
1504 BIO *in = NULL;
1506 store = get_part_contents(msgno, section);
1508 if(store){
1509 if(store->src == CharStar){
1510 int len;
1513 * We're reaching inside the STORE_S structure. We should
1514 * probably have a way to get the length, instead.
1516 len = (int) (store->eod - store->dp);
1517 in = BIO_new_mem_buf(store->txt, len);
1519 else{ /* just copy it */
1520 unsigned char c;
1522 in = BIO_new(BIO_s_mem());
1523 (void) BIO_reset(in);
1525 so_seek(store, 0L, 0);
1526 while(so_readc(&c, store)){
1527 BIO_write(in, &c, 1);
1531 if(in){
1532 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
1533 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
1534 /* error */
1537 BIO_free(in);
1540 so_give(&store);
1543 return p7;
1548 * Try to verify a signature.
1550 * p7 - the pkcs7 object to verify
1551 * in - the plain data to verify (NULL if not detached)
1552 * out - BIO to which to write the opaque data
1554 static int
1555 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out)
1557 STACK_OF(X509) *otherCerts = NULL;
1558 int result;
1559 const char *data;
1560 long err;
1562 #if 0
1563 if (in)
1564 dump_bio_to_file(in,"/tmp/verified-data");
1565 #endif
1567 if(!s_cert_store){
1568 q_status_message(SM_ORDER | SM_DING, 2, 2,
1569 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
1571 return -1;
1574 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, 0);
1576 if(result){
1577 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
1579 else{
1580 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
1582 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
1584 /* Retry verification so we can get the plain text */
1585 /* Might be better to reimplement PKCS7_verify here? */
1587 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY);
1590 q_status_message1(SM_ORDER | SM_DING, 3, 3,
1591 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
1594 /* now try to extract the certificates of any signers */
1596 STACK_OF(X509) *signers;
1597 int i;
1599 signers = PKCS7_get0_signers(p7, NULL, 0);
1601 if(signers)
1602 for(i=0; i<sk_X509_num(signers); i++){
1603 char **email;
1604 X509 *x = sk_X509_value(signers,i);
1605 X509 *cert;
1607 if(!x)
1608 continue;
1610 email = get_x509_subject_email(x);
1612 if(email){
1613 int i;
1614 for(i = 0; email[i] != NULL; i++){
1615 cert = get_cert_for(email[i]);
1616 if(cert)
1617 X509_free(cert);
1618 else
1619 save_cert_for(email[i], x);
1620 fs_give((void **) &email[i]);
1622 fs_give((void **) email);
1626 sk_X509_free(signers);
1629 return result;
1633 void
1634 free_smime_body_sparep(void **sparep)
1636 if(sparep && *sparep){
1637 PKCS7_free((PKCS7 *) (*sparep));
1638 *sparep = NULL;
1642 /* Big comment, explaining the mess that exists out there
1644 When Alpine sends a message, it constructs that message, computes the
1645 signature, but then it forgets the message it signed and reconstructs it
1646 again. Since it signs a message containing a notice about "mime aware
1647 tools", but it does not send that we do not include that in the part that
1648 is signed, and that takes care of much of the problems.
1650 Another problem is what is received from the servers. All servers tested
1651 seem to transmit the message that was signed intact and Alpine can check
1652 the signature correctly. That is not a problem. The problem arises when
1653 the message includes attachments. In this case different servers send
1654 different things, so it will be up to us to figure out what is the text
1655 that was actually signed. Confused? here is the story:
1657 When a message containing and attachment is sent by Alpine, UW-IMAP,
1658 Panda-IMAP, Gmail, and local reading of folders send exactly the message
1659 that was sent by Alpine, but GMX.com, Exchange, and probably other servers
1660 add a trailing \r\n in the message, so when validating the signature,
1661 these messages will not validate. There are several things that can be
1662 done.
1664 1. Add a trailing \r\n to any message that contains attachments, sign that
1665 and send that. In this way, all messages will validate with all
1666 servers.
1668 2. Compatibility mode: If a message has an attachment, contains a trailing
1669 \r\n and does not validate (sent by an earlier version of Alpine),
1670 remove the trailing \r\n and try to revalidate again.
1672 3. We do not add \r\n to validate a message that we sent, because that
1673 would only work in Alpine, and not in any other client. That would not
1674 be a good thing to do.
1678 * Given a multipart body of type multipart/signed, attempt to verify it.
1679 * Returns non-zero if the body was changed.
1681 static int
1682 do_detached_signature_verify(BODY *b, long msgno, char *section)
1684 PKCS7 *p7 = NULL;
1685 BIO *in = NULL;
1686 PART *p;
1687 int result, modified_the_body = 0;
1688 unsigned long mimelen, bodylen;
1689 char newSec[100], *mimetext, *bodytext;
1690 char *what_we_did;
1692 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"));
1693 smime_init();
1695 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
1697 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
1699 if(mimetext)
1700 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
1702 if (mimetext == NULL || bodytext == NULL)
1703 return modified_the_body;
1705 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
1707 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL)
1708 return modified_the_body;
1710 /* first try with what get got */
1711 if((in = BIO_new(BIO_s_mem())) == NULL)
1712 return modified_the_body;
1714 (void) BIO_reset(in);
1715 BIO_write(in, mimetext, mimelen);
1716 BIO_write(in, bodytext, bodylen);
1718 /* Try compatibility with the past and check if this message
1719 validates when we remove the last two characters
1721 if(((result = do_signature_verify(p7, in, NULL)) == 0)
1722 && bodylen > 2
1723 && (strncmp(bodytext+bodylen-2,"\r\n", 2) == 0)){
1724 BIO_free(in);
1725 if((in = BIO_new(BIO_s_mem())) == NULL)
1726 return modified_the_body;
1728 (void) BIO_reset(in);
1729 BIO_write(in, mimetext, mimelen);
1730 BIO_write(in, bodytext, bodylen-2);
1732 result = do_signature_verify(p7, in, NULL);
1735 BIO_free(in);
1736 if(b->subtype)
1737 fs_give((void**) &b->subtype);
1739 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
1740 b->encoding = ENC8BIT;
1742 if(b->description)
1743 fs_give ((void**) &b->description);
1745 what_we_did = result ? _("This message was cryptographically signed.") :
1746 _("This message was cryptographically signed but the signature could not be verified.");
1748 b->description = cpystr(what_we_did);
1750 b->sparep = p7;
1752 p = b->nested.part;
1754 /* p is signed plaintext */
1755 if(p && p->next)
1756 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
1758 modified_the_body = 1;
1760 return modified_the_body;
1764 PERSONAL_CERT *
1765 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
1767 PERSONAL_CERT *x = NULL;
1769 if(ps_global->smime){
1770 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
1771 X509 *mine;
1773 mine = x->cert;
1775 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,mine->cert_info->issuer) &&
1776 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,mine->cert_info->serialNumber)){
1777 break;
1782 return x;
1786 static PERSONAL_CERT *
1787 find_certificate_matching_pkcs7(PKCS7 *p7)
1789 int i;
1790 STACK_OF(PKCS7_RECIP_INFO) *recips;
1791 PERSONAL_CERT *x = NULL;
1793 recips = p7->d.enveloped->recipientinfo;
1795 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
1796 PKCS7_RECIP_INFO *ri;
1798 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
1800 if((x=find_certificate_matching_recip_info(ri))!=0){
1801 break;
1805 return x;
1808 /* decrypt an encrypted file.
1809 Args: fp - the path to the encrypted file.
1810 rv - a code that thells the caller what happened inside the function
1811 Returns the decoded text allocated in a char *, whose memory must be
1812 freed by caller
1815 char *
1816 decrypt_file(char *fp, int *rv)
1818 PKCS7 *p7 = NULL;
1819 char *text, *tmp;
1820 BIO *in = NULL, *out = NULL;
1821 EVP_PKEY *pkey = NULL, *key = NULL;
1822 PERSONAL_CERT *pcert = NULL;
1823 X509 *recip, *cert;
1824 STORE_S *outs = NULL, *store, *ins;
1825 int i, j;
1826 long unsigned int len;
1827 void *ret;
1829 smime_init();
1831 if((text = read_file(fp, 0)) == NULL)
1832 return NULL;
1834 tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1);
1835 for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0'
1836 && text[i] != '-'; j++, i++)
1837 tmp[j] = text[i];
1838 tmp[j] = '\0';
1840 ret = rfc822_base64(tmp, strlen(tmp), &len);
1842 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
1843 p7 = d2i_PKCS7_bio(in, NULL);
1844 BIO_free(in);
1847 if(text) fs_give((void **)&text);
1848 if(ret) fs_give((void **)&ret);
1850 if((pcert = ps_global->smime->personal_certs) == NULL)
1851 goto end;
1853 if((i = load_private_key(pcert)) == 0
1854 && ps_global->smime
1855 && ps_global->smime->need_passphrase
1856 && !ps_global->smime->already_auto_asked)
1857 for(; i == 0;){
1858 ps_global->smime->already_auto_asked = 1;
1859 if(pith_opt_smime_get_passphrase){
1860 switch((*pith_opt_smime_get_passphrase)()){
1861 case 0 : i = load_private_key(pcert);
1862 break;
1864 case 1 : i = -1;
1865 break;
1867 default: break; /* repeat until we cancel */
1870 else
1871 i = -2;
1874 if(rv) *rv = i;
1876 if((key = pcert->key) == NULL)
1877 goto end;
1879 recip = get_cert_for(pcert->name);
1880 out = BIO_new(BIO_s_mem());
1881 (void) BIO_reset(out);
1883 i = PKCS7_decrypt(p7, key, recip, out, 0);
1885 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
1886 forget_private_keys();
1888 if(i == 0){
1889 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
1890 (char*) openssl_error_string());
1891 goto end;
1894 BIO_get_mem_data(out, &tmp);
1896 text = cpystr(tmp);
1897 BIO_free(out);
1899 end:
1900 PKCS7_free(p7);
1902 return text;
1906 * Try to decode (decrypt or verify a signature) a PKCS7 body
1907 * Returns non-zero if something was changed.
1909 static int
1910 do_decoding(BODY *b, long msgno, const char *section)
1912 int modified_the_body = 0;
1913 BIO *out = NULL;
1914 PKCS7 *p7 = NULL;
1915 X509 *recip = NULL;
1916 EVP_PKEY *key = NULL;
1917 PERSONAL_CERT *pcert = NULL;
1918 char *what_we_did = "";
1919 char null[1];
1921 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"));
1922 null[0] = '\0';
1923 smime_init();
1926 * Extract binary data from part to an in-memory store
1929 if(b->sparep){ /* already done */
1930 p7 = (PKCS7*) b->sparep;
1932 else{
1934 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
1935 if(!p7){
1936 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
1937 (char*) openssl_error_string());
1938 goto end;
1942 * Save the PKCS7 object for later dealings by the user interface.
1943 * It will be cleaned up when the body is garbage collected.
1945 b->sparep = p7;
1948 if(PKCS7_type_is_signed(p7)){
1949 int sigok;
1951 out = BIO_new(BIO_s_mem());
1952 (void) BIO_reset(out);
1953 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
1955 sigok = do_signature_verify(p7, NULL, out);
1957 /* shouldn't really duplicate these messages */
1958 what_we_did = sigok ? _("This message was cryptographically signed.") :
1959 _("This message was cryptographically signed but the signature could not be verified.");
1961 /* make sure it's null terminated */
1962 BIO_write(out, null, 1);
1964 else if(!PKCS7_type_is_enveloped(p7)){
1965 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
1966 goto end;
1968 else{ /* It *is* enveloped */
1969 int decrypt_result;
1971 what_we_did = _("This message was encrypted.");
1973 /* now need to find a cert that can decrypt this */
1974 pcert = find_certificate_matching_pkcs7(p7);
1976 if(!pcert){
1977 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
1978 goto end;
1981 recip = pcert->cert;
1983 if(!load_private_key(pcert)
1984 && ps_global->smime
1985 && ps_global->smime->need_passphrase
1986 && !ps_global->smime->already_auto_asked){
1987 /* Couldn't load key with blank password, ask user */
1988 ps_global->smime->already_auto_asked = 1;
1989 if(pith_opt_smime_get_passphrase){
1990 (*pith_opt_smime_get_passphrase)();
1991 load_private_key(pcert);
1995 key = pcert->key;
1996 if(!key)
1997 goto end;
1999 out = BIO_new(BIO_s_mem());
2000 (void) BIO_reset(out);
2001 BIO_puts(out, "MIME-Version: 1.0\r\n");
2003 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
2005 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
2006 forget_private_keys();
2008 if(!decrypt_result){
2009 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
2010 (char*) openssl_error_string());
2011 goto end;
2014 BIO_write(out, null, 1);
2018 * We've now produced a flattened MIME object in BIO out.
2019 * It needs to be turned back into a BODY.
2022 if(out){
2023 BODY *body;
2024 ENVELOPE *env;
2025 char *h = NULL;
2026 char *bstart;
2027 STRING s;
2028 BUF_MEM *bptr = NULL;
2030 BIO_get_mem_ptr(out, &bptr);
2031 if(bptr)
2032 h = bptr->data;
2034 /* look for start of body */
2035 bstart = strstr(h, "\r\n\r\n");
2037 if(!bstart){
2038 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
2040 else{
2041 bstart += 4; /* skip over CRLF*2 */
2043 INIT(&s, mail_string, bstart, strlen(bstart));
2044 rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
2045 mail_free_envelope(&env); /* Don't care about this */
2048 * Now convert original body (application/pkcs7-mime)
2049 * to a multipart body with one sub-part (the decrypted body).
2050 * Note that the sub-part may also be multipart!
2053 b->type = TYPEMULTIPART;
2054 if(b->subtype)
2055 fs_give((void**) &b->subtype);
2058 * This subtype is used in mailview.c to annotate the display of
2059 * encrypted or signed messages. We know for sure then that it's a PKCS7
2060 * part because the sparep field is set to the PKCS7 object (see above).
2062 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
2063 b->encoding = ENC8BIT;
2065 if(b->description)
2066 fs_give((void**) &b->description);
2068 b->description = cpystr(what_we_did);
2070 if(b->disposition.type)
2071 fs_give((void **) &b->disposition.type);
2073 if(b->contents.text.data)
2074 fs_give((void **) &b->contents.text.data);
2076 if(b->parameter)
2077 mail_free_body_parameter(&b->parameter);
2079 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
2080 b->nested.part = fs_get(sizeof(PART));
2081 b->nested.part->body = *body;
2082 b->nested.part->next = NULL;
2084 fs_give((void**) &body);
2087 * IMPORTANT BIT: set the body->contents.text.data elements to contain the decrypted
2088 * data. Otherwise, it'll try to load it from the original data. Eek.
2090 create_local_cache(bstart, &b->nested.part->body);
2092 modified_the_body = 1;
2096 end:
2097 if(out)
2098 BIO_free(out);
2100 return modified_the_body;
2105 * Recursively handle PKCS7 bodies in our message.
2107 * Returns non-zero if some fiddling was done.
2109 static int
2110 do_fiddle_smime_message(BODY *b, long msgno, char *section)
2112 int modified_the_body = 0;
2114 if(!b)
2115 return 0;
2117 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"));
2119 if(is_pkcs7_body(b)){
2121 if(do_decoding(b, msgno, section)){
2123 * b should now be a multipart message:
2124 * fiddle it too in case it's been multiply-encrypted!
2127 /* fallthru */
2128 modified_the_body = 1;
2132 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
2134 PART *p;
2135 int partNum;
2136 char newSec[100];
2138 if(MIME_MULT_SIGNED(b->type, b->subtype)){
2142 * Ahah. We have a multipart signed entity.
2144 * Multipart/signed
2145 * part 1 (signed thing)
2146 * part 2 (the pkcs7 signature)
2148 * We're going to convert that to
2150 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
2151 * part 1 (signed thing)
2152 * part 2 has been freed
2154 * We also extract the signature from part 2 and save it
2155 * in the multipart body->sparep, and we add a description
2156 * in the multipart body->description.
2159 * The results of a decrypted message will be similar. It
2160 * will be
2162 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
2163 * part 1 (decrypted thing)
2166 modified_the_body += do_detached_signature_verify(b, msgno, section);
2168 else if(MIME_MSG(b->type, b->subtype)){
2169 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
2171 else{
2173 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
2175 /* Append part number to the section string */
2177 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
2179 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
2184 return modified_the_body;
2189 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
2190 * Returns non-zero if something was changed.
2193 fiddle_smime_message(BODY *b, long msgno)
2195 return do_fiddle_smime_message(b, msgno, "");
2199 /********************************************************************************/
2203 * Output a string in a distinctive style
2205 void
2206 gf_puts_uline(char *txt, gf_io_t pc)
2208 pc(TAG_EMBED); pc(TAG_BOLDON);
2209 gf_puts(txt, pc);
2210 pc(TAG_EMBED); pc(TAG_BOLDOFF);
2215 * Sign a message. Called from call_mailer in send.c.
2217 * This takes the header for the outgoing message as well as a pointer
2218 * to the current body (which may be reallocated).
2221 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach)
2223 STORE_S *outs = NULL;
2224 BODY *body = *bodyP;
2225 BODY *newBody = NULL;
2226 PART *p1 = NULL;
2227 PART *p2 = NULL;
2228 PERSONAL_CERT *pcert;
2229 BIO *in = NULL;
2230 BIO *out = NULL;
2231 PKCS7 *p7 = NULL;
2232 int result = 0;
2233 int flags = dont_detach ? 0 : PKCS7_DETACHED;
2235 dprint((9, "sign_outgoing_message()"));
2237 smime_init();
2239 /* Look for a private key matching the sender address... */
2241 pcert = match_personal_cert(header->env);
2243 if(!pcert){
2244 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
2245 goto end;
2248 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
2249 /* Couldn't load key with blank password, try again */
2250 if(pith_opt_smime_get_passphrase){
2251 (*pith_opt_smime_get_passphrase)();
2252 load_private_key(pcert);
2256 if(!pcert->key)
2257 goto end;
2259 in = body_to_bio(body);
2261 #ifdef notdef
2262 dump_bio_to_file(in,"/tmp/signed-data");
2263 #endif
2265 p7 = PKCS7_sign(pcert->cert, pcert->key, NULL, in, flags);
2267 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
2268 forget_private_keys();
2270 if(!p7){
2271 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
2272 goto end;
2275 outs = so_get(BioType, NULL, EDIT_ACCESS);
2276 out = bio_from_store(outs);
2278 i2d_PKCS7_bio(out, p7);
2279 (void) BIO_flush(out);
2281 so_seek(outs, 0, SEEK_SET);
2283 if((flags&PKCS7_DETACHED)==0){
2285 /* the simple case: the signed data is in the pkcs7 object */
2287 newBody = mail_newbody();
2289 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m");
2291 newBody->contents.text.data = (unsigned char *) outs;
2292 *bodyP = newBody;
2294 result = 1;
2296 else{
2299 * OK.
2300 * We have to create a new body as follows:
2302 * multipart/signed; blah blah blah
2303 * reference to existing body
2305 * pkcs7 object
2308 newBody = mail_newbody();
2310 newBody->type = TYPEMULTIPART;
2311 newBody->subtype = cpystr("signed");
2312 newBody->encoding = ENC7BIT;
2314 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
2315 set_parameter(&newBody->parameter, "micalg", "sha1");
2317 p1 = mail_newbody_part();
2318 p2 = mail_newbody_part();
2321 * This is nasty. We're just copying the body in here,
2322 * but since our newBody is freed at the end of call_mailer,
2323 * we mustn't let this body (the original one) be freed twice.
2325 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
2327 p1->next = p2;
2329 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s");
2330 p2->body.contents.text.data = (unsigned char *) outs;
2332 newBody->nested.part = p1;
2334 *bodyP = newBody;
2336 result = 1;
2339 end:
2341 PKCS7_free(p7);
2342 BIO_free(in);
2344 dprint((9, "sign_outgoing_message returns %d", result));
2345 return result;
2349 SMIME_STUFF_S *
2350 new_smime_struct(void)
2352 SMIME_STUFF_S *ret = NULL;
2354 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
2355 memset((void *) ret, 0, sizeof(*ret));
2356 ret->publictype = Nada;
2358 return ret;
2362 static void
2363 free_smime_struct(SMIME_STUFF_S **smime)
2365 if(smime && *smime){
2366 if((*smime)->passphrase_emailaddr){
2367 int i;
2368 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
2369 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
2370 fs_give((void **) (*smime)->passphrase_emailaddr);
2373 if((*smime)->publicpath)
2374 fs_give((void **) &(*smime)->publicpath);
2376 if((*smime)->publiccertlist)
2377 free_certlist(&(*smime)->publiccertlist);
2379 if((*smime)->publiccontent)
2380 fs_give((void **) &(*smime)->publiccontent);
2382 if((*smime)->privatepath)
2383 fs_give((void **) &(*smime)->privatepath);
2385 if((*smime)->personal_certs){
2386 PERSONAL_CERT *pc;
2388 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
2389 free_personal_certs(&pc);
2390 (*smime)->personal_certs = NULL;
2393 if((*smime)->privatecontent)
2394 fs_give((void **) &(*smime)->privatecontent);
2396 if((*smime)->capath)
2397 fs_give((void **) &(*smime)->capath);
2399 if((*smime)->cacontent)
2400 fs_give((void **) &(*smime)->cacontent);
2402 fs_give((void **) smime);
2406 #endif /* SMIME */