* Several changes to the compilation of Alpine in Windows to use
[alpine.git] / pith / smime.c
blobf8e5829dc187e760cf775d66639a4322dd1df2be
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-2018 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 straigth 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), 4);
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), 4);
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 coulr 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), 4);
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), 4);
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 X509_STORE_free(store);
1208 store = NULL;
1211 setup_certs_backup_by_type(ctype);
1214 void
1215 smime_reinit(void)
1217 smime_deinit();
1218 smime_init();
1221 /* Installed as an atexit() handler to save the random data */
1222 void
1223 smime_deinit(void)
1225 dprint((9, "smime_deinit()"));
1226 app_RAND_write_file(NULL);
1227 if (s_cert_store != NULL) X509_STORE_free(s_cert_store);
1228 #ifdef ERR_free_strings
1229 ERR_free_strings();
1230 #endif /* ERR_free_strings */
1231 #ifdef EVP_cleanup
1232 EVP_cleanup();
1233 #endif /* EVP_cleanup */
1234 free_smime_struct(&ps_global->smime);
1237 /* we renew the store when it has changed */
1238 void
1239 renew_store(void)
1241 if(ps_global->smime->inited){
1242 if(s_cert_store != NULL)
1243 X509_STORE_free(s_cert_store);
1244 s_cert_store = get_ca_store();
1248 /* Initialise openssl stuff if needed */
1249 void
1250 smime_init(void)
1252 if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
1254 dprint((9, "smime_init()"));
1255 if(!ps_global->smime)
1256 ps_global->smime = new_smime_struct();
1258 setup_storage_locations();
1260 s_cert_store = get_ca_store();
1261 setup_certs_backup_by_type(CACert);
1263 #ifdef OPENSSL_1_1_0
1264 OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS|OPENSSL_INIT_ADD_ALL_DIGESTS|OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
1265 #else
1266 OpenSSL_add_all_algorithms();
1267 ERR_load_crypto_strings();
1268 #endif /* OPENSSL_1_1_0 */
1270 app_RAND_load_file(NULL);
1271 openssl_extra_randomness();
1272 ps_global->smime->inited = 1;
1275 ERR_clear_error();
1279 /* validate a certificate. Return value : 0 for no error, -1 for error.
1280 * In the latter case, set the openssl smime error in *error.
1283 smime_validate_cert(X509 *cert, long *error)
1285 X509_STORE_CTX *csc;
1287 ERR_clear_error();
1288 *error = 0;
1289 if((s_cert_store != NULL) && (csc = X509_STORE_CTX_new()) != NULL){
1290 X509_STORE_set_flags(s_cert_store, 0);
1291 if(X509_STORE_CTX_init(csc,s_cert_store,cert,NULL)
1292 && X509_verify_cert(csc) <= 0)
1293 *error = X509_STORE_CTX_get_error(csc);
1294 X509_STORE_CTX_free(csc);
1296 return *error ? -1 : 0;
1299 PERSONAL_CERT *
1300 get_personal_certs(char *path)
1302 PERSONAL_CERT *result = NULL;
1303 char buf2[MAXPATH], *fname;
1304 X509 *cert;
1305 size_t ll;
1306 #ifndef _WINDOWS
1307 struct dirent *d;
1308 DIR *dirp;
1309 #else /* _WINDOWS */
1310 struct _finddata_t dbuf;
1311 char buf[_MAX_PATH + 4];
1312 long findrv;
1313 #endif /* _WINDOWS */
1315 ps_global->smime->privatepath = cpystr(path);
1317 #ifndef _WINDOWS
1318 dirp = opendir(path);
1319 if(dirp){
1320 while((d=readdir(dirp)) != NULL){
1321 fname = d->d_name;
1322 #else /* _WINDOWS */
1323 snprintf(buf, sizeof(buf), "%s%s*.*", path, (path[strlen(path)-1] == '\\') ? "" : "\\");
1324 buf[sizeof(buf)-1] = '\0';
1325 if((findrv = _findfirst(buf, &dbuf)) < 0)
1326 return(NULL);
1328 do {
1329 fname = fname_to_utf8(dbuf.name);
1330 #endif
1331 if((ll=strlen(fname)) && ll > 4 && !strcmp(fname+ll-4, ".key")){
1333 /* copy file name to temp buffer */
1334 strncpy(buf2, fname, sizeof(buf2)-1);
1335 buf2[sizeof(buf2)-1] = '\0';
1336 /* chop off ".key" trailier */
1337 buf2[strlen(buf2)-4] = '\0';
1338 /* Look for certificate */
1339 cert = get_cert_for(buf2, Public, 1);
1341 if(cert){
1342 PERSONAL_CERT *pc;
1344 /* create a new PERSONAL_CERT, fill it in */
1346 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1347 pc->cert = cert;
1348 pc->name = cpystr(buf2);
1349 strncat(buf2, EXTCERT(Public), 4);
1350 pc->cname = cpystr(buf2);
1352 /* Try to load the key with an empty password */
1353 pc->key = load_key(pc, "", SM_NORMALCERT);
1355 pc->next = result;
1356 result = pc;
1359 #ifndef _WINDOWS
1361 closedir(dirp);
1363 #else /* _WINDOWS */
1364 } while(_findnext(findrv, &dbuf) == 0);
1365 _findclose(findrv);
1366 #endif /* !_WINDOWS */
1367 return result;
1371 void
1372 setup_privatekey_storage(void)
1374 char path[MAXPATH+1], *contents;
1375 int privatekeycontainer = 0;
1377 /* private keys in a container */
1378 if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
1380 privatekeycontainer = 1;
1381 contents = NULL;
1382 path[0] = '\0';
1383 if(!smime_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
1384 privatekeycontainer = 0;
1386 if(privatekeycontainer && !IS_REMOTE(path)
1387 && ps_global->VAR_OPER_DIR
1388 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1389 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1390 /* TRANSLATORS: First arg is the directory name, second is
1391 the file user wants to read but can't. */
1392 _("Can't read file outside %s: %s"),
1393 ps_global->VAR_OPER_DIR, path);
1394 privatekeycontainer = 0;
1397 if(privatekeycontainer
1398 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1399 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1401 !(contents = read_file(path, READ_FROM_LOCALE)))
1402 privatekeycontainer = 0;
1405 if(privatekeycontainer && path[0]){
1406 ps_global->smime->privatetype = Container;
1407 ps_global->smime->privatepath = cpystr(path);
1409 if(contents){
1410 ps_global->smime->privatecontent = contents;
1411 ps_global->smime->personal_certs = mem_to_personal_certs(contents);
1416 /* private keys in a directory of files */
1417 if(!privatekeycontainer){
1418 ps_global->smime->privatetype = Directory;
1420 path[0] = '\0';
1421 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1422 && !IS_REMOTE(path)))
1423 ps_global->smime->privatetype = Nada;
1424 else if(can_access(path, ACCESS_EXISTS)){
1425 if(our_mkpath(path, 0700)){
1426 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1427 ps_global->smime->privatetype = Nada;
1431 if(ps_global->smime->privatetype == Directory)
1432 ps_global->smime->personal_certs = get_personal_certs(path);
1434 setup_certs_backup_by_type(Private);
1437 static void
1438 setup_storage_locations(void)
1440 int publiccertcontainer = 0, cacertcontainer = 0;
1441 char path[MAXPATH+1], *contents;
1443 if(!ps_global->smime)
1444 return;
1446 #ifdef APPLEKEYCHAIN
1447 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1448 ps_global->smime->publictype = Keychain;
1450 else{
1451 #endif /* APPLEKEYCHAIN */
1452 /* Public certificates in a container */
1453 if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
1455 publiccertcontainer = 1;
1456 contents = NULL;
1457 path[0] = '\0';
1458 if(!smime_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
1459 publiccertcontainer = 0;
1461 if(publiccertcontainer && !IS_REMOTE(path)
1462 && ps_global->VAR_OPER_DIR
1463 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1464 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1465 /* TRANSLATORS: First arg is the directory name, second is
1466 the file user wants to read but can't. */
1467 _("Can't read file outside %s: %s"),
1468 ps_global->VAR_OPER_DIR, path);
1469 publiccertcontainer = 0;
1472 if(publiccertcontainer
1473 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1474 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1476 !(contents = read_file(path, READ_FROM_LOCALE)))
1477 publiccertcontainer = 0;
1480 if(publiccertcontainer && path[0]){
1481 ps_global->smime->publictype = Container;
1482 ps_global->smime->publicpath = cpystr(path);
1484 if(contents){
1485 ps_global->smime->publiccontent = contents;
1486 ps_global->smime->publiccertlist = mem_to_certlist(contents, Public);
1491 /* Public certificates in a directory of files */
1492 if(!publiccertcontainer){
1493 ps_global->smime->publictype = Directory;
1495 path[0] = '\0';
1496 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1497 && !IS_REMOTE(path)))
1498 ps_global->smime->publictype = Nada;
1499 else if(can_access(path, ACCESS_EXISTS)){
1500 if(our_mkpath(path, 0700)){
1501 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1502 ps_global->smime->publictype = Nada;
1506 if(ps_global->smime->publictype == Directory)
1507 ps_global->smime->publicpath = cpystr(path);
1510 #ifdef APPLEKEYCHAIN
1512 #endif /* APPLEKEYCHAIN */
1514 setup_privatekey_storage();
1516 /* extra cacerts in a container */
1517 if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
1519 cacertcontainer = 1;
1520 contents = NULL;
1521 path[0] = '\0';
1522 if(!smime_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
1523 cacertcontainer = 0;
1525 if(cacertcontainer && !IS_REMOTE(path)
1526 && ps_global->VAR_OPER_DIR
1527 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1528 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1529 /* TRANSLATORS: First arg is the directory name, second is
1530 the file user wants to read but can't. */
1531 _("Can't read file outside %s: %s"),
1532 ps_global->VAR_OPER_DIR, path);
1533 cacertcontainer = 0;
1536 if(cacertcontainer
1537 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1538 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1540 !(contents = read_file(path, READ_FROM_LOCALE)))
1541 cacertcontainer = 0;
1544 if(cacertcontainer && path[0]){
1545 ps_global->smime->catype = Container;
1546 ps_global->smime->capath = cpystr(path);
1547 ps_global->smime->cacontent = contents;
1548 if(contents)
1549 ps_global->smime->cacertlist = mem_to_certlist(contents, CACert);
1553 if(!cacertcontainer){
1554 ps_global->smime->catype = Directory;
1556 path[0] = '\0';
1557 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1558 && !IS_REMOTE(path)))
1559 ps_global->smime->catype = Nada;
1560 else if(can_access(path, ACCESS_EXISTS)){
1561 if(our_mkpath(path, 0700)){
1562 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1563 ps_global->smime->catype = Nada;
1567 if(ps_global->smime->catype == Directory)
1568 ps_global->smime->capath = cpystr(path);
1574 copy_publiccert_dir_to_container(void)
1576 return(copy_dir_to_container(Public, NULL));
1581 copy_publiccert_container_to_dir(void)
1583 return(copy_container_to_dir(Public));
1588 copy_privatecert_dir_to_container(void)
1590 return(copy_dir_to_container(Private, NULL));
1595 copy_privatecert_container_to_dir(void)
1597 return(copy_container_to_dir(Private));
1602 copy_cacert_dir_to_container(void)
1604 return(copy_dir_to_container(CACert, NULL));
1609 copy_cacert_container_to_dir(void)
1611 return(copy_container_to_dir(CACert));
1614 /* Add the contents of a file to a container. Do not check the content
1615 * of the file, just add it using the format for that container. The
1616 * caller must check the format, so that there is no data corruption
1617 * in the future.
1618 * return value: 0 - success,
1619 * != 0 - failure.
1622 add_file_to_container(WhichCerts ctype, char *fpath, char *altname)
1624 char *sep = (ctype == Public || ctype == Private)
1625 ? EMAILADDRLEADER : CACERTSTORELEADER;
1626 char *content = ctype == Public ? ps_global->smime->publiccontent
1627 : (ctype == Private ? ps_global->smime->privatecontent
1628 : ps_global->smime->cacontent);
1629 char *name;
1630 char *s;
1631 unsigned char c;
1632 struct stat sbuf;
1633 STORE_S *in = NULL;
1634 int rv = -1; /* assume error */
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 fs_resize((void **)&content, strlen(content) + strlen(sep) + strlen(name) + sbuf.st_size + 2*strlen(NEWLINE) + 1); /* 1 = \0*/
1652 s = content;
1653 content += strlen(content);
1655 else{
1656 s = content = fs_get(strlen(sep) + strlen(name) + sbuf.st_size + strlen(NEWLINE) + 1); /* 1 = \0 */
1657 *content = '\0';
1659 strncat(content, sep, strlen(sep));
1660 strncat(content, name, strlen(name));
1661 content += strlen(content);
1662 #ifdef _WINDOWS
1663 *content++ = '\r';
1664 #endif /* _WINDOWS */
1665 *content++ = '\n';
1667 while(so_readc(&c, in))
1668 *content++ = (char) c;
1669 *content = '\0';
1671 switch(ctype){
1672 case Private: ps_global->smime->privatecontent = s; break;
1673 case Public : ps_global->smime->publiccontent = s; break;
1674 case CACert : ps_global->smime->cacontent = s; break;
1675 default : break;
1678 rv = copy_dir_to_container(ctype, s);
1680 endadd:
1681 if(in) so_give(&in);
1683 return rv;
1688 * returns 0 on success, -1 on failure
1689 * contents is an argument which tells this function to write the value
1690 * of this variable instead of reading the contents of the directory.
1691 * If the var contents is not null use its value as the value of the
1692 * container.
1695 copy_dir_to_container(WhichCerts which, char *contents)
1697 int ret = 0, container = 0;
1698 BIO *bio_out = NULL, *bio_in = NULL;
1699 char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
1700 char *tempfile = NULL, fpath[MAXPATH+1], *fname;
1701 size_t ll;
1702 #ifndef _WINDOWS
1703 DIR *dirp;
1704 struct dirent *d;
1705 #else /* _WINDOWS */
1706 struct _finddata_t dbuf;
1707 char buf[_MAX_PATH + 4];
1708 long findrv;
1709 #endif /* _WINDOWS */
1710 REMDATA_S *rd = NULL;
1711 char *configdir = NULL;
1712 char *configpath = NULL;
1713 char *configcontainer = NULL;
1714 char *filesuffix = NULL;
1715 char *ret_dir = NULL;
1717 dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1718 smime_init();
1720 srcpath[0] = '\0';
1721 dstpath[0] = '\0';
1722 file[0] = '\0';
1723 emailaddr[0] = '\0';
1725 if(which == Public){
1726 configdir = ps_global->VAR_PUBLICCERT_DIR;
1727 configpath = ps_global->smime->publicpath;
1728 configcontainer = cpystr(DF_PUBLIC_CONTAINER);
1729 filesuffix = ".crt";
1731 else if(which == Private){
1732 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1733 configpath = ps_global->smime->privatepath;
1734 configcontainer = cpystr(DF_PRIVATE_CONTAINER);
1735 filesuffix = ".key";
1737 else if(which == CACert){
1738 configdir = ps_global->VAR_CACERT_DIR;
1739 configpath = ps_global->smime->capath;
1740 configcontainer = cpystr(DF_CA_CONTAINER);
1741 filesuffix = ".crt";
1743 container = SMHOLDERTYPE(which) == Container;
1745 if(!(configdir && configdir[0])){
1746 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1747 return -1;
1750 if(!(configpath && configpath[0])){
1751 #ifdef APPLEKEYCHAIN
1752 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1753 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1754 return -1;
1756 #endif /* APPLEKEYCHAIN */
1757 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1758 return -1;
1761 if(!(filesuffix && strlen(filesuffix) == 4)){
1762 return -1;
1767 * If there is a legit directory to read from set up the
1768 * container file to write to.
1770 if(smime_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
1772 if(IS_REMOTE(configpath)){
1773 rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
1774 NULL, "Error: ",
1775 _("Can't access remote smime configuration."));
1776 if(!rd)
1777 return -1;
1779 (void) rd_read_metadata(rd);
1781 if(rd->access == MaybeRorW){
1782 if(rd->read_status == 'R')
1783 rd->access = ReadOnly;
1784 else
1785 rd->access = ReadWrite;
1788 if(rd->access != NoExists){
1790 rd_check_remvalid(rd, 1L);
1793 * If the cached info says it is readonly but
1794 * it looks like it's been fixed now, change it to readwrite.
1796 if(rd->read_status == 'R'){
1797 rd_check_readonly_access(rd);
1798 if(rd->read_status == 'W'){
1799 rd->access = ReadWrite;
1800 rd->flags |= REM_OUTOFDATE;
1802 else
1803 rd->access = ReadOnly;
1807 if(rd->flags & REM_OUTOFDATE){
1808 if(rd_update_local(rd) != 0){
1810 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1811 rd_close_remdata(&rd);
1812 return -1;
1815 else
1816 rd_open_remote(rd);
1818 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1819 rd_close_remdata(&rd);
1820 return -1;
1823 rd->flags |= DO_REMTRIM;
1825 strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
1826 dstpath[sizeof(dstpath)-1] = '\0';
1828 else{
1829 strncpy(dstpath, configpath, sizeof(dstpath)-1);
1830 dstpath[sizeof(dstpath)-1] = '\0';
1834 * dstpath is either the local Container file or the local cache file
1835 * for the remote Container file.
1837 tempfile = tempfile_in_same_dir(dstpath, "az", &ret_dir);
1841 * If there is a legit directory to read from and a tempfile
1842 * to write to we continue.
1844 if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
1846 if(contents != NULL){
1847 if(BIO_puts(bio_out, contents) < 0)
1848 ret = -1;
1850 else {
1851 #ifndef _WINDOWS
1852 if((dirp = opendir(srcpath)) != NULL){
1854 while((d=readdir(dirp)) && !ret){
1855 fname = d->d_name;
1856 #else /* _WINDOWS */
1857 snprintf(buf, sizeof(buf), "%s%s*.*", srcpath, (srcpath[strlen(srcpath)-1] == '\\') ? "" : "\\");
1858 buf[sizeof(buf)-1] = '\0';
1859 if((findrv = _findfirst(buf, &dbuf)) < 0)
1860 return -1;
1863 fname = fname_to_utf8(dbuf.name);
1864 #endif /* ! _WINDOWS */
1865 if((ll=strlen(fname)) && ll > 4 && !strcmp(fname+ll-4, filesuffix)){
1867 /* copy file name to temp buffer */
1868 strncpy(emailaddr, fname, sizeof(emailaddr)-1);
1869 emailaddr[sizeof(emailaddr)-1] = '\0';
1870 /* chop off suffix trailier */
1871 emailaddr[strlen(emailaddr)-4] = 0;
1874 * This is the separator between the contents of
1875 * different files.
1877 if(which == CACert){
1878 if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
1879 && (BIO_puts(bio_out, emailaddr) > 0)
1880 && (BIO_puts(bio_out, NEWLINE) > 0)))
1881 ret = -1;
1883 else{
1884 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1885 && (BIO_puts(bio_out, emailaddr) > 0)
1886 && (BIO_puts(bio_out, NEWLINE) > 0)))
1887 ret = -1;
1890 /* read then write contents of file */
1891 build_path(file, srcpath, fname, sizeof(file));
1892 if(!(bio_in = BIO_new_file(file, "r")))
1893 ret = -1;
1895 if(!ret){
1896 int good_stuff = 0;
1898 while(BIO_gets(bio_in, line, sizeof(line)) > 0){
1899 if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
1900 good_stuff = 1;
1902 if(good_stuff)
1903 BIO_puts(bio_out, line);
1905 if(strncmp("-----END", line, strlen("-----END")) == 0)
1906 good_stuff = 0;
1910 BIO_free(bio_in);
1912 #ifndef _WINDOWS
1914 closedir(dirp);
1916 #else /* _WINDOWS */
1917 } while (_findnext(findrv, &dbuf) == 0);
1918 _findclose(findrv);
1919 #endif /* ! _WINDOWS */
1922 BIO_free(bio_out);
1924 if(!ret){
1925 if(container && configpath && *configpath){
1926 strncpy(fpath, configpath, sizeof(fpath));
1927 fpath[sizeof(fpath) - 1] = '\0';
1929 else if(ret_dir){
1930 if(strlen(dstpath) + strlen(configcontainer) - strlen(ret_dir) + 1 < sizeof(dstpath))
1931 snprintf(fpath, sizeof(fpath), "%s%c%s",
1932 dstpath, tempfile[strlen(ret_dir)], configcontainer);
1933 else
1934 ret = -1;
1936 else ret = -1;
1938 if(!ret){
1939 if(!IS_REMOTE(configpath)){
1940 if(rename_file(tempfile, fpath) < 0){
1941 q_status_message2(SM_ORDER, 3, 3,
1942 _("Can't rename %s to %s"), tempfile, fpath);
1943 ret = -1;
1944 } else q_status_message1(SM_ORDER, 3, 3,
1945 _("saved container to %s"), fpath);
1947 else { /* if the container is remote, copy it */
1948 int e;
1949 char datebuf[200];
1951 if(rd != NULL && rename_file(tempfile, rd->lf) < 0){
1952 q_status_message2(SM_ORDER, 3, 3,
1953 _("Can't rename %s to %s"), tempfile, rd->lf);
1954 ret = -1;
1957 datebuf[0] = '\0';
1959 if((e = rd_update_remote(rd, datebuf)) != 0){
1960 if(e == -1){
1961 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1962 _("Error opening temporary smime file %s: %s"),
1963 rd->lf, error_description(errno));
1964 dprint((1,
1965 "write_remote_smime: error opening temp file %s\n",
1966 rd->lf ? rd->lf : "?"));
1968 else{
1969 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1970 _("Error copying to %s: %s"),
1971 rd->rn, error_description(errno));
1972 dprint((1,
1973 "write_remote_smime: error copying from %s to %s\n",
1974 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1977 q_status_message(SM_ORDER | SM_DING, 5, 5,
1978 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1980 else{
1981 rd_update_metadata(rd, datebuf);
1982 rd->read_status = 'W';
1985 rd_close_remdata(&rd);
1991 if(tempfile)
1992 fs_give((void **) &tempfile);
1994 if(ret_dir)
1995 fs_give((void **) &ret_dir);
1997 if(configcontainer)
1998 fs_give((void **) &configcontainer);
2000 return ret;
2005 * returns 0 on success, -1 on failure
2008 copy_container_to_dir(WhichCerts which)
2010 char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
2011 char iobuf[4096];
2012 char *contents = NULL;
2013 char *leader = NULL;
2014 char *filesuffix = NULL;
2015 char *configdir = NULL;
2016 char *configpath = NULL;
2017 char *tempfile = NULL;
2018 char *p, *q, *line, *name, *certtext, *save_p;
2019 int len;
2020 BIO *in, *out;
2022 dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
2023 smime_init();
2025 path[0] = '\0';
2027 if(which == Public){
2028 leader = EMAILADDRLEADER;
2029 contents = ps_global->smime->publiccontent;
2030 configdir = ps_global->VAR_PUBLICCERT_DIR;
2031 configpath = ps_global->smime->publicpath;
2032 filesuffix = ".crt";
2033 if(!(configpath && configpath[0])){
2034 #ifdef APPLEKEYCHAIN
2035 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
2036 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
2037 return -1;
2039 #endif /* APPLEKEYCHAIN */
2040 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2041 return -1;
2044 fs_give((void **) &ps_global->smime->publicpath);
2046 path[0] = '\0';
2047 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
2048 && !IS_REMOTE(path))){
2049 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2050 return -1;
2053 if(can_access(path, ACCESS_EXISTS)){
2054 if(our_mkpath(path, 0700)){
2055 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2056 return -1;
2060 ps_global->smime->publicpath = cpystr(path);
2061 configpath = ps_global->smime->publicpath;
2063 else if(which == Private){
2064 leader = EMAILADDRLEADER;
2065 contents = ps_global->smime->privatecontent;
2066 configdir = ps_global->VAR_PRIVATEKEY_DIR;
2067 configpath = ps_global->smime->privatepath;
2068 filesuffix = ".key";
2069 if(!(configpath && configpath[0])){
2070 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2071 return -1;
2074 fs_give((void **) &ps_global->smime->privatepath);
2076 path[0] = '\0';
2077 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
2078 && !IS_REMOTE(path))){
2079 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2080 return -1;
2083 if(can_access(path, ACCESS_EXISTS)){
2084 if(our_mkpath(path, 0700)){
2085 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2086 return -1;
2090 ps_global->smime->privatepath = cpystr(path);
2091 configpath = ps_global->smime->privatepath;
2093 else if(which == CACert){
2094 leader = CACERTSTORELEADER;
2095 contents = ps_global->smime->cacontent;
2096 configdir = ps_global->VAR_CACERT_DIR;
2097 configpath = ps_global->smime->capath;
2098 filesuffix = ".crt";
2099 if(!(configpath && configpath[0])){
2100 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2101 return -1;
2104 fs_give((void **) &ps_global->smime->capath);
2106 path[0] = '\0';
2107 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
2108 && !IS_REMOTE(path))){
2109 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2110 return -1;
2113 if(can_access(path, ACCESS_EXISTS)){
2114 if(our_mkpath(path, 0700)){
2115 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2116 return -1;
2120 ps_global->smime->capath = cpystr(path);
2121 configpath = ps_global->smime->capath;
2124 if(!(configdir && configdir[0])){
2125 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
2126 return -1;
2129 if(!(configpath && configpath[0])){
2130 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2131 return -1;
2134 if(!(filesuffix && strlen(filesuffix) == 4)){
2135 return -1;
2139 if(contents && *contents){
2140 for(p = contents; *p != '\0';){
2141 line = p;
2143 while(*p && *p != '\n')
2144 p++;
2146 save_p = NULL;
2147 if(*p == '\n'){
2148 save_p = p;
2149 *p++ = '\0';
2152 if(strncmp(leader, line, strlen(leader)) == 0){
2153 name = line + strlen(leader);
2154 certtext = p;
2155 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
2156 if((q = strstr(certtext, leader)) != NULL){
2157 p = q;
2159 else{ /* end of file */
2160 q = certtext + strlen(certtext);
2161 p = q;
2164 strncpy(buf, name, sizeof(buf)-5);
2165 buf[sizeof(buf)-5] = '\0';
2166 strncat(buf, filesuffix, 5);
2167 build_path(file, configpath, buf, sizeof(file));
2169 in = BIO_new_mem_buf(certtext, q-certtext);
2170 if(in){
2171 tempfile = tempfile_in_same_dir(file, "az", NULL);
2172 out = NULL;
2173 if(tempfile)
2174 out = BIO_new_file(tempfile, "w");
2176 if(out){
2177 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
2178 BIO_write(out, iobuf, len);
2180 BIO_free(out);
2182 if(rename_file(tempfile, file) < 0){
2183 q_status_message2(SM_ORDER, 3, 3,
2184 _("Can't rename %s to %s"),
2185 tempfile, file);
2186 return -1;
2189 fs_give((void **) &tempfile);
2192 BIO_free(in);
2197 if(save_p)
2198 *save_p = '\n';
2202 return 0;
2206 #ifdef APPLEKEYCHAIN
2209 copy_publiccert_container_to_keychain(void)
2211 /* NOT IMPLEMNTED */
2212 return -1;
2216 copy_publiccert_keychain_to_container(void)
2218 /* NOT IMPLEMNTED */
2219 return -1;
2222 #endif /* APPLEKEYCHAIN */
2226 * Get a pointer to a string describing the most recent OpenSSL error.
2227 * It's statically allocated, so don't change or attempt to free it.
2229 static const char *
2230 openssl_error_string(void)
2232 char *errs;
2233 const char *data = NULL;
2234 long errn;
2236 errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2237 errs = (char*) ERR_reason_error_string(errn);
2239 if(errs)
2240 return errs;
2241 else if(data)
2242 return data;
2244 return "unknown error";
2248 /* Return true if the body looks like a PKCS7 object */
2250 is_pkcs7_body(BODY *body)
2252 int result;
2254 result = body->type==TYPEAPPLICATION &&
2255 body->subtype &&
2256 (strucmp(body->subtype,"pkcs7-mime")==0 ||
2257 strucmp(body->subtype,"x-pkcs7-mime")==0 ||
2258 strucmp(body->subtype,"pkcs7-signature")==0 ||
2259 strucmp(body->subtype,"x-pkcs7-signature")==0);
2261 return result;
2266 * Recursively stash a pointer to the decrypted data in our
2267 * manufactured body.
2268 * parameters: type: call of type 1, save the base and header for multipart messages
2269 call of type 0, do not save the base and header for multipart messages
2271 static void
2272 create_local_cache(char *h, char *base, BODY *b, int type)
2274 if(b->type==TYPEMULTIPART){
2275 PART *p;
2277 if(type == 1){
2278 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2279 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2280 } else if(type == 0){
2282 * We don't really want to copy the real body contents. It shouldn't be
2283 * used, and in the case of a message with attachments, we'll be
2284 * duplicating the files multiple times.
2286 cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
2288 for(p=b->nested.part; p; p=p->next)
2289 create_local_cache(h, base, (BODY *) p, type);
2292 else{
2293 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2294 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2299 static long
2300 rfc822_output_func(void *b, char *string)
2302 BIO *bio = (BIO *) b;
2304 return(string ? *string ? (BIO_puts(bio, string) > 0 ? 1L : 0L)
2305 : (BIO_puts(bio, string) >= 0 ? 1L : 0L)
2306 : 0L);
2311 * Attempt to load the private key for the given PERSONAL_CERT.
2312 * This sets the appropriate passphrase globals in order to
2313 * interact with the user correctly.
2315 static int
2316 load_private_key(PERSONAL_CERT *pcert)
2318 if(!pcert->key){
2320 /* Try empty password by default */
2321 char *password = "";
2323 if(ps_global->smime
2324 && (ps_global->smime->need_passphrase
2325 || ps_global->smime->entered_passphrase)){
2326 /* We've already been in here and discovered we need a different password */
2328 if(ps_global->smime->entered_passphrase)
2329 password = (char *) ps_global->smime->passphrase; /* already entered */
2330 else
2331 return 0;
2334 ERR_clear_error();
2336 if(!(pcert->key = load_key(pcert, password, SM_NORMALCERT))){
2337 long err = ERR_get_error();
2339 /* Couldn't load key... */
2341 if(ps_global->smime && ps_global->smime->entered_passphrase){
2343 /* The user got the password wrong maybe? */
2345 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
2346 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
2347 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
2348 else
2349 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2351 /* This passphrase is no good; forget it */
2352 ps_global->smime->entered_passphrase = 0;
2355 if(ps_global->smime){
2356 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2357 ps_global->smime->need_passphrase = 1;
2358 if(ps_global->smime->passphrase_emailaddr){
2359 int i;
2360 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
2361 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
2362 fs_give((void **) ps_global->smime->passphrase_emailaddr);
2365 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
2368 return 0;
2370 else{
2371 /* This key will be cached, so we won't be called again */
2372 if(ps_global->smime){
2373 ps_global->smime->entered_passphrase = 0;
2374 ps_global->smime->need_passphrase = 0;
2378 return 1;
2381 return 0;
2385 static void
2386 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename, char *smime_type)
2388 b->type = TYPEAPPLICATION;
2389 b->subtype = cpystr(type);
2390 b->encoding = ENCBINARY;
2391 b->description = cpystr(description);
2393 b->disposition.type = cpystr("attachment");
2394 set_parameter(&b->disposition.parameter, "filename", filename);
2396 set_parameter(&b->parameter, "name", filename);
2397 if(smime_type && *smime_type)
2398 set_parameter(&b->parameter, "smime-type", smime_type);
2403 * Look for a personal certificate matching the
2404 * given address
2406 PERSONAL_CERT *
2407 match_personal_cert_to_email(ADDRESS *a)
2409 PERSONAL_CERT *pcert = NULL;
2410 char buf[MAXPATH];
2411 char **email;
2412 int i, done;
2414 if(!a || !a->mailbox || !a->host)
2415 return NULL;
2417 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2419 if(ps_global->smime){
2420 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
2421 pcert;
2422 pcert=pcert->next){
2424 if(!pcert->cert)
2425 continue;
2427 email = get_x509_subject_email(pcert->cert);
2429 done = 0;
2430 if(email != NULL){
2431 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
2432 if(email[i] != NULL) done++;
2433 for(i = 0; email[i] != NULL; i++)
2434 fs_give((void **)&email[i]);
2435 fs_give((void **)email);
2438 if(done > 0)
2439 break;
2443 return pcert;
2448 * Look for a personal certificate matching the from
2449 * (or reply_to? in the given envelope)
2451 PERSONAL_CERT *
2452 match_personal_cert(ENVELOPE *env)
2454 PERSONAL_CERT *pcert;
2456 pcert = match_personal_cert_to_email(env->reply_to);
2457 if(!pcert)
2458 pcert = match_personal_cert_to_email(env->from);
2460 return pcert;
2465 * Flatten the given body into its MIME representation.
2466 * Return the result in a BIO.
2468 static BIO *
2469 body_to_bio(BODY *body)
2471 BIO *bio = NULL;
2472 int len;
2474 bio = BIO_new(BIO_s_mem());
2475 if(!bio)
2476 return NULL;
2478 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
2479 pine_write_body_header(body, rfc822_output_func, bio);
2480 pine_rfc822_output_body(body, rfc822_output_func, bio);
2483 * Now need to truncate by two characters since the above
2484 * appends CRLF.
2486 if((len=BIO_ctrl_pending(bio)) > 1){
2487 BUF_MEM *biobuf = NULL;
2489 BIO_get_mem_ptr(bio, &biobuf);
2490 if(biobuf){
2491 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
2495 return bio;
2499 static BIO *
2500 bio_from_store(STORE_S *store)
2502 BIO *ret = NULL;
2504 if(store && store->src == BioType && store->txt){
2505 ret = (BIO *) store->txt;
2508 return(ret);
2512 * Encrypt file; given a path (char *) fp, replace the file
2513 * by an encrypted version of it. If (char *) text is not null, then
2514 * replace the text of (char *) fp by the encrypted version of (char *) text.
2515 * certpath is the FULL path to the file containing the certificate used for
2516 * encryption.
2517 * return value: 0 - failed to encrypt; 1 - success!
2520 encrypt_file(char *fp, char *text, PERSONAL_CERT *pc)
2522 const EVP_CIPHER *cipher = NULL;
2523 STACK_OF(X509) *encerts = NULL;
2524 BIO *out = NULL;
2525 PKCS7 *p7 = NULL;
2526 int rv = 0;
2528 if(pc == NULL)
2529 return 0;
2531 cipher = EVP_aes_256_cbc();
2532 encerts = sk_X509_new_null();
2534 sk_X509_push(encerts, X509_dup(pc->cert));
2536 if(text){
2537 if((out = BIO_new(BIO_s_mem())) != NULL){
2538 (void) BIO_reset(out);
2539 BIO_puts(out, text);
2542 else if((out = BIO_new_file(fp, "rb")) != NULL)
2543 BIO_read_filename(out, fp);
2545 if((p7 = PKCS7_encrypt(encerts, out, cipher, 0)) != NULL){
2546 BIO_set_close(out, BIO_CLOSE);
2547 BIO_free(out);
2548 if((out = BIO_new_file(fp, "w")) != NULL){
2549 BIO_reset(out);
2550 rv = PEM_write_bio_PKCS7(out, p7);
2551 BIO_flush(out);
2555 if(out != NULL)
2556 BIO_free(out);
2557 PKCS7_free(p7);
2558 sk_X509_pop_free(encerts, X509_free);
2560 return rv;
2564 * Encrypt a message on the way out. Called from call_mailer in send.c
2565 * The body may be reallocated.
2568 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
2570 PKCS7 *p7 = NULL;
2571 BIO *in = NULL;
2572 BIO *out = NULL;
2573 const EVP_CIPHER *cipher = NULL;
2574 STACK_OF(X509) *encerts = NULL;
2575 STORE_S *outs = NULL;
2576 PINEFIELD *pf;
2577 ADDRESS *a;
2578 BODY *body = *bodyP;
2579 BODY *newBody = NULL;
2580 int result = 0;
2581 X509 *cert;
2582 char buf[MAXPATH];
2584 dprint((9, "encrypt_outgoing_message()"));
2585 smime_init();
2587 cipher = EVP_aes_256_cbc();
2589 encerts = sk_X509_new_null();
2591 /* Look for a certificate for each of the recipients */
2592 for(pf = header->local; pf && pf->name; pf = pf->next)
2593 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
2594 for(a=*pf->addr; a; a=a->next){
2595 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2597 if((cert = get_cert_for(buf, Public, 1)) != NULL){
2598 sk_X509_push(encerts,cert);
2599 }else{
2600 q_status_message2(SM_ORDER, 1, 1,
2601 _("Unable to find certificate for <%s@%s>"),
2602 a->mailbox, a->host);
2603 goto end;
2608 /* add the sender's certificate so that they can decrypt the message too */
2609 for(a=header->env->from; a ; a = a->next){
2610 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2612 if((cert = get_cert_for(buf, Public, 1)) != NULL
2613 && sk_X509_find(encerts, cert) == -1)
2614 sk_X509_push(encerts,cert);
2617 in = body_to_bio(body);
2619 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
2621 outs = so_get(BioType, NULL, EDIT_ACCESS);
2622 out = bio_from_store(outs);
2624 i2d_PKCS7_bio(out, p7);
2625 (void) BIO_flush(out);
2627 so_seek(outs, 0, SEEK_SET);
2629 newBody = mail_newbody();
2631 newBody->type = TYPEAPPLICATION;
2632 newBody->subtype = cpystr("pkcs7-mime");
2633 newBody->encoding = ENCBINARY;
2635 newBody->disposition.type = cpystr("attachment");
2636 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
2638 newBody->description = cpystr("S/MIME Encrypted Message");
2639 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
2640 set_parameter(&newBody->parameter, "name", "smime.p7m");
2642 newBody->contents.text.data = (unsigned char *) outs;
2644 *bodyP = newBody;
2646 result = 1;
2648 end:
2650 BIO_free(in);
2651 PKCS7_free(p7);
2652 sk_X509_pop_free(encerts, X509_free);
2654 dprint((9, "encrypt_outgoing_message returns %d", result));
2655 return result;
2660 Get (and decode) the body of the given section of msg
2662 static STORE_S*
2663 get_part_contents(long msgno, const char *section)
2665 long len;
2666 gf_io_t pc;
2667 STORE_S *store = NULL;
2668 char *err;
2670 store = so_get(CharStar, NULL, EDIT_ACCESS);
2671 if(store){
2672 gf_set_so_writec(&pc,store);
2674 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
2676 gf_clear_so_writec(store);
2678 so_seek(store, 0, SEEK_SET);
2680 if(err)
2681 so_give(&store);
2684 return store;
2688 static PKCS7 *
2689 get_pkcs7_from_part(long msgno,const char *section)
2691 STORE_S *store = NULL;
2692 PKCS7 *p7 = NULL;
2693 BIO *in = NULL;
2695 store = get_part_contents(msgno, section);
2697 if(store){
2698 if(store->src == CharStar){
2699 int len;
2702 * We're reaching inside the STORE_S structure. We should
2703 * probably have a way to get the length, instead.
2705 len = (int) (store->eod - store->dp);
2706 in = BIO_new_mem_buf(store->txt, len);
2708 else{ /* just copy it */
2709 unsigned char c;
2711 in = BIO_new(BIO_s_mem());
2712 (void) BIO_reset(in);
2714 so_seek(store, 0L, 0);
2715 while(so_readc(&c, store)){
2716 BIO_write(in, &c, 1);
2720 if(in){
2721 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2722 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
2723 /* error */
2726 BIO_free(in);
2729 so_give(&store);
2732 return p7;
2736 same_cert(X509 *x, X509 *cert)
2738 char bufcert[256], bufx[256];
2739 int rv = 0;
2741 get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert), ":");
2742 get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx), ":");
2743 if(strcmp(bufx, bufcert) == 0)
2744 rv = 1;
2746 return rv;
2750 /* extract and save certificates from a PKCS7 package.
2751 * Return value:
2752 * 0 - no errors. Either the certificate was in public/
2753 * or we could save it there.
2754 * < 0 - the certificate was not in public/ and the user
2755 * did not save it there.
2759 smime_extract_and_save_cert(PKCS7 *p7)
2761 STACK_OF(X509) *signers;
2762 X509 *x, *cert;
2763 char **email;
2764 int i, j, rv, already_saved;
2765 long error;
2767 /* any signers for this message? */
2768 if((signers = PKCS7_get0_signers(p7, NULL, 0)) == NULL)
2769 return -1;
2771 rv = 0;
2772 for(i = 0; i < sk_X509_num(signers); i++){
2773 if((x = sk_X509_value(signers,i)) == NULL)
2774 continue;
2776 if((email = get_x509_subject_email(x)) != NULL){
2777 for(j = 0; email[j] != NULL; j++){
2778 already_saved = 0;
2779 /* check if we have the certificate for this address */
2780 cert = get_cert_for(email[j], Public, 1);
2781 /* if we have one, check if it is the one packaged */
2782 if(cert != NULL){
2783 already_saved = same_cert(x, cert);
2784 X509_free(cert);
2787 /* if not saved, try to save it */
2788 if(already_saved == 0
2789 && (*pith_smime_confirm_save)(email[j]) == 1)
2790 save_cert_for(email[j], x, Public);
2792 /* check if it got saved */
2793 cert = get_cert_for(email[j], Public, 1);
2794 /* if saved, all is good */
2795 if(cert != NULL)
2796 X509_free(cert);
2797 else /* else, we do not have this certificate saved */
2798 rv += -1;
2800 fs_give((void **) &email[j]);
2802 fs_give((void **) email);
2805 sk_X509_free(signers);
2807 return rv;
2811 * Try to verify a signature.
2813 * p7 - the pkcs7 object to verify
2814 * in - the plain data to verify (NULL if not detached)
2815 * out - BIO to which to write the opaque data
2816 * silent - if non zero, do not print errors, only print success.
2818 static int
2819 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
2821 STACK_OF(X509) *otherCerts = NULL;
2822 CertList *cl;
2823 int result, flags;
2824 const char *data;
2825 long err;
2827 if(!s_cert_store){
2828 if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
2829 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2831 return -1;
2834 flags = F_ON(F_USE_CERT_STORE_ONLY, ps_global) ? PKCS7_NOINTERN : 0;
2836 if(ps_global->smime->publiccertlist == NULL){
2837 renew_cert_data(&ps_global->smime->publiccertlist, Public);
2838 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next){
2839 if(cl->x509_cert == NULL){
2840 char *s = strrchr(cl->name, '.');
2841 *s = '\0';
2842 cl->x509_cert = get_cert_for(cl->name, Public, 1);
2843 *s = '.';
2848 if(ps_global->smime->publiccertlist){
2849 otherCerts = sk_X509_new_null();
2850 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next)
2851 if(cl->x509_cert != NULL)
2852 sk_X509_push(otherCerts, X509_dup(cl->x509_cert));
2855 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, flags);
2857 if(result){
2858 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
2860 else{
2861 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2863 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
2866 * verification failed due to an error in verifying a certificate.
2867 * Just write the "out" BIO, and leave. Of course let the user
2868 * know about this. Make two more attempts to get the data out. The
2869 * last one should succeed. In any case, let the user know why it
2870 * failed.
2872 if(PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY) == 0)
2873 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY|PKCS7_NOSIGS);
2875 if (!silent) q_status_message1(SM_ORDER | SM_DING, 3, 3,
2876 _("Couldn't verify S/MIME signature: %s"), (char *) openssl_error_string());
2879 sk_X509_pop_free(otherCerts, X509_free);
2881 return result;
2884 /* Big comment, explaining the mess that exists out there, and how we deal
2885 with it, and also how we solve the problems that are created this way.
2887 When Alpine sends a message, it constructs that message, computes the
2888 signature, but then it forgets the message it signed and reconstructs it
2889 again. Since it signs a message containing a notice about "mime aware
2890 tools", but it does not send that we do not include that in the part
2891 that is signed, and that takes care of much of the problems.
2893 Another problem is what is received from the servers. All servers tested
2894 seem to transmit the message that was signed intact and Alpine can check
2895 the signature correctly. That is not a problem. The problem arises when
2896 the message includes attachments. In this case different servers send
2897 different things, so it will be up to us to figure out what is the text
2898 that was actually signed. Confused? here is the story:
2900 When a message containing and attachment is sent by Alpine, UW-IMAP,
2901 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2902 that was sent by Alpine, but GMX.com, Exchange, and probably other
2903 servers add a trailing \r\n in the message, so when validating the
2904 signature, these messages will not validate. There are several things
2905 that can be done.
2907 1. Add a trailing \r\n to any message that contains attachments, sign that
2908 and send that. In this way, all messages will validate with all
2909 servers.
2911 2. Compatibility mode: If a message has an attachment, contains a trailing
2912 \r\n and does not validate (sent by an earlier version of Alpine),
2913 remove the trailing \r\n and try to revalidate again.
2915 3. We do not add \r\n to validate a message that we sent, because that
2916 would only work in Alpine, and not in any other client. That would
2917 not be a good thing to do.
2919 PART II
2921 Now we have to deal with encrypted and signed messages. The problem is
2922 that c-client makes all its pointers point to "on disk" content, but
2923 since we decrypted the data earlier, we have to make sure of two things.
2924 One is that we saved that data (so we do not have to decrypt it again)
2925 and second that we can use it.
2927 In order to save the data we use create_local_cache, so that we do not
2928 have to redecrypt the message. Once this is saved, c-client functions will
2929 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2931 PART III
2933 When we are trying to verify messages with detached signatures, some
2934 imap servers send incorrect information in the mail_fetch_mime call. By
2935 incorrect I mean that this is not fetched directly from the message, but
2936 it is read from the message, processed, and then the processed part is
2937 sent to us, so this text might not agree with what is in the message,
2938 and so the validation of the signature might fail. However, the good
2939 news is that the message validates if saved to a local folder. This
2940 means that if normal validation does not work we can make it work by
2941 saving the message locally and validating that. This is implemented
2942 below, and causes delay in the display of the message. I am considering
2943 at this time not to do this automatically, but wait for the user to tell
2944 us to do it for them by means of a command available in the
2945 mail_view_screen. This might help in other situations, where a message
2946 is supposed to have an attachment, but it can not be seen in the
2947 processed text. Nevertheless, at this time, this is automatic, and is
2948 causing a delay in the processing of the message, but it is validating
2949 correctly all messages.
2951 PART IV
2953 When the user sends a message as encrypted and signed, this code used to
2954 encrypt first, and then sign the pkcs7 body, but it turns out that some
2955 other clients can not handle these messages. While we could argue that the
2956 other clients need to improve, we will support reading messages in both
2957 ways, and will send messages using this technique; that is, signed first,
2958 encrypted second. It seems that all tested clients support this way, so it
2959 should be safe to do so.
2962 typedef struct smime_filter_s {
2963 void (*filter)();
2964 } SMIME_FILTER_S;
2966 SMIME_FILTER_S sig_filter[] = {
2967 {smime_remove_trailing_crlf},
2968 {smime_remove_folding_space}
2971 #define TOTAL_FILTERS (sizeof(sig_filter)/sizeof(sig_filter[0]))
2972 #define TOTAL_SIGFLTR (1 << TOTAL_FILTERS) /* not good, keep filters to a low number */
2974 void
2975 smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen,
2976 char **bodytext, unsigned long *bodylen)
2978 if(*bodylen > 2 && !strncmp(*bodytext+*bodylen-2, "\r\n", 2))
2979 *bodylen -= 2;
2982 void
2983 smime_remove_folding_space(char **mimetext, unsigned long *mimelen,
2984 char **bodytext, unsigned long *bodylen)
2986 char *s = NULL, *t;
2987 unsigned long mlen = *mimelen;
2989 if(*mimetext){
2990 for (s = t = *mimetext; t - *mimetext < *mimelen; ){
2991 if(*t == '\r' && *(t+1) == '\n' && (*(t+2) == '\t' || *(t+2) == ' ')){
2992 *s++ = ' ';
2993 t += 3;
2994 mlen -= 2;
2996 else
2997 *s++ = *t++;
2999 *mimelen = mlen;
3004 smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag)
3006 int result, i, j, flag;
3007 char *mtext, *btext;
3008 unsigned long mlen, blen;
3009 BIO *in;
3011 mtext = mimelen ? fs_get(mimelen+1) : NULL;
3012 btext = fs_get(bodylen+1);
3013 result = 0;
3015 flag = 1; /* silence all failures */
3016 for(i = 1; result == 0 && i < TOTAL_SIGFLTR; i++){
3017 if((in = BIO_new(BIO_s_mem())) == NULL)
3018 return -1;
3020 (void) BIO_reset(in);
3022 if(i+1 == TOTAL_SIGFLTR)
3023 flag = nflag;
3025 if(mimelen)
3026 strncpy(mtext, mimetext, mlen = mimelen);
3027 strncpy(btext, bodytext, blen = bodylen);
3028 for(j = 0; j < TOTAL_FILTERS; j++)
3029 if((i >> j) & 1)
3030 (sig_filter[j].filter)(&mtext, &mlen, &btext, &blen);
3031 if(mtext != NULL)
3032 BIO_write(in, mtext, mlen);
3033 BIO_write(in, btext, blen);
3034 result = do_signature_verify(p7, in, NULL, flag);
3035 BIO_free(in);
3037 if(mtext) fs_give((void **)&mtext);
3038 if(btext) fs_give((void **)&btext);
3039 return result;
3043 * Given a multipart body of type multipart/signed, attempt to verify it.
3044 * Returns non-zero if the body was changed.
3046 static int
3047 do_detached_signature_verify(BODY *b, long msgno, char *section)
3049 PKCS7 *p7 = NULL;
3050 BIO *in = NULL;
3051 PART *p;
3052 int result, modified_the_body = 0;
3053 int flag; /* 1 silent, 0 not silent */
3054 unsigned long mimelen, bodylen;
3055 char newSec[100], *mimetext, *bodytext;
3056 char *what_we_did;
3057 SIZEDTEXT *st;
3059 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"));
3061 smime_init();
3063 /* if it was signed and then encrypted, use the decrypted text
3064 * to check the validity of the signature
3066 if(b->sparep){
3067 if(get_body_sparep_type(b->sparep) == SizedText){
3068 /* bodytext includes mimetext */
3069 st = (SIZEDTEXT *) get_body_sparep_data(b->sparep);
3070 bodytext = (char *) st->data;
3071 bodylen = st->size;
3072 mimetext = NULL;
3073 mimelen = 0L;
3076 else{
3077 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3078 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3079 if(mimetext)
3080 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3082 if(mimetext == NULL || bodytext == NULL)
3083 return modified_the_body;
3086 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3088 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
3089 || (in = BIO_new(BIO_s_mem())) == NULL)
3090 return modified_the_body;
3092 (void) BIO_reset(in);
3093 if(mimetext != NULL)
3094 BIO_write(in, mimetext, mimelen);
3095 BIO_write(in, bodytext, bodylen);
3097 smime_extract_and_save_cert(p7);
3099 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3100 flag = (mimelen == 0 || !IS_REMOTE(ps_global->mail_stream->mailbox))
3101 ? 0 : 1;
3102 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, flag);
3103 if(result < 0)
3104 return modified_the_body;
3105 if(result == 0
3106 && mimelen > 0 /* do not do this for encrypted messages */
3107 && IS_REMOTE(ps_global->mail_stream->mailbox)){
3108 char *fetch;
3109 unsigned long hlen, tlen;
3110 STORE_S *msg_so;
3112 BIO_free(in);
3113 if((in = BIO_new(BIO_s_mem())) != NULL
3114 && (fetch = mail_fetch_header(ps_global->mail_stream, msgno, NULL,
3115 NULL, &hlen, FT_PEEK)) != NULL
3116 && (msg_so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL
3117 && so_nputs(msg_so, fetch, (long) hlen)
3118 && (fetch = pine_mail_fetch_text(ps_global->mail_stream, msgno, NULL,
3119 &tlen, FT_PEEK)) != NULL
3120 && so_nputs(msg_so, fetch, tlen)){
3121 STRING bs;
3122 char *h = (char *) so_text(msg_so);
3123 char *bstart = strstr(h, "\r\n\r\n");
3124 ENVELOPE *env;
3125 BODY *body, *tmpB;
3127 bstart += 4;
3128 INIT(&bs, mail_string, bstart, tlen);
3129 rfc822_parse_msg_full(&env, &body, h, bstart-h-4, &bs, BADHOST, 0, 0);
3130 mail_free_envelope(&env);
3132 mail_free_body_part(&b->nested.part);
3133 tmpB = mail_body_section(body, (unsigned char *) section);
3134 if(MIME_MSG(tmpB->type, tmpB->subtype))
3135 b->nested.part = tmpB->nested.msg->body->nested.part;
3136 else
3137 b->nested.part = tmpB->nested.part;
3138 create_local_cache(bstart, bstart, &b->nested.part->body, 1);
3139 modified_the_body = 1;
3141 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3143 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3145 if(mimetext)
3146 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3148 if (mimetext == NULL || bodytext == NULL)
3149 return modified_the_body;
3151 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3153 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL)
3154 return modified_the_body;
3156 (void) BIO_reset(in);
3157 BIO_write(in, mimetext, mimelen);
3158 BIO_write(in, bodytext, bodylen);
3159 so_give(&msg_so);
3161 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3162 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, 0);
3163 if(result < 0)
3164 return modified_the_body;
3170 BIO_free(in);
3171 if(b->subtype)
3172 fs_give((void**) &b->subtype);
3174 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3175 b->encoding = ENC8BIT;
3177 if(b->description)
3178 fs_give ((void**) &b->description);
3180 what_we_did = result ? _("This message was cryptographically signed.") :
3181 _("This message was cryptographically signed but the signature could not be verified.");
3183 b->description = cpystr(what_we_did);
3185 b->sparep = create_body_sparep(P7Type, p7);
3187 p = b->nested.part;
3189 /* p is signed plaintext */
3190 if(p && p->next)
3191 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
3193 modified_the_body = 1;
3195 return modified_the_body;
3199 PERSONAL_CERT *
3200 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
3202 PERSONAL_CERT *x = NULL;
3204 if(ps_global->smime){
3205 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
3206 X509 *mine;
3208 mine = x->cert;
3210 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,X509_get_issuer_name(mine)) &&
3211 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,X509_get_serialNumber(mine))){
3212 break;
3217 return x;
3221 static PERSONAL_CERT *
3222 find_certificate_matching_pkcs7(PKCS7 *p7)
3224 int i;
3225 STACK_OF(PKCS7_RECIP_INFO) *recips;
3226 PERSONAL_CERT *x = NULL;
3228 recips = p7->d.enveloped->recipientinfo;
3230 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
3231 PKCS7_RECIP_INFO *ri;
3233 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
3235 if((x=find_certificate_matching_recip_info(ri))!=0){
3236 break;
3240 return x;
3243 /* decrypt an encrypted file.
3244 Args: fp - the path to the encrypted file.
3245 rv - a code that tells the caller what happened inside the function
3246 pcert - a personal certificate that was used to encrypt this file
3247 Returns the decoded text allocated in a char *, whose memory must be
3248 freed by caller
3251 char *
3252 decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc)
3254 PKCS7 *p7 = NULL;
3255 char *text, *tmp;
3256 BIO *in = NULL, *out = NULL;
3257 int i, j;
3258 long unsigned int len;
3259 void *ret;
3261 if(pc == NULL || (text = read_file(fp, 0)) == NULL || *text == '\0')
3262 return NULL;
3264 tmp = strchr(text + strlen("-----BEGIN PKCS7-----") + strlen(NEWLINE), '-');
3265 if(tmp != NULL) *tmp = '\0';
3266 tmp = text + strlen("-----BEGIN PKCS7-----") + strlen(NEWLINE);
3268 ret = rfc822_base64((unsigned char *)tmp, strlen(tmp), &len);
3270 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
3271 p7 = d2i_PKCS7_bio(in, NULL);
3272 BIO_free(in);
3275 if (text) fs_give((void **)&text);
3276 if (ret) fs_give((void **)&ret);
3278 if (rv) *rv = pc->key == NULL ? -1 : 1;
3280 out = BIO_new(BIO_s_mem());
3281 (void) BIO_reset(out);
3283 if(PKCS7_decrypt(p7, pc->key, pc->cert, out, 0) != 0){
3284 len = BIO_get_mem_data(out, &tmp);
3285 text = fs_get((len+1)*sizeof(char));
3286 strncpy(text, tmp, len);
3287 text[len] = '\0';
3288 BIO_free(out);
3289 } else
3290 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3291 (char *) openssl_error_string());
3292 PKCS7_free(p7);
3294 return text;
3298 * Try to decode (decrypt or verify a signature) a PKCS7 body
3299 * Returns non-zero if something was changed.
3301 static int
3302 do_decoding(BODY *b, long msgno, const char *section)
3304 int modified_the_body = 0;
3305 BIO *out = NULL;
3306 PKCS7 *p7 = NULL;
3307 X509 *recip = NULL;
3308 EVP_PKEY *key = NULL;
3309 PERSONAL_CERT *pcert = NULL;
3310 char *what_we_did = "";
3311 char null[1];
3313 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"));
3314 null[0] = '\0';
3315 smime_init();
3318 * Extract binary data from part to an in-memory store
3321 if(b->sparep){
3322 if(get_body_sparep_type(b->sparep) == P7Type)
3323 p7 = (PKCS7*) get_body_sparep_data(b->sparep);
3325 else{
3326 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
3327 if(!p7){
3328 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
3329 (char*) openssl_error_string());
3330 goto end;
3334 * Save the PKCS7 object for later dealings by the user interface.
3335 * It will be cleaned up when the body is garbage collected.
3337 b->sparep = create_body_sparep(P7Type, p7);
3340 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
3342 if(PKCS7_type_is_signed(p7)){
3343 int sigok;
3345 out = BIO_new(BIO_s_mem());
3346 (void) BIO_reset(out);
3347 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3349 sigok = do_signature_verify(p7, NULL, out, 0);
3351 what_we_did = sigok ? _("This message was cryptographically signed.") :
3352 _("This message was cryptographically signed but the signature could not be verified.");
3354 /* make sure it's null terminated */
3355 BIO_write(out, null, 1);
3357 else if(!PKCS7_type_is_enveloped(p7)){
3358 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
3359 goto end;
3361 else{ /* It *is* enveloped */
3362 int decrypt_result;
3364 what_we_did = _("This message was encrypted.");
3366 /* now need to find a cert that can decrypt this */
3367 pcert = find_certificate_matching_pkcs7(p7);
3369 if(!pcert){
3370 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3371 goto end;
3374 recip = pcert->cert;
3376 if(!load_private_key(pcert)
3377 && ps_global->smime
3378 && ps_global->smime->need_passphrase
3379 && !ps_global->smime->already_auto_asked){
3380 /* Couldn't load key with blank password, ask user */
3381 ps_global->smime->already_auto_asked = 1;
3382 if(pith_opt_smime_get_passphrase){
3383 (*pith_opt_smime_get_passphrase)();
3384 load_private_key(pcert);
3388 key = pcert->key;
3389 if(!key)
3390 goto end;
3392 out = BIO_new(BIO_s_mem());
3393 (void) BIO_reset(out);
3394 BIO_puts(out, "MIME-Version: 1.0\r\n");
3396 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
3398 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3399 forget_private_keys();
3401 if(!decrypt_result){
3402 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3403 (char*) openssl_error_string());
3404 goto end; }
3406 BIO_write(out, null, 1);
3410 * We've now produced a flattened MIME object in BIO out.
3411 * It needs to be turned back into a BODY.
3414 if(out){
3415 BODY *body;
3416 ENVELOPE *env;
3417 char *h = NULL;
3418 char *bstart;
3419 STRING s;
3420 BUF_MEM *bptr = NULL;
3422 BIO_get_mem_ptr(out, &bptr);
3423 if(bptr)
3424 h = bptr->data;
3426 /* look for start of body */
3427 bstart = strstr(h, "\r\n\r\n");
3429 if(!bstart){
3430 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
3432 else{
3433 SIZEDTEXT *st;
3434 bstart += 4; /* skip over CRLF*2 */
3436 INIT(&s, mail_string, bstart, strlen(bstart));
3437 rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
3438 mail_free_envelope(&env); /* Don't care about this */
3440 if(body->type == TYPEMULTIPART
3441 && !strucmp(body->subtype, "SIGNED")){
3442 char *cookie = NULL;
3443 PARAMETER *param;
3444 for (param = body->parameter; param && !cookie; param = param->next)
3445 if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value;
3446 if(cookie != NULL){
3447 st = fs_get(sizeof(SIZEDTEXT));
3448 st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */
3449 st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4);
3450 body->sparep = create_body_sparep(SizedText, (void *)st);
3452 else
3453 q_status_message(SM_ORDER, 3, 3, _("Couldn't find cookie in attachment list."));
3455 body->mime.offset = 0;
3456 body->mime.text.size = 0;
3459 * Now convert original body (application/pkcs7-mime)
3460 * to a multipart body with one sub-part (the decrypted body).
3461 * Note that the sub-part may also be multipart!
3464 b->type = TYPEMULTIPART;
3465 if(b->subtype)
3466 fs_give((void**) &b->subtype);
3469 * This subtype is used in mailview.c to annotate the display of
3470 * encrypted or signed messages. We know for sure then that it's a PKCS7
3471 * part because the sparep field is set to the PKCS7 object (see above).
3473 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3474 b->encoding = ENC8BIT;
3476 if(b->description)
3477 fs_give((void**) &b->description);
3479 b->description = cpystr(what_we_did);
3481 if(b->disposition.type)
3482 fs_give((void **) &b->disposition.type);
3484 if(b->contents.text.data)
3485 fs_give((void **) &b->contents.text.data);
3487 if(b->parameter)
3488 mail_free_body_parameter(&b->parameter);
3490 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3491 b->nested.part = fs_get(sizeof(PART));
3492 b->nested.part->body = *body;
3493 b->nested.part->next = NULL;
3495 fs_give((void**) &body);
3498 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3499 * the decrypted data. Otherwise, it'll try to load it from the original
3500 * data. Eek.
3502 create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body, 0);
3504 modified_the_body = 1;
3508 end:
3509 if(out)
3510 BIO_free(out);
3512 return modified_the_body;
3517 * Recursively handle PKCS7 bodies in our message.
3519 * Returns non-zero if some fiddling was done.
3521 static int
3522 do_fiddle_smime_message(BODY *b, long msgno, char *section)
3524 int modified_the_body = 0;
3526 if(!b)
3527 return 0;
3529 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"));
3531 if(is_pkcs7_body(b)){
3533 if(do_decoding(b, msgno, section)){
3535 * b should now be a multipart message:
3536 * fiddle it too in case it's been multiply-encrypted!
3539 /* fallthru */
3540 modified_the_body = 1;
3544 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
3546 PART *p;
3547 int partNum;
3548 char newSec[100];
3550 if(MIME_MULT_SIGNED(b->type, b->subtype)){
3554 * Ahah. We have a multipart signed entity.
3556 * Multipart/signed
3557 * part 1 (signed thing)
3558 * part 2 (the pkcs7 signature)
3560 * We're going to convert that to
3562 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3563 * part 1 (signed thing)
3564 * part 2 has been freed
3566 * We also extract the signature from part 2 and save it
3567 * in the multipart body->sparep, and we add a description
3568 * in the multipart body->description.
3571 * The results of a decrypted message will be similar. It
3572 * will be
3574 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3575 * part 1 (decrypted thing)
3578 modified_the_body += do_detached_signature_verify(b, msgno, section);
3580 else if(MIME_MSG(b->type, b->subtype)){
3581 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
3583 else{
3585 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
3586 /* Append part number to the section string */
3588 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
3590 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
3595 return modified_the_body;
3600 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3601 * Returns non-zero if something was changed.
3604 fiddle_smime_message(BODY *b, long msgno)
3606 return do_fiddle_smime_message(b, msgno, "");
3610 /********************************************************************************/
3614 * Output a string in a distinctive style
3616 void
3617 gf_puts_uline(char *txt, gf_io_t pc)
3619 pc(TAG_EMBED); pc(TAG_BOLDON);
3620 gf_puts(txt, pc);
3621 pc(TAG_EMBED); pc(TAG_BOLDOFF);
3624 /* get_chain_for_cert: error and level are mandatory arguments */
3625 STACK_OF(X509) *
3626 get_chain_for_cert(X509 *cert, int *error, int *level)
3628 STACK_OF(X509) *chain = NULL;
3629 X509_STORE_CTX *ctx;
3630 X509 *x, *xtmp;
3631 int rc; /* return code */
3633 *level = -1;
3634 *error = 0;
3635 ERR_clear_error();
3636 if((s_cert_store != NULL) && (ctx = X509_STORE_CTX_new()) != NULL){
3637 X509_STORE_set_flags(s_cert_store, 0);
3638 if(!X509_STORE_CTX_init(ctx, s_cert_store, cert, NULL))
3639 *error = X509_STORE_CTX_get_error(ctx);
3640 else if((chain = sk_X509_new_null()) != NULL){
3641 for(x = cert; ; x = xtmp){
3642 if(++*level > 0)
3643 sk_X509_push(chain, X509_dup(x));
3644 rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x);
3645 if(rc < 0)
3646 *error = 1;
3647 if(rc <= 0)
3648 break;
3649 if(!X509_check_issued(xtmp, xtmp))
3650 break;
3653 X509_STORE_CTX_free(ctx);
3655 return chain;
3660 * Sign a message. Called from call_mailer in send.c.
3662 * This takes the header for the outgoing message as well as a pointer
3663 * to the current body (which may be reallocated).
3664 * The last argument (BODY **bp) is an argument that tells Alpine
3665 * if the body has 8 bit. if *bp is not null we compute two signatures
3666 * one for the quoted-printable encoded message, and another for the
3667 * 8bit encoded message. We return the signature for the 8bit encoded
3668 * part in p2->body.mime.text.data.
3669 * The reason why we compute two signatures is so that we can decide
3670 * which one to use later, and we only do it in the case that *bp is
3671 * not null. If we did not do this, then we might not be able to sign
3672 * a message until we log in to the smtp server, so instead of doing
3673 * that, we get ready for any possible situation we might find.
3676 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp)
3678 STORE_S *outs = NULL;
3679 STORE_S *outs_2 = NULL;
3680 BODY *body = *bodyP;
3681 BODY *newBody = NULL;
3682 PART *p1 = NULL;
3683 PART *p2 = NULL;
3684 PERSONAL_CERT *pcert;
3685 BIO *in = NULL;
3686 BIO *in_2 = NULL;
3687 BIO *out = NULL;
3688 BIO *out_2 = NULL;
3689 PKCS7 *p7 = NULL;
3690 PKCS7 *p7_2 = NULL;
3691 STACK_OF(X509) *chain;
3692 const EVP_MD *md = EVP_sha256(); /* use this digest instead of sha1 */
3693 int result = 0, error;
3694 int flags = dont_detach ? 0 : PKCS7_DETACHED;
3695 int level;
3697 dprint((9, "sign_outgoing_message()"));
3699 smime_init();
3701 /* Look for a private key matching the sender address... */
3703 pcert = match_personal_cert(header->env);
3705 if(!pcert){
3706 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
3707 goto end;
3710 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
3711 /* Couldn't load key with blank password, try again */
3712 if(pith_opt_smime_get_passphrase){
3713 (*pith_opt_smime_get_passphrase)();
3714 load_private_key(pcert);
3718 if(!pcert->key)
3719 goto end;
3721 if(((chain = get_chain_for_cert(pcert->cert, &error, &level)) != NULL && error)
3722 || level == 0){
3723 sk_X509_pop_free(chain, X509_free);
3724 chain = NULL;
3727 if(error)
3728 q_status_message(SM_ORDER, 1, 1,
3729 _("Not all certificates needed to verify signature included in signed message"));
3731 in = body_to_bio(body);
3733 flags |= PKCS7_PARTIAL;
3734 if((p7 = PKCS7_sign(NULL, NULL, chain, in, flags)) != NULL
3735 && PKCS7_sign_add_signer(p7, pcert->cert, pcert->key, md, flags))
3736 PKCS7_final(p7, in, flags);
3738 if(bp && *bp){
3739 int i, save_encoding;
3741 for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++);
3743 if(i > ENCMAX){ /* no empty encoding slots! */
3744 *bp = NULL;
3746 else {
3747 save_encoding = (*bp)->encoding;
3748 body_encodings[(*bp)->encoding = i] = body_encodings[ENC8BIT];
3750 in_2 = body_to_bio(body);
3752 body_encodings[i] = NULL;
3753 (*bp)->encoding = save_encoding;
3757 if(bp && *bp){
3758 if((p7_2 = PKCS7_sign(NULL, NULL, chain, in_2, flags)) != NULL
3759 && PKCS7_sign_add_signer(p7_2, pcert->cert, pcert->key, md, flags))
3760 PKCS7_final(p7_2, in_2, flags);
3763 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3764 forget_private_keys();
3766 if(chain)
3767 sk_X509_pop_free(chain, X509_free);
3769 if(!p7){
3770 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
3771 goto end;
3774 outs = so_get(BioType, NULL, EDIT_ACCESS);
3775 out = bio_from_store(outs);
3777 i2d_PKCS7_bio(out, p7);
3778 (void) BIO_flush(out);
3780 so_seek(outs, 0, SEEK_SET);
3782 if(bp && *bp && p7_2){
3783 outs_2 = so_get(BioType, NULL, EDIT_ACCESS);
3784 out_2 = bio_from_store(outs_2);
3786 i2d_PKCS7_bio(out_2, p7_2);
3787 (void) BIO_flush(out_2);
3789 so_seek(outs_2, 0, SEEK_SET);
3792 if((flags&PKCS7_DETACHED)==0){
3794 /* the simple case: the signed data is in the pkcs7 object */
3796 newBody = mail_newbody();
3798 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3800 newBody->contents.text.data = (unsigned char *) outs;
3801 *bodyP = newBody;
3803 result = 1;
3805 else{
3808 * OK.
3809 * We have to create a new body as follows:
3811 * multipart/signed; blah blah blah
3812 * reference to existing body
3814 * pkcs7 object
3817 newBody = mail_newbody();
3819 newBody->type = TYPEMULTIPART;
3820 newBody->subtype = cpystr("signed");
3821 newBody->encoding = ENC7BIT;
3823 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
3824 set_parameter(&newBody->parameter, "micalg", "sha-256");
3826 p1 = mail_newbody_part();
3827 p2 = mail_newbody_part();
3830 * This is nasty. We're just copying the body in here,
3831 * but since our newBody is freed at the end of call_mailer,
3832 * we mustn't let this body (the original one) be freed twice.
3834 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
3836 p1->next = p2;
3838 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL);
3839 p2->body.mime.text.data = (unsigned char *) outs_2;
3840 p2->body.contents.text.data = (unsigned char *) outs;
3842 newBody->nested.part = p1;
3844 *bodyP = newBody;
3846 result = 1;
3849 end:
3851 PKCS7_free(p7);
3852 BIO_free(in);
3854 if(bp && *bp){
3855 if(p7_2) PKCS7_free(p7_2);
3856 BIO_free(in_2);
3859 dprint((9, "sign_outgoing_message returns %d", result));
3860 return result;
3864 SMIME_STUFF_S *
3865 new_smime_struct(void)
3867 SMIME_STUFF_S *ret = NULL;
3869 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
3870 memset((void *) ret, 0, sizeof(*ret));
3871 ret->publictype = Nada;
3873 return ret;
3877 static void
3878 free_smime_struct(SMIME_STUFF_S **smime)
3880 if(smime && *smime){
3881 if((*smime)->passphrase_emailaddr){
3882 int i;
3883 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
3884 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
3885 fs_give((void **) (*smime)->passphrase_emailaddr);
3888 if((*smime)->publicpath)
3889 fs_give((void **) &(*smime)->publicpath);
3891 if((*smime)->publiccertlist)
3892 free_certlist(&(*smime)->publiccertlist);
3894 if((*smime)->backuppubliccertlist)
3895 free_certlist(&(*smime)->backuppubliccertlist);
3897 if((*smime)->cacertlist)
3898 free_certlist(&(*smime)->cacertlist);
3900 if((*smime)->backupcacertlist)
3901 free_certlist(&(*smime)->backupcacertlist);
3903 if((*smime)->privatecertlist)
3904 free_certlist(&(*smime)->privatecertlist);
3906 if((*smime)->backupprivatecertlist)
3907 free_certlist(&(*smime)->backupprivatecertlist);
3909 if((*smime)->publiccontent)
3910 fs_give((void **) &(*smime)->publiccontent);
3912 if((*smime)->privatepath)
3913 fs_give((void **) &(*smime)->privatepath);
3915 if((*smime)->personal_certs){
3916 PERSONAL_CERT *pc;
3918 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
3919 free_personal_certs(&pc);
3920 (*smime)->personal_certs = NULL;
3923 if((*smime)->privatecontent)
3924 fs_give((void **) &(*smime)->privatecontent);
3926 if((*smime)->capath)
3927 fs_give((void **) &(*smime)->capath);
3929 if((*smime)->cacontent)
3930 fs_give((void **) &(*smime)->cacontent);
3932 fs_give((void **) smime);
3936 #endif /* SMIME */