* Forwarding messages with attachments of content-type multipart,
[alpine.git] / pith / smime.c
blobe691ae681e93b8ccba5a19934fe890f8b2f3962a
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>
48 /* internal prototypes */
49 static void forget_private_keys(void);
50 static int app_RAND_load_file(const char *file);
51 static void openssl_extra_randomness(void);
52 static int app_RAND_write_file(const char *file);
53 static const char *openssl_error_string(void);
54 static int load_private_key(PERSONAL_CERT *pcert);
55 static void create_local_cache(char *h, char *base, BODY *b);
56 static long rfc822_output_func(void *b, char *string);
57 static void setup_pkcs7_body_for_signature(BODY *b, char *description,
58 char *type, char *filename);
59 static BIO *body_to_bio(BODY *body);
60 static BIO *bio_from_store(STORE_S *store);
61 static STORE_S *get_part_contents(long msgno, const char *section);
62 static PKCS7 *get_pkcs7_from_part(long msgno, const char *section);
63 static int do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent);
64 static int do_detached_signature_verify(BODY *b, long msgno, char *section);
65 static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7);
66 static int do_decoding(BODY *b, long msgno, const char *section);
67 static void free_smime_struct(SMIME_STUFF_S **smime);
68 static void setup_storage_locations(void);
69 static int copy_dir_to_container(WhichCerts which);
70 static int copy_container_to_dir(WhichCerts which);
71 int smime_path(char *rpath, char *fpath, size_t len);
72 int smime_extract_and_save_cert(PKCS7 *p7);
73 int same_cert(X509 *, X509 *);
74 CertList * certlist_from_personal_certs(PERSONAL_CERT *pc);
76 int (*pith_opt_smime_get_passphrase)(void);
77 int (*pith_smime_import_certificate)(char *, char *, size_t);
78 char *(*pith_enter_password)(char *prompt, char *, size_t);
80 static X509_STORE *s_cert_store;
82 /* State management for randomness functions below */
83 static int seeded = 0;
84 static int egdsocket = 0;
87 int
88 import_certificate(WhichCerts ctype)
90 int r = 1;
91 char filename[MAXPATH+1], full_filename[MAXPATH+1], buf[MAXPATH+1];
93 if(pith_smime_import_certificate == NULL)
94 q_status_message(SM_ORDER, 0, 2,
95 _("import of certificates not implemented yet!"));
97 smime_init();
99 r = (*pith_smime_import_certificate)(filename, full_filename, sizeof(filename) - 20);
100 if(r < 0){
101 switch(r){
102 default:
103 case -1:
104 cmd_cancelled("Import certificate");
105 break;
107 case -2:
108 q_status_message1(SM_ORDER, 0, 2,
109 _("Can't import certificate outside of %s"),
110 ps_global->VAR_OPER_DIR);
111 break;
113 } else if (ctype == Private){
114 char prompt[500], *s, *t;
115 char pass[MAILTMPLEN+1];
116 BIO *in;
117 EVP_PKEY *key = NULL;
118 PERSONAL_CERT *pc;
120 if(!ps_global->smime->privatecertdata){
121 ps_global->smime->privatecertdata = fs_get(sizeof(CertList));
122 memset((void *)DATACERT(ctype), 0, sizeof(CertList));
125 if(!(in = BIO_new_file(full_filename, "r")))
126 return -1;
128 for(s = t = filename; (t = strstr(s, ".key")) != NULL; s = t + 1);
129 if(s) *(s-1) = 0;
131 snprintf(prompt, sizeof(prompt),
132 _("Enter passphrase for <%s>: "), s ? s : filename);
134 pass[0] = '\0';
135 if(pith_enter_password)
136 (*pith_enter_password)(prompt, (char *)pass, sizeof(pass));
138 if((key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass)) != NULL){
139 if(SMHOLDERTYPE(ctype) == Directory){
140 STORE_S *in_cert, *out_cert;
141 char c;
143 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
144 if(strcmp(buf + strlen(buf) - 4, EXTCERT(ctype)) != 0 && strlen(buf) + 4 < sizeof(buf))
145 strcat(buf, EXTCERT(ctype));
147 in_cert = so_get(FileStar, full_filename, READ_ACCESS | READ_FROM_LOCALE);
148 out_cert = so_get(FileStar, buf, WRITE_ACCESS | WRITE_TO_LOCALE);
150 if(in_cert != NULL && out_cert != NULL){
151 while(so_readc(&c, in_cert) > 0)
152 so_writec(c, out_cert);
153 q_status_message(SM_ORDER, 1, 3, _("Certificate saved"));
155 else
156 q_status_message(SM_ORDER, 1, 3, _("Error saving certificate"));
158 so_give(&in_cert);
159 so_give(&out_cert);
161 if(ps_global->smime->publiccertdata)
162 ps_global->smime->publiccertdata->data.renew = 1;
164 else
165 q_status_message(SM_ORDER, 1, 3, _("Problem unlocking key (not a certificate and/or wrong password)"));
166 BIO_free(in);
167 } else if (ctype == CACert){
168 BIO *ins;
169 X509 *cert;
171 if((ins = BIO_new_file(full_filename, "r")) != NULL){
172 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
173 if(SMHOLDERTYPE(ctype) == Directory){
174 STORE_S *in_cert, *out_cert;
175 char c;
177 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
178 if(strcmp(buf + strlen(buf) - 4, ".crt") != 0 && strlen(buf) + 4 < sizeof(buf))
179 strcat(buf, EXTCERT(ctype));
181 in_cert = so_get(FileStar, full_filename, READ_ACCESS | READ_FROM_LOCALE);
182 out_cert = so_get(FileStar, buf, WRITE_ACCESS | WRITE_TO_LOCALE);
184 if(in_cert != NULL && out_cert != NULL){
185 while(so_readc(&c, in_cert) > 0)
186 so_writec(c, out_cert);
187 q_status_message(SM_ORDER, 1, 3, _("Certificate saved"));
189 else
190 q_status_message(SM_ORDER, 1, 3, _("Error saving certificate"));
192 so_give(&in_cert);
193 so_give(&out_cert);
195 X509_free(cert); /* not needed anymore */
197 else
198 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
199 BIO_free(ins);
201 renew_store();
202 } else { /* ctype == Public. save certificate, but first validate that it is one */
203 BIO *ins;
204 X509 *cert;
206 if((ins = BIO_new_file(full_filename, "r")) != NULL){
207 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
208 char **email = get_x509_subject_email(cert);
209 int i;
211 for(i = 0; email[i] != NULL; i++){
212 save_cert_for(email[i], cert, Public);
213 fs_give((void **)&email[i]);
215 fs_give((void **)email);
216 X509_free(cert);
217 if(ps_global->smime->publiccertdata)
218 ps_global->smime->publiccertdata->data.renew = 1;
220 else
221 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
222 BIO_free(ins);
225 if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1;
226 ps_global->mangled_screen = 1;
227 return 0;
230 /* itype: information type to add: 0 - public, 1 - private.
231 * Memory freed by caller
233 BIO *
234 print_private_key_information(char *email, int itype)
236 BIO *out;
237 PERSONAL_CERT *pc;
239 if(ps_global->smime == NULL
240 || ps_global->smime->personal_certs == NULL
241 || (itype != 0 && itype != 1))
242 return NULL;
244 for(pc = ps_global->smime->personal_certs;
245 pc != NULL && strcmp(pc->name, email) != 0; pc = pc->next);
246 if(pc->key == NULL
247 && !load_private_key(pc)
248 && ps_global->smime
249 && ps_global->smime->need_passphrase){
250 if (*pith_opt_smime_get_passphrase)
251 (*pith_opt_smime_get_passphrase)();
252 load_private_key(pc);
255 if(pc->key == NULL)
256 return NULL;
258 out = BIO_new(BIO_s_mem());
259 if(itype == 0) /* 0 means public */
260 EVP_PKEY_print_public(out, pc->key, 0, NULL);
261 else if (itype == 1) /* 1 means private */
262 EVP_PKEY_print_private(out, pc->key, 0, NULL);
264 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
265 forget_private_keys();
267 return out;
271 * Forget any cached private keys
273 static void
274 forget_private_keys(void)
276 PERSONAL_CERT *pcert;
277 size_t len;
278 volatile char *p;
280 dprint((9, "forget_private_keys()"));
281 if(ps_global->smime){
282 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
283 pcert;
284 pcert=pcert->next){
286 if(pcert->key){
287 EVP_PKEY_free(pcert->key);
288 pcert->key = NULL;
292 ps_global->smime->entered_passphrase = 0;
293 len = sizeof(ps_global->smime->passphrase);
294 p = ps_global->smime->passphrase;
296 while(len-- > 0)
297 *p++ = '\0';
301 /* modelled after signature_path in reply.c, but uses home dir instead of the
302 * directory where the .pinerc is located, since according to documentation,
303 * the .alpine-smime directories are subdirectories of the home directory
306 int smime_path(char *rpath, char *fpath, size_t len)
308 *fpath = '\0';
309 if(rpath && *rpath){
310 size_t spl = strlen(rpath);
312 *fpath = '\0';
313 if(IS_REMOTE(rpath)){
314 if(spl < len - 1)
315 strncpy(fpath, rpath, len-1);
316 fpath[len-1] = '\0';
318 else if(is_absolute_path(rpath)){
319 strncpy(fpath, rpath, len-1);
320 fpath[len-1] = '\0';
321 fnexpand(fpath, len);
323 else if(ps_global->VAR_OPER_DIR){
324 if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
325 build_path(fpath, ps_global->VAR_OPER_DIR, rpath, len);
327 else if(ps_global->home_dir){
328 if(strlen(ps_global->home_dir) + spl < len - 1)
329 build_path(fpath, ps_global->home_dir, rpath, len);
332 return fpath && *fpath ? 1 : 0;
338 * taken from openssl/apps/app_rand.c
340 static int
341 app_RAND_load_file(const char *file)
343 char buffer[200];
345 if(file == NULL)
346 file = RAND_file_name(buffer, sizeof buffer);
347 else if(RAND_egd(file) > 0){
348 /* we try if the given filename is an EGD socket.
349 if it is, we don't write anything back to the file. */
350 egdsocket = 1;
351 return 1;
354 if(file == NULL || !RAND_load_file(file, -1)){
355 if(RAND_status() == 0){
356 dprint((1, "unable to load 'random state'\n"));
357 dprint((1, "This means that the random number generator has not been seeded\n"));
358 dprint((1, "with much random data.\n"));
361 return 0;
364 seeded = 1;
365 return 1;
370 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
372 static void
373 openssl_extra_randomness(void)
375 #if !defined(WIN32)
376 int fd;
377 unsigned long i;
378 char *tf = NULL;
379 char tmp[MAXPATH];
380 struct stat sbuf;
381 /* if system doesn't have /dev/urandom */
382 if(stat ("/dev/urandom", &sbuf)){
383 tmp[0] = '0';
384 tf = temp_nam(NULL, NULL);
385 if(tf){
386 strncpy(tmp, tf, sizeof(tmp));
387 tmp[sizeof(tmp)-1] = '\0';
388 fs_give((void **) &tf);
391 if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
392 i = (unsigned long) tmp;
393 else{
394 unlink(tmp); /* don't need the file */
395 fstat(fd, &sbuf); /* get information about the file */
396 i = sbuf.st_ino; /* remember its inode */
397 close(fd); /* or its descriptor */
399 /* not great but it'll have to do */
400 snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
401 tcp_serverhost (),i,
402 (unsigned long) (time (0) ^ gethostid ()),
403 (unsigned long) getpid ());
404 RAND_seed(tmp, strlen(tmp));
406 #endif
410 /* taken from openssl/apps/app_rand.c */
411 static int
412 app_RAND_write_file(const char *file)
414 char buffer[200];
416 if(egdsocket || !seeded)
418 * If we did not manage to read the seed file,
419 * we should not write a low-entropy seed file back --
420 * it would suppress a crucial warning the next time
421 * we want to use it.
423 return 0;
425 if(file == NULL)
426 file = RAND_file_name(buffer, sizeof buffer);
428 if(file == NULL || !RAND_write_file(file)){
429 dprint((1, "unable to write 'random state'\n"));
430 return 0;
433 return 1;
436 CertList *
437 certlist_from_personal_certs(PERSONAL_CERT *pc)
439 CertList *cl;
441 if(pc == NULL)
442 return NULL;
444 cl = fs_get(sizeof(CertList));
445 memset((void *)cl, 0, sizeof(CertList));
446 cl->name = cpystr(pc->name);
447 cl->next = certlist_from_personal_certs(pc->next);
449 return cl;
453 void
454 renew_cert_data(CertList **data, WhichCerts ctype)
456 smime_init();
457 if(ctype == Private){
458 if(data){
459 PERSONAL_CERT *pc = (PERSONAL_CERT *)ps_global->smime->personal_certs;
460 if(*data)
461 free_certlist(data);
462 free_personal_certs(&pc);
463 ps_global->smime->personal_certs = (void *) get_personal_certs(ps_global->smime->privatepath);
464 *data = certlist_from_personal_certs((PERSONAL_CERT *)ps_global->smime->personal_certs);
465 if(data && *data)
466 RENEWCERT(*data) = 0;
467 ps_global->smime->privatecertdata = (CertList *) *data;
469 if(ps_global->smime->privatecertdata)
470 RENEWCERT(ps_global->smime->privatecertdata) = 0;
471 } else {
472 X509_LOOKUP *lookup = NULL;
473 X509_STORE *store = NULL;
475 if((store = X509_STORE_new()) != NULL)
476 if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL){
477 X509_STORE_free(store);
478 store = NULL;
480 else{
481 if(PATHCERTDIR(ctype)){
482 free_certlist(data);
483 add_certs_in_dir(lookup, PATHCERTDIR(ctype), EXTCERT(ctype), data);
484 if(data && *data)
485 RENEWCERT(*data) = 0;
486 if(ctype == Public)
487 ps_global->smime->publiccertdata = *data;
488 else
489 ps_global->smime->cacertdata = *data;
497 /* Installed as an atexit() handler to save the random data */
498 void
499 smime_deinit(void)
501 dprint((9, "smime_deinit()"));
502 app_RAND_write_file(NULL);
503 free_smime_struct(&ps_global->smime);
506 /* we renew the store when it has changed */
507 void renew_store(void)
509 if(ps_global->smime->inited){
510 if(s_cert_store != NULL)
511 X509_STORE_free(s_cert_store);
512 s_cert_store = get_ca_store();
516 /* Initialise openssl stuff if needed */
517 void
518 smime_init(void)
520 if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
522 dprint((9, "smime_init()"));
523 if(!ps_global->smime)
524 ps_global->smime = new_smime_struct();
526 setup_storage_locations();
528 s_cert_store = get_ca_store();
530 OpenSSL_add_all_algorithms();
531 ERR_load_crypto_strings();
533 app_RAND_load_file(NULL);
535 openssl_extra_randomness();
536 ps_global->smime->inited = 1;
539 ERR_clear_error();
543 /* validate a certificate. Return value : 0 for no error, -1 for error.
544 * In the latter case, set the openssl smime error in *error.
546 int smime_validate_cert(X509 *cert, long *error)
548 X509_STORE_CTX *csc;
550 ERR_clear_error();
551 *error = 0;
552 if((csc = X509_STORE_CTX_new()) != NULL){
553 X509_STORE_set_flags(s_cert_store, 0);
554 if(X509_STORE_CTX_init(csc,s_cert_store,cert,NULL)
555 && X509_verify_cert(csc) <= 0)
556 *error = X509_STORE_CTX_get_error(csc);
557 X509_STORE_CTX_free(csc);
559 return *error ? -1 : 0;
562 PERSONAL_CERT *
563 get_personal_certs(char *path)
565 PERSONAL_CERT *result = NULL;
566 char buf2[MAXPATH];
567 struct dirent *d;
568 DIR *dirp;
570 ps_global->smime->privatepath = cpystr(path);
571 dirp = opendir(path);
572 if(dirp){
573 while((d=readdir(dirp)) != NULL){
574 X509 *cert;
575 size_t ll;
577 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, ".key")){
579 /* copy file name to temp buffer */
580 strncpy(buf2, d->d_name, sizeof(buf2)-1);
581 buf2[sizeof(buf2)-1] = '\0';
582 /* chop off ".key" trailier */
583 buf2[strlen(buf2)-4] = 0;
584 /* Look for certificate */
585 cert = get_cert_for(buf2, Public);
587 if(cert){
588 PERSONAL_CERT *pc;
590 /* create a new PERSONAL_CERT, fill it in */
592 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
593 pc->cert = cert;
594 pc->name = cpystr(buf2);
596 /* Try to load the key with an empty password */
597 pc->key = load_key(pc, "");
599 pc->next = result;
600 result = pc;
604 closedir(dirp);
606 return result;
610 static void
611 setup_storage_locations(void)
613 int publiccertcontainer = 0, privatekeycontainer = 0, cacertcontainer = 0;
614 char path[MAXPATH+1], *contents;
616 if(!ps_global->smime)
617 return;
619 #ifdef APPLEKEYCHAIN
620 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
621 ps_global->smime->publictype = Keychain;
623 else{
624 #endif /* APPLEKEYCHAIN */
625 /* Public certificates in a container */
626 if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
628 publiccertcontainer = 1;
629 contents = NULL;
630 path[0] = '\0';
631 if(!smime_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
632 publiccertcontainer = 0;
634 if(publiccertcontainer && !IS_REMOTE(path)
635 && ps_global->VAR_OPER_DIR
636 && !in_dir(ps_global->VAR_OPER_DIR, path)){
637 q_status_message2(SM_ORDER | SM_DING, 3, 4,
638 /* TRANSLATORS: First arg is the directory name, second is
639 the file user wants to read but can't. */
640 _("Can't read file outside %s: %s"),
641 ps_global->VAR_OPER_DIR, path);
642 publiccertcontainer = 0;
645 if(publiccertcontainer
646 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
647 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
649 !(contents = read_file(path, READ_FROM_LOCALE)))
650 publiccertcontainer = 0;
653 if(publiccertcontainer && path[0]){
654 ps_global->smime->publictype = Container;
655 ps_global->smime->publicpath = cpystr(path);
657 if(contents){
658 ps_global->smime->publiccontent = contents;
659 ps_global->smime->publiccertlist = mem_to_certlist(contents);
664 /* Public certificates in a directory of files */
665 if(!publiccertcontainer){
666 ps_global->smime->publictype = Directory;
668 path[0] = '\0';
669 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
670 && !IS_REMOTE(path)))
671 ps_global->smime->publictype = Nada;
672 else if(can_access(path, ACCESS_EXISTS)){
673 if(our_mkpath(path, 0700)){
674 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
675 ps_global->smime->publictype = Nada;
679 if(ps_global->smime->publictype == Directory)
680 ps_global->smime->publicpath = cpystr(path);
683 #ifdef APPLEKEYCHAIN
685 #endif /* APPLEKEYCHAIN */
687 /* private keys in a container */
688 if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
690 privatekeycontainer = 1;
691 contents = NULL;
692 path[0] = '\0';
693 if(!smime_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
694 privatekeycontainer = 0;
696 if(privatekeycontainer && !IS_REMOTE(path)
697 && ps_global->VAR_OPER_DIR
698 && !in_dir(ps_global->VAR_OPER_DIR, path)){
699 q_status_message2(SM_ORDER | SM_DING, 3, 4,
700 /* TRANSLATORS: First arg is the directory name, second is
701 the file user wants to read but can't. */
702 _("Can't read file outside %s: %s"),
703 ps_global->VAR_OPER_DIR, path);
704 privatekeycontainer = 0;
707 if(privatekeycontainer
708 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
709 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
711 !(contents = read_file(path, READ_FROM_LOCALE)))
712 privatekeycontainer = 0;
715 if(privatekeycontainer && path[0]){
716 ps_global->smime->privatetype = Container;
717 ps_global->smime->privatepath = cpystr(path);
719 if(contents){
720 ps_global->smime->privatecontent = contents;
721 ps_global->smime->personal_certs = mem_to_personal_certs(contents);
726 /* private keys in a directory of files */
727 if(!privatekeycontainer){
728 PERSONAL_CERT *result = NULL;
730 ps_global->smime->privatetype = Directory;
732 path[0] = '\0';
733 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
734 && !IS_REMOTE(path)))
735 ps_global->smime->privatetype = Nada;
736 else if(can_access(path, ACCESS_EXISTS)){
737 if(our_mkpath(path, 0700)){
738 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
739 ps_global->smime->privatetype = Nada;
743 if(ps_global->smime->privatetype == Directory)
744 ps_global->smime->personal_certs = get_personal_certs(path);
747 /* extra cacerts in a container */
748 if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
750 cacertcontainer = 1;
751 contents = NULL;
752 path[0] = '\0';
753 if(!smime_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
754 cacertcontainer = 0;
756 if(cacertcontainer && !IS_REMOTE(path)
757 && ps_global->VAR_OPER_DIR
758 && !in_dir(ps_global->VAR_OPER_DIR, path)){
759 q_status_message2(SM_ORDER | SM_DING, 3, 4,
760 /* TRANSLATORS: First arg is the directory name, second is
761 the file user wants to read but can't. */
762 _("Can't read file outside %s: %s"),
763 ps_global->VAR_OPER_DIR, path);
764 cacertcontainer = 0;
767 if(cacertcontainer
768 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
769 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
771 !(contents = read_file(path, READ_FROM_LOCALE)))
772 cacertcontainer = 0;
775 if(cacertcontainer && path[0]){
776 ps_global->smime->catype = Container;
777 ps_global->smime->capath = cpystr(path);
778 ps_global->smime->cacontent = contents;
782 if(!cacertcontainer){
783 ps_global->smime->catype = Directory;
785 path[0] = '\0';
786 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
787 && !IS_REMOTE(path)))
788 ps_global->smime->catype = Nada;
789 else if(can_access(path, ACCESS_EXISTS)){
790 if(our_mkpath(path, 0700)){
791 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
792 ps_global->smime->catype = Nada;
796 if(ps_global->smime->catype == Directory)
797 ps_global->smime->capath = cpystr(path);
803 copy_publiccert_dir_to_container(void)
805 return(copy_dir_to_container(Public));
810 copy_publiccert_container_to_dir(void)
812 return(copy_container_to_dir(Public));
817 copy_privatecert_dir_to_container(void)
819 return(copy_dir_to_container(Private));
824 copy_privatecert_container_to_dir(void)
826 return(copy_container_to_dir(Private));
831 copy_cacert_dir_to_container(void)
833 return(copy_dir_to_container(CACert));
838 copy_cacert_container_to_dir(void)
840 return(copy_container_to_dir(CACert));
845 * returns 0 on success, -1 on failure
848 copy_dir_to_container(WhichCerts which)
850 int ret = 0;
851 BIO *bio_out = NULL, *bio_in = NULL;
852 char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
853 char *tempfile = NULL;
854 DIR *dirp;
855 struct dirent *d;
856 REMDATA_S *rd = NULL;
857 char *configdir = NULL;
858 char *configpath = NULL;
859 char *filesuffix = NULL;
861 dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
862 smime_init();
864 srcpath[0] = '\0';
865 dstpath[0] = '\0';
866 file[0] = '\0';
867 emailaddr[0] = '\0';
869 if(which == Public){
870 configdir = ps_global->VAR_PUBLICCERT_DIR;
871 configpath = ps_global->smime->publicpath;
872 filesuffix = ".crt";
874 else if(which == Private){
875 configdir = ps_global->VAR_PRIVATEKEY_DIR;
876 configpath = ps_global->smime->privatepath;
877 filesuffix = ".key";
879 else if(which == CACert){
880 configdir = ps_global->VAR_CACERT_DIR;
881 configpath = ps_global->smime->capath;
882 filesuffix = ".crt";
885 if(!(configdir && configdir[0])){
886 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
887 return -1;
890 if(!(configpath && configpath[0])){
891 #ifdef APPLEKEYCHAIN
892 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
893 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
894 return -1;
896 #endif /* APPLEKEYCHAIN */
897 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
898 return -1;
901 if(!(filesuffix && strlen(filesuffix) == 4)){
902 return -1;
907 * If there is a legit directory to read from set up the
908 * container file to write to.
910 if(smime_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
912 if(IS_REMOTE(configpath)){
913 rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
914 NULL, "Error: ",
915 _("Can't access remote smime configuration."));
916 if(!rd)
917 return -1;
919 (void) rd_read_metadata(rd);
921 if(rd->access == MaybeRorW){
922 if(rd->read_status == 'R')
923 rd->access = ReadOnly;
924 else
925 rd->access = ReadWrite;
928 if(rd->access != NoExists){
930 rd_check_remvalid(rd, 1L);
933 * If the cached info says it is readonly but
934 * it looks like it's been fixed now, change it to readwrite.
936 if(rd->read_status == 'R'){
937 rd_check_readonly_access(rd);
938 if(rd->read_status == 'W'){
939 rd->access = ReadWrite;
940 rd->flags |= REM_OUTOFDATE;
942 else
943 rd->access = ReadOnly;
947 if(rd->flags & REM_OUTOFDATE){
948 if(rd_update_local(rd) != 0){
950 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
951 rd_close_remdata(&rd);
952 return -1;
955 else
956 rd_open_remote(rd);
958 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
959 rd_close_remdata(&rd);
960 return -1;
963 rd->flags |= DO_REMTRIM;
965 strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
966 dstpath[sizeof(dstpath)-1] = '\0';
968 else{
969 strncpy(dstpath, configpath, sizeof(dstpath)-1);
970 dstpath[sizeof(dstpath)-1] = '\0';
974 * dstpath is either the local Container file or the local cache file
975 * for the remote Container file.
977 tempfile = tempfile_in_same_dir(dstpath, "az", NULL);
981 * If there is a legit directory to read from and a tempfile
982 * to write to we continue.
984 if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
986 dirp = opendir(srcpath);
987 if(dirp){
989 while((d=readdir(dirp)) && !ret){
990 size_t ll;
992 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, filesuffix)){
994 /* copy file name to temp buffer */
995 strncpy(emailaddr, d->d_name, sizeof(emailaddr)-1);
996 emailaddr[sizeof(emailaddr)-1] = '\0';
997 /* chop off suffix trailier */
998 emailaddr[strlen(emailaddr)-4] = 0;
1001 * This is the separator between the contents of
1002 * different files.
1004 if(which == CACert){
1005 if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
1006 && (BIO_puts(bio_out, emailaddr) > 0)
1007 && (BIO_puts(bio_out, "\n") > 0)))
1008 ret = -1;
1010 else{
1011 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1012 && (BIO_puts(bio_out, emailaddr) > 0)
1013 && (BIO_puts(bio_out, "\n") > 0)))
1014 ret = -1;
1017 /* read then write contents of file */
1018 build_path(file, srcpath, d->d_name, sizeof(file));
1019 if(!(bio_in = BIO_new_file(file, "r")))
1020 ret = -1;
1022 if(!ret){
1023 int good_stuff = 0;
1025 while(BIO_gets(bio_in, line, sizeof(line)) > 0){
1026 if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
1027 good_stuff = 1;
1029 if(good_stuff)
1030 BIO_puts(bio_out, line);
1032 if(strncmp("-----END", line, strlen("-----END")) == 0)
1033 good_stuff = 0;
1037 BIO_free(bio_in);
1041 closedir(dirp);
1044 BIO_free(bio_out);
1046 if(!ret){
1047 if(rename_file(tempfile, dstpath) < 0){
1048 q_status_message2(SM_ORDER, 3, 3,
1049 _("Can't rename %s to %s"), tempfile, dstpath);
1050 ret = -1;
1053 /* if the container is remote, copy it */
1054 if(!ret && IS_REMOTE(configpath)){
1055 int e;
1056 char datebuf[200];
1058 datebuf[0] = '\0';
1060 if((e = rd_update_remote(rd, datebuf)) != 0){
1061 if(e == -1){
1062 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1063 _("Error opening temporary smime file %s: %s"),
1064 rd->lf, error_description(errno));
1065 dprint((1,
1066 "write_remote_smime: error opening temp file %s\n",
1067 rd->lf ? rd->lf : "?"));
1069 else{
1070 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1071 _("Error copying to %s: %s"),
1072 rd->rn, error_description(errno));
1073 dprint((1,
1074 "write_remote_smime: error copying from %s to %s\n",
1075 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1078 q_status_message(SM_ORDER | SM_DING, 5, 5,
1079 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1081 else{
1082 rd_update_metadata(rd, datebuf);
1083 rd->read_status = 'W';
1086 rd_close_remdata(&rd);
1091 if(tempfile)
1092 fs_give((void **) &tempfile);
1094 return ret;
1099 * returns 0 on success, -1 on failure
1102 copy_container_to_dir(WhichCerts which)
1104 char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
1105 char iobuf[4096];
1106 char *contents = NULL;
1107 char *leader = NULL;
1108 char *filesuffix = NULL;
1109 char *configdir = NULL;
1110 char *configpath = NULL;
1111 char *tempfile = NULL;
1112 char *p, *q, *line, *name, *certtext, *save_p;
1113 int len;
1114 BIO *in, *out;
1116 dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1117 smime_init();
1119 path[0] = '\0';
1121 if(which == Public){
1122 leader = EMAILADDRLEADER;
1123 contents = ps_global->smime->publiccontent;
1124 configdir = ps_global->VAR_PUBLICCERT_DIR;
1125 configpath = ps_global->smime->publicpath;
1126 filesuffix = ".crt";
1127 if(!(configpath && configpath[0])){
1128 #ifdef APPLEKEYCHAIN
1129 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1130 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1131 return -1;
1133 #endif /* APPLEKEYCHAIN */
1134 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1135 return -1;
1138 fs_give((void **) &ps_global->smime->publicpath);
1140 path[0] = '\0';
1141 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1142 && !IS_REMOTE(path))){
1143 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1144 return -1;
1147 if(can_access(path, ACCESS_EXISTS)){
1148 if(our_mkpath(path, 0700)){
1149 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1150 return -1;
1154 ps_global->smime->publicpath = cpystr(path);
1155 configpath = ps_global->smime->publicpath;
1157 else if(which == Private){
1158 leader = EMAILADDRLEADER;
1159 contents = ps_global->smime->privatecontent;
1160 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1161 configpath = ps_global->smime->privatepath;
1162 filesuffix = ".key";
1163 if(!(configpath && configpath[0])){
1164 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1165 return -1;
1168 fs_give((void **) &ps_global->smime->privatepath);
1170 path[0] = '\0';
1171 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1172 && !IS_REMOTE(path))){
1173 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1174 return -1;
1177 if(can_access(path, ACCESS_EXISTS)){
1178 if(our_mkpath(path, 0700)){
1179 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1180 return -1;
1184 ps_global->smime->privatepath = cpystr(path);
1185 configpath = ps_global->smime->privatepath;
1187 else if(which == CACert){
1188 leader = CACERTSTORELEADER;
1189 contents = ps_global->smime->cacontent;
1190 configdir = ps_global->VAR_CACERT_DIR;
1191 configpath = ps_global->smime->capath;
1192 filesuffix = ".crt";
1193 if(!(configpath && configpath[0])){
1194 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1195 return -1;
1198 fs_give((void **) &ps_global->smime->capath);
1200 path[0] = '\0';
1201 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1202 && !IS_REMOTE(path))){
1203 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1204 return -1;
1207 if(can_access(path, ACCESS_EXISTS)){
1208 if(our_mkpath(path, 0700)){
1209 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1210 return -1;
1214 ps_global->smime->capath = cpystr(path);
1215 configpath = ps_global->smime->capath;
1218 if(!(configdir && configdir[0])){
1219 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1220 return -1;
1223 if(!(configpath && configpath[0])){
1224 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1225 return -1;
1228 if(!(filesuffix && strlen(filesuffix) == 4)){
1229 return -1;
1233 if(contents && *contents){
1234 for(p = contents; *p != '\0';){
1235 line = p;
1237 while(*p && *p != '\n')
1238 p++;
1240 save_p = NULL;
1241 if(*p == '\n'){
1242 save_p = p;
1243 *p++ = '\0';
1246 if(strncmp(leader, line, strlen(leader)) == 0){
1247 name = line + strlen(leader);
1248 certtext = p;
1249 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
1250 if((q = strstr(certtext, leader)) != NULL){
1251 p = q;
1253 else{ /* end of file */
1254 q = certtext + strlen(certtext);
1255 p = q;
1258 strncpy(buf, name, sizeof(buf)-5);
1259 buf[sizeof(buf)-5] = '\0';
1260 strncat(buf, filesuffix, 5);
1261 build_path(file, configpath, buf, sizeof(file));
1263 in = BIO_new_mem_buf(certtext, q-certtext);
1264 if(in){
1265 tempfile = tempfile_in_same_dir(file, "az", NULL);
1266 out = NULL;
1267 if(tempfile)
1268 out = BIO_new_file(tempfile, "w");
1270 if(out){
1271 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1272 BIO_write(out, iobuf, len);
1274 BIO_free(out);
1276 if(rename_file(tempfile, file) < 0){
1277 q_status_message2(SM_ORDER, 3, 3,
1278 _("Can't rename %s to %s"),
1279 tempfile, file);
1280 return -1;
1283 fs_give((void **) &tempfile);
1286 BIO_free(in);
1291 if(save_p)
1292 *save_p = '\n';
1296 return 0;
1300 #ifdef APPLEKEYCHAIN
1303 copy_publiccert_container_to_keychain(void)
1305 /* NOT IMPLEMNTED */
1306 return -1;
1310 copy_publiccert_keychain_to_container(void)
1312 /* NOT IMPLEMNTED */
1313 return -1;
1316 #endif /* APPLEKEYCHAIN */
1320 * Get a pointer to a string describing the most recent OpenSSL error.
1321 * It's statically allocated, so don't change or attempt to free it.
1323 static const char *
1324 openssl_error_string(void)
1326 char *errs;
1327 const char *data = NULL;
1328 long errn;
1330 errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
1331 errs = (char*) ERR_reason_error_string(errn);
1333 if(errs)
1334 return errs;
1335 else if(data)
1336 return data;
1338 return "unknown error";
1342 /* Return true if the body looks like a PKCS7 object */
1344 is_pkcs7_body(BODY *body)
1346 int result;
1348 result = body->type==TYPEAPPLICATION &&
1349 body->subtype &&
1350 (strucmp(body->subtype,"pkcs7-mime")==0 ||
1351 strucmp(body->subtype,"x-pkcs7-mime")==0 ||
1352 strucmp(body->subtype,"pkcs7-signature")==0 ||
1353 strucmp(body->subtype,"x-pkcs7-signature")==0);
1355 return result;
1359 #ifdef notdef
1361 * Somewhat useful debug utility to dump the contents of a BIO to a file.
1362 * Note that a memory BIO will have its contents eliminated after they
1363 * are read so this will break the next step.
1365 static void
1366 dump_bio_to_file(BIO *in, char *filename)
1368 char iobuf[4096];
1369 int len;
1370 BIO *out;
1372 out = BIO_new_file(filename, "w");
1374 if(out){
1375 if(BIO_method_type(in) != BIO_TYPE_MEM)
1376 BIO_reset(in);
1378 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1379 BIO_write(out, iobuf, len);
1381 BIO_free(out);
1384 BIO_reset(in);
1386 #endif
1390 * Recursively stash a pointer to the decrypted data in our
1391 * manufactured body.
1393 static void
1394 create_local_cache(char *h, char *base, BODY *b)
1396 if(b->type==TYPEMULTIPART){
1397 PART *p;
1399 #if 0
1400 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
1401 #else
1403 * We don't really want to copy the real body contents. It shouldn't be
1404 * used, and in the case of a message with attachments, we'll be
1405 * duplicating the files multiple times.
1407 cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
1408 #endif
1410 for(p=b->nested.part; p; p=p->next)
1411 create_local_cache(h, base, (BODY*) p);
1413 else{
1414 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
1415 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
1420 static long
1421 rfc822_output_func(void *b, char *string)
1423 BIO *bio = (BIO *) b;
1425 return(string ? *string ? (BIO_puts(bio, string) > 0 ? 1L : 0L)
1426 : (BIO_puts(bio, string) >= 0 ? 1L : 0L)
1427 : 0L);
1432 * Attempt to load the private key for the given PERSONAL_CERT.
1433 * This sets the appropriate passphrase globals in order to
1434 * interact with the user correctly.
1436 static int
1437 load_private_key(PERSONAL_CERT *pcert)
1439 if(!pcert->key){
1441 /* Try empty password by default */
1442 char *password = "";
1444 if(ps_global->smime
1445 && (ps_global->smime->need_passphrase
1446 || ps_global->smime->entered_passphrase)){
1447 /* We've already been in here and discovered we need a different password */
1449 if(ps_global->smime->entered_passphrase)
1450 password = (char *) ps_global->smime->passphrase; /* already entered */
1451 else
1452 return 0;
1455 ERR_clear_error();
1457 if(!(pcert->key = load_key(pcert, password))){
1458 long err = ERR_get_error();
1460 /* Couldn't load key... */
1462 if(ps_global->smime && ps_global->smime->entered_passphrase){
1464 /* The user got the password wrong maybe? */
1466 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
1467 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
1468 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
1469 else
1470 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
1472 /* This passphrase is no good; forget it */
1473 ps_global->smime->entered_passphrase = 0;
1476 if(ps_global->smime){
1477 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
1478 ps_global->smime->need_passphrase = 1;
1479 if(ps_global->smime->passphrase_emailaddr){
1480 int i;
1481 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
1482 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
1483 fs_give((void **) ps_global->smime->passphrase_emailaddr);
1486 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
1489 return 0;
1491 else{
1492 /* This key will be cached, so we won't be called again */
1493 if(ps_global->smime){
1494 ps_global->smime->entered_passphrase = 0;
1495 ps_global->smime->need_passphrase = 0;
1499 return 1;
1502 return 0;
1506 static void
1507 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename)
1509 b->type = TYPEAPPLICATION;
1510 b->subtype = cpystr(type);
1511 b->encoding = ENCBINARY;
1512 b->description = cpystr(description);
1514 b->disposition.type = cpystr("attachment");
1515 set_parameter(&b->disposition.parameter, "filename", filename);
1517 set_parameter(&b->parameter, "name", filename);
1522 * Look for a personal certificate matching the
1523 * given address
1525 PERSONAL_CERT *
1526 match_personal_cert_to_email(ADDRESS *a)
1528 PERSONAL_CERT *pcert = NULL;
1529 char buf[MAXPATH];
1530 char **email;
1531 int i, done;
1533 if(!a || !a->mailbox || !a->host)
1534 return NULL;
1536 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
1538 if(ps_global->smime){
1539 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
1540 pcert;
1541 pcert=pcert->next){
1543 if(!pcert->cert)
1544 continue;
1546 email = get_x509_subject_email(pcert->cert);
1548 done = 0;
1549 if(email != NULL){
1550 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
1551 if(email[i] != NULL) done++;
1552 for(i = 0; email[i] != NULL; i++)
1553 fs_give((void **)&email[i]);
1554 fs_give((void **)email);
1557 if(done > 0)
1558 break;
1562 return pcert;
1567 * Look for a personal certificate matching the from
1568 * (or reply_to? in the given envelope)
1570 PERSONAL_CERT *
1571 match_personal_cert(ENVELOPE *env)
1573 PERSONAL_CERT *pcert;
1575 pcert = match_personal_cert_to_email(env->reply_to);
1576 if(!pcert)
1577 pcert = match_personal_cert_to_email(env->from);
1579 return pcert;
1584 * Flatten the given body into its MIME representation.
1585 * Return the result in a BIO.
1587 static BIO *
1588 body_to_bio(BODY *body)
1590 BIO *bio = NULL;
1591 int len;
1593 bio = BIO_new(BIO_s_mem());
1594 if(!bio)
1595 return NULL;
1597 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
1598 pine_write_body_header(body, rfc822_output_func, bio);
1599 pine_rfc822_output_body(body, rfc822_output_func, bio);
1602 * Now need to truncate by two characters since the above
1603 * appends CRLF.
1605 if((len=BIO_ctrl_pending(bio)) > 1){
1606 BUF_MEM *biobuf = NULL;
1608 BIO_get_mem_ptr(bio, &biobuf);
1609 if(biobuf){
1610 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
1614 return bio;
1618 static BIO *
1619 bio_from_store(STORE_S *store)
1621 BIO *ret = NULL;
1623 if(store && store->src == BioType && store->txt){
1624 ret = (BIO *) store->txt;
1627 return(ret);
1631 * Encrypt file; given a path (char *) fp, replace the file
1632 * by an encrypted version of it. If (char *) text is not null, then
1633 * replace the text of (char *) fp by the encrypted version of (char *) text.
1636 encrypt_file(char *fp, char *text)
1638 const EVP_CIPHER *cipher = NULL;
1639 STACK_OF(X509) *encerts = NULL;
1640 X509 *cert;
1641 PERSONAL_CERT *pcert;
1642 BIO *in;
1643 PKCS7 *p7 = NULL;
1644 FILE *fpp;
1645 int rv = 0;
1647 smime_init();
1648 if((pcert = ps_global->smime->personal_certs) == NULL)
1649 return 0;
1651 cipher = EVP_aes_256_cbc();
1652 encerts = sk_X509_new_null();
1654 if((cert = get_cert_for(pcert->name, Public)) != NULL)
1655 sk_X509_push(encerts, cert);
1656 else
1657 goto end;
1659 if(text){
1660 in = BIO_new(BIO_s_mem());
1661 if(in == NULL)
1662 goto end;
1663 (void) BIO_reset(in);
1664 BIO_puts(in, text);
1666 else{
1667 if(!(in = BIO_new_file(fp, "rb")))
1668 goto end;
1670 BIO_read_filename(in, fp);
1673 if((p7 = PKCS7_encrypt(encerts, in, cipher, 0)) == NULL)
1674 goto end;
1675 BIO_set_close(in, BIO_CLOSE);
1676 BIO_free(in);
1677 if(!(in = BIO_new_file(fp, "w")))
1678 goto end;
1679 BIO_reset(in);
1680 rv = PEM_write_bio_PKCS7(in, p7);
1681 BIO_flush(in);
1683 end:
1684 BIO_free(in);
1685 PKCS7_free(p7);
1686 sk_X509_pop_free(encerts, X509_free);
1688 return rv;
1692 * Encrypt a message on the way out. Called from call_mailer in send.c
1693 * The body may be reallocated.
1696 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
1698 PKCS7 *p7 = NULL;
1699 BIO *in = NULL;
1700 BIO *out = NULL;
1701 const EVP_CIPHER *cipher = NULL;
1702 STACK_OF(X509) *encerts = NULL;
1703 STORE_S *outs = NULL;
1704 PINEFIELD *pf;
1705 ADDRESS *a;
1706 BODY *body = *bodyP;
1707 BODY *newBody = NULL;
1708 int result = 0;
1710 dprint((9, "encrypt_outgoing_message()"));
1711 smime_init();
1713 cipher = EVP_aes_256_cbc();
1715 encerts = sk_X509_new_null();
1717 /* Look for a certificate for each of the recipients */
1718 for(pf = header->local; pf && pf->name; pf = pf->next)
1719 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
1720 for(a=*pf->addr; a; a=a->next){
1721 X509 *cert;
1722 char buf[MAXPATH];
1724 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
1726 cert = get_cert_for(buf, Public);
1727 if(cert)
1728 sk_X509_push(encerts,cert);
1729 else{
1730 q_status_message2(SM_ORDER, 1, 1,
1731 _("Unable to find certificate for <%s@%s>"),
1732 a->mailbox, a->host);
1733 goto end;
1739 in = body_to_bio(body);
1741 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
1743 outs = so_get(BioType, NULL, EDIT_ACCESS);
1744 out = bio_from_store(outs);
1746 i2d_PKCS7_bio(out, p7);
1747 (void) BIO_flush(out);
1749 so_seek(outs, 0, SEEK_SET);
1751 newBody = mail_newbody();
1753 newBody->type = TYPEAPPLICATION;
1754 newBody->subtype = cpystr("pkcs7-mime");
1755 newBody->encoding = ENCBINARY;
1757 newBody->disposition.type = cpystr("attachment");
1758 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
1760 newBody->description = cpystr("S/MIME Encrypted Message");
1761 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
1762 set_parameter(&newBody->parameter, "name", "smime.p7m");
1764 newBody->contents.text.data = (unsigned char *) outs;
1766 *bodyP = newBody;
1768 result = 1;
1770 end:
1772 BIO_free(in);
1773 PKCS7_free(p7);
1774 sk_X509_pop_free(encerts, X509_free);
1776 dprint((9, "encrypt_outgoing_message returns %d", result));
1777 return result;
1782 Get (and decode) the body of the given section of msg
1784 static STORE_S*
1785 get_part_contents(long msgno, const char *section)
1787 long len;
1788 gf_io_t pc;
1789 STORE_S *store = NULL;
1790 char *err;
1792 store = so_get(CharStar, NULL, EDIT_ACCESS);
1793 if(store){
1794 gf_set_so_writec(&pc,store);
1796 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
1798 gf_clear_so_writec(store);
1800 so_seek(store, 0, SEEK_SET);
1802 if(err)
1803 so_give(&store);
1806 return store;
1810 static PKCS7 *
1811 get_pkcs7_from_part(long msgno,const char *section)
1813 STORE_S *store = NULL;
1814 PKCS7 *p7 = NULL;
1815 BIO *in = NULL;
1817 store = get_part_contents(msgno, section);
1819 if(store){
1820 if(store->src == CharStar){
1821 int len;
1824 * We're reaching inside the STORE_S structure. We should
1825 * probably have a way to get the length, instead.
1827 len = (int) (store->eod - store->dp);
1828 in = BIO_new_mem_buf(store->txt, len);
1830 else{ /* just copy it */
1831 unsigned char c;
1833 in = BIO_new(BIO_s_mem());
1834 (void) BIO_reset(in);
1836 so_seek(store, 0L, 0);
1837 while(so_readc(&c, store)){
1838 BIO_write(in, &c, 1);
1842 if(in){
1843 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
1844 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
1845 /* error */
1848 BIO_free(in);
1851 so_give(&store);
1854 return p7;
1857 int same_cert(X509 *x, X509 *cert)
1859 char bufcert[256], bufx[256];
1860 int rv = 0;
1862 get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert));
1863 get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx));
1864 if(strcmp(bufx, bufcert) == 0)
1865 rv = 1;
1867 return rv;
1871 /* extract and save certificates from a PKCS7 package. The ctype variable
1872 * tells us if we want to extract it to a public/ or a ca/ directory. The
1873 * later makes sense only for recoverable errors (errors that can be fixed
1874 * by saving to the ca/ directory before we verify the signature).
1875 * Return value:
1876 * 0 - no errors (in public/) no need to try again,
1877 * or validated self signed certificate (in ca/)
1878 * 1 - self signed certificate was saved in public/, try again to validate it
1879 * 2 - user was prompted about self-signed certificate, but certificate was not saved
1880 * < 0 - certificate error is not recoverable, don't even think about it.
1883 int smime_extract_and_save_cert(PKCS7 *p7)
1885 STACK_OF(X509) *signers;
1886 X509 *x, *cert;
1887 char **email;
1888 int i, j, rv;
1889 long error;
1891 if((signers = PKCS7_get0_signers(p7, NULL, 0)) == NULL)
1892 return -1;
1894 rv = 0; /* assume no error */
1895 for(i = 0; i < sk_X509_num(signers); i++){
1896 if((x = sk_X509_value(signers,i)) == NULL)
1897 continue;
1899 if((email = get_x509_subject_email(x)) != NULL){
1900 for(j = 0; email[j] != NULL; j++){
1901 if((cert = get_cert_for(email[j], Public)) == NULL || same_cert(x, cert) == 0)
1902 save_cert_for(email[j], x, Public);
1904 if(smime_validate_cert(cert, &error) < 0){
1905 const char *error_string = X509_verify_cert_error_string(error);
1906 dprint((1, "Certificate verify error code %lu for <%s>", error, email[j]));
1907 switch(error){
1908 case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
1909 case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
1910 rv = 2; /* recoverable error */
1911 break;
1912 default : rv = -1; /* not recoverable error */
1913 break;
1915 sprintf(tmp_20k_buf, "%s <%s>: %s", _("Error in certificate for "), email[j], error_string);
1916 q_status_message(SM_ORDER | SM_DING, 2, 2, tmp_20k_buf);
1917 #if 0
1918 if(pith_opt_action_certificate_error)
1919 switch((*pith_opt_action_certificate_error)(cert_error)){
1920 case 1 : save_cert_for(email[j], x, CACert);
1921 renew_store();
1922 rv = 0;
1923 break;
1924 case 0 :
1925 default : break;
1927 #endif
1929 X509_free(cert);
1931 fs_give((void **) &email[i]);
1933 fs_give((void **) email);
1935 sk_X509_free(signers);
1937 return rv;
1941 * Try to verify a signature.
1943 * p7 - the pkcs7 object to verify
1944 * in - the plain data to verify (NULL if not detached)
1945 * out - BIO to which to write the opaque data
1946 * silent - if non zero, do not print errors, only print success.
1948 static int
1949 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
1951 STACK_OF(X509) *otherCerts = NULL;
1952 int result;
1953 const char *data;
1954 long err;
1956 if(!s_cert_store){
1957 if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
1958 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
1960 return -1;
1963 smime_extract_and_save_cert(p7);
1965 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, 0);
1967 if(result){
1968 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
1970 else{
1971 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
1973 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
1975 /* Retry verification so we can get the plain text */
1976 /* Might be better to reimplement PKCS7_verify here? */
1978 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY);
1980 if (!silent) q_status_message1(SM_ORDER | SM_DING, 3, 3,
1981 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
1984 return result;
1988 void
1989 free_smime_body_sparep(void **sparep)
1991 if(sparep && *sparep){
1992 PKCS7_free((PKCS7 *) (*sparep));
1993 *sparep = NULL;
1997 /* Big comment, explaining the mess that exists out there, and how we deal
1998 with it, and also how we solve the problems that are created this way.
2000 When Alpine sends a message, it constructs that message, computes the
2001 signature, but then it forgets the message it signed and reconstructs it
2002 again. Since it signs a message containing a notice about "mime aware
2003 tools", but it does not send that we do not include that in the part that
2004 is signed, and that takes care of much of the problems.
2006 Another problem is what is received from the servers. All servers tested
2007 seem to transmit the message that was signed intact and Alpine can check
2008 the signature correctly. That is not a problem. The problem arises when
2009 the message includes attachments. In this case different servers send
2010 different things, so it will be up to us to figure out what is the text
2011 that was actually signed. Confused? here is the story:
2013 When a message containing and attachment is sent by Alpine, UW-IMAP,
2014 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2015 that was sent by Alpine, but GMX.com, Exchange, and probably other servers
2016 add a trailing \r\n in the message, so when validating the signature,
2017 these messages will not validate. There are several things that can be
2018 done.
2020 1. Add a trailing \r\n to any message that contains attachments, sign that
2021 and send that. In this way, all messages will validate with all
2022 servers.
2024 2. Compatibility mode: If a message has an attachment, contains a trailing
2025 \r\n and does not validate (sent by an earlier version of Alpine),
2026 remove the trailing \r\n and try to revalidate again.
2028 3. We do not add \r\n to validate a message that we sent, because that
2029 would only work in Alpine, and not in any other client. That would not
2030 be a good thing to do.
2032 PART II
2034 Now we have to deal with encrypted and signed messages. The problem is that
2035 c-client makes all its pointers point to "on disk" content, but since we
2036 decrypted the data earlier, we have to make sure of two things. One is that we
2037 saved that data (so we do not have to decrypt it again) and second that we can
2038 use it.
2040 In order to save the data we use create_local_cache, so that we do not
2041 have to redecrypt the message. Once this is saved, c-client functions will
2042 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2047 * Given a multipart body of type multipart/signed, attempt to verify it.
2048 * Returns non-zero if the body was changed.
2050 static int
2051 do_detached_signature_verify(BODY *b, long msgno, char *section)
2053 PKCS7 *p7 = NULL;
2054 BIO *in = NULL;
2055 PART *p;
2056 int result, modified_the_body = 0;
2057 unsigned long mimelen, bodylen;
2058 char newSec[100], *mimetext, *bodytext;
2059 char *what_we_did;
2061 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"));
2062 smime_init();
2064 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
2066 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
2068 if(mimetext)
2069 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
2071 if (mimetext == NULL || bodytext == NULL)
2072 return modified_the_body;
2074 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
2076 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
2077 || (in = BIO_new(BIO_s_mem())) == NULL)
2078 return modified_the_body;
2080 (void) BIO_reset(in);
2081 BIO_write(in, mimetext, mimelen);
2082 BIO_write(in, bodytext, bodylen);
2084 /* Try compatibility with the past and check if this message
2085 * validates when we remove the last two characters. Silence
2086 * any failures first.
2088 if(((result = do_signature_verify(p7, in, NULL, 1)) == 0)
2089 && bodylen > 2
2090 && (strncmp(bodytext+bodylen-2,"\r\n", 2) == 0)){
2091 BUF_MEM *biobuf = NULL;
2093 BIO_get_mem_ptr(in, &biobuf);
2094 if(biobuf)
2095 BUF_MEM_grow(biobuf, mimelen + bodylen - 2);
2096 result = do_signature_verify(p7, in, NULL, 0);
2099 BIO_free(in);
2100 if(b->subtype)
2101 fs_give((void**) &b->subtype);
2103 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
2104 b->encoding = ENC8BIT;
2106 if(b->description)
2107 fs_give ((void**) &b->description);
2109 what_we_did = result ? _("This message was cryptographically signed.") :
2110 _("This message was cryptographically signed but the signature could not be verified.");
2112 b->description = cpystr(what_we_did);
2114 b->sparep = p7;
2116 p = b->nested.part;
2118 /* p is signed plaintext */
2119 if(p && p->next)
2120 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
2122 modified_the_body = 1;
2124 return modified_the_body;
2128 PERSONAL_CERT *
2129 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
2131 PERSONAL_CERT *x = NULL;
2133 if(ps_global->smime){
2134 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
2135 X509 *mine;
2137 mine = x->cert;
2139 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,mine->cert_info->issuer) &&
2140 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,mine->cert_info->serialNumber)){
2141 break;
2146 return x;
2150 static PERSONAL_CERT *
2151 find_certificate_matching_pkcs7(PKCS7 *p7)
2153 int i;
2154 STACK_OF(PKCS7_RECIP_INFO) *recips;
2155 PERSONAL_CERT *x = NULL;
2157 recips = p7->d.enveloped->recipientinfo;
2159 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
2160 PKCS7_RECIP_INFO *ri;
2162 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
2164 if((x=find_certificate_matching_recip_info(ri))!=0){
2165 break;
2169 return x;
2172 /* decrypt an encrypted file.
2173 Args: fp - the path to the encrypted file.
2174 rv - a code that thells the caller what happened inside the function
2175 Returns the decoded text allocated in a char *, whose memory must be
2176 freed by caller
2179 char *
2180 decrypt_file(char *fp, int *rv)
2182 PKCS7 *p7 = NULL;
2183 char *text, *tmp;
2184 BIO *in = NULL, *out = NULL;
2185 EVP_PKEY *pkey = NULL, *key = NULL;
2186 PERSONAL_CERT *pcert = NULL;
2187 X509 *recip, *cert;
2188 STORE_S *outs = NULL, *store, *ins;
2189 int i, j;
2190 long unsigned int len;
2191 void *ret;
2193 smime_init();
2195 if((text = read_file(fp, 0)) == NULL)
2196 return NULL;
2198 tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1);
2199 for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0'
2200 && text[i] != '-'; j++, i++)
2201 tmp[j] = text[i];
2202 tmp[j] = '\0';
2204 ret = rfc822_base64(tmp, strlen(tmp), &len);
2206 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
2207 p7 = d2i_PKCS7_bio(in, NULL);
2208 BIO_free(in);
2211 if(text) fs_give((void **)&text);
2212 if(ret) fs_give((void **)&ret);
2214 if((pcert = ps_global->smime->personal_certs) == NULL)
2215 goto end;
2217 if((i = load_private_key(pcert)) == 0
2218 && ps_global->smime
2219 && ps_global->smime->need_passphrase
2220 && !ps_global->smime->already_auto_asked)
2221 for(; i == 0;){
2222 ps_global->smime->already_auto_asked = 1;
2223 if(pith_opt_smime_get_passphrase){
2224 switch((*pith_opt_smime_get_passphrase)()){
2225 case 0 : i = load_private_key(pcert);
2226 break;
2228 case 1 : i = -1;
2229 break;
2231 default: break; /* repeat until we cancel */
2234 else
2235 i = -2;
2238 if(rv) *rv = i;
2240 if((key = pcert->key) == NULL)
2241 goto end;
2243 recip = get_cert_for(pcert->name, Public);
2244 out = BIO_new(BIO_s_mem());
2245 (void) BIO_reset(out);
2247 i = PKCS7_decrypt(p7, key, recip, out, 0);
2249 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
2250 forget_private_keys();
2252 if(i == 0){
2253 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
2254 (char*) openssl_error_string());
2255 goto end;
2258 BIO_get_mem_data(out, &tmp);
2260 text = cpystr(tmp);
2261 BIO_free(out);
2263 end:
2264 PKCS7_free(p7);
2266 return text;
2270 * Try to decode (decrypt or verify a signature) a PKCS7 body
2271 * Returns non-zero if something was changed.
2273 static int
2274 do_decoding(BODY *b, long msgno, const char *section)
2276 int modified_the_body = 0;
2277 BIO *out = NULL;
2278 PKCS7 *p7 = NULL;
2279 X509 *recip = NULL;
2280 EVP_PKEY *key = NULL;
2281 PERSONAL_CERT *pcert = NULL;
2282 char *what_we_did = "";
2283 char null[1];
2285 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"));
2286 null[0] = '\0';
2287 smime_init();
2290 * Extract binary data from part to an in-memory store
2293 if(b->sparep){ /* already done */
2294 p7 = (PKCS7*) b->sparep;
2296 else{
2298 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
2299 if(!p7){
2300 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
2301 (char*) openssl_error_string());
2302 goto end;
2306 * Save the PKCS7 object for later dealings by the user interface.
2307 * It will be cleaned up when the body is garbage collected.
2309 b->sparep = p7;
2312 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
2314 if(PKCS7_type_is_signed(p7)){
2315 int sigok;
2317 out = BIO_new(BIO_s_mem());
2318 (void) BIO_reset(out);
2319 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
2321 sigok = do_signature_verify(p7, NULL, out, 0);
2323 what_we_did = sigok ? _("This message was cryptographically signed.") :
2324 _("This message was cryptographically signed but the signature could not be verified.");
2326 /* make sure it's null terminated */
2327 BIO_write(out, null, 1);
2329 else if(!PKCS7_type_is_enveloped(p7)){
2330 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
2331 goto end;
2333 else{ /* It *is* enveloped */
2334 int decrypt_result;
2336 what_we_did = _("This message was encrypted.");
2338 /* now need to find a cert that can decrypt this */
2339 pcert = find_certificate_matching_pkcs7(p7);
2341 if(!pcert){
2342 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
2343 goto end;
2346 recip = pcert->cert;
2348 if(!load_private_key(pcert)
2349 && ps_global->smime
2350 && ps_global->smime->need_passphrase
2351 && !ps_global->smime->already_auto_asked){
2352 /* Couldn't load key with blank password, ask user */
2353 ps_global->smime->already_auto_asked = 1;
2354 if(pith_opt_smime_get_passphrase){
2355 (*pith_opt_smime_get_passphrase)();
2356 load_private_key(pcert);
2360 key = pcert->key;
2361 if(!key)
2362 goto end;
2364 out = BIO_new(BIO_s_mem());
2365 (void) BIO_reset(out);
2366 BIO_puts(out, "MIME-Version: 1.0\r\n");
2368 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
2370 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
2371 forget_private_keys();
2373 if(!decrypt_result){
2374 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
2375 (char*) openssl_error_string());
2376 goto end;
2379 BIO_write(out, null, 1);
2383 * We've now produced a flattened MIME object in BIO out.
2384 * It needs to be turned back into a BODY.
2387 if(out){
2388 BODY *body;
2389 ENVELOPE *env;
2390 char *h = NULL;
2391 char *bstart;
2392 STRING s;
2393 BUF_MEM *bptr = NULL;
2395 BIO_get_mem_ptr(out, &bptr);
2396 if(bptr)
2397 h = bptr->data;
2399 /* look for start of body */
2400 bstart = strstr(h, "\r\n\r\n");
2402 if(!bstart){
2403 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
2405 else{
2406 bstart += 4; /* skip over CRLF*2 */
2408 INIT(&s, mail_string, bstart, strlen(bstart));
2409 rfc822_parse_msg_full(&env, &body, h, (bstart-h)-2, &s, BADHOST, 0, 0);
2410 mail_free_envelope(&env); /* Don't care about this */
2412 body->mime.offset = 0;
2413 body->mime.text.size = 0;
2415 * Now convert original body (application/pkcs7-mime)
2416 * to a multipart body with one sub-part (the decrypted body).
2417 * Note that the sub-part may also be multipart!
2420 b->type = TYPEMULTIPART;
2421 if(b->subtype)
2422 fs_give((void**) &b->subtype);
2425 * This subtype is used in mailview.c to annotate the display of
2426 * encrypted or signed messages. We know for sure then that it's a PKCS7
2427 * part because the sparep field is set to the PKCS7 object (see above).
2429 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
2430 b->encoding = ENC8BIT;
2432 if(b->description)
2433 fs_give((void**) &b->description);
2435 b->description = cpystr(what_we_did);
2437 if(b->disposition.type)
2438 fs_give((void **) &b->disposition.type);
2440 if(b->contents.text.data)
2441 fs_give((void **) &b->contents.text.data);
2443 if(b->parameter)
2444 mail_free_body_parameter(&b->parameter);
2446 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
2447 b->nested.part = fs_get(sizeof(PART));
2448 b->nested.part->body = *body;
2449 b->nested.part->next = NULL;
2451 fs_give((void**) &body);
2454 * IMPORTANT BIT: set the body->contents.text.data elements to contain the decrypted
2455 * data. Otherwise, it'll try to load it from the original data. Eek.
2457 create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body);
2459 modified_the_body = 1;
2463 end:
2464 if(out)
2465 BIO_free(out);
2467 return modified_the_body;
2472 * Recursively handle PKCS7 bodies in our message.
2474 * Returns non-zero if some fiddling was done.
2476 static int
2477 do_fiddle_smime_message(BODY *b, long msgno, char *section)
2479 int modified_the_body = 0;
2481 if(!b)
2482 return 0;
2484 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"));
2486 if(is_pkcs7_body(b)){
2488 if(do_decoding(b, msgno, section)){
2490 * b should now be a multipart message:
2491 * fiddle it too in case it's been multiply-encrypted!
2494 /* fallthru */
2495 modified_the_body = 1;
2499 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
2501 PART *p;
2502 int partNum;
2503 char newSec[100];
2505 if(MIME_MULT_SIGNED(b->type, b->subtype)){
2509 * Ahah. We have a multipart signed entity.
2511 * Multipart/signed
2512 * part 1 (signed thing)
2513 * part 2 (the pkcs7 signature)
2515 * We're going to convert that to
2517 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
2518 * part 1 (signed thing)
2519 * part 2 has been freed
2521 * We also extract the signature from part 2 and save it
2522 * in the multipart body->sparep, and we add a description
2523 * in the multipart body->description.
2526 * The results of a decrypted message will be similar. It
2527 * will be
2529 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
2530 * part 1 (decrypted thing)
2533 modified_the_body += do_detached_signature_verify(b, msgno, section);
2535 else if(MIME_MSG(b->type, b->subtype)){
2536 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
2538 else{
2540 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
2542 /* Append part number to the section string */
2544 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
2546 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
2551 return modified_the_body;
2556 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
2557 * Returns non-zero if something was changed.
2560 fiddle_smime_message(BODY *b, long msgno)
2562 return do_fiddle_smime_message(b, msgno, "");
2566 /********************************************************************************/
2570 * Output a string in a distinctive style
2572 void
2573 gf_puts_uline(char *txt, gf_io_t pc)
2575 pc(TAG_EMBED); pc(TAG_BOLDON);
2576 gf_puts(txt, pc);
2577 pc(TAG_EMBED); pc(TAG_BOLDOFF);
2582 * Sign a message. Called from call_mailer in send.c.
2584 * This takes the header for the outgoing message as well as a pointer
2585 * to the current body (which may be reallocated).
2588 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach)
2590 STORE_S *outs = NULL;
2591 BODY *body = *bodyP;
2592 BODY *newBody = NULL;
2593 PART *p1 = NULL;
2594 PART *p2 = NULL;
2595 PERSONAL_CERT *pcert;
2596 BIO *in = NULL;
2597 BIO *out = NULL;
2598 PKCS7 *p7 = NULL;
2599 int result = 0;
2600 int flags = dont_detach ? 0 : PKCS7_DETACHED;
2602 dprint((9, "sign_outgoing_message()"));
2604 smime_init();
2606 /* Look for a private key matching the sender address... */
2608 pcert = match_personal_cert(header->env);
2610 if(!pcert){
2611 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
2612 goto end;
2615 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
2616 /* Couldn't load key with blank password, try again */
2617 if(pith_opt_smime_get_passphrase){
2618 (*pith_opt_smime_get_passphrase)();
2619 load_private_key(pcert);
2623 if(!pcert->key)
2624 goto end;
2626 in = body_to_bio(body);
2628 #ifdef notdef
2629 dump_bio_to_file(in,"/tmp/signed-data");
2630 #endif
2632 p7 = PKCS7_sign(pcert->cert, pcert->key, NULL, in, flags);
2634 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
2635 forget_private_keys();
2637 if(!p7){
2638 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
2639 goto end;
2642 outs = so_get(BioType, NULL, EDIT_ACCESS);
2643 out = bio_from_store(outs);
2645 i2d_PKCS7_bio(out, p7);
2646 (void) BIO_flush(out);
2648 so_seek(outs, 0, SEEK_SET);
2650 if((flags&PKCS7_DETACHED)==0){
2652 /* the simple case: the signed data is in the pkcs7 object */
2654 newBody = mail_newbody();
2656 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m");
2658 newBody->contents.text.data = (unsigned char *) outs;
2659 *bodyP = newBody;
2661 result = 1;
2663 else{
2666 * OK.
2667 * We have to create a new body as follows:
2669 * multipart/signed; blah blah blah
2670 * reference to existing body
2672 * pkcs7 object
2675 newBody = mail_newbody();
2677 newBody->type = TYPEMULTIPART;
2678 newBody->subtype = cpystr("signed");
2679 newBody->encoding = ENC7BIT;
2681 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
2682 set_parameter(&newBody->parameter, "micalg", "sha1");
2684 p1 = mail_newbody_part();
2685 p2 = mail_newbody_part();
2688 * This is nasty. We're just copying the body in here,
2689 * but since our newBody is freed at the end of call_mailer,
2690 * we mustn't let this body (the original one) be freed twice.
2692 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
2694 p1->next = p2;
2696 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s");
2697 p2->body.contents.text.data = (unsigned char *) outs;
2699 newBody->nested.part = p1;
2701 *bodyP = newBody;
2703 result = 1;
2706 end:
2708 PKCS7_free(p7);
2709 BIO_free(in);
2711 dprint((9, "sign_outgoing_message returns %d", result));
2712 return result;
2716 SMIME_STUFF_S *
2717 new_smime_struct(void)
2719 SMIME_STUFF_S *ret = NULL;
2721 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
2722 memset((void *) ret, 0, sizeof(*ret));
2723 ret->publictype = Nada;
2725 return ret;
2729 static void
2730 free_smime_struct(SMIME_STUFF_S **smime)
2732 if(smime && *smime){
2733 if((*smime)->passphrase_emailaddr){
2734 int i;
2735 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
2736 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
2737 fs_give((void **) (*smime)->passphrase_emailaddr);
2740 if((*smime)->publicpath)
2741 fs_give((void **) &(*smime)->publicpath);
2743 if((*smime)->publiccertlist)
2744 free_certlist(&(*smime)->publiccertlist);
2746 if((*smime)->publiccertdata)
2747 free_certlist(&(*smime)->publiccertdata);
2749 if((*smime)->privatecertdata)
2750 free_certlist(&(*smime)->privatecertdata);
2752 if((*smime)->cacertdata)
2753 free_certlist(&(*smime)->cacertdata);
2755 if((*smime)->publiccontent)
2756 fs_give((void **) &(*smime)->publiccontent);
2758 if((*smime)->privatepath)
2759 fs_give((void **) &(*smime)->privatepath);
2761 if((*smime)->personal_certs){
2762 PERSONAL_CERT *pc;
2764 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
2765 free_personal_certs(&pc);
2766 (*smime)->personal_certs = NULL;
2769 if((*smime)->privatecontent)
2770 fs_give((void **) &(*smime)->privatecontent);
2772 if((*smime)->capath)
2773 fs_give((void **) &(*smime)->capath);
2775 if((*smime)->cacontent)
2776 fs_give((void **) &(*smime)->cacontent);
2778 fs_give((void **) smime);
2782 #endif /* SMIME */