* Remove dead code.
[alpine.git] / pith / smime.c
blob795be7f183b71f04e28759384b827da82c15b8aa
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-2020 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"
45 #include "../pith/body.h"
46 #ifdef PASSFILE
47 #include "../pith/imap.h"
48 #endif /* PASSFILE */
50 #include <openssl/buffer.h>
51 #include <openssl/x509v3.h>
52 #include <openssl/evp.h>
54 /* internal prototypes */
55 static void forget_private_keys(void);
56 static int app_RAND_load_file(const char *file);
57 static void openssl_extra_randomness(void);
58 static int app_RAND_write_file(const char *file);
59 static const char *openssl_error_string(void);
60 static int load_private_key(PERSONAL_CERT *pcert);
61 static void create_local_cache(char *h, char *base, BODY *b, int type);
62 static long rfc822_output_func(void *b, char *string);
63 static void setup_pkcs7_body_for_signature(BODY *b, char *description,
64 char *type, char *filename, char *smime_type);
65 static BIO *body_to_bio(BODY *body);
66 static BIO *bio_from_store(STORE_S *store);
67 static STORE_S *get_part_contents(long msgno, const char *section);
68 static PKCS7 *get_pkcs7_from_part(long msgno, const char *section);
69 static int do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent);
70 static int do_detached_signature_verify(BODY *b, long msgno, char *section);
71 static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7);
72 static int do_decoding(BODY *b, long msgno, const char *section);
73 static void free_smime_struct(SMIME_STUFF_S **smime);
74 static void setup_storage_locations(void);
75 static int copy_container_to_dir(WhichCerts which);
76 static int do_fiddle_smime_message(BODY *b, long msgno, char *section);
77 void setup_privatekey_storage(void);
78 int smime_extract_and_save_cert(PKCS7 *p7);
79 int same_cert(X509 *, X509 *);
80 #ifdef PASSFILE
81 int load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile, char **certfile, EVP_PKEY **pkey, X509 **pcert);
82 #endif /* PASSFILE */
83 EVP_PKEY *load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *);
84 void smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen);
85 void smime_remove_folding_space(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen);
86 int smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag);
88 int (*pith_opt_smime_get_passphrase)(void);
89 int (*pith_smime_import_certificate)(char *, char *, char *, size_t);
90 int (*pith_smime_enter_password)(char *, char *, size_t);
91 int (*pith_smime_confirm_save)(char *);
93 static X509_STORE *s_cert_store;
95 /* State management for randomness functions below */
96 static int seeded = 0;
98 #ifdef PASSFILE
99 /*
100 * This code does not work in Windows, because of the PASSFILE thing, so
101 * I did not try to fix it. If you think it does need to be applied to
102 * the Windows version of alpine, there are more changes that are needed
103 * than fixing this function in this module. E. Chappa 09/28/17.
105 * load key from pathkeydir and cert from pathcertdir. It chooses the first
106 * key/certificate pair that matches. Delete pairs that you do not want used,
107 * if you do not want them selected. All parameters must be non-null.
108 * Memory freed by caller.
109 * Return values:
110 * -1 : user cancelled load
111 * 0 : load was successful
112 * 1 : there was an error in the loading.
115 load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile,
116 char **certfile, EVP_PKEY **pkey, X509 **pcert)
118 char buf[MAXPATH+1], pathkey[MAXPATH+1], prompt[MAILTMPLEN];
119 DIR *dirp;
120 struct dirent *d;
121 int b = 0, ret = 1; /* assume error */
123 if(pathkeydir == NULL || pathcertdir == NULL || keyfile == NULL
124 || pkey == NULL || certfile == NULL || pcert == NULL)
125 return 1;
127 *keyfile = NULL;
128 *certfile = NULL;
129 *pkey = NULL;
130 *pcert = NULL;
132 if((dirp = opendir(pathkeydir)) != NULL){
133 while(b == 0 && (d=readdir(dirp)) != NULL){
134 size_t ll;
136 if((ll=strlen(d->d_name)) && ll > 4){
137 if(!strcmp(d->d_name+ll-4, ".key")){
138 strncpy(buf, d->d_name, sizeof(buf));
139 buf[sizeof(buf)-1] = '\0';
140 build_path(pathkey, pathkeydir, buf, sizeof(pathkey));
141 buf[strlen(buf)-4] = '\0';
142 snprintf(prompt, sizeof(prompt),
143 _("Enter password of key <%s> to unlock password file: "), buf);
144 if((*pkey = load_pkey_with_prompt(pathkey, NULL, prompt, &ret)) != NULL){
145 if(load_cert_for_key(pathcertdir, *pkey, certfile, pcert)){
146 b = 1; /* break */
147 *keyfile = cpystr(buf);
148 } else {
149 EVP_PKEY_free(*pkey);
150 *pkey = NULL;
151 q_status_message1(SM_ORDER, 0, 2,
152 _("Cannot find certificate that matches key <%s>. Continuing..."), buf);
158 closedir(dirp);
160 return ret;
164 /* setup a key and certificate to encrypt and decrypt a password file.
165 * These files will be saved in the .alpine-smime/.pwd directory, but its
166 * location can be setup in the command line with the -pwdcertdir option.
167 * Here are the rules:
169 * Check if the .alpine-smime/.pwd (or -pwdcertdir directory) exists,
170 * if not create it. If we are successful, move to the next step
172 * - If the user has a key/cert pair, in the .alpine-smime/.pwd dir
173 * setup is successful;
174 * - if the user does not have a key/cert pair, look to see if
175 * ps_global->smime->personal_certs is already setup, if so, use it.
176 * - if ps_global->smime->personal_certs is not set up, see if we can
177 * find a certificate/cert pair in the default locations at compilation
178 * time. (~/.alpine-smime/private and ~/.alpine-smime/public
179 * - if none of this is successful, create a key/certificate pair
180 * (TODO: implement this)
181 * - in any other case, setup is not successful.
183 * If setup is successful, setup ps_global->pwdcert.
184 * If any of this fails, ps_global->pwdcert will be null.
185 * Ok, that should do it.
187 * return values: 0 - everything is normal
188 * 1 - User could not unlock key or no key in directory.
189 * 2 - User cancelled to create self signed certificate
190 * -1 - we do not know which directory to use
191 * -2 - "-pwdcertdir" was given by user, but directory does not exist
192 * -3 - "DF_PASSWORD_DIR" exists but it is not a directory!!??
193 * -4 - we tried to create DF_PASSWORD_DIR but failed.
194 * -5 - password directory exists, but it is empty
198 setup_pwdcert(void **pwdcert)
200 int rv;
201 int we_inited = 0;
202 int setup_dir = 0; /* make it non zero if we know which dir to use */
203 struct stat sbuf;
204 char pathdir[MAXPATH+1], pathkey[MAXPATH+1], fpath[MAXPATH+1], pathcert[MAXPATH+1];
205 char fpath2[MAXPATH+1], prompt[MAILTMPLEN];
206 char *keyfile, *certfile, *text;
207 EVP_PKEY *pkey = NULL;
208 X509 *pcert = NULL;
209 PERSONAL_CERT *pc, *pc2 = NULL;
210 static int was_here = 0;
212 if(pwdcert == NULL || was_here == 1)
213 return -1;
215 was_here++;
216 if(ps_global->pwdcertdir){
217 if(our_stat(ps_global->pwdcertdir, &sbuf) == 0
218 && ((sbuf.st_mode & S_IFMT) == S_IFDIR)){
219 setup_dir++;
220 strncpy(pathdir, ps_global->pwdcertdir, sizeof(pathdir));
221 pathdir[sizeof(pathdir)-1] = '\0';
223 else rv = -2;
224 } else {
225 smime_path(DF_PASSWORD_DIR, pathdir, sizeof(pathdir));
226 if(our_stat(pathdir, &sbuf) == 0){
227 if((sbuf.st_mode & S_IFMT) == S_IFDIR)
228 setup_dir++;
229 else rv = -3;
230 } else if(can_access(pathdir, ACCESS_EXISTS) != 0
231 && our_mkpath(pathdir, 0700) == 0)
232 setup_dir++;
233 else rv = -4;
236 if(setup_dir == 0){
237 was_here = 0;
238 return rv;
241 if(load_key_and_cert(pathdir, pathdir, &keyfile, &certfile, &pkey, &pcert) < 0){
242 was_here = 0;
243 return 1;
246 if(ps_global->pwdcertdir == NULL){ /* save the result of pwdcertdir */
247 ps_global->pwdcertdir = cpystr(pathdir);
248 /* if the user gave a pwdcertdir and there is nothing there, do not
249 * continue. Let the user initialize on their own this directory.
251 if(certfile == NULL || keyfile == NULL){
252 was_here = 0;
253 return -5;
257 if(certfile && keyfile){
258 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
259 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
260 pc->name = keyfile;
261 pc->key = pkey;
262 pc->cert = pcert;
263 pc->cname = certfile;
264 *pwdcert = (void *) pc;
265 was_here = 0;
266 return 0;
269 /* look to see if there are any certificates lying around, first
270 * we try to load ps_global->smime to see if that has information
271 * we can use. If we are the process filling the smime structure
272 * we deinit at the end, since this might not do a full init.
274 if(ps_global && ps_global->smime && !ps_global->smime->inited){
275 we_inited++;
276 smime_init();
279 /* at this point ps_global->smime->inited == 1 */
280 if(ps_global->smime && ps_global->smime->personal_certs != NULL){
281 pc = (PERSONAL_CERT *) ps_global->smime->personal_certs;
282 if(ps_global->smime->privatetype == Directory){
283 build_path(pathkey, ps_global->smime->privatepath, pc->name, sizeof(pathkey));
284 strncat(pathkey, ".key", 4);
285 pathkey[sizeof(pathkey)-1] = '\0';
286 text = NULL;
287 } else if (ps_global->smime->privatetype == Container){
288 if(pc->keytext == NULL){ /* we should *never* be here, but just in case */
289 if(ps_global->smime->privatecontent != NULL){
290 char tmp[MAILTMPLEN], *s, *t, c;
291 snprintf(tmp, sizeof(tmp), "%s%s", EMAILADDRLEADER, pc->name);
292 tmp[sizeof(tmp)-1] = '\0';
293 if((s = strstr(ps_global->smime->privatecontent, tmp)) != NULL){
294 if((t = strstr(s+strlen(tmp), EMAILADDRLEADER)) != NULL){
295 c = *t;
296 *t = '\0';
297 pc->keytext = cpystr(s + strlen(tmp) + strlen(NEWLINE));
298 *t = c;
300 else
301 pc->keytext = cpystr(s + strlen(tmp) + strlen(NEWLINE));
305 if(pc->keytext != NULL) /* we should go straight here */
306 text = pc->keytext;
307 } else if (ps_global->smime->privatetype == Keychain){
308 pathkey[0] = '\0'; /* no apple key chain support yet */
309 text = NULL;
311 if((pathkey && *pathkey) || text){
312 snprintf(prompt, sizeof(prompt),
313 _("Enter password of key <%s> to unlock password file: "), pc->name);
315 if((pkey = load_pkey_with_prompt(pathkey, text, prompt, NULL)) != NULL){
316 pc2 = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
317 memset((void *)pc2, 0, sizeof(PERSONAL_CERT));
318 pc2->name = cpystr(pc->name);
319 pc2->key = pkey;
320 pc2->cert = X509_dup(pc->cert);
322 /* now copy the keys and certs, starting by the key... */
323 build_path(fpath, pathdir, pc->name, sizeof(fpath));
324 strncat(fpath, ".key", 4);
325 fpath[sizeof(fpath)-1] = '\0';
326 if(our_stat(fpath, &sbuf) == 0){ /* if fpath exists */
327 if((sbuf.st_mode & S_IFMT) == S_IFREG) /* and is a regular file */
328 setup_dir++; /* we are done */
329 } else if(ps_global->smime->privatetype == Directory){
330 if(our_copy(fpath, pathkey) == 0)
331 setup_dir++;
332 } else if(ps_global->smime->privatetype == Container){
333 BIO *out;
334 if((out = BIO_new_file(fpath, "w")) != NULL){
335 if(BIO_puts(out, pc->keytext) > 0)
336 setup_dir++;
337 BIO_free(out);
339 } else if(ps_global->smime->privatetype == Keychain){
340 /* add support for Apple Mac OS X */
344 /* successful copy of key, now continue with certificate */
345 if(setup_dir){
346 setup_dir = 0;
348 build_path(pathkey, ps_global->smime->publicpath, pc->name, sizeof(pathkey));
349 strncat(pathkey, ".crt", 4);
350 pathkey[sizeof(pathkey)-1] = '\0';
352 build_path(fpath, pathdir, pc->name, sizeof(fpath));
353 strncat(fpath, ".crt", 4);
354 fpath[sizeof(fpath)-1] = '\0';
356 if(our_stat(fpath, &sbuf) == 0){
357 if((sbuf.st_mode & S_IFMT) == S_IFREG)
358 setup_dir++;
360 else if(ps_global->smime->privatetype == Directory){
361 if(our_copy(fpath, pathkey) == 0)
362 setup_dir++;
363 } else if(ps_global->smime->privatetype == Container) {
364 BIO *out;
365 if((out = BIO_new_file(fpath, "w")) != NULL){
366 if(PEM_write_bio_X509(out, pc->cert))
367 setup_dir++;
368 BIO_free(out);
370 } else if (ps_global->smime->privatetype == Keychain) {
371 /* add support for Mac OS X */
375 if(setup_dir){
376 *pwdcert = (void *) pc2;
377 was_here = 0;
378 return 0;
380 else if(pc2 != NULL)
381 free_personal_certs(&pc2);
382 } /* if (pathkey...) */
383 } /* if(ps_global->smime->personal_certs) */
386 if(setup_dir == 0){
387 /* PATHCERTDIR(Private) must be null, so create a path */
388 set_current_val(&ps_global->vars[V_PRIVATEKEY_DIR], TRUE, TRUE);
389 smime_path(ps_global->VAR_PRIVATEKEY_DIR, pathkey, sizeof(pathkey));
391 /* PATHCERTDIR(Public) must be null, so create a path */
392 set_current_val(&ps_global->vars[V_PUBLICCERT_DIR], TRUE, TRUE);
393 smime_path(ps_global->VAR_PUBLICCERT_DIR, pathcert, sizeof(pathcert));
395 /* BUG: this does not support local containers */
396 load_key_and_cert(pathkey, pathcert, &keyfile, &certfile, &pkey, &pcert);
398 if(certfile && keyfile){
399 build_path(fpath, pathdir, keyfile, sizeof(fpath));
400 strncat(fpath, ".key", 4);
401 fpath[sizeof(fpath)-1] = '\0';
403 build_path(fpath2, pathkey, keyfile, sizeof(fpath));
404 strncat(fpath2, ".key", 4);
405 fpath2[sizeof(fpath2)-1] = '\0';
407 if(our_copy(fpath, fpath2) == 0)
408 setup_dir++;
410 if(setup_dir){
411 setup_dir = 0;
413 build_path(fpath, pathdir, certfile, sizeof(fpath));
414 build_path(fpath2, pathcert, certfile, sizeof(fpath2));
416 if(our_copy(fpath, fpath2) == 0)
417 setup_dir++;
422 if(keyfile && certfile){
423 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
424 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
425 pc->name = keyfile;
426 pc->key = pkey;
427 pc->cert = pcert;
428 *pwdcert = (void *) pc;
429 fs_give((void **)&certfile);
430 was_here = 0;
431 return 0;
434 was_here = 0;
435 if(we_inited)
436 smime_deinit();
437 return 0;
439 #endif /* PASSFILE */
441 /* smime_expunge_cert.
442 * Return values: < 0 there was an error.
443 * >=0 the number of messages expunged
446 smime_expunge_cert(WhichCerts ctype)
448 int count, removed;
449 CertList *cl, *dummy, *data;
450 char *path, buf[MAXPATH+1];
451 char *contents;
453 if(DATACERT(ctype)== NULL)
454 return -1;
456 /* data cert is the way we unify certificate management across
457 * functions, but it is not where we really save the information in the
458 * case ctype is equal to Private. What we will do is to update the
459 * datacert, and in the case of ctype equal to Private use the updated
460 * certdata to update the personal_certs data.
463 path = PATHCERTDIR(ctype);
465 if(path){
466 /* add a fake certificate at the beginning of the list */
467 dummy = fs_get(sizeof(CertList));
468 memset((void *)dummy, 0, sizeof(CertList));
469 dummy->next = DATACERT(ctype);
471 for(cl = dummy, count = 0; cl && cl->next;){
472 if(cl->next->data.deleted == 0){
473 cl = cl->next;
474 continue;
477 removed = 1; /* assume success */
478 if(SMHOLDERTYPE(ctype) == Directory){
479 build_path(buf, path, cl->next->name, sizeof(buf));
480 if(ctype == Private && strlen(buf) + strlen(EXTCERT(Private)) < sizeof(buf)){
481 strncat(buf, EXTCERT(Private), sizeof(buf) - strlen(buf)-1);
482 buf[sizeof(buf)-1] = '\0';
485 if(our_unlink(buf) < 0){
486 q_status_message1(SM_ORDER, 3, 3, _("Error removing certificate %s"), cl->next->name);
487 cl = cl->next;
488 removed = 0;
491 else if(SMHOLDERTYPE(ctype) == Container){
492 char *prefix= ctype == CACert ? CACERTSTORELEADER : EMAILADDRLEADER;
493 char tmp[MAILTMPLEN], *s, *t;
495 contents = CONTENTCERTLIST(ctype);
496 snprintf(tmp, sizeof(tmp), "%s%s", prefix, cl->next->name);
497 tmp[sizeof(tmp) - 1] = '\0';
498 if((s = strstr(contents, tmp)) != NULL){
499 if((t = strstr(s+strlen(tmp), prefix)) == NULL)
500 *s = '\0';
501 else
502 memmove(s, t, strlen(t)+1);
503 fs_resize((void **)&contents, strlen(contents)+1);
504 switch(ctype){
505 case Private: ps_global->smime->privatecontent = contents; break;
506 case Public : ps_global->smime->publiccontent = contents; break;
507 case CACert : ps_global->smime->cacontent = contents; break;
508 default : break;
511 else
512 removed = 0;
513 } else { /* unhandled case */
516 if(removed > 0){
517 count++; /* count it! */
518 data = cl->next;
519 cl->next = data->next;
520 if(data->name) fs_give((void **)&data->name);
521 fs_give((void **)&data);
524 } else
525 q_status_message(SM_ORDER, 3, 3, _("Error expunging certificate"));
527 switch(ctype){
528 case Private: ps_global->smime->privatecertlist = dummy->next; break;
529 case Public : ps_global->smime->publiccertlist = dummy->next; break;
530 case CACert : ps_global->smime->cacertlist = dummy->next; break;
531 default : break;
533 fs_give((void **)&dummy);
534 if(SMHOLDERTYPE(ctype) == Container){
535 if(copy_dir_to_container(ctype, contents) < 0)
536 count = 0;
538 if(count > 0){
539 q_status_message2(SM_ORDER, 3, 3, _("Removed %s certificate%s"), comatose(count), plural(count));
541 else
542 q_status_message(SM_ORDER, 3, 3, _("Error: No certificates were removed"));
543 return count;
546 void
547 mark_cert_deleted(WhichCerts ctype, int num, unsigned state)
549 CertList *cl;
550 int i;
552 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
553 cl->data.deleted = state;
556 unsigned
557 get_cert_deleted(WhichCerts ctype, int num)
559 CertList *cl;
560 int i;
562 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
563 return (cl && cl->data.deleted) ? 1 : 0;
566 EVP_PKEY *
567 load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *ret)
569 EVP_PKEY *pkey;
570 int rc = 0; /* rc == 1, cancel, rc == 0 success */
571 char pass[MAILTMPLEN+1];
572 BIO *in;
574 /* attempt to load with empty password */
575 in = text ? BIO_new_mem_buf(text, strlen(text)) : BIO_new_file(fpath, "r");
576 if(in != NULL){
577 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, "");
578 if(pkey != NULL) return pkey;
579 } else return NULL;
581 if(pith_smime_enter_password)
582 while(pkey == NULL && rc != 1){
583 do {
584 rc = (*pith_smime_enter_password)(prompt, (char *)pass, sizeof(pass));
585 } while (rc!=0 && rc!=1 && rc>0);
587 (void) BIO_reset(in);
588 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, (char *)pass);
591 BIO_free(in);
593 if(ret) *ret = rc == 1 ? -1 : pkey != NULL ? 0 : 1;
594 return pkey;
597 /* This is a tool for conf_screen, The return value must be zero when
598 * nothing changed, so if there is a failure in the import return 0
599 * and return 1 when we succeeded.\
600 * We call this function in two ways:
601 * either fname is null or not. If they fname is null, so is p_cert.
602 * if p_cert is not null, it is the PERSONAL_CERT structure of fname if this
603 * is available, otherwise we will fill it up here.
606 import_certificate(WhichCerts ctype, PERSONAL_CERT *p_cert, char *fname)
608 int r = 1, rc;
609 char filename[MAXPATH+1], full_filename[MAXPATH+1], buf[MAXPATH+1];
610 char *what;
612 if(pith_smime_import_certificate == NULL
613 || pith_smime_enter_password == NULL){
614 q_status_message(SM_ORDER, 0, 2,
615 _("import of certificates not implemented yet!"));
616 return 0;
619 if(fname == NULL){
620 what = ctype == Public || ctype == CACert ? "certificate" : "key";
621 r = (*pith_smime_import_certificate)(filename, full_filename, what, sizeof(filename) - 20);
623 if(r < 0)
624 return 0;
625 } else {
626 char *s;
627 strncpy(full_filename, fname, sizeof(full_filename));
628 if((s = strrchr(full_filename, '/')) != NULL)
629 strncpy(filename, s+1, sizeof(filename));
632 /* we are trying to import a new key for the password file. First we ask for the
633 * private key. Once this is loaded, we make a reasonable attempt to find the
634 * public key in the same directory as the key was loaded from. We do this by
635 * looking for a file with the correct public certificate name, then we look
636 * in the same private key, and if not, we ask the user for its location. If all
637 * of this works, we import the key and public to the password directory.
639 #ifdef PASSFILE
640 if(ctype == Password){
641 char PrivateKeyPath[MAXPATH+1], PublicCertPath[MAXPATH+1], s[MAXPATH+1];
642 char full_name_key[MAXPATH+1], full_name_cert[MAXPATH+1];
643 char *use_this_file;
644 char prompt[500];
645 EVP_PKEY *key = p_cert ? p_cert->key : NULL;
647 rc = 1; /* assume success :) */
648 if(strlen(filename) > 4){
649 strncpy(s, filename, sizeof(s));
650 s[sizeof(s)-1] = '\0';
651 if(!strcmp(s + strlen(s) - strlen(EXTCERT(Private)), EXTCERT(Private)))
652 s[strlen(s) - strlen(EXTCERT(Private))] = '\0';
653 else
654 rc = 0;
655 } else rc = 0;
657 if(rc == 0){
658 q_status_message(SM_ORDER, 1, 3, _("Error in key name. Check file extension"));
659 return 0;
662 snprintf(prompt, sizeof(prompt), _("Enter passphrase to unlock new key <%s>: "), filename);
663 prompt[sizeof(prompt)-1] = '\0';
664 if(key != NULL
665 || (key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){
666 BIO *ins = NULL;
667 X509 *cert = p_cert ? p_cert->cert : NULL, *cert2;
669 strncpy(full_name_key, full_filename, sizeof(full_filename));
670 full_name_key[sizeof(full_name_key)-1] = '\0';
672 build_path(buf, PATHCERTDIR(ctype), s, sizeof(buf));
674 strncpy(PrivateKeyPath, buf, sizeof(PrivateKeyPath));
675 PrivateKeyPath[sizeof(PrivateKeyPath)-1] = '\0';
676 if(strlen(PrivateKeyPath) + 4 < sizeof(PrivateKeyPath)){
677 strncat(PrivateKeyPath, EXTCERT(Private), 4);
678 PrivateKeyPath[sizeof(PrivateKeyPath)-1] = '\0';
681 /* remove .key extension and replace it with .crt extension */
682 strncpy(full_name_cert, full_name_key, sizeof(full_name_key));
683 full_name_cert[sizeof(full_name_cert)-1] = '\0';
684 full_name_cert[strlen(full_name_cert) - strlen(EXTCERT(Private))] = '\0';
685 strncat(full_name_cert, EXTCERT(Public), 4);
686 full_name_cert[sizeof(full_name_cert)-1] = '\0';
689 /* set up path to location where we will save public cert */
690 strncpy(PublicCertPath, buf, sizeof(PublicCertPath));
691 PublicCertPath[sizeof(PublicCertPath)-1] = '\0';
692 if(strlen(PublicCertPath) + 4 < sizeof(PublicCertPath)){
693 strncat(PublicCertPath, EXTCERT(Public), 4);
694 PublicCertPath[sizeof(PublicCertPath)-1] = '\0';
696 /* attempt #1, use provided certificate,
697 * assumption is that full_name_cert is the file that this
698 * certificate derives from (which is obtained by substitution
699 * of .key extension in key by .crt extension)
701 if(cert != NULL) /* attempt #1 */
702 use_this_file = &full_name_cert[0];
703 else if((ins = BIO_new_file(full_name_cert, "r")) != NULL){
704 /* attempt #2 to guess public cert name, use .crt extension */
705 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
706 use_this_file = &full_name_cert[0];
709 else{ /* attempt #3 to guess public cert name: use the original key */
710 if((ins = BIO_new_file(full_name_key, "r")) != NULL){
711 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
712 use_this_file = &full_name_key[0];
715 else {
716 int done = 0;
717 /* attempt #4, ask the user */
718 do {
719 r = (*pith_smime_import_certificate)(filename, use_this_file, "certificate", sizeof(filename) - 20);
720 if(r < 0){
721 if(ins != NULL) BIO_free(ins);
722 if(cert != NULL) X509_free(cert);
723 return 0;
725 if((ins = BIO_new_file(use_this_file, "r")) != NULL){
726 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL)
727 done++;
728 else
729 q_status_message(SM_ORDER, 1, 3, _("Error parsing certificate"));
731 else
732 q_status_message(SM_ORDER, 1, 3, _("Error reading certificate"));
733 } while (done == 0);
736 if(ins != NULL){
737 if(cert != NULL){ /* check that certificate matches key */
738 if(!X509_check_private_key(cert, key)){
739 rc = 0;
740 q_status_message(SM_ORDER, 1, 3, _("Certificate does not match key"));
742 else
743 rc = 1; /* Success! */
745 else
746 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
748 if(rc == 1){ /* if everything has been successful,
749 * copy the files to their final destination */
750 if(our_copy(PrivateKeyPath, full_filename) == 0){ /* <-- save the private key */
751 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
752 if(our_copy(PublicCertPath, use_this_file) == 0){
753 char tmp[MAILTMPLEN];
754 FILE *fp;
756 if(!passfile_name(ps_global->pinerc, tmp, sizeof(tmp))
757 || !(fp = our_fopen(tmp, "rb"))){
758 q_status_message(SM_ORDER, 1, 3, _("Error reading password file!"));
759 rc = 0;
761 else {
762 char tmp2[MAILTMPLEN];
763 int encrypted = 0;
764 char *text;
765 PERSONAL_CERT *pwdcert, *pc = p_cert;
767 pwdcert = (PERSONAL_CERT *) ps_global->pwdcert;
768 if(pwdcert == NULL)
769 setup_pwdcert((void **)&pwdcert);
771 tmp2[0] = '\0';
772 fgets(tmp2, sizeof(tmp2), fp);
773 fclose(fp);
774 if(strcmp(tmp2, "-----BEGIN PKCS7-----\n")){
775 if(encrypt_file((char *)tmp, NULL, pwdcert))
776 encrypted++;
778 else
779 encrypted++;
781 if(encrypted){
782 text = decrypt_file((char *)tmp, NULL, pwdcert);
783 if(text != NULL){
784 if(pc == NULL){
785 pc = fs_get(sizeof(PERSONAL_CERT));
786 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
787 filename[strlen(filename)-strlen(EXTCERT(Private))] = '\0';
788 pc->name = cpystr(filename);
789 snprintf(buf, sizeof(buf), "%s%s", filename, EXTCERT(Public));
790 buf[sizeof(buf)-1] = '\0';
791 pc->cname = cpystr(buf);
792 pc->key = key;
793 pc->cert = cert;
796 if(encrypt_file((char *)tmp, text, pc)){ /* we did it! */
797 build_path(buf, PATHCERTDIR(ctype), pwdcert->name, sizeof(buf));
798 strncat(buf, EXTCERT(Private), sizeof(buf) - strlen(buf));
799 buf[sizeof(buf)-1] = '\0';
800 if(strcmp(PrivateKeyPath, buf)){
801 if (unlink(buf) < 0)
802 q_status_message(SM_ORDER, 1, 3, _("Failed to remove old key"));
804 build_path(buf, PATHCERTDIR(ctype), pwdcert->cname, sizeof(buf));
805 if(strcmp(PublicCertPath, buf)){
806 if(unlink(buf) < 0)
807 q_status_message(SM_ORDER, 1, 3, _("Failed to remove old certificate"));
809 free_personal_certs((PERSONAL_CERT **)&ps_global->pwdcert);
810 ps_global->pwdcert = pc;
811 rc = 1;
812 q_status_message(SM_ORDER, 1, 3, _("Password file reencrypted"));
813 } else {
814 q_status_message(SM_ORDER, 1, 3, _("Failed to reencrypt password file"));
815 rc = 0;
817 } else {
818 q_status_message(SM_ORDER, 1, 3, _("Error decrypting Password file"));
820 } else {
821 q_status_message(SM_ORDER, 1, 3, _("Password file not encrypted and could not encrypt"));
822 rc = 0;
826 else{
827 q_status_message(SM_ORDER, 1, 3, _("Error saving public certificate"));
828 if(our_unlink(PrivateKeyPath) < 0)
829 q_status_message(SM_ORDER, 1, 3, _("Error while cleaning private key"));
830 rc = 0;
833 else{
834 rc = 0;
835 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
837 if(ins != NULL) BIO_free(ins);
838 if(rc == 0 && cert != NULL) X509_free(cert);
840 } else {
841 rc = 0;
842 q_status_message(SM_ORDER, 1, 3, _("Error unlocking private key"));
845 return rc;
847 #endif /* PASSFILE */
849 smime_init();
850 ps_global->mangled_screen = 1;
852 if (ctype == Private){
853 char prompt[500], *s, *t;
854 EVP_PKEY *key = NULL;
856 if(!ps_global->smime->privatecertlist){
857 ps_global->smime->privatecertlist = fs_get(sizeof(CertList));
858 memset((void *)DATACERT(ctype), 0, sizeof(CertList));
861 for(s = t = filename; (t = strstr(s, ".key")) != NULL; s = t + 1);
862 if(s) *(s-1) = 0;
864 snprintf(prompt, sizeof(prompt), _("Enter passphrase for <%s>: "), filename);
865 prompt[sizeof(prompt)-1] = '\0';
866 if((key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){
867 if(SMHOLDERTYPE(ctype) == Directory){
868 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
869 if(strcmp(buf + strlen(buf) - 4, EXTCERT(ctype)) != 0 && strlen(buf) + 4 < sizeof(buf)){
870 strncat(buf, EXTCERT(ctype), sizeof(buf) - strlen(buf) -1);
871 buf[sizeof(buf)-1] = '\0';
873 rc = our_copy(buf, full_filename);
875 else /* if(SMHOLDERTYPE(ctype) == Container){ */
876 rc = add_file_to_container(ctype, full_filename, NULL);
877 if(rc == 0)
878 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
879 else
880 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
881 if(ps_global->smime->publiccertlist)
882 ps_global->smime->publiccertlist->data.renew = 1;
884 else
885 q_status_message(SM_ORDER, 1, 3, _("Problem unlocking key (not a certificate or wrong password)"));
886 } else if (ctype == CACert){
887 BIO *ins;
888 X509 *cert;
890 if((ins = BIO_new_file(full_filename, "r")) != NULL){
891 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
892 if(SMHOLDERTYPE(ctype) == Directory){
893 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
894 if(strcmp(buf + strlen(buf) - 4, ".crt") != 0 && strlen(buf) + 4 < sizeof(buf)){
895 strncat(buf, EXTCERT(ctype), sizeof(buf) - strlen(buf) - 1);
896 buf[sizeof(buf)-1] = '\0';
899 rc = our_copy(buf, full_filename);
901 else /* if(SMHOLDERTYPE(ctype) == Container){ */
902 rc = add_file_to_container(ctype, full_filename, NULL);
903 if(rc == 0)
904 q_status_message(SM_ORDER, 1, 3, _("Certificate saved"));
905 else
906 q_status_message(SM_ORDER, 1, 3, _("Error saving certificate"));
907 X509_free(cert); /* not needed anymore */
909 else
910 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
911 BIO_free(ins);
913 renew_store();
914 } else { /* ctype == Public. save certificate, but first validate that it is one */
915 BIO *ins;
916 X509 *cert;
918 if((ins = BIO_new_file(full_filename, "r")) != NULL){
919 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
920 if(SMHOLDERTYPE(ctype) == Directory){
921 char **email;
923 if((email = get_x509_subject_email(cert)) != NULL){
924 int i;
925 for(i = 0; email[i] != NULL; i++){
926 save_cert_for(email[i], cert, Public);
927 fs_give((void **)&email[i]);
929 fs_give((void **)email);
931 if(strcmp(filename + strlen(filename) - 4, ".crt") == 0)
932 filename[strlen(filename) - 4] = '\0';
933 save_cert_for(filename, cert, Public);
935 else /* if(SMHOLDERTYPE(ctype) == Container){ */
936 add_file_to_container(ctype, full_filename, NULL);
937 X509_free(cert);
938 if(ps_global->smime->publiccertlist)
939 ps_global->smime->publiccertlist->data.renew = 1;
941 else
942 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
943 BIO_free(ins);
946 if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1;
947 return 1;
950 /* itype: information type to add: 0 - public, 1 - private.
951 * Memory freed by caller
953 BIO *
954 print_private_key_information(char *email, int itype)
956 BIO *out;
957 PERSONAL_CERT *pc;
959 if(ps_global->smime == NULL
960 || ps_global->smime->personal_certs == NULL
961 || (itype != 0 && itype != 1))
962 return NULL;
964 for(pc = ps_global->smime->personal_certs;
965 pc != NULL && strcmp(pc->name, email) != 0; pc = pc->next);
966 if(pc->key == NULL
967 && !load_private_key(pc)
968 && ps_global->smime
969 && ps_global->smime->need_passphrase){
970 if (pith_opt_smime_get_passphrase)
971 (*pith_opt_smime_get_passphrase)();
972 load_private_key(pc);
975 if(pc->key == NULL)
976 return NULL;
978 out = BIO_new(BIO_s_mem());
979 if(itype == 0) /* 0 means public */
980 EVP_PKEY_print_public(out, pc->key, 0, NULL);
981 else if (itype == 1) /* 1 means private */
982 EVP_PKEY_print_private(out, pc->key, 0, NULL);
984 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
985 forget_private_keys();
987 return out;
991 * Forget any cached private keys
993 static void
994 forget_private_keys(void)
996 PERSONAL_CERT *pcert;
997 size_t len;
998 volatile char *p;
1000 dprint((9, "forget_private_keys()"));
1001 if(ps_global->smime){
1002 ps_global->smime->already_auto_asked = 0;
1003 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
1004 pcert;
1005 pcert=pcert->next){
1007 if(pcert->key){
1008 EVP_PKEY_free(pcert->key);
1009 pcert->key = NULL;
1013 ps_global->smime->entered_passphrase = 0;
1014 len = sizeof(ps_global->smime->passphrase);
1015 p = ps_global->smime->passphrase;
1017 while(len-- > 0)
1018 *p++ = '\0';
1022 /* modelled after signature_path in reply.c, but uses home dir instead of the
1023 * directory where the .pinerc is located, since according to documentation,
1024 * the .alpine-smime directories are subdirectories of the home directory
1027 smime_path(char *rpath, char *fpath, size_t len)
1029 *fpath = '\0';
1030 if(rpath && *rpath){
1031 size_t spl = strlen(rpath);
1033 if(IS_REMOTE(rpath)){
1034 if(spl < len - 1)
1035 strncpy(fpath, rpath, len-1);
1036 fpath[len-1] = '\0';
1038 else if(is_absolute_path(rpath)){
1039 strncpy(fpath, rpath, len-1);
1040 fpath[len-1] = '\0';
1041 fnexpand(fpath, len);
1043 else if(ps_global->VAR_OPER_DIR){
1044 if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
1045 build_path(fpath, ps_global->VAR_OPER_DIR, rpath, len);
1047 else if(ps_global->home_dir){
1048 if(strlen(ps_global->home_dir) + spl < len - 1)
1049 build_path(fpath, ps_global->home_dir, rpath, len);
1052 return fpath && *fpath ? 1 : 0;
1058 * taken from openssl/apps/app_rand.c
1060 static int
1061 app_RAND_load_file(const char *file)
1063 #define RANDBUFLEN 200
1064 char buffer[RANDBUFLEN];
1066 if(file == NULL)
1067 file = RAND_file_name(buffer, RANDBUFLEN);
1069 if(file == NULL || !RAND_load_file(file, -1)){
1070 if(RAND_status() == 0){
1071 dprint((1, "unable to load 'random state'\n"));
1072 dprint((1, "This means that the random number generator has not been seeded\n"));
1073 dprint((1, "with much random data.\n"));
1076 return 0;
1079 seeded = 1;
1080 return 1;
1085 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
1087 static void
1088 openssl_extra_randomness(void)
1090 #if !defined(WIN32)
1091 int fd;
1092 unsigned long i;
1093 char *tf = NULL;
1094 char tmp[MAXPATH];
1095 struct stat sbuf;
1096 /* if system doesn't have /dev/urandom */
1097 if(stat ("/dev/urandom", &sbuf)){
1098 tmp[0] = '0';
1099 tf = temp_nam(NULL, NULL);
1100 if(tf){
1101 strncpy(tmp, tf, sizeof(tmp));
1102 tmp[sizeof(tmp)-1] = '\0';
1103 fs_give((void **) &tf);
1106 if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
1107 i = (unsigned long) tmp;
1108 else{
1109 unlink(tmp); /* don't need the file */
1110 fstat(fd, &sbuf); /* get information about the file */
1111 i = sbuf.st_ino; /* remember its inode */
1112 close(fd); /* or its descriptor */
1114 /* not great but it'll have to do */
1115 snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
1116 tcp_serverhost (),i,
1117 (unsigned long) (time (0) ^ gethostid ()),
1118 (unsigned long) getpid ());
1119 RAND_seed(tmp, strlen(tmp));
1121 #endif
1125 /* taken from openssl/apps/app_rand.c */
1126 static int
1127 app_RAND_write_file(const char *file)
1129 char buffer[200];
1131 if(!seeded)
1133 * If we did not manage to read the seed file,
1134 * we should not write a low-entropy seed file back --
1135 * it would suppress a crucial warning the next time
1136 * we want to use it.
1138 return 0;
1140 if(file == NULL)
1141 file = RAND_file_name(buffer, sizeof buffer);
1143 if(file == NULL || !RAND_write_file(file)){
1144 dprint((1, "unable to write 'random state'\n"));
1145 return 0;
1148 return 1;
1151 CertList *
1152 certlist_from_personal_certs(PERSONAL_CERT *pc)
1154 CertList *cl;
1155 X509 *x;
1157 if(pc == NULL)
1158 return NULL;
1160 if((x = get_cert_for(pc->name, Public, 1)) != NULL)
1161 cl = smime_X509_to_cert_info(x, pc->name);
1162 cl->next = certlist_from_personal_certs(pc->next);
1164 return cl;
1167 void
1168 renew_cert_data(CertList **data, WhichCerts ctype)
1170 smime_init();
1171 if(ctype == Private){
1172 if(data){
1173 PERSONAL_CERT *pc = (PERSONAL_CERT *)ps_global->smime->personal_certs;
1174 if(*data)
1175 free_certlist(data);
1176 free_personal_certs(&pc);
1177 setup_privatekey_storage();
1178 *data = certlist_from_personal_certs((PERSONAL_CERT *)ps_global->smime->personal_certs);
1179 if(data && *data){
1180 resort_certificates(data, ctype);
1181 RENEWCERT(*data) = 0;
1183 ps_global->smime->privatecertlist = *data;
1185 if(ps_global->smime->privatecertlist)
1186 RENEWCERT(ps_global->smime->privatecertlist) = 0;
1187 } else {
1188 X509_LOOKUP *lookup = NULL;
1189 X509_STORE *store = NULL;
1191 if((store = X509_STORE_new()) != NULL){
1192 if((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) != NULL){
1193 free_certlist(data);
1194 if(SMHOLDERTYPE(ctype) == Directory)
1195 add_certs_in_dir(lookup, PATHCERTDIR(ctype), EXTCERT(ctype), data);
1196 else /* if(SMHOLDERTYPE(ctype) == Container) */
1197 *data = mem_to_certlist(CONTENTCERTLIST(ctype), ctype);
1198 if(data && *data){
1199 resort_certificates(data, ctype);
1200 RENEWCERT(*data) = 0;
1202 if(ctype == Public)
1203 ps_global->smime->publiccertlist = *data;
1204 else
1205 ps_global->smime->cacertlist = *data;
1207 free_x509_store(&store);
1210 setup_certs_backup_by_type(ctype);
1213 void
1214 smime_reinit(void)
1216 smime_deinit();
1217 smime_init();
1220 /* Installed as an atexit() handler to save the random data */
1221 void
1222 smime_deinit(void)
1224 dprint((9, "smime_deinit()"));
1225 app_RAND_write_file(NULL);
1226 if (s_cert_store != NULL) free_x509_store(&s_cert_store);
1227 #ifdef ERR_free_strings
1228 ERR_free_strings();
1229 #endif /* ERR_free_strings */
1230 #ifdef EVP_cleanup
1231 EVP_cleanup();
1232 #endif /* EVP_cleanup */
1233 free_smime_struct(&ps_global->smime);
1236 /* we renew the store when it has changed */
1237 void
1238 renew_store(void)
1240 if(ps_global->smime->inited){
1241 if(s_cert_store != NULL)
1242 free_x509_store(&s_cert_store);
1243 s_cert_store = get_ca_store();
1247 /* Initialise openssl stuff if needed */
1248 void
1249 smime_init(void)
1251 if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
1253 dprint((9, "smime_init()"));
1254 if(!ps_global->smime)
1255 ps_global->smime = new_smime_struct();
1257 setup_storage_locations();
1259 s_cert_store = get_ca_store();
1260 setup_certs_backup_by_type(CACert);
1262 #ifdef OPENSSL_1_1_0
1263 OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS|OPENSSL_INIT_ADD_ALL_DIGESTS|OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
1264 #else
1265 OpenSSL_add_all_algorithms();
1266 ERR_load_crypto_strings();
1267 #endif /* OPENSSL_1_1_0 */
1269 app_RAND_load_file(NULL);
1270 openssl_extra_randomness();
1271 ps_global->smime->inited = 1;
1274 ERR_clear_error();
1278 /* validate a certificate. Return value : 0 for no error, -1 for error.
1279 * In the latter case, set the openssl smime error in *error.
1282 smime_validate_cert(X509 *cert, long *error)
1284 X509_STORE_CTX *csc;
1286 ERR_clear_error();
1287 *error = 0;
1288 if((s_cert_store != NULL) && (csc = X509_STORE_CTX_new()) != NULL){
1289 X509_STORE_set_flags(s_cert_store, 0);
1290 if(X509_STORE_CTX_init(csc,s_cert_store,cert,NULL)
1291 && X509_verify_cert(csc) <= 0)
1292 *error = X509_STORE_CTX_get_error(csc);
1293 X509_STORE_CTX_free(csc);
1295 return *error ? -1 : 0;
1298 PERSONAL_CERT *
1299 get_personal_certs(char *path)
1301 PERSONAL_CERT *result = NULL;
1302 char buf2[MAXPATH], *fname;
1303 X509 *cert;
1304 size_t ll;
1305 #ifndef _WINDOWS
1306 struct dirent *d;
1307 DIR *dirp;
1308 #else /* _WINDOWS */
1309 struct _finddata_t dbuf;
1310 char buf[_MAX_PATH + 4];
1311 long findrv;
1312 #endif /* _WINDOWS */
1314 ps_global->smime->privatepath = cpystr(path);
1316 #ifndef _WINDOWS
1317 dirp = opendir(path);
1318 if(dirp){
1319 while((d=readdir(dirp)) != NULL){
1320 fname = d->d_name;
1321 #else /* _WINDOWS */
1322 snprintf(buf, sizeof(buf), "%s%s*.*", path, (path[strlen(path)-1] == '\\') ? "" : "\\");
1323 buf[sizeof(buf)-1] = '\0';
1324 if((findrv = _findfirst(buf, &dbuf)) < 0)
1325 return(NULL);
1327 do {
1328 fname = fname_to_utf8(dbuf.name);
1329 #endif
1330 if((ll=strlen(fname)) && ll > 4 && !strcmp(fname+ll-4, ".key")){
1332 /* copy file name to temp buffer */
1333 strncpy(buf2, fname, sizeof(buf2)-1);
1334 buf2[sizeof(buf2)-1] = '\0';
1335 /* chop off ".key" trailier */
1336 buf2[strlen(buf2)-4] = '\0';
1337 /* Look for certificate */
1338 cert = get_cert_for(buf2, Public, 1);
1340 if(cert){
1341 PERSONAL_CERT *pc;
1343 /* create a new PERSONAL_CERT, fill it in */
1345 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1346 pc->cert = cert;
1347 pc->name = cpystr(buf2);
1348 strncat(buf2, EXTCERT(Public), sizeof(buf2) - strlen(buf2) - 1);
1349 pc->cname = cpystr(buf2);
1351 /* Try to load the key with an empty password */
1352 pc->key = load_key(pc, "", SM_NORMALCERT);
1354 pc->next = result;
1355 result = pc;
1358 #ifndef _WINDOWS
1360 closedir(dirp);
1362 #else /* _WINDOWS */
1363 } while(_findnext(findrv, &dbuf) == 0);
1364 _findclose(findrv);
1365 #endif /* !_WINDOWS */
1366 return result;
1370 void
1371 setup_privatekey_storage(void)
1373 char path[MAXPATH+1], *contents;
1374 int privatekeycontainer = 0;
1376 /* private keys in a container */
1377 if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
1379 privatekeycontainer = 1;
1380 contents = NULL;
1381 path[0] = '\0';
1382 if(!smime_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
1383 privatekeycontainer = 0;
1385 if(privatekeycontainer && !IS_REMOTE(path)
1386 && ps_global->VAR_OPER_DIR
1387 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1388 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1389 /* TRANSLATORS: First arg is the directory name, second is
1390 the file user wants to read but can't. */
1391 _("Can't read file outside %s: %s"),
1392 ps_global->VAR_OPER_DIR, path);
1393 privatekeycontainer = 0;
1396 if(privatekeycontainer
1397 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1398 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1400 !(contents = read_file(path, READ_FROM_LOCALE)))
1401 privatekeycontainer = 0;
1404 if(privatekeycontainer && path[0]){
1405 ps_global->smime->privatetype = Container;
1406 ps_global->smime->privatepath = cpystr(path);
1408 if(contents){
1409 ps_global->smime->privatecontent = contents;
1410 ps_global->smime->personal_certs = mem_to_personal_certs(contents);
1415 /* private keys in a directory of files */
1416 if(!privatekeycontainer){
1417 ps_global->smime->privatetype = Directory;
1419 path[0] = '\0';
1420 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1421 && !IS_REMOTE(path)))
1422 ps_global->smime->privatetype = Nada;
1423 else if(can_access(path, ACCESS_EXISTS)){
1424 if(our_mkpath(path, 0700)){
1425 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1426 ps_global->smime->privatetype = Nada;
1430 if(ps_global->smime->privatetype == Directory)
1431 ps_global->smime->personal_certs = get_personal_certs(path);
1433 setup_certs_backup_by_type(Private);
1436 static void
1437 setup_storage_locations(void)
1439 int publiccertcontainer = 0, cacertcontainer = 0;
1440 char path[MAXPATH+1], *contents;
1442 if(!ps_global->smime)
1443 return;
1445 #ifdef APPLEKEYCHAIN
1446 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1447 ps_global->smime->publictype = Keychain;
1449 else{
1450 #endif /* APPLEKEYCHAIN */
1451 /* Public certificates in a container */
1452 if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
1454 publiccertcontainer = 1;
1455 contents = NULL;
1456 path[0] = '\0';
1457 if(!smime_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
1458 publiccertcontainer = 0;
1460 if(publiccertcontainer && !IS_REMOTE(path)
1461 && ps_global->VAR_OPER_DIR
1462 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1463 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1464 /* TRANSLATORS: First arg is the directory name, second is
1465 the file user wants to read but can't. */
1466 _("Can't read file outside %s: %s"),
1467 ps_global->VAR_OPER_DIR, path);
1468 publiccertcontainer = 0;
1471 if(publiccertcontainer
1472 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1473 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1475 !(contents = read_file(path, READ_FROM_LOCALE)))
1476 publiccertcontainer = 0;
1479 if(publiccertcontainer && path[0]){
1480 ps_global->smime->publictype = Container;
1481 ps_global->smime->publicpath = cpystr(path);
1483 if(contents){
1484 ps_global->smime->publiccontent = contents;
1485 ps_global->smime->publiccertlist = mem_to_certlist(contents, Public);
1490 /* Public certificates in a directory of files */
1491 if(!publiccertcontainer){
1492 ps_global->smime->publictype = Directory;
1494 path[0] = '\0';
1495 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1496 && !IS_REMOTE(path)))
1497 ps_global->smime->publictype = Nada;
1498 else if(can_access(path, ACCESS_EXISTS)){
1499 if(our_mkpath(path, 0700)){
1500 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1501 ps_global->smime->publictype = Nada;
1505 if(ps_global->smime->publictype == Directory)
1506 ps_global->smime->publicpath = cpystr(path);
1509 #ifdef APPLEKEYCHAIN
1511 #endif /* APPLEKEYCHAIN */
1513 setup_privatekey_storage();
1515 /* extra cacerts in a container */
1516 if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
1518 cacertcontainer = 1;
1519 contents = NULL;
1520 path[0] = '\0';
1521 if(!smime_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
1522 cacertcontainer = 0;
1524 if(cacertcontainer && !IS_REMOTE(path)
1525 && ps_global->VAR_OPER_DIR
1526 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1527 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1528 /* TRANSLATORS: First arg is the directory name, second is
1529 the file user wants to read but can't. */
1530 _("Can't read file outside %s: %s"),
1531 ps_global->VAR_OPER_DIR, path);
1532 cacertcontainer = 0;
1535 if(cacertcontainer
1536 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1537 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1539 !(contents = read_file(path, READ_FROM_LOCALE)))
1540 cacertcontainer = 0;
1543 if(cacertcontainer && path[0]){
1544 ps_global->smime->catype = Container;
1545 ps_global->smime->capath = cpystr(path);
1546 ps_global->smime->cacontent = contents;
1547 if(contents)
1548 ps_global->smime->cacertlist = mem_to_certlist(contents, CACert);
1552 if(!cacertcontainer){
1553 ps_global->smime->catype = Directory;
1555 path[0] = '\0';
1556 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1557 && !IS_REMOTE(path)))
1558 ps_global->smime->catype = Nada;
1559 else if(can_access(path, ACCESS_EXISTS)){
1560 if(our_mkpath(path, 0700)){
1561 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1562 ps_global->smime->catype = Nada;
1566 if(ps_global->smime->catype == Directory)
1567 ps_global->smime->capath = cpystr(path);
1573 copy_publiccert_dir_to_container(void)
1575 return(copy_dir_to_container(Public, NULL));
1580 copy_publiccert_container_to_dir(void)
1582 return(copy_container_to_dir(Public));
1587 copy_privatecert_dir_to_container(void)
1589 return(copy_dir_to_container(Private, NULL));
1594 copy_privatecert_container_to_dir(void)
1596 return(copy_container_to_dir(Private));
1601 copy_cacert_dir_to_container(void)
1603 return(copy_dir_to_container(CACert, NULL));
1608 copy_cacert_container_to_dir(void)
1610 return(copy_container_to_dir(CACert));
1613 /* Add the contents of a file to a container. Do not check the content
1614 * of the file, just add it using the format for that container. The
1615 * caller must check the format, so that there is no data corruption
1616 * in the future.
1617 * return value: 0 - success,
1618 * != 0 - failure.
1621 add_file_to_container(WhichCerts ctype, char *fpath, char *altname)
1623 char *sep = (ctype == Public || ctype == Private)
1624 ? EMAILADDRLEADER : CACERTSTORELEADER;
1625 char *content = ctype == Public ? ps_global->smime->publiccontent
1626 : (ctype == Private ? ps_global->smime->privatecontent
1627 : ps_global->smime->cacontent);
1628 char *name;
1629 char *s;
1630 unsigned char c;
1631 struct stat sbuf;
1632 STORE_S *in = NULL;
1633 int rv = -1; /* assume error */
1634 size_t clen; /* content buffer size */
1636 if(our_stat(fpath, &sbuf) < 0
1637 || (in = so_get(FileStar, fpath, READ_ACCESS | READ_FROM_LOCALE)) == NULL)
1638 goto endadd;
1640 if(altname != NULL)
1641 name = altname;
1642 else if((name = strrchr(fpath, '/')) != NULL){
1643 size_t ll;
1644 if((ll = strlen(++name)) > 4 && strucmp(name + ll - 4, EXTCERT(ctype)) == 0)
1645 name[ll-strlen(EXTCERT(ctype))] = '\0';
1647 else
1648 goto endadd;
1650 if(content){
1651 clen = strlen(content) + strlen(sep) + strlen(name) + sbuf.st_size + 2*strlen(NEWLINE) + 1;
1652 fs_resize((void **)&content, clen);
1653 s = content;
1654 content += strlen(content);
1656 else{
1657 clen = strlen(sep) + strlen(name) + sbuf.st_size + strlen(NEWLINE) + 1;
1658 s = content = fs_get(clen);
1659 *content = '\0';
1661 strncat(content, sep, clen - strlen(content));
1662 strncat(content, name, clen - strlen(content));
1663 content += strlen(content);
1664 #ifdef _WINDOWS
1665 *content++ = '\r';
1666 #endif /* _WINDOWS */
1667 *content++ = '\n';
1669 while(so_readc(&c, in))
1670 *content++ = (char) c;
1671 *content = '\0';
1673 switch(ctype){
1674 case Private: ps_global->smime->privatecontent = s; break;
1675 case Public : ps_global->smime->publiccontent = s; break;
1676 case CACert : ps_global->smime->cacontent = s; break;
1677 default : break;
1680 rv = copy_dir_to_container(ctype, s);
1682 endadd:
1683 if(in) so_give(&in);
1685 return rv;
1690 * returns 0 on success, -1 on failure
1691 * contents is an argument which tells this function to write the value
1692 * of this variable instead of reading the contents of the directory.
1693 * If the var contents is not null use its value as the value of the
1694 * container.
1697 copy_dir_to_container(WhichCerts which, char *contents)
1699 int ret = 0, container = 0;
1700 BIO *bio_out = NULL, *bio_in = NULL;
1701 char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
1702 char *tempfile = NULL, fpath[MAXPATH+1], *fname;
1703 size_t ll;
1704 #ifndef _WINDOWS
1705 DIR *dirp;
1706 struct dirent *d;
1707 #else /* _WINDOWS */
1708 struct _finddata_t dbuf;
1709 char buf[_MAX_PATH + 4];
1710 long findrv;
1711 #endif /* _WINDOWS */
1712 REMDATA_S *rd = NULL;
1713 char *configdir = NULL;
1714 char *configpath = NULL;
1715 char *configcontainer = NULL;
1716 char *filesuffix = NULL;
1717 char *ret_dir = NULL;
1719 dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1720 smime_init();
1722 srcpath[0] = '\0';
1723 dstpath[0] = '\0';
1724 file[0] = '\0';
1725 emailaddr[0] = '\0';
1727 if(which == Public){
1728 configdir = ps_global->VAR_PUBLICCERT_DIR;
1729 configpath = ps_global->smime->publicpath;
1730 configcontainer = cpystr(DF_PUBLIC_CONTAINER);
1731 filesuffix = ".crt";
1733 else if(which == Private){
1734 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1735 configpath = ps_global->smime->privatepath;
1736 configcontainer = cpystr(DF_PRIVATE_CONTAINER);
1737 filesuffix = ".key";
1739 else if(which == CACert){
1740 configdir = ps_global->VAR_CACERT_DIR;
1741 configpath = ps_global->smime->capath;
1742 configcontainer = cpystr(DF_CA_CONTAINER);
1743 filesuffix = ".crt";
1745 container = SMHOLDERTYPE(which) == Container;
1747 if(!(configdir && configdir[0])){
1748 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1749 return -1;
1752 if(!(configpath && configpath[0])){
1753 #ifdef APPLEKEYCHAIN
1754 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1755 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1756 return -1;
1758 #endif /* APPLEKEYCHAIN */
1759 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1760 return -1;
1763 if(!(filesuffix && strlen(filesuffix) == 4)){
1764 return -1;
1769 * If there is a legit directory to read from set up the
1770 * container file to write to.
1772 if(smime_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
1774 if(IS_REMOTE(configpath)){
1775 rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
1776 NULL, "Error: ",
1777 _("Can't access remote smime configuration."));
1778 if(!rd)
1779 return -1;
1781 (void) rd_read_metadata(rd);
1783 if(rd->access == MaybeRorW){
1784 if(rd->read_status == 'R')
1785 rd->access = ReadOnly;
1786 else
1787 rd->access = ReadWrite;
1790 if(rd->access != NoExists){
1792 rd_check_remvalid(rd, 1L);
1795 * If the cached info says it is readonly but
1796 * it looks like it's been fixed now, change it to readwrite.
1798 if(rd->read_status == 'R'){
1799 rd_check_readonly_access(rd);
1800 if(rd->read_status == 'W'){
1801 rd->access = ReadWrite;
1802 rd->flags |= REM_OUTOFDATE;
1804 else
1805 rd->access = ReadOnly;
1809 if(rd->flags & REM_OUTOFDATE){
1810 if(rd_update_local(rd) != 0){
1812 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1813 rd_close_remdata(&rd);
1814 return -1;
1817 else
1818 rd_open_remote(rd);
1820 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1821 rd_close_remdata(&rd);
1822 return -1;
1825 rd->flags |= DO_REMTRIM;
1827 strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
1828 dstpath[sizeof(dstpath)-1] = '\0';
1830 else{
1831 strncpy(dstpath, configpath, sizeof(dstpath)-1);
1832 dstpath[sizeof(dstpath)-1] = '\0';
1836 * dstpath is either the local Container file or the local cache file
1837 * for the remote Container file.
1839 tempfile = tempfile_in_same_dir(dstpath, "az", &ret_dir);
1843 * If there is a legit directory to read from and a tempfile
1844 * to write to we continue.
1846 if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
1848 if(contents != NULL){
1849 if(BIO_puts(bio_out, contents) < 0)
1850 ret = -1;
1852 else {
1853 #ifndef _WINDOWS
1854 if((dirp = opendir(srcpath)) != NULL){
1856 while((d=readdir(dirp)) && !ret){
1857 fname = d->d_name;
1858 #else /* _WINDOWS */
1859 snprintf(buf, sizeof(buf), "%s%s*.*", srcpath, (srcpath[strlen(srcpath)-1] == '\\') ? "" : "\\");
1860 buf[sizeof(buf)-1] = '\0';
1861 if((findrv = _findfirst(buf, &dbuf)) < 0)
1862 return -1;
1865 fname = fname_to_utf8(dbuf.name);
1866 #endif /* ! _WINDOWS */
1867 if((ll=strlen(fname)) && ll > 4 && !strcmp(fname+ll-4, filesuffix)){
1869 /* copy file name to temp buffer */
1870 strncpy(emailaddr, fname, sizeof(emailaddr)-1);
1871 emailaddr[sizeof(emailaddr)-1] = '\0';
1872 /* chop off suffix trailier */
1873 emailaddr[strlen(emailaddr)-4] = 0;
1876 * This is the separator between the contents of
1877 * different files.
1879 if(which == CACert){
1880 if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
1881 && (BIO_puts(bio_out, emailaddr) > 0)
1882 && (BIO_puts(bio_out, NEWLINE) > 0)))
1883 ret = -1;
1885 else{
1886 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1887 && (BIO_puts(bio_out, emailaddr) > 0)
1888 && (BIO_puts(bio_out, NEWLINE) > 0)))
1889 ret = -1;
1892 /* read then write contents of file */
1893 build_path(file, srcpath, fname, sizeof(file));
1894 if(!(bio_in = BIO_new_file(file, "r")))
1895 ret = -1;
1897 if(!ret){
1898 int good_stuff = 0;
1900 while(BIO_gets(bio_in, line, sizeof(line)) > 0){
1901 if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
1902 good_stuff = 1;
1904 if(good_stuff)
1905 BIO_puts(bio_out, line);
1907 if(strncmp("-----END", line, strlen("-----END")) == 0)
1908 good_stuff = 0;
1912 BIO_free(bio_in);
1914 #ifndef _WINDOWS
1916 closedir(dirp);
1918 #else /* _WINDOWS */
1919 } while (_findnext(findrv, &dbuf) == 0);
1920 _findclose(findrv);
1921 #endif /* ! _WINDOWS */
1924 BIO_free(bio_out);
1926 if(!ret){
1927 if(container && configpath && *configpath){
1928 strncpy(fpath, configpath, sizeof(fpath));
1929 fpath[sizeof(fpath) - 1] = '\0';
1931 else if(ret_dir){
1932 if(strlen(dstpath) + strlen(configcontainer) - strlen(ret_dir) + 1 < sizeof(dstpath))
1933 snprintf(fpath, sizeof(fpath), "%s%c%s",
1934 dstpath, tempfile[strlen(ret_dir)], configcontainer);
1935 else
1936 ret = -1;
1938 else ret = -1;
1940 if(!ret){
1941 if(!IS_REMOTE(configpath)){
1942 if(rename_file(tempfile, fpath) < 0){
1943 q_status_message2(SM_ORDER, 3, 3,
1944 _("Can't rename %s to %s"), tempfile, fpath);
1945 ret = -1;
1946 } else q_status_message1(SM_ORDER, 3, 3,
1947 _("saved container to %s"), fpath);
1949 else { /* if the container is remote, copy it */
1950 int e;
1951 char datebuf[200];
1953 if(rd != NULL && rename_file(tempfile, rd->lf) < 0){
1954 q_status_message2(SM_ORDER, 3, 3,
1955 _("Can't rename %s to %s"), tempfile, rd->lf);
1956 ret = -1;
1959 datebuf[0] = '\0';
1961 if((e = rd_update_remote(rd, datebuf)) != 0){
1962 if(e == -1){
1963 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1964 _("Error opening temporary smime file %s: %s"),
1965 rd->lf, error_description(errno));
1966 dprint((1,
1967 "write_remote_smime: error opening temp file %s\n",
1968 rd->lf ? rd->lf : "?"));
1970 else{
1971 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1972 _("Error copying to %s: %s"),
1973 rd->rn, error_description(errno));
1974 dprint((1,
1975 "write_remote_smime: error copying from %s to %s\n",
1976 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1979 q_status_message(SM_ORDER | SM_DING, 5, 5,
1980 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1982 else{
1983 rd_update_metadata(rd, datebuf);
1984 rd->read_status = 'W';
1987 rd_close_remdata(&rd);
1993 if(tempfile)
1994 fs_give((void **) &tempfile);
1996 if(ret_dir)
1997 fs_give((void **) &ret_dir);
1999 if(configcontainer)
2000 fs_give((void **) &configcontainer);
2002 return ret;
2007 * returns 0 on success, -1 on failure
2010 copy_container_to_dir(WhichCerts which)
2012 char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
2013 char iobuf[4096];
2014 char *contents = NULL;
2015 char *leader = NULL;
2016 char *filesuffix = NULL;
2017 char *configdir = NULL;
2018 char *configpath = NULL;
2019 char *tempfile = NULL;
2020 char *p, *q, *line, *name, *certtext, *save_p;
2021 int len;
2022 BIO *in, *out;
2024 dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
2025 smime_init();
2027 path[0] = '\0';
2029 if(which == Public){
2030 leader = EMAILADDRLEADER;
2031 contents = ps_global->smime->publiccontent;
2032 configdir = ps_global->VAR_PUBLICCERT_DIR;
2033 configpath = ps_global->smime->publicpath;
2034 filesuffix = ".crt";
2035 if(!(configpath && configpath[0])){
2036 #ifdef APPLEKEYCHAIN
2037 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
2038 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
2039 return -1;
2041 #endif /* APPLEKEYCHAIN */
2042 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2043 return -1;
2046 fs_give((void **) &ps_global->smime->publicpath);
2048 path[0] = '\0';
2049 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
2050 && !IS_REMOTE(path))){
2051 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2052 return -1;
2055 if(can_access(path, ACCESS_EXISTS)){
2056 if(our_mkpath(path, 0700)){
2057 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2058 return -1;
2062 ps_global->smime->publicpath = cpystr(path);
2063 configpath = ps_global->smime->publicpath;
2065 else if(which == Private){
2066 leader = EMAILADDRLEADER;
2067 contents = ps_global->smime->privatecontent;
2068 configdir = ps_global->VAR_PRIVATEKEY_DIR;
2069 configpath = ps_global->smime->privatepath;
2070 filesuffix = ".key";
2071 if(!(configpath && configpath[0])){
2072 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2073 return -1;
2076 fs_give((void **) &ps_global->smime->privatepath);
2078 path[0] = '\0';
2079 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
2080 && !IS_REMOTE(path))){
2081 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2082 return -1;
2085 if(can_access(path, ACCESS_EXISTS)){
2086 if(our_mkpath(path, 0700)){
2087 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2088 return -1;
2092 ps_global->smime->privatepath = cpystr(path);
2093 configpath = ps_global->smime->privatepath;
2095 else if(which == CACert){
2096 leader = CACERTSTORELEADER;
2097 contents = ps_global->smime->cacontent;
2098 configdir = ps_global->VAR_CACERT_DIR;
2099 configpath = ps_global->smime->capath;
2100 filesuffix = ".crt";
2101 if(!(configpath && configpath[0])){
2102 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2103 return -1;
2106 fs_give((void **) &ps_global->smime->capath);
2108 path[0] = '\0';
2109 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
2110 && !IS_REMOTE(path))){
2111 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2112 return -1;
2115 if(can_access(path, ACCESS_EXISTS)){
2116 if(our_mkpath(path, 0700)){
2117 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2118 return -1;
2122 ps_global->smime->capath = cpystr(path);
2123 configpath = ps_global->smime->capath;
2126 if(!(configdir && configdir[0])){
2127 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
2128 return -1;
2131 if(!(configpath && configpath[0])){
2132 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2133 return -1;
2136 if(!(filesuffix && strlen(filesuffix) == 4)){
2137 return -1;
2141 if(contents && *contents){
2142 for(p = contents; *p != '\0';){
2143 line = p;
2145 while(*p && *p != '\n')
2146 p++;
2148 save_p = NULL;
2149 if(*p == '\n'){
2150 save_p = p;
2151 *p++ = '\0';
2154 if(strncmp(leader, line, strlen(leader)) == 0){
2155 name = line + strlen(leader);
2156 certtext = p;
2157 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
2158 if((q = strstr(certtext, leader)) != NULL){
2159 p = q;
2161 else{ /* end of file */
2162 q = certtext + strlen(certtext);
2163 p = q;
2166 strncpy(buf, name, sizeof(buf)-5);
2167 buf[sizeof(buf)-5] = '\0';
2168 strncat(buf, filesuffix, 5);
2169 build_path(file, configpath, buf, sizeof(file));
2171 in = BIO_new_mem_buf(certtext, q-certtext);
2172 if(in){
2173 tempfile = tempfile_in_same_dir(file, "az", NULL);
2174 out = NULL;
2175 if(tempfile)
2176 out = BIO_new_file(tempfile, "w");
2178 if(out){
2179 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
2180 BIO_write(out, iobuf, len);
2182 BIO_free(out);
2184 if(rename_file(tempfile, file) < 0){
2185 q_status_message2(SM_ORDER, 3, 3,
2186 _("Can't rename %s to %s"),
2187 tempfile, file);
2188 return -1;
2191 fs_give((void **) &tempfile);
2194 BIO_free(in);
2199 if(save_p)
2200 *save_p = '\n';
2204 return 0;
2208 #ifdef APPLEKEYCHAIN
2211 copy_publiccert_container_to_keychain(void)
2213 /* NOT IMPLEMNTED */
2214 return -1;
2218 copy_publiccert_keychain_to_container(void)
2220 /* NOT IMPLEMNTED */
2221 return -1;
2224 #endif /* APPLEKEYCHAIN */
2228 * Get a pointer to a string describing the most recent OpenSSL error.
2229 * It's statically allocated, so don't change or attempt to free it.
2231 static const char *
2232 openssl_error_string(void)
2234 char *errs;
2235 const char *data = NULL;
2236 long errn;
2238 errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2239 errs = (char*) ERR_reason_error_string(errn);
2241 if(errs)
2242 return errs;
2243 else if(data)
2244 return data;
2246 return "unknown error";
2250 /* Return true if the body looks like a PKCS7 object */
2252 is_pkcs7_body(BODY *body)
2254 int result;
2256 result = body->type==TYPEAPPLICATION &&
2257 body->subtype &&
2258 (strucmp(body->subtype,"pkcs7-mime")==0 ||
2259 strucmp(body->subtype,"x-pkcs7-mime")==0 ||
2260 strucmp(body->subtype,"pkcs7-signature")==0 ||
2261 strucmp(body->subtype,"x-pkcs7-signature")==0);
2263 return result;
2268 * Recursively stash a pointer to the decrypted data in our
2269 * manufactured body.
2270 * parameters: type: call of type 1, save the base and header for multipart messages
2271 call of type 0, do not save the base and header for multipart messages
2273 static void
2274 create_local_cache(char *h, char *base, BODY *b, int type)
2276 if(b->type==TYPEMULTIPART){
2277 PART *p;
2279 if(type == 1){
2280 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2281 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2282 } else if(type == 0){
2284 * We don't really want to copy the real body contents. It shouldn't be
2285 * used, and in the case of a message with attachments, we'll be
2286 * duplicating the files multiple times.
2288 cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
2290 for(p=b->nested.part; p; p=p->next)
2291 create_local_cache(h, base, (BODY *) p, type);
2294 else{
2295 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2296 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2301 static long
2302 rfc822_output_func(void *b, char *string)
2304 BIO *bio = (BIO *) b;
2306 return(string ? *string ? (BIO_puts(bio, string) > 0 ? 1L : 0L)
2307 : (BIO_puts(bio, string) >= 0 ? 1L : 0L)
2308 : 0L);
2313 * Attempt to load the private key for the given PERSONAL_CERT.
2314 * This sets the appropriate passphrase globals in order to
2315 * interact with the user correctly.
2317 static int
2318 load_private_key(PERSONAL_CERT *pcert)
2320 if(!pcert->key){
2322 /* Try empty password by default */
2323 char *password = "";
2325 if(ps_global->smime
2326 && (ps_global->smime->need_passphrase
2327 || ps_global->smime->entered_passphrase)){
2328 /* We've already been in here and discovered we need a different password */
2330 if(ps_global->smime->entered_passphrase)
2331 password = (char *) ps_global->smime->passphrase; /* already entered */
2332 else
2333 return 0;
2336 ERR_clear_error();
2338 if(!(pcert->key = load_key(pcert, password, SM_NORMALCERT))){
2339 long err = ERR_get_error();
2341 /* Couldn't load key... */
2343 if(ps_global->smime && ps_global->smime->entered_passphrase){
2345 /* The user got the password wrong maybe? */
2347 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
2348 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
2349 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
2350 else
2351 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2353 /* This passphrase is no good; forget it */
2354 ps_global->smime->entered_passphrase = 0;
2357 if(ps_global->smime){
2358 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2359 ps_global->smime->need_passphrase = 1;
2360 if(ps_global->smime->passphrase_emailaddr){
2361 int i;
2362 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
2363 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
2364 fs_give((void **) ps_global->smime->passphrase_emailaddr);
2367 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
2370 return 0;
2372 else{
2373 /* This key will be cached, so we won't be called again */
2374 if(ps_global->smime){
2375 ps_global->smime->entered_passphrase = 0;
2376 ps_global->smime->need_passphrase = 0;
2380 return 1;
2383 return 0;
2387 static void
2388 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename, char *smime_type)
2390 b->type = TYPEAPPLICATION;
2391 b->subtype = cpystr(type);
2392 b->encoding = ENCBINARY;
2393 b->description = cpystr(description);
2395 b->disposition.type = cpystr("attachment");
2396 set_parameter(&b->disposition.parameter, "filename", filename);
2398 set_parameter(&b->parameter, "name", filename);
2399 if(smime_type && *smime_type)
2400 set_parameter(&b->parameter, "smime-type", smime_type);
2405 * Look for a personal certificate matching the
2406 * given address
2408 PERSONAL_CERT *
2409 match_personal_cert_to_email(ADDRESS *a)
2411 PERSONAL_CERT *pcert = NULL;
2412 char buf[MAXPATH];
2413 char **email;
2414 int i, done;
2416 if(!a || !a->mailbox || !a->host)
2417 return NULL;
2419 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2421 if(ps_global->smime){
2422 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
2423 pcert;
2424 pcert=pcert->next){
2426 if(!pcert->cert)
2427 continue;
2429 email = get_x509_subject_email(pcert->cert);
2431 done = 0;
2432 if(email != NULL){
2433 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
2434 if(email[i] != NULL) done++;
2435 for(i = 0; email[i] != NULL; i++)
2436 fs_give((void **)&email[i]);
2437 fs_give((void **)email);
2440 if(done > 0)
2441 break;
2445 return pcert;
2450 * Look for a personal certificate matching the from
2451 * (or reply_to? in the given envelope)
2453 PERSONAL_CERT *
2454 match_personal_cert(ENVELOPE *env)
2456 PERSONAL_CERT *pcert;
2458 pcert = match_personal_cert_to_email(env->reply_to);
2459 if(!pcert)
2460 pcert = match_personal_cert_to_email(env->from);
2462 return pcert;
2467 * Flatten the given body into its MIME representation.
2468 * Return the result in a BIO.
2470 static BIO *
2471 body_to_bio(BODY *body)
2473 BIO *bio = NULL;
2474 int len;
2476 bio = BIO_new(BIO_s_mem());
2477 if(!bio)
2478 return NULL;
2480 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
2481 pine_write_body_header(body, rfc822_output_func, bio);
2482 pine_rfc822_output_body(body, rfc822_output_func, bio);
2485 * Now need to truncate by two characters since the above
2486 * appends CRLF.
2488 if((len=BIO_ctrl_pending(bio)) > 1){
2489 BUF_MEM *biobuf = NULL;
2491 /* this code used to truncate without closing the bio, and
2492 then resetting the memory, causing non validation in
2493 signatures. Fix contributed by Bernd Edlinger.
2495 BIO_get_mem_ptr(bio, &biobuf);
2496 BIO_set_close(bio, BIO_NOCLOSE);
2497 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
2498 BIO_set_mem_buf(bio, biobuf, BIO_CLOSE);
2501 return bio;
2505 static BIO *
2506 bio_from_store(STORE_S *store)
2508 BIO *ret = NULL;
2510 if(store && store->src == BioType && store->txt){
2511 ret = (BIO *) store->txt;
2514 return(ret);
2518 * Encrypt file; given a path (char *) fp, replace the file
2519 * by an encrypted version of it. If (char *) text is not null, then
2520 * replace the text of (char *) fp by the encrypted version of (char *) text.
2521 * certpath is the FULL path to the file containing the certificate used for
2522 * encryption.
2523 * return value: 0 - failed to encrypt; 1 - success!
2526 encrypt_file(char *fp, char *text, PERSONAL_CERT *pc)
2528 const EVP_CIPHER *cipher = NULL;
2529 STACK_OF(X509) *encerts = NULL;
2530 BIO *out = NULL;
2531 PKCS7 *p7 = NULL;
2532 int rv = 0;
2534 if(pc == NULL)
2535 return 0;
2537 cipher = EVP_aes_256_cbc();
2538 encerts = sk_X509_new_null();
2540 sk_X509_push(encerts, X509_dup(pc->cert));
2542 if(text){
2543 if((out = BIO_new(BIO_s_mem())) != NULL){
2544 (void) BIO_reset(out);
2545 BIO_puts(out, text);
2548 else if((out = BIO_new_file(fp, "rb")) != NULL)
2549 BIO_read_filename(out, fp);
2551 if((p7 = PKCS7_encrypt(encerts, out, cipher, 0)) != NULL){
2552 BIO_set_close(out, BIO_CLOSE);
2553 BIO_free(out);
2554 if((out = BIO_new_file(fp, "w")) != NULL){
2555 BIO_reset(out);
2556 rv = PEM_write_bio_PKCS7(out, p7);
2557 BIO_flush(out);
2561 if(out != NULL)
2562 BIO_free(out);
2563 PKCS7_free(p7);
2564 sk_X509_pop_free(encerts, X509_free);
2566 return rv;
2570 * Encrypt a message on the way out. Called from call_mailer in send.c
2571 * The body may be reallocated.
2574 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
2576 PKCS7 *p7 = NULL;
2577 BIO *in = NULL;
2578 BIO *out = NULL;
2579 const EVP_CIPHER *cipher = NULL;
2580 STACK_OF(X509) *encerts = NULL;
2581 STORE_S *outs = NULL;
2582 PINEFIELD *pf;
2583 ADDRESS *a;
2584 BODY *body = *bodyP;
2585 BODY *newBody = NULL;
2586 int result = 0;
2587 X509 *cert;
2588 char buf[MAXPATH];
2590 dprint((9, "encrypt_outgoing_message()"));
2591 smime_init();
2593 cipher = EVP_aes_256_cbc();
2595 encerts = sk_X509_new_null();
2597 /* Look for a certificate for each of the recipients */
2598 for(pf = header->local; pf && pf->name; pf = pf->next)
2599 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
2600 for(a=*pf->addr; a; a=a->next){
2601 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2603 if((cert = get_cert_for(buf, Public, 1)) != NULL){
2604 sk_X509_push(encerts,cert);
2605 }else{
2606 q_status_message2(SM_ORDER, 1, 1,
2607 _("Unable to find certificate for <%s@%s>"),
2608 a->mailbox, a->host);
2609 goto end;
2614 /* add the sender's certificate so that they can decrypt the message too */
2615 for(a=header->env->from; a ; a = a->next){
2616 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2618 if((cert = get_cert_for(buf, Public, 1)) != NULL
2619 && sk_X509_find(encerts, cert) == -1)
2620 sk_X509_push(encerts,cert);
2623 in = body_to_bio(body);
2625 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
2627 outs = so_get(BioType, NULL, EDIT_ACCESS);
2628 out = bio_from_store(outs);
2630 i2d_PKCS7_bio(out, p7);
2631 (void) BIO_flush(out);
2633 so_seek(outs, 0, SEEK_SET);
2635 newBody = mail_newbody();
2637 newBody->type = TYPEAPPLICATION;
2638 newBody->subtype = cpystr("pkcs7-mime");
2639 newBody->encoding = ENCBINARY;
2641 newBody->disposition.type = cpystr("attachment");
2642 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
2644 newBody->description = cpystr("S/MIME Encrypted Message");
2645 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
2646 set_parameter(&newBody->parameter, "name", "smime.p7m");
2648 newBody->contents.text.data = (unsigned char *) outs;
2650 *bodyP = newBody;
2652 result = 1;
2654 end:
2656 BIO_free(in);
2657 PKCS7_free(p7);
2658 sk_X509_pop_free(encerts, X509_free);
2660 dprint((9, "encrypt_outgoing_message returns %d", result));
2661 return result;
2666 Get (and decode) the body of the given section of msg
2668 static STORE_S*
2669 get_part_contents(long msgno, const char *section)
2671 long len;
2672 gf_io_t pc;
2673 STORE_S *store = NULL;
2674 char *err;
2676 store = so_get(CharStar, NULL, EDIT_ACCESS);
2677 if(store){
2678 gf_set_so_writec(&pc,store);
2680 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
2682 gf_clear_so_writec(store);
2684 so_seek(store, 0, SEEK_SET);
2686 if(err)
2687 so_give(&store);
2690 return store;
2694 static PKCS7 *
2695 get_pkcs7_from_part(long msgno,const char *section)
2697 STORE_S *store = NULL;
2698 PKCS7 *p7 = NULL;
2699 BIO *in = NULL;
2701 store = get_part_contents(msgno, section);
2703 if(store){
2704 if(store->src == CharStar){
2705 int len;
2708 * We're reaching inside the STORE_S structure. We should
2709 * probably have a way to get the length, instead.
2711 len = (int) (store->eod - store->dp);
2712 in = BIO_new_mem_buf(store->txt, len);
2714 else{ /* just copy it */
2715 unsigned char c;
2717 in = BIO_new(BIO_s_mem());
2718 (void) BIO_reset(in);
2720 so_seek(store, 0L, 0);
2721 while(so_readc(&c, store)){
2722 BIO_write(in, &c, 1);
2726 if(in){
2727 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2728 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
2729 /* error */
2732 BIO_free(in);
2735 so_give(&store);
2738 return p7;
2742 same_cert(X509 *x, X509 *cert)
2744 char bufcert[256], bufx[256];
2745 int rv = 0;
2747 get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert), ":");
2748 get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx), ":");
2749 if(strcmp(bufx, bufcert) == 0)
2750 rv = 1;
2752 return rv;
2756 /* extract and save certificates from a PKCS7 package.
2757 * Return value:
2758 * 0 - no errors. Either the certificate was in public/
2759 * or we could save it there.
2760 * < 0 - the certificate was not in public/ and the user
2761 * did not save it there.
2765 smime_extract_and_save_cert(PKCS7 *p7)
2767 STACK_OF(X509) *signers;
2768 X509 *x, *cert;
2769 char **email;
2770 int i, j, rv, already_saved;
2771 long error;
2773 /* any signers for this message? */
2774 if((signers = PKCS7_get0_signers(p7, NULL, 0)) == NULL)
2775 return -1;
2777 rv = 0;
2778 for(i = 0; i < sk_X509_num(signers); i++){
2779 if((x = sk_X509_value(signers,i)) == NULL)
2780 continue;
2782 if((email = get_x509_subject_email(x)) != NULL){
2783 for(j = 0; email[j] != NULL; j++){
2784 already_saved = 0;
2785 /* check if we have the certificate for this address */
2786 cert = get_cert_for(email[j], Public, 1);
2787 /* if we have one, check if it is the one packaged */
2788 if(cert != NULL){
2789 already_saved = same_cert(x, cert);
2790 X509_free(cert);
2793 /* if not saved, try to save it */
2794 if(already_saved == 0
2795 && (*pith_smime_confirm_save)(email[j]) == 1){
2796 save_cert_for(email[j], x, Public);
2797 if(ps_global->smime->publiccertlist) /* renew store */
2798 free_certlist(&ps_global->smime->publiccertlist);
2801 /* check if it got saved */
2802 cert = get_cert_for(email[j], Public, 1);
2803 /* if saved, all is good */
2804 if(cert != NULL)
2805 X509_free(cert);
2806 else /* else, we do not have this certificate saved */
2807 rv += -1;
2809 fs_give((void **) &email[j]);
2811 fs_give((void **) email);
2814 sk_X509_free(signers);
2816 return rv;
2820 * Try to verify a signature.
2822 * p7 - the pkcs7 object to verify
2823 * in - the plain data to verify (NULL if not detached)
2824 * out - BIO to which to write the opaque data
2825 * silent - if non zero, do not print errors, only print success.
2827 static int
2828 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
2830 STACK_OF(X509) *otherCerts = NULL;
2831 CertList *cl;
2832 int result, flags;
2833 const char *data;
2834 long err;
2836 if(!s_cert_store){
2837 if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
2838 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2840 return -1;
2843 flags = F_ON(F_USE_CERT_STORE_ONLY, ps_global) ? PKCS7_NOINTERN : 0;
2845 if(ps_global->smime->publiccertlist == NULL){
2846 renew_cert_data(&ps_global->smime->publiccertlist, Public);
2847 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next){
2848 if(cl->x509_cert == NULL){
2849 char *s = strrchr(cl->name, '.');
2850 *s = '\0';
2851 cl->x509_cert = get_cert_for(cl->name, Public, 1);
2852 *s = '.';
2857 if(ps_global->smime->publiccertlist){
2858 otherCerts = sk_X509_new_null();
2859 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next)
2860 if(cl->x509_cert != NULL)
2861 sk_X509_push(otherCerts, X509_dup(cl->x509_cert));
2864 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, flags);
2866 if(result){
2867 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
2869 else{
2870 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2872 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
2875 * verification failed due to an error in verifying a certificate.
2876 * Just write the "out" BIO, and leave. Of course let the user
2877 * know about this. Make two more attempts to get the data out. The
2878 * last one should succeed. In any case, let the user know why it
2879 * failed.
2881 if(PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY) == 0)
2882 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY|PKCS7_NOSIGS);
2884 if (!silent) q_status_message1(SM_ORDER | SM_DING, 3, 3,
2885 _("Couldn't verify S/MIME signature: %s"), (char *) openssl_error_string());
2888 sk_X509_pop_free(otherCerts, X509_free);
2890 return result;
2893 /* Big comment, explaining the mess that exists out there, and how we deal
2894 with it, and also how we solve the problems that are created this way.
2896 When Alpine sends a message, it constructs that message, computes the
2897 signature, but then it forgets the message it signed and reconstructs it
2898 again. Since it signs a message containing a notice about "mime aware
2899 tools", but it does not send that we do not include that in the part
2900 that is signed, and that takes care of much of the problems.
2902 Another problem is what is received from the servers. All servers tested
2903 seem to transmit the message that was signed intact and Alpine can check
2904 the signature correctly. That is not a problem. The problem arises when
2905 the message includes attachments. In this case different servers send
2906 different things, so it will be up to us to figure out what is the text
2907 that was actually signed. Confused? here is the story:
2909 When a message containing and attachment is sent by Alpine, UW-IMAP,
2910 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2911 that was sent by Alpine, but GMX.com, Exchange, and probably other
2912 servers add a trailing \r\n in the message, so when validating the
2913 signature, these messages will not validate. There are several things
2914 that can be done.
2916 1. Add a trailing \r\n to any message that contains attachments, sign that
2917 and send that. In this way, all messages will validate with all
2918 servers.
2920 2. Compatibility mode: If a message has an attachment, contains a trailing
2921 \r\n and does not validate (sent by an earlier version of Alpine),
2922 remove the trailing \r\n and try to revalidate again.
2924 3. We do not add \r\n to validate a message that we sent, because that
2925 would only work in Alpine, and not in any other client. That would
2926 not be a good thing to do.
2928 PART II
2930 Now we have to deal with encrypted and signed messages. The problem is
2931 that c-client makes all its pointers point to "on disk" content, but
2932 since we decrypted the data earlier, we have to make sure of two things.
2933 One is that we saved that data (so we do not have to decrypt it again)
2934 and second that we can use it.
2936 In order to save the data we use create_local_cache, so that we do not
2937 have to redecrypt the message. Once this is saved, c-client functions will
2938 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2940 PART III
2942 When we are trying to verify messages with detached signatures, some
2943 imap servers send incorrect information in the mail_fetch_mime call. By
2944 incorrect I mean that this is not fetched directly from the message, but
2945 it is read from the message, processed, and then the processed part is
2946 sent to us, so this text might not agree with what is in the message,
2947 and so the validation of the signature might fail. However, the good
2948 news is that the message validates if saved to a local folder. This
2949 means that if normal validation does not work we can make it work by
2950 saving the message locally and validating that. This is implemented
2951 below, and causes delay in the display of the message. I am considering
2952 at this time not to do this automatically, but wait for the user to tell
2953 us to do it for them by means of a command available in the
2954 mail_view_screen. This might help in other situations, where a message
2955 is supposed to have an attachment, but it can not be seen in the
2956 processed text. Nevertheless, at this time, this is automatic, and is
2957 causing a delay in the processing of the message, but it is validating
2958 correctly all messages.
2960 PART IV
2962 When the user sends a message as encrypted and signed, this code used to
2963 encrypt first, and then sign the pkcs7 body, but it turns out that some
2964 other clients can not handle these messages. While we could argue that the
2965 other clients need to improve, we will support reading messages in both
2966 ways, and will send messages using this technique; that is, signed first,
2967 encrypted second. It seems that all tested clients support this way, so it
2968 should be safe to do so.
2971 typedef struct smime_filter_s {
2972 void (*filter)();
2973 } SMIME_FILTER_S;
2975 SMIME_FILTER_S sig_filter[] = {
2976 {smime_remove_trailing_crlf},
2977 {smime_remove_folding_space}
2980 #define TOTAL_FILTERS (sizeof(sig_filter)/sizeof(sig_filter[0]))
2981 #define TOTAL_SIGFLTR (1 << TOTAL_FILTERS) /* not good, keep filters to a low number */
2983 void
2984 smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen,
2985 char **bodytext, unsigned long *bodylen)
2987 if(*bodylen > 2 && !strncmp(*bodytext+*bodylen-2, "\r\n", 2))
2988 *bodylen -= 2;
2991 void
2992 smime_remove_folding_space(char **mimetext, unsigned long *mimelen,
2993 char **bodytext, unsigned long *bodylen)
2995 char *s = NULL, *t;
2996 unsigned long mlen = *mimelen;
2998 if(*mimetext){
2999 for (s = t = *mimetext; t - *mimetext < *mimelen; ){
3000 if(*t == '\r' && *(t+1) == '\n' && (*(t+2) == '\t' || *(t+2) == ' ')){
3001 *s++ = ' ';
3002 t += 3;
3003 mlen -= 2;
3005 else
3006 *s++ = *t++;
3008 *mimelen = mlen;
3013 smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag)
3015 int result, i, j, flag;
3016 char *mtext, *btext;
3017 unsigned long mlen, blen;
3018 BIO *in;
3020 mtext = mimelen ? fs_get(mimelen+1) : NULL;
3021 btext = fs_get(bodylen+1);
3022 result = 0;
3024 flag = 1; /* silence all failures */
3025 for(i = 1; result == 0 && i < TOTAL_SIGFLTR; i++){
3026 if((in = BIO_new(BIO_s_mem())) == NULL)
3027 return -1;
3029 (void) BIO_reset(in);
3031 if(i+1 == TOTAL_SIGFLTR)
3032 flag = nflag;
3034 if(mimelen)
3035 strncpy(mtext, mimetext, mlen = mimelen);
3036 strncpy(btext, bodytext, blen = bodylen);
3037 for(j = 0; j < TOTAL_FILTERS; j++)
3038 if((i >> j) & 1)
3039 (sig_filter[j].filter)(&mtext, &mlen, &btext, &blen);
3040 if(mtext != NULL)
3041 BIO_write(in, mtext, mlen);
3042 BIO_write(in, btext, blen);
3043 result = do_signature_verify(p7, in, NULL, flag);
3044 BIO_free(in);
3046 if(mtext) fs_give((void **)&mtext);
3047 if(btext) fs_give((void **)&btext);
3048 return result;
3052 * Given a multipart body of type multipart/signed, attempt to verify it.
3053 * Returns non-zero if the body was changed.
3055 static int
3056 do_detached_signature_verify(BODY *b, long msgno, char *section)
3058 PKCS7 *p7 = NULL;
3059 BIO *in = NULL;
3060 PART *p;
3061 int result, modified_the_body = 0;
3062 int flag; /* 1 silent, 0 not silent */
3063 unsigned long mimelen, bodylen;
3064 char newSec[100], *mimetext, *bodytext;
3065 char *what_we_did;
3066 SIZEDTEXT *st;
3068 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"));
3070 smime_init();
3072 /* if it was signed and then encrypted, use the decrypted text
3073 * to check the validity of the signature
3075 if(b->sparep){
3076 if(get_body_sparep_type(b->sparep) == SizedText){
3077 /* bodytext includes mimetext */
3078 st = (SIZEDTEXT *) get_body_sparep_data(b->sparep);
3079 bodytext = (char *) st->data;
3080 bodylen = st->size;
3081 mimetext = NULL;
3082 mimelen = 0L;
3085 else{
3086 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3087 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3088 if(mimetext)
3089 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3091 if(mimetext == NULL || bodytext == NULL)
3092 return modified_the_body;
3095 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3097 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
3098 || (in = BIO_new(BIO_s_mem())) == NULL)
3099 return modified_the_body;
3101 (void) BIO_reset(in);
3102 if(mimetext != NULL)
3103 BIO_write(in, mimetext, mimelen);
3104 BIO_write(in, bodytext, bodylen);
3106 smime_extract_and_save_cert(p7);
3108 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3109 flag = (mimelen == 0 || !IS_REMOTE(ps_global->mail_stream->mailbox))
3110 ? 0 : 1;
3111 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, flag);
3112 if(result < 0)
3113 return modified_the_body;
3114 if(result == 0
3115 && mimelen > 0 /* do not do this for encrypted messages */
3116 && IS_REMOTE(ps_global->mail_stream->mailbox)){
3117 char *fetch;
3118 unsigned long hlen, tlen;
3119 STORE_S *msg_so;
3121 BIO_free(in);
3122 if((in = BIO_new(BIO_s_mem())) != NULL
3123 && (fetch = mail_fetch_header(ps_global->mail_stream, msgno, NULL,
3124 NULL, &hlen, FT_PEEK)) != NULL
3125 && (msg_so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL
3126 && so_nputs(msg_so, fetch, (long) hlen)
3127 && (fetch = pine_mail_fetch_text(ps_global->mail_stream, msgno, NULL,
3128 &tlen, FT_PEEK)) != NULL
3129 && so_nputs(msg_so, fetch, tlen)){
3130 STRING bs;
3131 char *h = (char *) so_text(msg_so);
3132 char *bstart = strstr(h, "\r\n\r\n");
3133 ENVELOPE *env;
3134 BODY *body, *tmpB;
3136 bstart += 4;
3137 INIT(&bs, mail_string, bstart, tlen);
3138 rfc822_parse_msg_full(&env, &body, h, bstart-h-4, &bs, BADHOST, 0, 0);
3139 mail_free_envelope(&env);
3141 mail_free_body_part(&b->nested.part);
3142 tmpB = mail_body_section(body, (unsigned char *) section);
3143 if(MIME_MSG(tmpB->type, tmpB->subtype))
3144 b->nested.part = tmpB->nested.msg->body->nested.part;
3145 else
3146 b->nested.part = tmpB->nested.part;
3147 create_local_cache(bstart, bstart, &b->nested.part->body, 1);
3148 modified_the_body = 1;
3150 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3152 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3154 if(mimetext)
3155 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3157 if (mimetext == NULL || bodytext == NULL)
3158 return modified_the_body;
3160 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3162 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL)
3163 return modified_the_body;
3165 (void) BIO_reset(in);
3166 BIO_write(in, mimetext, mimelen);
3167 BIO_write(in, bodytext, bodylen);
3168 so_give(&msg_so);
3170 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3171 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, 0);
3172 if(result < 0)
3173 return modified_the_body;
3179 BIO_free(in);
3180 if(b->subtype)
3181 fs_give((void**) &b->subtype);
3183 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3184 b->encoding = ENC8BIT;
3186 if(b->description)
3187 fs_give ((void**) &b->description);
3189 what_we_did = result ? _("This message was cryptographically signed.") :
3190 _("This message was cryptographically signed but the signature could not be verified.");
3192 b->description = cpystr(what_we_did);
3194 b->sparep = create_body_sparep(P7Type, p7);
3196 p = b->nested.part;
3198 /* p is signed plaintext */
3199 if(p && p->next)
3200 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
3202 modified_the_body = 1;
3204 return modified_the_body;
3208 PERSONAL_CERT *
3209 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
3211 PERSONAL_CERT *x = NULL;
3213 if(ps_global->smime){
3214 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
3215 X509 *mine;
3217 mine = x->cert;
3219 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,X509_get_issuer_name(mine)) &&
3220 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,X509_get_serialNumber(mine))){
3221 break;
3226 return x;
3230 static PERSONAL_CERT *
3231 find_certificate_matching_pkcs7(PKCS7 *p7)
3233 int i;
3234 STACK_OF(PKCS7_RECIP_INFO) *recips;
3235 PERSONAL_CERT *x = NULL;
3237 recips = p7->d.enveloped->recipientinfo;
3239 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
3240 PKCS7_RECIP_INFO *ri;
3242 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
3244 if((x=find_certificate_matching_recip_info(ri))!=0){
3245 break;
3249 return x;
3252 /* decrypt an encrypted file.
3253 Args: fp - the path to the encrypted file.
3254 rv - a code that tells the caller what happened inside the function
3255 pcert - a personal certificate that was used to encrypt this file
3256 Returns the decoded text allocated in a char *, whose memory must be
3257 freed by caller
3260 char *
3261 decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc)
3263 PKCS7 *p7 = NULL;
3264 char *text, *tmp;
3265 BIO *in = NULL, *out = NULL;
3266 int i, j;
3267 long unsigned int len;
3268 void *ret;
3270 if(pc == NULL || (text = read_file(fp, 0)) == NULL || *text == '\0')
3271 return NULL;
3273 tmp = strchr(text + strlen("-----BEGIN PKCS7-----") + strlen(NEWLINE), '-');
3274 if(tmp != NULL) *tmp = '\0';
3275 tmp = text + strlen("-----BEGIN PKCS7-----") + strlen(NEWLINE);
3277 ret = rfc822_base64((unsigned char *)tmp, strlen(tmp), &len);
3279 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
3280 p7 = d2i_PKCS7_bio(in, NULL);
3281 BIO_free(in);
3284 if (text) fs_give((void **)&text);
3285 if (ret) fs_give((void **)&ret);
3287 if (rv) *rv = pc->key == NULL ? -1 : 1;
3289 out = BIO_new(BIO_s_mem());
3290 (void) BIO_reset(out);
3292 if(PKCS7_decrypt(p7, pc->key, pc->cert, out, 0) != 0){
3293 len = BIO_get_mem_data(out, &tmp);
3294 text = fs_get((len+1)*sizeof(char));
3295 strncpy(text, tmp, len);
3296 text[len] = '\0';
3297 BIO_free(out);
3298 } else
3299 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3300 (char *) openssl_error_string());
3301 PKCS7_free(p7);
3303 return text;
3307 * Try to decode (decrypt or verify a signature) a PKCS7 body
3308 * Returns non-zero if something was changed.
3310 static int
3311 do_decoding(BODY *b, long msgno, const char *section)
3313 int modified_the_body = 0;
3314 BIO *out = NULL;
3315 PKCS7 *p7 = NULL;
3316 X509 *recip = NULL;
3317 EVP_PKEY *key = NULL;
3318 PERSONAL_CERT *pcert = NULL;
3319 char *what_we_did = "";
3320 char null[1];
3322 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"));
3323 null[0] = '\0';
3324 smime_init();
3327 * Extract binary data from part to an in-memory store
3330 if(b->sparep){
3331 if(get_body_sparep_type(b->sparep) == P7Type)
3332 p7 = (PKCS7*) get_body_sparep_data(b->sparep);
3334 else{
3335 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
3336 if(!p7){
3337 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
3338 (char*) openssl_error_string());
3339 goto end;
3343 * Save the PKCS7 object for later dealings by the user interface.
3344 * It will be cleaned up when the body is garbage collected.
3346 b->sparep = create_body_sparep(P7Type, p7);
3349 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
3351 if(PKCS7_type_is_signed(p7)){
3352 int sigok;
3354 out = BIO_new(BIO_s_mem());
3355 (void) BIO_reset(out);
3356 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3358 sigok = do_signature_verify(p7, NULL, out, 0);
3360 what_we_did = sigok ? _("This message was cryptographically signed.") :
3361 _("This message was cryptographically signed but the signature could not be verified.");
3363 /* make sure it's null terminated */
3364 BIO_write(out, null, 1);
3366 else if(!PKCS7_type_is_enveloped(p7)){
3367 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
3368 goto end;
3370 else{ /* It *is* enveloped */
3371 int decrypt_result;
3373 what_we_did = _("This message was encrypted.");
3375 /* now need to find a cert that can decrypt this */
3376 pcert = find_certificate_matching_pkcs7(p7);
3378 if(!pcert){
3379 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3380 goto end;
3383 recip = pcert->cert;
3385 if(!load_private_key(pcert)
3386 && ps_global->smime
3387 && ps_global->smime->need_passphrase
3388 && !ps_global->smime->already_auto_asked){
3389 /* Couldn't load key with blank password, ask user */
3390 ps_global->smime->already_auto_asked = 1;
3391 if(pith_opt_smime_get_passphrase){
3392 (*pith_opt_smime_get_passphrase)();
3393 load_private_key(pcert);
3397 key = pcert->key;
3398 if(!key)
3399 goto end;
3401 out = BIO_new(BIO_s_mem());
3402 (void) BIO_reset(out);
3403 BIO_puts(out, "MIME-Version: 1.0\r\n");
3405 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
3407 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3408 forget_private_keys();
3410 if(!decrypt_result){
3411 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3412 (char*) openssl_error_string());
3413 goto end; }
3415 BIO_write(out, null, 1);
3419 * We've now produced a flattened MIME object in BIO out.
3420 * It needs to be turned back into a BODY.
3423 if(out){
3424 BODY *body;
3425 ENVELOPE *env;
3426 char *h = NULL;
3427 char *bstart;
3428 STRING s;
3429 BUF_MEM *bptr = NULL;
3430 int we_free = 0;
3432 BIO_get_mem_ptr(out, &bptr);
3433 if(bptr)
3434 h = bptr->data;
3436 /* look for start of body */
3437 bstart = strstr(h, "\r\n\r\n");
3439 if(!bstart){
3441 * Some clients do not canonicalize before encrypting, so
3442 * look for "\n\n" instead.
3444 bstart = strstr(h, "\n\n");
3445 if(bstart){
3446 int lines;
3447 char *s, *t;
3448 for(lines = 0, bstart = h; (bstart = strchr(bstart, '\n')) != NULL;
3449 bstart++, lines++);
3450 h = t = fs_get(strlen(bptr->data) + lines + 1);
3451 we_free++;
3452 for(s = bptr->data; *s != '\0'; s++)
3453 if(*s == '\n' && *(s-1) != '\r'){
3454 *t++ = '\r';
3455 *t++ = '\n';
3457 else
3458 *t++ = *s;
3459 *t = '\0';
3460 bstart = strstr(h, "\r\n\r\n");
3464 if(!bstart){
3465 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
3467 else{
3468 SIZEDTEXT *st;
3469 bstart += 4; /* skip over CRLF*2 */
3471 INIT(&s, mail_string, bstart, strlen(bstart));
3472 rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
3473 mail_free_envelope(&env); /* Don't care about this */
3475 if(body->type == TYPEMULTIPART
3476 && !strucmp(body->subtype, "SIGNED")){
3477 char *cookie = NULL;
3478 PARAMETER *param;
3479 for (param = body->parameter; param && !cookie; param = param->next)
3480 if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value;
3481 if(cookie != NULL){
3482 st = fs_get(sizeof(SIZEDTEXT));
3483 st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */
3484 st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4);
3485 body->sparep = create_body_sparep(SizedText, (void *)st);
3487 else
3488 q_status_message(SM_ORDER, 3, 3, _("Couldn't find cookie in attachment list."));
3490 body->mime.offset = 0;
3491 body->mime.text.size = 0;
3494 * Now convert original body (application/pkcs7-mime)
3495 * to a multipart body with one sub-part (the decrypted body).
3496 * Note that the sub-part may also be multipart!
3499 b->type = TYPEMULTIPART;
3500 if(b->subtype)
3501 fs_give((void **) &b->subtype);
3504 * This subtype is used in mailview.c to annotate the display of
3505 * encrypted or signed messages. We know for sure then that it's a PKCS7
3506 * part because the sparep field is set to the PKCS7 object (see above).
3508 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3509 b->encoding = ENC8BIT;
3511 if(b->description)
3512 fs_give((void **) &b->description);
3514 b->description = cpystr(what_we_did);
3516 if(b->disposition.type)
3517 fs_give((void **) &b->disposition.type);
3519 if(b->contents.text.data)
3520 fs_give((void **) &b->contents.text.data);
3522 if(b->parameter)
3523 mail_free_body_parameter(&b->parameter);
3525 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3526 b->nested.part = fs_get(sizeof(PART));
3527 b->nested.part->body = *body;
3528 b->nested.part->next = NULL;
3530 fs_give((void**) &body);
3533 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3534 * the decrypted data. Otherwise, it'll try to load it from the original
3535 * data. Eek.
3537 create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body, 0);
3539 modified_the_body = 1;
3541 if(we_free)
3542 fs_give((void **) &h);
3545 end:
3546 if(out)
3547 BIO_free(out);
3549 return modified_the_body;
3554 * Recursively handle PKCS7 bodies in our message.
3556 * Returns non-zero if some fiddling was done.
3558 static int
3559 do_fiddle_smime_message(BODY *b, long msgno, char *section)
3561 int modified_the_body = 0;
3563 if(!b)
3564 return 0;
3566 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"));
3568 if(is_pkcs7_body(b)){
3570 if(do_decoding(b, msgno, section)){
3572 * b should now be a multipart message:
3573 * fiddle it too in case it's been multiply-encrypted!
3576 /* fallthru */
3577 modified_the_body = 1;
3581 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
3583 PART *p;
3584 int partNum;
3585 char newSec[100];
3587 if(MIME_MULT_SIGNED(b->type, b->subtype)){
3591 * Ahah. We have a multipart signed entity.
3593 * Multipart/signed
3594 * part 1 (signed thing)
3595 * part 2 (the pkcs7 signature)
3597 * We're going to convert that to
3599 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3600 * part 1 (signed thing)
3601 * part 2 has been freed
3603 * We also extract the signature from part 2 and save it
3604 * in the multipart body->sparep, and we add a description
3605 * in the multipart body->description.
3608 * The results of a decrypted message will be similar. It
3609 * will be
3611 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3612 * part 1 (decrypted thing)
3615 modified_the_body += do_detached_signature_verify(b, msgno, section);
3617 else if(MIME_MSG(b->type, b->subtype)){
3618 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
3620 else{
3622 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
3623 /* Append part number to the section string */
3625 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
3627 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
3632 return modified_the_body;
3637 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3638 * Returns non-zero if something was changed.
3641 fiddle_smime_message(BODY *b, long msgno)
3643 return do_fiddle_smime_message(b, msgno, "");
3647 /********************************************************************************/
3651 * Output a string in a distinctive style
3653 void
3654 gf_puts_uline(char *txt, gf_io_t pc)
3656 pc(TAG_EMBED); pc(TAG_BOLDON);
3657 gf_puts(txt, pc);
3658 pc(TAG_EMBED); pc(TAG_BOLDOFF);
3661 /* get_chain_for_cert: error and level are mandatory arguments */
3662 STACK_OF(X509) *
3663 get_chain_for_cert(X509 *cert, int *error, int *level)
3665 STACK_OF(X509) *chain = NULL;
3666 X509_STORE_CTX *ctx;
3667 X509 *x, *xtmp;
3668 int rc; /* return code */
3670 *level = -1;
3671 *error = 0;
3672 ERR_clear_error();
3673 if((s_cert_store != NULL) && (ctx = X509_STORE_CTX_new()) != NULL){
3674 X509_STORE_set_flags(s_cert_store, 0);
3675 if(!X509_STORE_CTX_init(ctx, s_cert_store, cert, NULL))
3676 *error = X509_STORE_CTX_get_error(ctx);
3677 else if((chain = sk_X509_new_null()) != NULL){
3678 for(x = cert; ; x = xtmp){
3679 if(++*level > 0)
3680 sk_X509_push(chain, X509_dup(x));
3681 rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x);
3682 if(rc < 0)
3683 *error = 1;
3684 if(rc <= 0)
3685 break;
3686 if(!X509_check_issued(xtmp, xtmp))
3687 break;
3690 X509_STORE_CTX_free(ctx);
3692 return chain;
3697 * Sign a message. Called from call_mailer in send.c.
3699 * This takes the header for the outgoing message as well as a pointer
3700 * to the current body (which may be reallocated).
3701 * The last argument (BODY **bp) is an argument that tells Alpine
3702 * if the body has 8 bit. if *bp is not null we compute two signatures
3703 * one for the quoted-printable encoded message, and another for the
3704 * 8bit encoded message. We return the signature for the 8bit encoded
3705 * part in p2->body.mime.text.data.
3706 * The reason why we compute two signatures is so that we can decide
3707 * which one to use later, and we only do it in the case that *bp is
3708 * not null. If we did not do this, then we might not be able to sign
3709 * a message until we log in to the smtp server, so instead of doing
3710 * that, we get ready for any possible situation we might find.
3713 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp)
3715 STORE_S *outs = NULL;
3716 STORE_S *outs_2 = NULL;
3717 BODY *body = *bodyP;
3718 BODY *newBody = NULL;
3719 PART *p1 = NULL;
3720 PART *p2 = NULL;
3721 PERSONAL_CERT *pcert;
3722 BIO *in = NULL;
3723 BIO *in_2 = NULL;
3724 BIO *out = NULL;
3725 BIO *out_2 = NULL;
3726 PKCS7 *p7 = NULL;
3727 PKCS7 *p7_2 = NULL;
3728 STACK_OF(X509) *chain;
3729 const EVP_MD *md = EVP_sha256(); /* use this digest instead of sha1 */
3730 int result = 0, error;
3731 int flags = dont_detach ? 0 : PKCS7_DETACHED;
3732 int level;
3734 dprint((9, "sign_outgoing_message()"));
3736 smime_init();
3738 /* Look for a private key matching the sender address... */
3740 pcert = match_personal_cert(header->env);
3742 if(!pcert){
3743 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
3744 goto end;
3747 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
3748 /* Couldn't load key with blank password, try again */
3749 if(pith_opt_smime_get_passphrase){
3750 (*pith_opt_smime_get_passphrase)();
3751 load_private_key(pcert);
3755 if(!pcert->key)
3756 goto end;
3758 if(((chain = get_chain_for_cert(pcert->cert, &error, &level)) != NULL && error)
3759 || level == 0){
3760 sk_X509_pop_free(chain, X509_free);
3761 chain = NULL;
3764 if(error)
3765 q_status_message(SM_ORDER, 1, 1,
3766 _("Not all certificates needed to verify signature included in signed message"));
3768 in = body_to_bio(body);
3770 flags |= PKCS7_PARTIAL;
3771 if((p7 = PKCS7_sign(NULL, NULL, chain, in, flags)) != NULL
3772 && PKCS7_sign_add_signer(p7, pcert->cert, pcert->key, md, flags))
3773 PKCS7_final(p7, in, flags);
3775 if(bp && *bp){
3776 int i, save_encoding;
3778 for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++);
3780 if(i > ENCMAX){ /* no empty encoding slots! */
3781 *bp = NULL;
3783 else {
3784 save_encoding = (*bp)->encoding;
3785 body_encodings[(*bp)->encoding = i] = body_encodings[ENC8BIT];
3787 in_2 = body_to_bio(body);
3789 body_encodings[i] = NULL;
3790 (*bp)->encoding = save_encoding;
3794 if(bp && *bp){
3795 if((p7_2 = PKCS7_sign(NULL, NULL, chain, in_2, flags)) != NULL
3796 && PKCS7_sign_add_signer(p7_2, pcert->cert, pcert->key, md, flags))
3797 PKCS7_final(p7_2, in_2, flags);
3800 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3801 forget_private_keys();
3803 if(chain)
3804 sk_X509_pop_free(chain, X509_free);
3806 if(!p7){
3807 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
3808 goto end;
3811 outs = so_get(BioType, NULL, EDIT_ACCESS);
3812 out = bio_from_store(outs);
3814 i2d_PKCS7_bio(out, p7);
3815 (void) BIO_flush(out);
3817 so_seek(outs, 0, SEEK_SET);
3819 if(bp && *bp && p7_2){
3820 outs_2 = so_get(BioType, NULL, EDIT_ACCESS);
3821 out_2 = bio_from_store(outs_2);
3823 i2d_PKCS7_bio(out_2, p7_2);
3824 (void) BIO_flush(out_2);
3826 so_seek(outs_2, 0, SEEK_SET);
3829 if((flags&PKCS7_DETACHED)==0){
3831 /* the simple case: the signed data is in the pkcs7 object */
3833 newBody = mail_newbody();
3835 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3837 newBody->contents.text.data = (unsigned char *) outs;
3838 *bodyP = newBody;
3840 result = 1;
3842 else{
3845 * OK.
3846 * We have to create a new body as follows:
3848 * multipart/signed; blah blah blah
3849 * reference to existing body
3851 * pkcs7 object
3854 newBody = mail_newbody();
3856 newBody->type = TYPEMULTIPART;
3857 newBody->subtype = cpystr("signed");
3858 newBody->encoding = ENC7BIT;
3860 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
3861 set_parameter(&newBody->parameter, "micalg", "sha-256");
3863 p1 = mail_newbody_part();
3864 p2 = mail_newbody_part();
3867 * This is nasty. We're just copying the body in here,
3868 * but since our newBody is freed at the end of call_mailer,
3869 * we mustn't let this body (the original one) be freed twice.
3871 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
3873 p1->next = p2;
3875 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL);
3876 p2->body.mime.text.data = (unsigned char *) outs_2;
3877 p2->body.contents.text.data = (unsigned char *) outs;
3879 newBody->nested.part = p1;
3881 *bodyP = newBody;
3883 result = 1;
3886 end:
3888 PKCS7_free(p7);
3889 BIO_free(in);
3891 if(bp && *bp){
3892 if(p7_2) PKCS7_free(p7_2);
3893 BIO_free(in_2);
3896 dprint((9, "sign_outgoing_message returns %d", result));
3897 return result;
3901 SMIME_STUFF_S *
3902 new_smime_struct(void)
3904 SMIME_STUFF_S *ret = NULL;
3906 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
3907 memset((void *) ret, 0, sizeof(*ret));
3908 ret->publictype = Nada;
3910 return ret;
3914 static void
3915 free_smime_struct(SMIME_STUFF_S **smime)
3917 if(smime && *smime){
3918 if((*smime)->passphrase_emailaddr){
3919 int i;
3920 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
3921 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
3922 fs_give((void **) (*smime)->passphrase_emailaddr);
3925 if((*smime)->publicpath)
3926 fs_give((void **) &(*smime)->publicpath);
3928 if((*smime)->publiccertlist)
3929 free_certlist(&(*smime)->publiccertlist);
3931 if((*smime)->backuppubliccertlist)
3932 free_certlist(&(*smime)->backuppubliccertlist);
3934 if((*smime)->cacertlist)
3935 free_certlist(&(*smime)->cacertlist);
3937 if((*smime)->backupcacertlist)
3938 free_certlist(&(*smime)->backupcacertlist);
3940 if((*smime)->privatecertlist)
3941 free_certlist(&(*smime)->privatecertlist);
3943 if((*smime)->backupprivatecertlist)
3944 free_certlist(&(*smime)->backupprivatecertlist);
3946 if((*smime)->publiccontent)
3947 fs_give((void **) &(*smime)->publiccontent);
3949 if((*smime)->privatepath)
3950 fs_give((void **) &(*smime)->privatepath);
3952 if((*smime)->personal_certs){
3953 PERSONAL_CERT *pc;
3955 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
3956 free_personal_certs(&pc);
3957 (*smime)->personal_certs = NULL;
3960 if((*smime)->privatecontent)
3961 fs_give((void **) &(*smime)->privatecontent);
3963 if((*smime)->capath)
3964 fs_give((void **) &(*smime)->capath);
3966 if((*smime)->cacontent)
3967 fs_give((void **) &(*smime)->cacontent);
3969 fs_give((void **) smime);
3973 #endif /* SMIME */