* Implement a different way to delete a password from the cache.
[alpine.git] / pith / smime.c
blob1e74316e70969e9a5fd4e0ca23e1cfd79d38ab6b
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
16 * This is based on a contribution from Jonathan Paisley
18 * File: smime.c
19 * Author: paisleyj@dcs.gla.ac.uk
20 * Date: 01/2001
24 #include "../pith/headers.h"
26 #ifdef SMIME
28 #include "../pith/osdep/canaccess.h"
29 #include "../pith/helptext.h"
30 #include "../pith/store.h"
31 #include "../pith/status.h"
32 #include "../pith/detach.h"
33 #include "../pith/conf.h"
34 #include "../pith/smkeys.h"
35 #include "../pith/smime.h"
36 #include "../pith/mailpart.h"
37 #include "../pith/reply.h"
38 #include "../pith/tempfile.h"
39 #include "../pith/readfile.h"
40 #include "../pith/remote.h"
41 #include "../pith/body.h"
42 #ifdef PASSFILE
43 #include "../pith/imap.h"
44 #endif /* PASSFILE */
46 #include <openssl/buffer.h>
47 #include <openssl/x509v3.h>
48 #include <openssl/evp.h>
50 /* internal prototypes */
51 static void forget_private_keys(void);
52 static int app_RAND_load_file(const char *file);
53 static void openssl_extra_randomness(void);
54 static int app_RAND_write_file(const char *file);
55 static const char *openssl_error_string(void);
56 static int load_private_key(PERSONAL_CERT *pcert);
57 static void create_local_cache(char *h, char *base, BODY *b, int type);
58 static long rfc822_output_func(void *b, char *string);
59 static void setup_pkcs7_body_for_signature(BODY *b, char *description,
60 char *type, char *filename, char *smime_type);
61 static BIO *body_to_bio(BODY *body);
62 static BIO *bio_from_store(STORE_S *store);
63 static STORE_S *get_part_contents(long msgno, const char *section);
64 static PKCS7 *get_pkcs7_from_part(long msgno, const char *section);
65 static int do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent);
66 static int do_detached_signature_verify(BODY *b, long msgno, char *section);
67 static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7);
68 static int do_decoding(BODY *b, long msgno, const char *section);
69 static void free_smime_struct(SMIME_STUFF_S **smime);
70 static void setup_storage_locations(void);
71 static int copy_container_to_dir(WhichCerts which);
72 static int do_fiddle_smime_message(BODY *b, long msgno, char *section);
73 void setup_privatekey_storage(void);
74 int smime_extract_and_save_cert(PKCS7 *p7);
75 int same_cert(X509 *, X509 *);
76 #ifdef PASSFILE
77 int load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile, char **certfile, EVP_PKEY **pkey, X509 **pcert);
78 #endif /* PASSFILE */
79 EVP_PKEY *load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *);
80 void smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen);
81 void smime_remove_folding_space(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen);
82 int smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag);
84 int (*pith_opt_smime_get_passphrase)(void);
85 int (*pith_smime_import_certificate)(char *, char *, char *, size_t);
86 int (*pith_smime_enter_password)(char *, char *, size_t);
87 int (*pith_smime_confirm_save)(char *);
89 static X509_STORE *s_cert_store;
91 /* State management for randomness functions below */
92 static int seeded = 0;
94 #ifdef PASSFILE
95 /*
96 * This code does not work in Windows, because of the PASSFILE thing, so
97 * I did not try to fix it. If you think it does need to be applied to
98 * the Windows version of alpine, there are more changes that are needed
99 * than fixing this function in this module. E. Chappa 09/28/17.
101 * load key from pathkeydir and cert from pathcertdir. It chooses the first
102 * key/certificate pair that matches. Delete pairs that you do not want used,
103 * if you do not want them selected. All parameters must be non-null.
104 * Memory freed by caller.
105 * Return values:
106 * -1 : user cancelled load
107 * 0 : load was successful
108 * 1 : there was an error in the loading.
111 load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile,
112 char **certfile, EVP_PKEY **pkey, X509 **pcert)
114 char buf[MAXPATH+1], pathkey[MAXPATH+1], prompt[MAILTMPLEN];
115 DIR *dirp;
116 struct dirent *d;
117 int b = 0, ret = 1; /* assume error */
119 if(pathkeydir == NULL || pathcertdir == NULL || keyfile == NULL
120 || pkey == NULL || certfile == NULL || pcert == NULL)
121 return 1;
123 *keyfile = NULL;
124 *certfile = NULL;
125 *pkey = NULL;
126 *pcert = NULL;
128 if((dirp = opendir(pathkeydir)) != NULL){
129 while(b == 0 && (d=readdir(dirp)) != NULL){
130 size_t ll;
132 if((ll=strlen(d->d_name)) && ll > 4){
133 if(!strcmp(d->d_name+ll-4, ".key")){
134 strncpy(buf, d->d_name, sizeof(buf));
135 buf[sizeof(buf)-1] = '\0';
136 build_path(pathkey, pathkeydir, buf, sizeof(pathkey));
137 buf[strlen(buf)-4] = '\0';
138 snprintf(prompt, sizeof(prompt),
139 _("Enter password of key <%s> to unlock password file: "), buf);
140 if((*pkey = load_pkey_with_prompt(pathkey, NULL, prompt, &ret)) != NULL){
141 if(load_cert_for_key(pathcertdir, *pkey, certfile, pcert)){
142 b = 1; /* break */
143 *keyfile = cpystr(buf);
144 } else {
145 EVP_PKEY_free(*pkey);
146 *pkey = NULL;
147 q_status_message1(SM_ORDER, 0, 2,
148 _("Cannot find certificate that matches key <%s>. Continuing..."), buf);
154 closedir(dirp);
156 return ret;
160 /* setup a key and certificate to encrypt and decrypt a password file.
161 * These files will be saved in the .alpine-smime/.pwd directory, but its
162 * location can be setup in the command line with the -pwdcertdir option.
163 * Here are the rules:
165 * Check if the .alpine-smime/.pwd (or -pwdcertdir directory) exists,
166 * if not create it. If we are successful, move to the next step
168 * - If the user has a key/cert pair, in the .alpine-smime/.pwd dir
169 * setup is successful;
170 * - if the user does not have a key/cert pair, look to see if
171 * ps_global->smime->personal_certs is already setup, if so, use it.
172 * - if ps_global->smime->personal_certs is not set up, see if we can
173 * find a certificate/cert pair in the default locations at compilation
174 * time. (~/.alpine-smime/private and ~/.alpine-smime/public
175 * - if none of this is successful, create a key/certificate pair
176 * (TODO: implement this)
177 * - in any other case, setup is not successful.
179 * If setup is successful, setup ps_global->pwdcert.
180 * If any of this fails, ps_global->pwdcert will be null.
181 * Ok, that should do it.
183 * return values: 0 - everything is normal
184 * 1 - User could not unlock key or no key in directory.
185 * 2 - User cancelled to create self signed certificate
186 * -1 - we do not know which directory to use
187 * -2 - "-pwdcertdir" was given by user, but directory does not exist
188 * -3 - "DF_PASSWORD_DIR" exists but it is not a directory!!??
189 * -4 - we tried to create DF_PASSWORD_DIR but failed.
190 * -5 - password directory exists, but it is empty
194 setup_pwdcert(void **pwdcert)
196 int rv;
197 int we_inited = 0;
198 int setup_dir = 0; /* make it non zero if we know which dir to use */
199 struct stat sbuf;
200 char pathdir[MAXPATH+1], pathkey[MAXPATH+1], fpath[MAXPATH+1], pathcert[MAXPATH+1];
201 char fpath2[MAXPATH+1], prompt[MAILTMPLEN];
202 char *keyfile, *certfile, *text = NULL;
203 EVP_PKEY *pkey = NULL;
204 X509 *pcert = NULL;
205 PERSONAL_CERT *pc, *pc2 = NULL;
206 static int was_here = 0;
208 if(pwdcert == NULL || was_here == 1)
209 return -1;
211 was_here++;
212 if(ps_global->pwdcertdir){
213 if(our_stat(ps_global->pwdcertdir, &sbuf) == 0
214 && ((sbuf.st_mode & S_IFMT) == S_IFDIR)){
215 setup_dir++;
216 strncpy(pathdir, ps_global->pwdcertdir, sizeof(pathdir));
217 pathdir[sizeof(pathdir)-1] = '\0';
219 else rv = -2;
220 } else {
221 smime_path(DF_PASSWORD_DIR, pathdir, sizeof(pathdir));
222 if(our_stat(pathdir, &sbuf) == 0){
223 if((sbuf.st_mode & S_IFMT) == S_IFDIR)
224 setup_dir++;
225 else rv = -3;
226 } else if(can_access(pathdir, ACCESS_EXISTS) != 0
227 && our_mkpath(pathdir, 0700) == 0)
228 setup_dir++;
229 else rv = -4;
232 if(setup_dir == 0){
233 was_here = 0;
234 return rv;
237 if(load_key_and_cert(pathdir, pathdir, &keyfile, &certfile, &pkey, &pcert) < 0){
238 was_here = 0;
239 return 1;
242 if(ps_global->pwdcertdir == NULL){ /* save the result of pwdcertdir */
243 ps_global->pwdcertdir = cpystr(pathdir);
244 /* if the user gave a pwdcertdir and there is nothing there, do not
245 * continue. Let the user initialize on their own this directory.
247 if(certfile == NULL || keyfile == NULL){
248 was_here = 0;
249 return -5;
253 if(certfile && keyfile){
254 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
255 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
256 pc->name = keyfile;
257 pc->key = pkey;
258 pc->cert = pcert;
259 pc->cname = certfile;
260 *pwdcert = (void *) pc;
261 was_here = 0;
262 return 0;
265 /* look to see if there are any certificates lying around, first
266 * we try to load ps_global->smime to see if that has information
267 * we can use. If we are the process filling the smime structure
268 * we deinit at the end, since this might not do a full init.
270 if(ps_global && ps_global->smime && !ps_global->smime->inited){
271 we_inited++;
272 smime_init();
275 /* at this point ps_global->smime->inited == 1 */
276 if(ps_global->smime && ps_global->smime->personal_certs != NULL){
277 pc = (PERSONAL_CERT *) ps_global->smime->personal_certs;
278 if(ps_global->smime->privatetype == Directory){
279 build_path(pathkey, ps_global->smime->privatepath, pc->name, sizeof(pathkey));
280 strncat(pathkey, ".key", 5);
281 pathkey[sizeof(pathkey)-1] = '\0';
282 text = NULL;
283 } else if (ps_global->smime->privatetype == Container){
284 if(pc->keytext == NULL){ /* we should *never* be here, but just in case */
285 if(ps_global->smime->privatecontent != NULL){
286 char tmp[MAILTMPLEN], *s, *t, c;
287 snprintf(tmp, sizeof(tmp), "%s%s", EMAILADDRLEADER, pc->name);
288 tmp[sizeof(tmp)-1] = '\0';
289 if((s = strstr(ps_global->smime->privatecontent, tmp)) != NULL){
290 if((t = strstr(s+strlen(tmp), EMAILADDRLEADER)) != NULL){
291 c = *t;
292 *t = '\0';
293 pc->keytext = cpystr(s + strlen(tmp) + strlen(NEWLINE));
294 *t = c;
296 else
297 pc->keytext = cpystr(s + strlen(tmp) + strlen(NEWLINE));
301 if(pc->keytext != NULL) /* we should go straight here */
302 text = pc->keytext;
303 } else if (ps_global->smime->privatetype == Keychain){
304 pathkey[0] = '\0'; /* no apple key chain support yet */
305 text = NULL;
307 if((pathkey && *pathkey) || text){
308 snprintf(prompt, sizeof(prompt),
309 _("Enter password of key <%s> to unlock password file: "), pc->name);
311 if((pkey = load_pkey_with_prompt(pathkey, text, prompt, NULL)) != NULL){
312 pc2 = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
313 memset((void *)pc2, 0, sizeof(PERSONAL_CERT));
314 pc2->name = cpystr(pc->name);
315 pc2->key = pkey;
316 pc2->cert = X509_dup(pc->cert);
318 /* now copy the keys and certs, starting by the key... */
319 build_path(fpath, pathdir, pc->name, sizeof(fpath));
320 strncat(fpath, ".key", 5);
321 fpath[sizeof(fpath)-1] = '\0';
322 if(our_stat(fpath, &sbuf) == 0){ /* if fpath exists */
323 if((sbuf.st_mode & S_IFMT) == S_IFREG) /* and is a regular file */
324 setup_dir++; /* we are done */
325 } else if(ps_global->smime->privatetype == Directory){
326 if(our_copy(fpath, pathkey) == 0)
327 setup_dir++;
328 } else if(ps_global->smime->privatetype == Container){
329 BIO *out;
330 if((out = BIO_new_file(fpath, "w")) != NULL){
331 if(BIO_puts(out, pc->keytext) > 0)
332 setup_dir++;
333 BIO_free(out);
335 } else if(ps_global->smime->privatetype == Keychain){
336 /* add support for Apple Mac OS X */
340 /* successful copy of key, now continue with certificate */
341 if(setup_dir){
342 setup_dir = 0;
344 build_path(pathkey, ps_global->smime->publicpath, pc->name, sizeof(pathkey));
345 strncat(pathkey, ".crt", 5);
346 pathkey[sizeof(pathkey)-1] = '\0';
348 build_path(fpath, pathdir, pc->name, sizeof(fpath));
349 strncat(fpath, ".crt", 5);
350 fpath[sizeof(fpath)-1] = '\0';
352 if(our_stat(fpath, &sbuf) == 0){
353 if((sbuf.st_mode & S_IFMT) == S_IFREG)
354 setup_dir++;
356 else if(ps_global->smime->privatetype == Directory){
357 if(our_copy(fpath, pathkey) == 0)
358 setup_dir++;
359 } else if(ps_global->smime->privatetype == Container) {
360 BIO *out;
361 if((out = BIO_new_file(fpath, "w")) != NULL){
362 if(PEM_write_bio_X509(out, pc->cert))
363 setup_dir++;
364 BIO_free(out);
366 } else if (ps_global->smime->privatetype == Keychain) {
367 /* add support for Mac OS X */
371 if(setup_dir){
372 *pwdcert = (void *) pc2;
373 was_here = 0;
374 return 0;
376 else if(pc2 != NULL)
377 free_personal_certs(&pc2);
378 } /* if (pathkey...) */
379 } /* if(ps_global->smime->personal_certs) */
382 if(setup_dir == 0){
383 /* PATHCERTDIR(Private) must be null, so create a path */
384 set_current_val(&ps_global->vars[V_PRIVATEKEY_DIR], TRUE, TRUE);
385 smime_path(ps_global->VAR_PRIVATEKEY_DIR, pathkey, sizeof(pathkey));
387 /* PATHCERTDIR(Public) must be null, so create a path */
388 set_current_val(&ps_global->vars[V_PUBLICCERT_DIR], TRUE, TRUE);
389 smime_path(ps_global->VAR_PUBLICCERT_DIR, pathcert, sizeof(pathcert));
391 /* BUG: this does not support local containers */
392 load_key_and_cert(pathkey, pathcert, &keyfile, &certfile, &pkey, &pcert);
394 if(certfile && keyfile){
395 build_path(fpath, pathdir, keyfile, sizeof(fpath));
396 strncat(fpath, ".key", 5);
397 fpath[sizeof(fpath)-1] = '\0';
399 build_path(fpath2, pathkey, keyfile, sizeof(fpath));
400 strncat(fpath2, ".key", 5);
401 fpath2[sizeof(fpath2)-1] = '\0';
403 if(our_copy(fpath, fpath2) == 0)
404 setup_dir++;
406 if(setup_dir){
407 setup_dir = 0;
409 build_path(fpath, pathdir, certfile, sizeof(fpath));
410 build_path(fpath2, pathcert, certfile, sizeof(fpath2));
412 if(our_copy(fpath, fpath2) == 0)
413 setup_dir++;
418 if(keyfile && certfile){
419 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
420 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
421 pc->name = keyfile;
422 pc->key = pkey;
423 pc->cert = pcert;
424 *pwdcert = (void *) pc;
425 fs_give((void **)&certfile);
426 was_here = 0;
427 return 0;
430 was_here = 0;
431 if(we_inited)
432 smime_deinit();
433 return 0;
435 #endif /* PASSFILE */
437 /* smime_expunge_cert.
438 * Return values: < 0 there was an error.
439 * >=0 the number of messages expunged
442 smime_expunge_cert(WhichCerts ctype)
444 int count = 0, removed;
445 CertList *cl, *dummy, *data;
446 char *path, buf[MAXPATH+1];
447 char *contents;
449 if(DATACERT(ctype)== NULL)
450 return -1;
452 /* data cert is the way we unify certificate management across
453 * functions, but it is not where we really save the information in the
454 * case ctype is equal to Private. What we will do is to update the
455 * datacert, and in the case of ctype equal to Private use the updated
456 * certdata to update the personal_certs data.
459 path = PATHCERTDIR(ctype);
461 if(path){
462 /* add a fake certificate at the beginning of the list */
463 dummy = fs_get(sizeof(CertList));
464 memset((void *)dummy, 0, sizeof(CertList));
465 dummy->next = DATACERT(ctype);
467 for(cl = dummy, count = 0; cl && cl->next;){
468 if(cl->next->data.deleted == 0){
469 cl = cl->next;
470 continue;
473 removed = 1; /* assume success */
474 if(SMHOLDERTYPE(ctype) == Directory){
475 build_path(buf, path, cl->next->name, sizeof(buf));
476 if(ctype == Private && strlen(buf) + strlen(EXTCERT(Private)) < sizeof(buf)){
477 strncat(buf, EXTCERT(Private), 5);
478 buf[sizeof(buf)-1] = '\0';
481 if(our_unlink(buf) < 0){
482 q_status_message1(SM_ORDER, 3, 3, _("Error removing certificate %s"), cl->next->name);
483 cl = cl->next;
484 removed = 0;
487 else if(SMHOLDERTYPE(ctype) == Container){
488 char *prefix= ctype == CACert ? CACERTSTORELEADER : EMAILADDRLEADER;
489 char tmp[MAILTMPLEN], *s, *t;
491 contents = CONTENTCERTLIST(ctype);
492 snprintf(tmp, sizeof(tmp), "%s%s", prefix, cl->next->name);
493 tmp[sizeof(tmp) - 1] = '\0';
494 if((s = strstr(contents, tmp)) != NULL){
495 if((t = strstr(s+strlen(tmp), prefix)) == NULL)
496 *s = '\0';
497 else
498 memmove(s, t, strlen(t)+1);
499 fs_resize((void **)&contents, strlen(contents)+1);
500 switch(ctype){
501 case Private: ps_global->smime->privatecontent = contents; break;
502 case Public : ps_global->smime->publiccontent = contents; break;
503 case CACert : ps_global->smime->cacontent = contents; break;
504 default : break;
507 else
508 removed = 0;
509 } else { /* unhandled case */
512 if(removed > 0){
513 count++; /* count it! */
514 data = cl->next;
515 cl->next = data->next;
516 if(data->name) fs_give((void **)&data->name);
517 fs_give((void **)&data);
520 } else
521 q_status_message(SM_ORDER, 3, 3, _("Error expunging certificate"));
523 switch(ctype){
524 case Private: ps_global->smime->privatecertlist = dummy->next; break;
525 case Public : ps_global->smime->publiccertlist = dummy->next; break;
526 case CACert : ps_global->smime->cacertlist = dummy->next; break;
527 default : break;
529 fs_give((void **)&dummy);
530 if(SMHOLDERTYPE(ctype) == Container){
531 if(copy_dir_to_container(ctype, contents) < 0)
532 count = 0;
534 if(count > 0){
535 q_status_message2(SM_ORDER, 3, 3, _("Removed %s certificate%s"), comatose(count), plural(count));
537 else
538 q_status_message(SM_ORDER, 3, 3, _("Error: No certificates were removed"));
539 return count;
542 void
543 mark_cert_deleted(WhichCerts ctype, int num, unsigned state)
545 CertList *cl;
546 int i;
548 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
549 cl->data.deleted = state;
552 unsigned
553 get_cert_deleted(WhichCerts ctype, int num)
555 CertList *cl;
556 int i;
558 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
559 return (cl && cl->data.deleted) ? 1 : 0;
562 EVP_PKEY *
563 load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *ret)
565 EVP_PKEY *pkey;
566 int rc = 0; /* rc == 1, cancel, rc == 0 success */
567 char pass[MAILTMPLEN+1];
568 BIO *in;
570 /* attempt to load with empty password */
571 in = text ? BIO_new_mem_buf(text, strlen(text)) : BIO_new_file(fpath, "r");
572 if(in != NULL){
573 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, "");
574 if(pkey != NULL) return pkey;
575 } else return NULL;
577 if(pith_smime_enter_password)
578 while(pkey == NULL && rc != 1){
579 do {
580 rc = (*pith_smime_enter_password)(prompt, (char *)pass, sizeof(pass));
581 } while (rc!=0 && rc!=1 && rc>0);
583 (void) BIO_reset(in);
584 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, (char *)pass);
587 BIO_free(in);
589 if(ret) *ret = rc == 1 ? -1 : pkey != NULL ? 0 : 1;
590 return pkey;
593 /* This is a tool for conf_screen, The return value must be zero when
594 * nothing changed, so if there is a failure in the import return 0
595 * and return 1 when we succeeded.\
596 * We call this function in two ways:
597 * either fname is null or not. If they fname is null, so is p_cert.
598 * if p_cert is not null, it is the PERSONAL_CERT structure of fname if this
599 * is available, otherwise we will fill it up here.
602 import_certificate(WhichCerts ctype, PERSONAL_CERT *p_cert, char *fname)
604 int r = 1, rc;
605 char filename[MAXPATH+1], full_filename[MAXPATH+1], buf[MAXPATH+1];
606 char *what;
608 if(pith_smime_import_certificate == NULL
609 || pith_smime_enter_password == NULL){
610 q_status_message(SM_ORDER, 0, 2,
611 _("import of certificates not implemented yet!"));
612 return 0;
615 if(fname == NULL){
616 what = ctype == Public || ctype == CACert ? "certificate" : "key";
617 r = (*pith_smime_import_certificate)(filename, full_filename, what, sizeof(filename) - 20);
619 if(r < 0)
620 return 0;
621 } else {
622 char *s;
623 strncpy(full_filename, fname, sizeof(full_filename));
624 if((s = strrchr(full_filename, '/')) != NULL)
625 strncpy(filename, s+1, sizeof(filename));
628 /* we are trying to import a new key for the password file. First we ask for the
629 * private key. Once this is loaded, we make a reasonable attempt to find the
630 * public key in the same directory as the key was loaded from. We do this by
631 * looking for a file with the correct public certificate name, then we look
632 * in the same private key, and if not, we ask the user for its location. If all
633 * of this works, we import the key and public to the password directory.
635 #ifdef PASSFILE
636 if(ctype == Password){
637 char PrivateKeyPath[MAXPATH+1], PublicCertPath[MAXPATH+1], s[MAXPATH+1];
638 char full_name_key[MAXPATH+1], full_name_cert[MAXPATH+1];
639 char *use_this_file = NULL;
640 char prompt[500];
641 EVP_PKEY *key = p_cert ? p_cert->key : NULL;
643 rc = 1; /* assume success :) */
644 if(strlen(filename) > 4){
645 strncpy(s, filename, sizeof(s));
646 s[sizeof(s)-1] = '\0';
647 if(!strcmp(s + strlen(s) - strlen(EXTCERT(Private)), EXTCERT(Private)))
648 s[strlen(s) - strlen(EXTCERT(Private))] = '\0';
649 else
650 rc = 0;
651 } else rc = 0;
653 if(rc == 0){
654 q_status_message(SM_ORDER, 1, 3, _("Error in key name. Check file extension"));
655 return 0;
658 snprintf(prompt, sizeof(prompt), _("Enter passphrase to unlock new key <%s>: "), filename);
659 prompt[sizeof(prompt)-1] = '\0';
660 if(key != NULL
661 || (key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){
662 BIO *ins = NULL;
663 X509 *cert = p_cert ? p_cert->cert : NULL;
665 strncpy(full_name_key, full_filename, sizeof(full_filename));
666 full_name_key[sizeof(full_name_key)-1] = '\0';
668 build_path(buf, PATHCERTDIR(ctype), s, sizeof(buf));
670 strncpy(PrivateKeyPath, buf, sizeof(PrivateKeyPath));
671 PrivateKeyPath[sizeof(PrivateKeyPath)-1] = '\0';
672 if(strlen(PrivateKeyPath) + 4 < sizeof(PrivateKeyPath)){
673 strncat(PrivateKeyPath, EXTCERT(Private), 5);
674 PrivateKeyPath[sizeof(PrivateKeyPath)-1] = '\0';
677 /* remove .key extension and replace it with .crt extension */
678 strncpy(full_name_cert, full_name_key, sizeof(full_name_key));
679 full_name_cert[sizeof(full_name_cert)-1] = '\0';
680 full_name_cert[strlen(full_name_cert) - strlen(EXTCERT(Private))] = '\0';
681 strncat(full_name_cert, EXTCERT(Public), 5);
682 full_name_cert[sizeof(full_name_cert)-1] = '\0';
685 /* set up path to location where we will save public cert */
686 strncpy(PublicCertPath, buf, sizeof(PublicCertPath));
687 PublicCertPath[sizeof(PublicCertPath)-1] = '\0';
688 if(strlen(PublicCertPath) + 4 < sizeof(PublicCertPath)){
689 strncat(PublicCertPath, EXTCERT(Public), 5);
690 PublicCertPath[sizeof(PublicCertPath)-1] = '\0';
692 /* attempt #1, use provided certificate,
693 * assumption is that full_name_cert is the file that this
694 * certificate derives from (which is obtained by substitution
695 * of .key extension in key by .crt extension)
697 if(cert != NULL) /* attempt #1 */
698 use_this_file = &full_name_cert[0];
699 else if((ins = BIO_new_file(full_name_cert, "r")) != NULL){
700 /* attempt #2 to guess public cert name, use .crt extension */
701 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
702 use_this_file = &full_name_cert[0];
705 else{ /* attempt #3 to guess public cert name: use the original key */
706 if((ins = BIO_new_file(full_name_key, "r")) != NULL){
707 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
708 use_this_file = &full_name_key[0];
711 else {
712 int done = 0;
713 /* attempt #4, ask the user */
714 do {
715 r = (*pith_smime_import_certificate)(filename, use_this_file, "certificate", sizeof(filename) - 20);
716 if(r < 0){
717 if(ins != NULL) BIO_free(ins);
718 if(cert != NULL) X509_free(cert);
719 return 0;
721 if((ins = BIO_new_file(use_this_file, "r")) != NULL){
722 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL)
723 done++;
724 else
725 q_status_message(SM_ORDER, 1, 3, _("Error parsing certificate"));
727 else
728 q_status_message(SM_ORDER, 1, 3, _("Error reading certificate"));
729 } while (done == 0);
732 if(ins != NULL){
733 if(cert != NULL){ /* check that certificate matches key */
734 if(!X509_check_private_key(cert, key)){
735 rc = 0;
736 q_status_message(SM_ORDER, 1, 3, _("Certificate does not match key"));
738 else
739 rc = 1; /* Success! */
741 else
742 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
744 if(rc == 1){ /* if everything has been successful,
745 * copy the files to their final destination */
746 if(our_copy(PrivateKeyPath, full_filename) == 0){ /* <-- save the private key */
747 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
748 if(our_copy(PublicCertPath, use_this_file) == 0){
749 char tmp[MAILTMPLEN];
750 FILE *fp;
752 if(!passfile_name(ps_global->pinerc, tmp, sizeof(tmp))
753 || !(fp = our_fopen(tmp, "rb"))){
754 q_status_message(SM_ORDER, 1, 3, _("Error reading password file!"));
755 rc = 0;
757 else {
758 char tmp2[MAILTMPLEN];
759 int encrypted = 0;
760 char *text;
761 PERSONAL_CERT *pwdcert, *pc = p_cert;
763 pwdcert = (PERSONAL_CERT *) ps_global->pwdcert;
764 if(pwdcert == NULL)
765 setup_pwdcert((void **)&pwdcert);
767 tmp2[0] = '\0';
768 fgets(tmp2, sizeof(tmp2), fp);
769 fclose(fp);
770 if(strcmp(tmp2, "-----BEGIN PKCS7-----\n")){
771 if(encrypt_file((char *)tmp, NULL, pwdcert))
772 encrypted++;
774 else
775 encrypted++;
777 if(encrypted){
778 text = decrypt_file((char *)tmp, NULL, pwdcert);
779 if(text != NULL){
780 if(pc == NULL){
781 filename[strlen(filename)-strlen(EXTCERT(Private))] = '\0';
782 if(strlen(filename) + strlen(EXTCERT(Public)) < MAXPATH){
783 pc = fs_get(sizeof(PERSONAL_CERT));
784 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
785 pc->name = cpystr(filename);
786 pc->cname = fs_get(strlen(filename) + strlen(EXTCERT(Public)) + 1);
787 sprintf(pc->cname, "%s%s", filename, EXTCERT(Public));
788 pc->key = key;
789 pc->cert = cert;
793 if(encrypt_file((char *)tmp, text, pc)){ /* we did it! */
794 build_path(buf, PATHCERTDIR(ctype), pwdcert->name, sizeof(buf));
795 strncat(buf, EXTCERT(Private), 5);
796 buf[sizeof(buf)-1] = '\0';
797 if(strcmp(PrivateKeyPath, buf)){
798 if (unlink(buf) < 0)
799 q_status_message(SM_ORDER, 1, 3, _("Failed to remove old key"));
801 build_path(buf, PATHCERTDIR(ctype), pwdcert->cname, sizeof(buf));
802 if(strcmp(PublicCertPath, buf)){
803 if(unlink(buf) < 0)
804 q_status_message(SM_ORDER, 1, 3, _("Failed to remove old certificate"));
806 free_personal_certs((PERSONAL_CERT **)&ps_global->pwdcert);
807 ps_global->pwdcert = pc;
808 rc = 1;
809 q_status_message(SM_ORDER, 1, 3, _("Password file reencrypted"));
810 } else {
811 q_status_message(SM_ORDER, 1, 3, _("Failed to reencrypt password file"));
812 rc = 0;
814 } else {
815 q_status_message(SM_ORDER, 1, 3, _("Error decrypting Password file"));
817 } else {
818 q_status_message(SM_ORDER, 1, 3, _("Password file not encrypted and could not encrypt"));
819 rc = 0;
823 else{
824 q_status_message(SM_ORDER, 1, 3, _("Error saving public certificate"));
825 if(our_unlink(PrivateKeyPath) < 0)
826 q_status_message(SM_ORDER, 1, 3, _("Error while cleaning private key"));
827 rc = 0;
830 else{
831 rc = 0;
832 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
834 if(ins != NULL) BIO_free(ins);
835 if(rc == 0 && cert != NULL) X509_free(cert);
837 } else {
838 rc = 0;
839 q_status_message(SM_ORDER, 1, 3, _("Error unlocking private key"));
842 return rc;
844 #endif /* PASSFILE */
846 smime_init();
847 ps_global->mangled_screen = 1;
849 if (ctype == Private){
850 char prompt[500], *s, *t;
851 EVP_PKEY *key = NULL;
853 if(!ps_global->smime->privatecertlist){
854 ps_global->smime->privatecertlist = fs_get(sizeof(CertList));
855 memset((void *) ps_global->smime->privatecertlist, 0, sizeof(CertList));
858 for(s = t = filename; (t = strstr(s, ".key")) != NULL; s = t + 1);
859 if(s) *(s-1) = 0;
861 snprintf(prompt, sizeof(prompt), _("Enter passphrase for <%s>: "), filename);
862 prompt[sizeof(prompt)-1] = '\0';
863 if((key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){
864 if(SMHOLDERTYPE(ctype) == Directory){
865 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
866 if(strcmp(buf + strlen(buf) - 4, EXTCERT(ctype)) != 0 && strlen(buf) + 4 < sizeof(buf)){
867 strncat(buf, EXTCERT(ctype), 5);
868 buf[sizeof(buf)-1] = '\0';
870 rc = our_copy(buf, full_filename);
872 else /* if(SMHOLDERTYPE(ctype) == Container){ */
873 rc = add_file_to_container(ctype, full_filename, NULL);
874 if(rc == 0)
875 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
876 else
877 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
878 if(ps_global->smime->publiccertlist)
879 ps_global->smime->publiccertlist->data.renew = 1;
881 else
882 q_status_message(SM_ORDER, 1, 3, _("Problem unlocking key (not a certificate or wrong password)"));
883 } else if (ctype == CACert){
884 BIO *ins;
885 X509 *cert;
887 if((ins = BIO_new_file(full_filename, "r")) != NULL){
888 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
889 if(SMHOLDERTYPE(ctype) == Directory){
890 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
891 if(strcmp(buf + strlen(buf) - 4, ".crt") != 0 && strlen(buf) + 4 < sizeof(buf)){
892 strncat(buf, EXTCERT(ctype), 5);
893 buf[sizeof(buf)-1] = '\0';
896 rc = our_copy(buf, full_filename);
898 else /* if(SMHOLDERTYPE(ctype) == Container){ */
899 rc = add_file_to_container(ctype, full_filename, NULL);
900 if(rc == 0)
901 q_status_message(SM_ORDER, 1, 3, _("Certificate saved"));
902 else
903 q_status_message(SM_ORDER, 1, 3, _("Error saving certificate"));
904 X509_free(cert); /* not needed anymore */
906 else
907 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
908 BIO_free(ins);
910 renew_store();
911 } else { /* ctype == Public. save certificate, but first validate that it is one */
912 BIO *ins;
913 X509 *cert;
915 if((ins = BIO_new_file(full_filename, "r")) != NULL){
916 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
917 if(SMHOLDERTYPE(ctype) == Directory){
918 char **email;
920 if((email = get_x509_subject_email(cert)) != NULL){
921 int i;
922 for(i = 0; email[i] != NULL; i++){
923 save_cert_for(email[i], cert, Public);
924 fs_give((void **)&email[i]);
926 fs_give((void **)email);
928 if(strcmp(filename + strlen(filename) - 4, ".crt") == 0)
929 filename[strlen(filename) - 4] = '\0';
930 save_cert_for(filename, cert, Public);
932 else /* if(SMHOLDERTYPE(ctype) == Container){ */
933 add_file_to_container(ctype, full_filename, NULL);
934 X509_free(cert);
935 if(ps_global->smime->publiccertlist)
936 ps_global->smime->publiccertlist->data.renew = 1;
938 else
939 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
940 BIO_free(ins);
943 if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1;
944 return 1;
947 /* itype: information type to add: 0 - public, 1 - private.
948 * Memory freed by caller
950 BIO *
951 print_private_key_information(char *email, int itype)
953 BIO *out;
954 PERSONAL_CERT *pc;
956 if(ps_global->smime == NULL
957 || ps_global->smime->personal_certs == NULL
958 || (itype != 0 && itype != 1))
959 return NULL;
961 for(pc = ps_global->smime->personal_certs;
962 pc != NULL && strcmp(pc->name, email) != 0; pc = pc->next);
963 if(pc->key == NULL
964 && !load_private_key(pc)
965 && ps_global->smime
966 && ps_global->smime->need_passphrase){
967 if (pith_opt_smime_get_passphrase)
968 (*pith_opt_smime_get_passphrase)();
969 load_private_key(pc);
972 if(pc->key == NULL)
973 return NULL;
975 out = BIO_new(BIO_s_mem());
976 if(itype == 0) /* 0 means public */
977 EVP_PKEY_print_public(out, pc->key, 0, NULL);
978 else if (itype == 1) /* 1 means private */
979 EVP_PKEY_print_private(out, pc->key, 0, NULL);
981 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
982 forget_private_keys();
984 return out;
988 * Forget any cached private keys
990 static void
991 forget_private_keys(void)
993 PERSONAL_CERT *pcert;
994 size_t len;
995 volatile char *p;
997 dprint((9, "forget_private_keys()"));
998 if(ps_global->smime){
999 ps_global->smime->already_auto_asked = 0;
1000 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
1001 pcert;
1002 pcert=pcert->next){
1004 if(pcert->key){
1005 EVP_PKEY_free(pcert->key);
1006 pcert->key = NULL;
1010 ps_global->smime->entered_passphrase = 0;
1011 len = sizeof(ps_global->smime->passphrase);
1012 p = ps_global->smime->passphrase;
1014 while(len-- > 0)
1015 *p++ = '\0';
1019 /* modelled after signature_path in reply.c, but uses home dir instead of the
1020 * directory where the .pinerc is located, since according to documentation,
1021 * the .alpine-smime directories are subdirectories of the home directory
1024 smime_path(char *rpath, char *fpath, size_t len)
1026 *fpath = '\0';
1027 if(rpath && *rpath){
1028 size_t spl = strlen(rpath);
1030 if(IS_REMOTE(rpath)){
1031 if(spl < len - 1)
1032 strncpy(fpath, rpath, len-1);
1033 fpath[len-1] = '\0';
1035 else if(is_absolute_path(rpath)){
1036 strncpy(fpath, rpath, len-1);
1037 fpath[len-1] = '\0';
1038 fnexpand(fpath, len);
1040 else if(ps_global->VAR_OPER_DIR){
1041 if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
1042 build_path(fpath, ps_global->VAR_OPER_DIR, rpath, len);
1044 else if(ps_global->home_dir){
1045 if(strlen(ps_global->home_dir) + spl < len - 1)
1046 build_path(fpath, ps_global->home_dir, rpath, len);
1049 return fpath && *fpath ? 1 : 0;
1055 * taken from openssl/apps/app_rand.c
1057 static int
1058 app_RAND_load_file(const char *file)
1060 #define RANDBUFLEN 200
1061 char buffer[RANDBUFLEN];
1063 if(file == NULL)
1064 file = RAND_file_name(buffer, RANDBUFLEN);
1066 if(file == NULL || !RAND_load_file(file, -1)){
1067 if(RAND_status() == 0){
1068 dprint((1, "unable to load 'random state'\n"));
1069 dprint((1, "This means that the random number generator has not been seeded\n"));
1070 dprint((1, "with much random data.\n"));
1073 return 0;
1076 seeded = 1;
1077 return 1;
1082 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
1084 static void
1085 openssl_extra_randomness(void)
1087 #if !defined(WIN32)
1088 int fd;
1089 unsigned long i;
1090 char *tf = NULL;
1091 char tmp[MAXPATH];
1092 struct stat sbuf;
1093 /* if system doesn't have /dev/urandom */
1094 if(stat ("/dev/urandom", &sbuf)){
1095 tmp[0] = '0';
1096 tf = temp_nam(NULL, NULL);
1097 if(tf){
1098 strncpy(tmp, tf, sizeof(tmp));
1099 tmp[sizeof(tmp)-1] = '\0';
1100 fs_give((void **) &tf);
1103 if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
1104 i = (unsigned long) tmp;
1105 else{
1106 unlink(tmp); /* don't need the file */
1107 fstat(fd, &sbuf); /* get information about the file */
1108 i = sbuf.st_ino; /* remember its inode */
1109 close(fd); /* or its descriptor */
1111 /* not great but it'll have to do */
1112 snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
1113 tcp_serverhost (),i,
1114 (unsigned long) (time (0) ^ gethostid ()),
1115 (unsigned long) getpid ());
1116 RAND_seed(tmp, strlen(tmp));
1118 #endif
1122 /* taken from openssl/apps/app_rand.c */
1123 static int
1124 app_RAND_write_file(const char *file)
1126 char buffer[200];
1128 if(!seeded)
1130 * If we did not manage to read the seed file,
1131 * we should not write a low-entropy seed file back --
1132 * it would suppress a crucial warning the next time
1133 * we want to use it.
1135 return 0;
1137 if(file == NULL)
1138 file = RAND_file_name(buffer, sizeof buffer);
1140 if(file == NULL || !RAND_write_file(file)){
1141 dprint((1, "unable to write 'random state'\n"));
1142 return 0;
1145 return 1;
1148 CertList *
1149 certlist_from_personal_certs(PERSONAL_CERT *pc)
1151 CertList *cl = NULL;
1152 X509 *x;
1154 if(pc == NULL)
1155 return NULL;
1157 if((x = get_cert_for(pc->name, Public, 1)) != NULL){
1158 cl = smime_X509_to_cert_info(x, pc->name);
1159 cl->next = certlist_from_personal_certs(pc->next);
1162 return cl;
1165 void
1166 renew_cert_data(CertList **data, WhichCerts ctype)
1168 smime_init();
1169 if(ctype == Private){
1170 if(data){
1171 PERSONAL_CERT *pc = (PERSONAL_CERT *)ps_global->smime->personal_certs;
1172 if(*data)
1173 free_certlist(data);
1174 free_personal_certs(&pc);
1175 setup_privatekey_storage();
1176 *data = certlist_from_personal_certs((PERSONAL_CERT *)ps_global->smime->personal_certs);
1177 if(data && *data){
1178 resort_certificates(data, ctype);
1179 RENEWCERT(*data) = 0;
1181 ps_global->smime->privatecertlist = *data;
1183 if(ps_global->smime->privatecertlist)
1184 RENEWCERT(ps_global->smime->privatecertlist) = 0;
1185 } else {
1186 X509_LOOKUP *lookup = NULL;
1187 X509_STORE *store = NULL;
1189 if((store = X509_STORE_new()) != NULL){
1190 if((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) != NULL){
1191 free_certlist(data);
1192 if(SMHOLDERTYPE(ctype) == Directory)
1193 add_certs_in_dir(lookup, PATHCERTDIR(ctype), EXTCERT(ctype), data);
1194 else /* if(SMHOLDERTYPE(ctype) == Container) */
1195 *data = mem_to_certlist(CONTENTCERTLIST(ctype), ctype);
1196 if(data && *data){
1197 resort_certificates(data, ctype);
1198 RENEWCERT(*data) = 0;
1200 if(ctype == Public)
1201 ps_global->smime->publiccertlist = *data;
1202 else
1203 ps_global->smime->cacertlist = *data;
1205 free_x509_store(&store);
1208 setup_certs_backup_by_type(ctype);
1211 void
1212 smime_reinit(void)
1214 smime_deinit();
1215 smime_init();
1218 /* Installed as an atexit() handler to save the random data */
1219 void
1220 smime_deinit(void)
1222 dprint((9, "smime_deinit()"));
1223 app_RAND_write_file(NULL);
1224 if (s_cert_store != NULL) free_x509_store(&s_cert_store);
1225 #ifdef ERR_free_strings
1226 ERR_free_strings();
1227 #endif /* ERR_free_strings */
1228 #ifdef EVP_cleanup
1229 EVP_cleanup();
1230 #endif /* EVP_cleanup */
1231 free_smime_struct(&ps_global->smime);
1234 /* we renew the store when it has changed */
1235 void
1236 renew_store(void)
1238 if(ps_global->smime->inited){
1239 if(s_cert_store != NULL)
1240 free_x509_store(&s_cert_store);
1241 s_cert_store = get_ca_store();
1245 /* Initialise openssl stuff if needed */
1246 void
1247 smime_init(void)
1249 if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
1251 dprint((9, "smime_init()"));
1252 if(!ps_global->smime)
1253 ps_global->smime = new_smime_struct();
1255 setup_storage_locations();
1257 s_cert_store = get_ca_store();
1258 setup_certs_backup_by_type(CACert);
1260 #ifdef OPENSSL_1_1_0
1261 OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS|OPENSSL_INIT_ADD_ALL_DIGESTS|OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
1262 #else
1263 OpenSSL_add_all_algorithms();
1264 ERR_load_crypto_strings();
1265 #endif /* OPENSSL_1_1_0 */
1267 app_RAND_load_file(NULL);
1268 openssl_extra_randomness();
1269 ps_global->smime->inited = 1;
1272 ERR_clear_error();
1276 /* validate a certificate. Return value : 0 for no error, -1 for error.
1277 * In the latter case, set the openssl smime error in *error.
1280 smime_validate_cert(X509 *cert, long *error)
1282 X509_STORE_CTX *csc;
1284 ERR_clear_error();
1285 *error = 0;
1286 if((s_cert_store != NULL) && (csc = X509_STORE_CTX_new()) != NULL){
1287 X509_STORE_set_flags(s_cert_store, 0);
1288 if(X509_STORE_CTX_init(csc,s_cert_store,cert,NULL)
1289 && X509_verify_cert(csc) <= 0)
1290 *error = X509_STORE_CTX_get_error(csc);
1291 X509_STORE_CTX_free(csc);
1293 return *error ? -1 : 0;
1296 PERSONAL_CERT *
1297 get_personal_certs(char *path)
1299 PERSONAL_CERT *result = NULL;
1300 char buf2[MAXPATH], *fname;
1301 X509 *cert;
1302 size_t ll;
1303 #ifndef _WINDOWS
1304 struct dirent *d;
1305 DIR *dirp;
1306 #else /* _WINDOWS */
1307 struct _finddata_t dbuf;
1308 char buf[_MAX_PATH + 4];
1309 long findrv;
1310 #endif /* _WINDOWS */
1312 ps_global->smime->privatepath = cpystr(path);
1314 #ifndef _WINDOWS
1315 dirp = opendir(path);
1316 if(dirp){
1317 while((d=readdir(dirp)) != NULL){
1318 fname = d->d_name;
1319 #else /* _WINDOWS */
1320 snprintf(buf, sizeof(buf), "%s%s*.*", path, (path[strlen(path)-1] == '\\') ? "" : "\\");
1321 buf[sizeof(buf)-1] = '\0';
1322 if((findrv = _findfirst(buf, &dbuf)) < 0)
1323 return(NULL);
1325 do {
1326 fname = fname_to_utf8(dbuf.name);
1327 #endif
1328 if((ll=strlen(fname)) && ll > 4 && !strcmp(fname+ll-4, ".key")){
1330 /* copy file name to temp buffer */
1331 strncpy(buf2, fname, sizeof(buf2)-1);
1332 buf2[sizeof(buf2)-1] = '\0';
1333 /* chop off ".key" trailier */
1334 buf2[strlen(buf2)-4] = '\0';
1335 /* Look for certificate */
1336 cert = get_cert_for(buf2, Public, 1);
1338 if(cert){
1339 PERSONAL_CERT *pc;
1341 /* create a new PERSONAL_CERT, fill it in */
1343 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1344 pc->cert = cert;
1345 pc->name = cpystr(buf2);
1346 strncat(buf2, EXTCERT(Public), 5);
1347 pc->cname = cpystr(buf2);
1349 /* Try to load the key with an empty password */
1350 pc->key = load_key(pc, "", SM_NORMALCERT);
1352 pc->next = result;
1353 result = pc;
1356 #ifndef _WINDOWS
1358 closedir(dirp);
1360 #else /* _WINDOWS */
1361 } while(_findnext(findrv, &dbuf) == 0);
1362 _findclose(findrv);
1363 #endif /* !_WINDOWS */
1364 return result;
1368 void
1369 setup_privatekey_storage(void)
1371 char path[MAXPATH+1], *contents;
1372 int privatekeycontainer = 0;
1374 /* private keys in a container */
1375 if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
1377 privatekeycontainer = 1;
1378 contents = NULL;
1379 path[0] = '\0';
1380 if(!smime_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
1381 privatekeycontainer = 0;
1383 if(privatekeycontainer && !IS_REMOTE(path)
1384 && ps_global->VAR_OPER_DIR
1385 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1386 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1387 /* TRANSLATORS: First arg is the directory name, second is
1388 the file user wants to read but can't. */
1389 _("Can't read file outside %s: %s"),
1390 ps_global->VAR_OPER_DIR, path);
1391 privatekeycontainer = 0;
1394 if(privatekeycontainer
1395 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1396 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1398 !(contents = read_file(path, READ_FROM_LOCALE)))
1399 privatekeycontainer = 0;
1402 if(privatekeycontainer && path[0]){
1403 ps_global->smime->privatetype = Container;
1404 ps_global->smime->privatepath = cpystr(path);
1406 if(contents){
1407 ps_global->smime->privatecontent = contents;
1408 ps_global->smime->personal_certs = mem_to_personal_certs(contents);
1413 /* private keys in a directory of files */
1414 if(!privatekeycontainer){
1415 ps_global->smime->privatetype = Directory;
1417 path[0] = '\0';
1418 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1419 && !IS_REMOTE(path)))
1420 ps_global->smime->privatetype = Nada;
1421 else if(can_access(path, ACCESS_EXISTS)){
1422 if(our_mkpath(path, 0700)){
1423 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1424 ps_global->smime->privatetype = Nada;
1428 if(ps_global->smime->privatetype == Directory)
1429 ps_global->smime->personal_certs = get_personal_certs(path);
1431 setup_certs_backup_by_type(Private);
1434 static void
1435 setup_storage_locations(void)
1437 int publiccertcontainer = 0, cacertcontainer = 0;
1438 char path[MAXPATH+1], *contents;
1440 if(!ps_global->smime)
1441 return;
1443 #ifdef APPLEKEYCHAIN
1444 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1445 ps_global->smime->publictype = Keychain;
1447 else{
1448 #endif /* APPLEKEYCHAIN */
1449 /* Public certificates in a container */
1450 if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
1452 publiccertcontainer = 1;
1453 contents = NULL;
1454 path[0] = '\0';
1455 if(!smime_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
1456 publiccertcontainer = 0;
1458 if(publiccertcontainer && !IS_REMOTE(path)
1459 && ps_global->VAR_OPER_DIR
1460 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1461 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1462 /* TRANSLATORS: First arg is the directory name, second is
1463 the file user wants to read but can't. */
1464 _("Can't read file outside %s: %s"),
1465 ps_global->VAR_OPER_DIR, path);
1466 publiccertcontainer = 0;
1469 if(publiccertcontainer
1470 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1471 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1473 !(contents = read_file(path, READ_FROM_LOCALE)))
1474 publiccertcontainer = 0;
1477 if(publiccertcontainer && path[0]){
1478 ps_global->smime->publictype = Container;
1479 ps_global->smime->publicpath = cpystr(path);
1481 if(contents){
1482 ps_global->smime->publiccontent = contents;
1483 ps_global->smime->publiccertlist = mem_to_certlist(contents, Public);
1488 /* Public certificates in a directory of files */
1489 if(!publiccertcontainer){
1490 ps_global->smime->publictype = Directory;
1492 path[0] = '\0';
1493 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1494 && !IS_REMOTE(path)))
1495 ps_global->smime->publictype = Nada;
1496 else if(can_access(path, ACCESS_EXISTS)){
1497 if(our_mkpath(path, 0700)){
1498 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1499 ps_global->smime->publictype = Nada;
1503 if(ps_global->smime->publictype == Directory)
1504 ps_global->smime->publicpath = cpystr(path);
1507 #ifdef APPLEKEYCHAIN
1509 #endif /* APPLEKEYCHAIN */
1511 setup_privatekey_storage();
1513 /* extra cacerts in a container */
1514 if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
1516 cacertcontainer = 1;
1517 contents = NULL;
1518 path[0] = '\0';
1519 if(!smime_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
1520 cacertcontainer = 0;
1522 if(cacertcontainer && !IS_REMOTE(path)
1523 && ps_global->VAR_OPER_DIR
1524 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1525 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1526 /* TRANSLATORS: First arg is the directory name, second is
1527 the file user wants to read but can't. */
1528 _("Can't read file outside %s: %s"),
1529 ps_global->VAR_OPER_DIR, path);
1530 cacertcontainer = 0;
1533 if(cacertcontainer
1534 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1535 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1537 !(contents = read_file(path, READ_FROM_LOCALE)))
1538 cacertcontainer = 0;
1541 if(cacertcontainer && path[0]){
1542 ps_global->smime->catype = Container;
1543 ps_global->smime->capath = cpystr(path);
1544 ps_global->smime->cacontent = contents;
1545 if(contents)
1546 ps_global->smime->cacertlist = mem_to_certlist(contents, CACert);
1550 if(!cacertcontainer){
1551 ps_global->smime->catype = Directory;
1553 path[0] = '\0';
1554 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1555 && !IS_REMOTE(path)))
1556 ps_global->smime->catype = Nada;
1557 else if(can_access(path, ACCESS_EXISTS)){
1558 if(our_mkpath(path, 0700)){
1559 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1560 ps_global->smime->catype = Nada;
1564 if(ps_global->smime->catype == Directory)
1565 ps_global->smime->capath = cpystr(path);
1571 copy_publiccert_dir_to_container(void)
1573 return(copy_dir_to_container(Public, NULL));
1578 copy_publiccert_container_to_dir(void)
1580 return(copy_container_to_dir(Public));
1585 copy_privatecert_dir_to_container(void)
1587 return(copy_dir_to_container(Private, NULL));
1592 copy_privatecert_container_to_dir(void)
1594 return(copy_container_to_dir(Private));
1599 copy_cacert_dir_to_container(void)
1601 return(copy_dir_to_container(CACert, NULL));
1606 copy_cacert_container_to_dir(void)
1608 return(copy_container_to_dir(CACert));
1611 /* Add the contents of a file to a container. Do not check the content
1612 * of the file, just add it using the format for that container. The
1613 * caller must check the format, so that there is no data corruption
1614 * in the future.
1615 * return value: 0 - success,
1616 * != 0 - failure.
1619 add_file_to_container(WhichCerts ctype, char *fpath, char *altname)
1621 char *sep = (ctype == Public || ctype == Private)
1622 ? EMAILADDRLEADER : CACERTSTORELEADER;
1623 char *content = ctype == Public ? ps_global->smime->publiccontent
1624 : (ctype == Private ? ps_global->smime->privatecontent
1625 : ps_global->smime->cacontent);
1626 char *name;
1627 char *s;
1628 unsigned char c;
1629 struct stat sbuf;
1630 STORE_S *in = NULL;
1631 int rv = -1; /* assume error */
1632 size_t clen; /* content buffer size */
1634 if(our_stat(fpath, &sbuf) < 0
1635 || (in = so_get(FileStar, fpath, READ_ACCESS | READ_FROM_LOCALE)) == NULL)
1636 goto endadd;
1638 if(altname != NULL)
1639 name = altname;
1640 else if((name = strrchr(fpath, '/')) != NULL){
1641 size_t ll;
1642 if((ll = strlen(++name)) > 4 && strucmp(name + ll - 4, EXTCERT(ctype)) == 0)
1643 name[ll-strlen(EXTCERT(ctype))] = '\0';
1645 else
1646 goto endadd;
1648 if(content){
1649 clen = strlen(content) + strlen(sep) + strlen(name) + sbuf.st_size + 2*strlen(NEWLINE) + 1;
1650 fs_resize((void **)&content, clen);
1651 s = content;
1652 content += strlen(content);
1654 else{
1655 clen = strlen(sep) + strlen(name) + sbuf.st_size + strlen(NEWLINE) + 1;
1656 s = content = fs_get(clen);
1657 *content = '\0';
1659 strncat(content, sep, clen - strlen(content));
1660 strncat(content, name, clen - strlen(content));
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) + 2 < sizeof(dstpath))
1931 snprintf(fpath, sizeof(fpath), "%.*s%c%.*s",
1932 (int) strlen(dstpath), dstpath,
1933 tempfile[strlen(ret_dir)],
1934 (int) (sizeof(fpath) - strlen(dstpath) - 1), configcontainer);
1935 else
1936 ret = -1;
1938 else ret = -1;
1940 if(!ret){
1941 if(!IS_REMOTE(configpath)){
1942 if(rename_file(tempfile, fpath) < 0){
1943 q_status_message2(SM_ORDER, 3, 3,
1944 _("Can't rename %s to %s"), tempfile, fpath);
1945 ret = -1;
1946 } else q_status_message1(SM_ORDER, 3, 3,
1947 _("saved container to %s"), fpath);
1949 else { /* if the container is remote, copy it */
1950 int e;
1951 char datebuf[200];
1953 if(rd != NULL && rename_file(tempfile, rd->lf) < 0){
1954 q_status_message2(SM_ORDER, 3, 3,
1955 _("Can't rename %s to %s"), tempfile, rd->lf);
1956 ret = -1;
1959 datebuf[0] = '\0';
1961 if((e = rd_update_remote(rd, datebuf)) != 0){
1962 if(e == -1){
1963 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1964 _("Error opening temporary smime file %s: %s"),
1965 rd->lf, error_description(errno));
1966 dprint((1,
1967 "write_remote_smime: error opening temp file %s\n",
1968 rd->lf ? rd->lf : "?"));
1970 else{
1971 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1972 _("Error copying to %s: %s"),
1973 rd->rn, error_description(errno));
1974 dprint((1,
1975 "write_remote_smime: error copying from %s to %s\n",
1976 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1979 q_status_message(SM_ORDER | SM_DING, 5, 5,
1980 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1982 else{
1983 rd_update_metadata(rd, datebuf);
1984 rd->read_status = 'W';
1987 rd_close_remdata(&rd);
1993 if(tempfile)
1994 fs_give((void **) &tempfile);
1996 if(ret_dir)
1997 fs_give((void **) &ret_dir);
1999 if(configcontainer)
2000 fs_give((void **) &configcontainer);
2002 return ret;
2007 * returns 0 on success, -1 on failure
2010 copy_container_to_dir(WhichCerts which)
2012 char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
2013 char iobuf[4096];
2014 char *contents = NULL;
2015 char *leader = NULL;
2016 char *filesuffix = NULL;
2017 char *configdir = NULL;
2018 char *configpath = NULL;
2019 char *tempfile = NULL;
2020 char *p, *q, *line, *name, *certtext, *save_p;
2021 int len;
2022 BIO *in, *out;
2024 dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
2025 smime_init();
2027 path[0] = '\0';
2029 if(which == Public){
2030 leader = EMAILADDRLEADER;
2031 contents = ps_global->smime->publiccontent;
2032 configdir = ps_global->VAR_PUBLICCERT_DIR;
2033 configpath = ps_global->smime->publicpath;
2034 filesuffix = ".crt";
2035 if(!(configpath && configpath[0])){
2036 #ifdef APPLEKEYCHAIN
2037 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
2038 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
2039 return -1;
2041 #endif /* APPLEKEYCHAIN */
2042 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2043 return -1;
2046 fs_give((void **) &ps_global->smime->publicpath);
2048 path[0] = '\0';
2049 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
2050 && !IS_REMOTE(path))){
2051 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2052 return -1;
2055 if(can_access(path, ACCESS_EXISTS)){
2056 if(our_mkpath(path, 0700)){
2057 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2058 return -1;
2062 ps_global->smime->publicpath = cpystr(path);
2063 configpath = ps_global->smime->publicpath;
2065 else if(which == Private){
2066 leader = EMAILADDRLEADER;
2067 contents = ps_global->smime->privatecontent;
2068 configdir = ps_global->VAR_PRIVATEKEY_DIR;
2069 configpath = ps_global->smime->privatepath;
2070 filesuffix = ".key";
2071 if(!(configpath && configpath[0])){
2072 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2073 return -1;
2076 fs_give((void **) &ps_global->smime->privatepath);
2078 path[0] = '\0';
2079 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
2080 && !IS_REMOTE(path))){
2081 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2082 return -1;
2085 if(can_access(path, ACCESS_EXISTS)){
2086 if(our_mkpath(path, 0700)){
2087 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2088 return -1;
2092 ps_global->smime->privatepath = cpystr(path);
2093 configpath = ps_global->smime->privatepath;
2095 else if(which == CACert){
2096 leader = CACERTSTORELEADER;
2097 contents = ps_global->smime->cacontent;
2098 configdir = ps_global->VAR_CACERT_DIR;
2099 configpath = ps_global->smime->capath;
2100 filesuffix = ".crt";
2101 if(!(configpath && configpath[0])){
2102 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2103 return -1;
2106 fs_give((void **) &ps_global->smime->capath);
2108 path[0] = '\0';
2109 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
2110 && !IS_REMOTE(path))){
2111 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2112 return -1;
2115 if(can_access(path, ACCESS_EXISTS)){
2116 if(our_mkpath(path, 0700)){
2117 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2118 return -1;
2122 ps_global->smime->capath = cpystr(path);
2123 configpath = ps_global->smime->capath;
2126 if(!(configdir && configdir[0])){
2127 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
2128 return -1;
2131 if(!(configpath && configpath[0])){
2132 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2133 return -1;
2136 if(!(filesuffix && strlen(filesuffix) == 4)){
2137 return -1;
2141 if(contents && *contents){
2142 for(p = contents; *p != '\0';){
2143 line = p;
2145 while(*p && *p != '\n')
2146 p++;
2148 save_p = NULL;
2149 if(*p == '\n'){
2150 save_p = p;
2151 *p++ = '\0';
2154 if(strncmp(leader, line, strlen(leader)) == 0){
2155 name = line + strlen(leader);
2156 certtext = p;
2157 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
2158 if((q = strstr(certtext, leader)) != NULL){
2159 p = q;
2161 else{ /* end of file */
2162 q = certtext + strlen(certtext);
2163 p = q;
2166 strncpy(buf, name, sizeof(buf)-5);
2167 buf[sizeof(buf)-5] = '\0';
2168 strncat(buf, filesuffix, 5);
2169 build_path(file, configpath, buf, sizeof(file));
2171 in = BIO_new_mem_buf(certtext, q-certtext);
2172 if(in){
2173 tempfile = tempfile_in_same_dir(file, "az", NULL);
2174 out = NULL;
2175 if(tempfile)
2176 out = BIO_new_file(tempfile, "w");
2178 if(out){
2179 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
2180 BIO_write(out, iobuf, len);
2182 BIO_free(out);
2184 if(rename_file(tempfile, file) < 0){
2185 q_status_message2(SM_ORDER, 3, 3,
2186 _("Can't rename %s to %s"),
2187 tempfile, file);
2188 return -1;
2191 fs_give((void **) &tempfile);
2194 BIO_free(in);
2199 if(save_p)
2200 *save_p = '\n';
2204 return 0;
2208 #ifdef APPLEKEYCHAIN
2211 copy_publiccert_container_to_keychain(void)
2213 /* NOT IMPLEMNTED */
2214 return -1;
2218 copy_publiccert_keychain_to_container(void)
2220 /* NOT IMPLEMNTED */
2221 return -1;
2224 #endif /* APPLEKEYCHAIN */
2228 * Get a pointer to a string describing the most recent OpenSSL error.
2229 * It's statically allocated, so don't change or attempt to free it.
2231 static const char *
2232 openssl_error_string(void)
2234 char *errs;
2235 const char *data = NULL;
2236 long errn;
2238 errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2239 errs = (char*) ERR_reason_error_string(errn);
2241 if(errs)
2242 return errs;
2243 else if(data)
2244 return data;
2246 return "unknown error";
2250 /* Return true if the body looks like a PKCS7 object */
2252 is_pkcs7_body(BODY *body)
2254 int result;
2256 result = body->type==TYPEAPPLICATION &&
2257 body->subtype &&
2258 (strucmp(body->subtype,"pkcs7-mime")==0 ||
2259 strucmp(body->subtype,"x-pkcs7-mime")==0 ||
2260 strucmp(body->subtype,"pkcs7-signature")==0 ||
2261 strucmp(body->subtype,"x-pkcs7-signature")==0);
2263 return result;
2268 * Recursively stash a pointer to the decrypted data in our
2269 * manufactured body.
2270 * parameters: type: call of type 1, save the base and header for multipart messages
2271 call of type 0, do not save the base and header for multipart messages
2273 static void
2274 create_local_cache(char *h, char *base, BODY *b, int type)
2276 if(b->type==TYPEMULTIPART){
2277 PART *p;
2279 if(type == 1){
2280 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2281 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2282 } else if(type == 0){
2284 * We don't really want to copy the real body contents. It shouldn't be
2285 * used, and in the case of a message with attachments, we'll be
2286 * duplicating the files multiple times.
2288 cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
2290 for(p=b->nested.part; p; p=p->next)
2291 create_local_cache(h, base, (BODY *) p, type);
2294 else{
2295 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2296 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2301 static long
2302 rfc822_output_func(void *b, char *string)
2304 BIO *bio = (BIO *) b;
2306 return(string ? *string ? (BIO_puts(bio, string) > 0 ? 1L : 0L)
2307 : (BIO_puts(bio, string) >= 0 ? 1L : 0L)
2308 : 0L);
2313 * Attempt to load the private key for the given PERSONAL_CERT.
2314 * This sets the appropriate passphrase globals in order to
2315 * interact with the user correctly.
2317 static int
2318 load_private_key(PERSONAL_CERT *pcert)
2320 if(!pcert->key){
2322 /* Try empty password by default */
2323 char *password = "";
2325 if(ps_global->smime
2326 && (ps_global->smime->need_passphrase
2327 || ps_global->smime->entered_passphrase)){
2328 /* We've already been in here and discovered we need a different password */
2330 if(ps_global->smime->entered_passphrase)
2331 password = (char *) ps_global->smime->passphrase; /* already entered */
2332 else
2333 return 0;
2336 ERR_clear_error();
2338 if(!(pcert->key = load_key(pcert, password, SM_NORMALCERT))){
2339 long err = ERR_get_error();
2341 /* Couldn't load key... */
2343 if(ps_global->smime && ps_global->smime->entered_passphrase){
2345 /* The user got the password wrong maybe? */
2347 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
2348 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
2349 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
2350 else
2351 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2353 /* This passphrase is no good; forget it */
2354 ps_global->smime->entered_passphrase = 0;
2357 if(ps_global->smime){
2358 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2359 ps_global->smime->need_passphrase = 1;
2360 if(ps_global->smime->passphrase_emailaddr){
2361 int i;
2362 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
2363 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
2364 fs_give((void **) ps_global->smime->passphrase_emailaddr);
2367 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
2370 return 0;
2372 else{
2373 /* This key will be cached, so we won't be called again */
2374 if(ps_global->smime){
2375 ps_global->smime->entered_passphrase = 0;
2376 ps_global->smime->need_passphrase = 0;
2380 return 1;
2383 return 0;
2387 static void
2388 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename, char *smime_type)
2390 b->type = TYPEAPPLICATION;
2391 b->subtype = cpystr(type);
2392 b->encoding = ENCBINARY;
2393 b->description = cpystr(description);
2395 b->disposition.type = cpystr("attachment");
2396 set_parameter(&b->disposition.parameter, "filename", filename);
2398 set_parameter(&b->parameter, "name", filename);
2399 if(smime_type && *smime_type)
2400 set_parameter(&b->parameter, "smime-type", smime_type);
2405 * Look for a personal certificate matching the
2406 * given address
2408 PERSONAL_CERT *
2409 match_personal_cert_to_email(ADDRESS *a)
2411 PERSONAL_CERT *pcert = NULL;
2412 char buf[MAXPATH];
2413 char **email;
2414 int i, done;
2416 if(!a || !a->mailbox || !a->host)
2417 return NULL;
2419 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2421 if(ps_global->smime){
2422 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
2423 pcert;
2424 pcert=pcert->next){
2426 if(!pcert->cert)
2427 continue;
2429 email = get_x509_subject_email(pcert->cert);
2431 done = 0;
2432 if(email != NULL){
2433 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
2434 if(email[i] != NULL) done++;
2435 for(i = 0; email[i] != NULL; i++)
2436 fs_give((void **)&email[i]);
2437 fs_give((void **)email);
2440 if(done > 0)
2441 break;
2445 return pcert;
2450 * Look for a personal certificate matching the from
2451 * (or reply_to? in the given envelope)
2453 PERSONAL_CERT *
2454 match_personal_cert(ENVELOPE *env)
2456 PERSONAL_CERT *pcert;
2458 pcert = match_personal_cert_to_email(env->reply_to);
2459 if(!pcert)
2460 pcert = match_personal_cert_to_email(env->from);
2462 return pcert;
2467 * Flatten the given body into its MIME representation.
2468 * Return the result in a BIO.
2470 static BIO *
2471 body_to_bio(BODY *body)
2473 BIO *bio = NULL;
2474 int len;
2476 bio = BIO_new(BIO_s_mem());
2477 if(!bio)
2478 return NULL;
2480 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
2481 pine_write_body_header(body, rfc822_output_func, bio);
2482 pine_rfc822_output_body(body, rfc822_output_func, bio);
2485 * Now need to truncate by two characters since the above
2486 * appends CRLF.
2488 if((len=BIO_ctrl_pending(bio)) > 1){
2489 BUF_MEM *biobuf = NULL;
2491 /* this code used to truncate without closing the bio, and
2492 then resetting the memory, causing non validation in
2493 signatures. Fix contributed by Bernd Edlinger.
2495 BIO_get_mem_ptr(bio, &biobuf);
2496 BIO_set_close(bio, BIO_NOCLOSE);
2497 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
2498 BIO_set_mem_buf(bio, biobuf, BIO_CLOSE);
2501 return bio;
2505 static BIO *
2506 bio_from_store(STORE_S *store)
2508 BIO *ret = NULL;
2510 if(store && store->src == BioType && store->txt){
2511 ret = (BIO *) store->txt;
2514 return(ret);
2518 * Encrypt file; given a path (char *) fp, replace the file
2519 * by an encrypted version of it. If (char *) text is not null, then
2520 * replace the text of (char *) fp by the encrypted version of (char *) text.
2521 * certpath is the FULL path to the file containing the certificate used for
2522 * encryption.
2523 * return value: 0 - failed to encrypt; 1 - success!
2526 encrypt_file(char *fp, char *text, PERSONAL_CERT *pc)
2528 const EVP_CIPHER *cipher = NULL;
2529 STACK_OF(X509) *encerts = NULL;
2530 BIO *out = NULL;
2531 PKCS7 *p7 = NULL;
2532 int rv = 0;
2534 if(pc == NULL)
2535 return 0;
2537 cipher = EVP_aes_256_cbc();
2538 encerts = sk_X509_new_null();
2540 sk_X509_push(encerts, X509_dup(pc->cert));
2542 if(text){
2543 if((out = BIO_new(BIO_s_mem())) != NULL){
2544 (void) BIO_reset(out);
2545 BIO_puts(out, text);
2548 else if((out = BIO_new_file(fp, "rb")) != NULL)
2549 BIO_read_filename(out, fp);
2551 if((p7 = PKCS7_encrypt(encerts, out, cipher, 0)) != NULL){
2552 BIO_set_close(out, BIO_CLOSE);
2553 BIO_free(out);
2554 if((out = BIO_new_file(fp, "w")) != NULL){
2555 BIO_reset(out);
2556 rv = PEM_write_bio_PKCS7(out, p7);
2557 BIO_flush(out);
2561 if(out != NULL)
2562 BIO_free(out);
2563 PKCS7_free(p7);
2564 sk_X509_pop_free(encerts, X509_free);
2566 return rv;
2570 * Encrypt a message on the way out. Called from call_mailer in send.c
2571 * The body may be reallocated.
2574 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
2576 PKCS7 *p7 = NULL;
2577 BIO *in = NULL;
2578 BIO *out = NULL;
2579 const EVP_CIPHER *cipher = NULL;
2580 STACK_OF(X509) *encerts = NULL;
2581 STORE_S *outs = NULL;
2582 PINEFIELD *pf;
2583 ADDRESS *a;
2584 BODY *body = *bodyP;
2585 BODY *newBody = NULL;
2586 int result = 0;
2587 X509 *cert;
2588 char buf[MAXPATH];
2590 dprint((9, "encrypt_outgoing_message()"));
2591 smime_init();
2593 cipher = EVP_aes_256_cbc();
2595 encerts = sk_X509_new_null();
2597 /* Look for a certificate for each of the recipients */
2598 for(pf = header->local; pf && pf->name; pf = pf->next)
2599 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
2600 for(a=*pf->addr; a; a=a->next){
2601 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2603 if((cert = get_cert_for(buf, Public, 1)) != NULL){
2604 sk_X509_push(encerts,cert);
2605 }else{
2606 q_status_message2(SM_ORDER, 1, 1,
2607 _("Unable to find certificate for <%s@%s>"),
2608 a->mailbox, a->host);
2609 goto end;
2614 /* add the sender's certificate so that they can decrypt the message too */
2615 for(a=header->env->from; a ; a = a->next){
2616 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2618 if((cert = get_cert_for(buf, Public, 1)) != NULL
2619 && sk_X509_find(encerts, cert) == -1)
2620 sk_X509_push(encerts,cert);
2623 in = body_to_bio(body);
2625 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
2627 outs = so_get(BioType, NULL, EDIT_ACCESS);
2628 out = bio_from_store(outs);
2630 i2d_PKCS7_bio(out, p7);
2631 (void) BIO_flush(out);
2633 so_seek(outs, 0, SEEK_SET);
2635 newBody = mail_newbody();
2637 newBody->type = TYPEAPPLICATION;
2638 newBody->subtype = cpystr("pkcs7-mime");
2639 newBody->encoding = ENCBINARY;
2641 newBody->disposition.type = cpystr("attachment");
2642 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
2644 newBody->description = cpystr("S/MIME Encrypted Message");
2645 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
2646 set_parameter(&newBody->parameter, "name", "smime.p7m");
2648 newBody->contents.text.data = (unsigned char *) outs;
2650 *bodyP = newBody;
2652 result = 1;
2654 end:
2656 BIO_free(in);
2657 PKCS7_free(p7);
2658 sk_X509_pop_free(encerts, X509_free);
2660 dprint((9, "encrypt_outgoing_message returns %d", result));
2661 return result;
2666 Get (and decode) the body of the given section of msg
2668 static STORE_S*
2669 get_part_contents(long msgno, const char *section)
2671 long len;
2672 gf_o_t pc;
2673 STORE_S *store = NULL;
2674 char *err;
2676 store = so_get(CharStar, NULL, EDIT_ACCESS);
2677 if(store){
2678 gf_set_so_writec(&pc,store);
2680 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
2682 gf_clear_so_writec(store);
2684 so_seek(store, 0, SEEK_SET);
2686 if(err)
2687 so_give(&store);
2690 return store;
2694 static PKCS7 *
2695 get_pkcs7_from_part(long msgno,const char *section)
2697 STORE_S *store = NULL;
2698 PKCS7 *p7 = NULL;
2699 BIO *in = NULL;
2701 store = get_part_contents(msgno, section);
2703 if(store){
2704 if(store->src == CharStar){
2705 int len;
2708 * We're reaching inside the STORE_S structure. We should
2709 * probably have a way to get the length, instead.
2711 len = (int) (store->eod - store->dp);
2712 in = BIO_new_mem_buf(store->txt, len);
2714 else{ /* just copy it */
2715 unsigned char c;
2717 in = BIO_new(BIO_s_mem());
2718 (void) BIO_reset(in);
2720 so_seek(store, 0L, 0);
2721 while(so_readc(&c, store)){
2722 BIO_write(in, &c, 1);
2726 if(in){
2727 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2728 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
2729 /* error */
2732 BIO_free(in);
2735 so_give(&store);
2738 return p7;
2742 same_cert(X509 *x, X509 *cert)
2744 char bufcert[256], bufx[256];
2745 int rv = 0;
2747 get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert), ":");
2748 get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx), ":");
2749 if(strcmp(bufx, bufcert) == 0)
2750 rv = 1;
2752 return rv;
2756 /* extract and save certificates from a PKCS7 package.
2757 * Return value:
2758 * 0 - no errors. Either the certificate was in public/
2759 * or we could save it there.
2760 * < 0 - the certificate was not in public/ and the user
2761 * did not save it there.
2765 smime_extract_and_save_cert(PKCS7 *p7)
2767 STACK_OF(X509) *signers;
2768 X509 *x, *cert;
2769 char **email;
2770 int i, j, rv, already_saved;
2772 /* any signers for this message? */
2773 if((signers = PKCS7_get0_signers(p7, NULL, 0)) == NULL)
2774 return -1;
2776 rv = 0;
2777 for(i = 0; i < sk_X509_num(signers); i++){
2778 if((x = sk_X509_value(signers,i)) == NULL)
2779 continue;
2781 if((email = get_x509_subject_email(x)) != NULL){
2782 for(j = 0; email[j] != NULL; j++){
2783 already_saved = 0;
2784 /* check if we have the certificate for this address */
2785 cert = get_cert_for(email[j], Public, 1);
2786 /* if we have one, check if it is the one packaged */
2787 if(cert != NULL){
2788 already_saved = same_cert(x, cert);
2789 X509_free(cert);
2792 /* if not saved, try to save it */
2793 if(already_saved == 0
2794 && (*pith_smime_confirm_save)(email[j]) == 1){
2795 save_cert_for(email[j], x, Public);
2796 if(ps_global->smime->publiccertlist) /* renew store */
2797 free_certlist(&ps_global->smime->publiccertlist);
2800 /* check if it got saved */
2801 cert = get_cert_for(email[j], Public, 1);
2802 /* if saved, all is good */
2803 if(cert != NULL)
2804 X509_free(cert);
2805 else /* else, we do not have this certificate saved */
2806 rv += -1;
2808 fs_give((void **) &email[j]);
2810 fs_give((void **) email);
2813 sk_X509_free(signers);
2815 return rv;
2819 * Try to verify a signature.
2821 * p7 - the pkcs7 object to verify
2822 * in - the plain data to verify (NULL if not detached)
2823 * out - BIO to which to write the opaque data
2824 * silent - if non zero, do not print errors, only print success.
2826 static int
2827 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
2829 STACK_OF(X509) *otherCerts = NULL;
2830 CertList *cl;
2831 int result, flags;
2832 const char *data;
2833 long err;
2835 if(!s_cert_store){
2836 if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
2837 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2839 return -1;
2842 flags = F_ON(F_USE_CERT_STORE_ONLY, ps_global) ? PKCS7_NOINTERN : 0;
2844 if(ps_global->smime->publiccertlist == NULL){
2845 renew_cert_data(&ps_global->smime->publiccertlist, Public);
2846 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next){
2847 if(cl->x509_cert == NULL){
2848 char *s = strrchr(cl->name, '.');
2849 *s = '\0';
2850 cl->x509_cert = get_cert_for(cl->name, Public, 1);
2851 *s = '.';
2856 if(ps_global->smime->publiccertlist){
2857 otherCerts = sk_X509_new_null();
2858 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next)
2859 if(cl->x509_cert != NULL)
2860 sk_X509_push(otherCerts, X509_dup(cl->x509_cert));
2863 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, flags);
2865 if(result){
2866 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
2868 else{
2869 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2871 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
2874 * verification failed due to an error in verifying a certificate.
2875 * Just write the "out" BIO, and leave. Of course let the user
2876 * know about this. Make two more attempts to get the data out. The
2877 * last one should succeed. In any case, let the user know why it
2878 * failed.
2880 if(PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY) == 0)
2881 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY|PKCS7_NOSIGS);
2883 if (!silent) q_status_message1(SM_ORDER | SM_DING, 3, 3,
2884 _("Couldn't verify S/MIME signature: %s"), (char *) openssl_error_string());
2887 sk_X509_pop_free(otherCerts, X509_free);
2889 return result;
2892 /* Big comment, explaining the mess that exists out there, and how we deal
2893 with it, and also how we solve the problems that are created this way.
2895 When Alpine sends a message, it constructs that message, computes the
2896 signature, but then it forgets the message it signed and reconstructs it
2897 again. Since it signs a message containing a notice about "mime aware
2898 tools", but it does not send that we do not include that in the part
2899 that is signed, and that takes care of much of the problems.
2901 Another problem is what is received from the servers. All servers tested
2902 seem to transmit the message that was signed intact and Alpine can check
2903 the signature correctly. That is not a problem. The problem arises when
2904 the message includes attachments. In this case different servers send
2905 different things, so it will be up to us to figure out what is the text
2906 that was actually signed. Confused? here is the story:
2908 When a message containing and attachment is sent by Alpine, UW-IMAP,
2909 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2910 that was sent by Alpine, but GMX.com, Exchange, and probably other
2911 servers add a trailing \r\n in the message, so when validating the
2912 signature, these messages will not validate. There are several things
2913 that can be done.
2915 1. Add a trailing \r\n to any message that contains attachments, sign that
2916 and send that. In this way, all messages will validate with all
2917 servers.
2919 2. Compatibility mode: If a message has an attachment, contains a trailing
2920 \r\n and does not validate (sent by an earlier version of Alpine),
2921 remove the trailing \r\n and try to revalidate again.
2923 3. We do not add \r\n to validate a message that we sent, because that
2924 would only work in Alpine, and not in any other client. That would
2925 not be a good thing to do.
2927 PART II
2929 Now we have to deal with encrypted and signed messages. The problem is
2930 that c-client makes all its pointers point to "on disk" content, but
2931 since we decrypted the data earlier, we have to make sure of two things.
2932 One is that we saved that data (so we do not have to decrypt it again)
2933 and second that we can use it.
2935 In order to save the data we use create_local_cache, so that we do not
2936 have to redecrypt the message. Once this is saved, c-client functions will
2937 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2939 PART III
2941 When we are trying to verify messages with detached signatures, some
2942 imap servers send incorrect information in the mail_fetch_mime call. By
2943 incorrect I mean that this is not fetched directly from the message, but
2944 it is read from the message, processed, and then the processed part is
2945 sent to us, so this text might not agree with what is in the message,
2946 and so the validation of the signature might fail. However, the good
2947 news is that the message validates if saved to a local folder. This
2948 means that if normal validation does not work we can make it work by
2949 saving the message locally and validating that. This is implemented
2950 below, and causes delay in the display of the message. I am considering
2951 at this time not to do this automatically, but wait for the user to tell
2952 us to do it for them by means of a command available in the
2953 mail_view_screen. This might help in other situations, where a message
2954 is supposed to have an attachment, but it can not be seen in the
2955 processed text. Nevertheless, at this time, this is automatic, and is
2956 causing a delay in the processing of the message, but it is validating
2957 correctly all messages.
2959 PART IV
2961 When the user sends a message as encrypted and signed, this code used to
2962 encrypt first, and then sign the pkcs7 body, but it turns out that some
2963 other clients can not handle these messages. While we could argue that the
2964 other clients need to improve, we will support reading messages in both
2965 ways, and will send messages using this technique; that is, signed first,
2966 encrypted second. It seems that all tested clients support this way, so it
2967 should be safe to do so.
2970 typedef struct smime_filter_s {
2971 void (*filter)(char **, unsigned long *, char **, unsigned long *);
2972 } SMIME_FILTER_S;
2974 SMIME_FILTER_S sig_filter[] = {
2975 {smime_remove_trailing_crlf},
2976 {smime_remove_folding_space}
2979 #define TOTAL_FILTERS (sizeof(sig_filter)/sizeof(sig_filter[0]))
2980 #define TOTAL_SIGFLTR (1 << TOTAL_FILTERS) /* not good, keep filters to a low number */
2982 void
2983 smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen,
2984 char **bodytext, unsigned long *bodylen)
2986 if(*bodylen > 2 && !strncmp(*bodytext+*bodylen-2, "\r\n", 2))
2987 *bodylen -= 2;
2990 void
2991 smime_remove_folding_space(char **mimetext, unsigned long *mimelen,
2992 char **bodytext, unsigned long *bodylen)
2994 char *s = NULL, *t;
2995 unsigned long mlen = *mimelen;
2997 if(*mimetext){
2998 for (s = t = *mimetext; t - *mimetext < *mimelen; ){
2999 if(*t == '\r' && *(t+1) == '\n' && (*(t+2) == '\t' || *(t+2) == ' ')){
3000 *s++ = ' ';
3001 t += 3;
3002 mlen -= 2;
3004 else
3005 *s++ = *t++;
3007 *mimelen = mlen;
3012 smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag)
3014 int result, i, j, flag;
3015 char *mtext, *btext;
3016 unsigned long mlen, blen;
3017 BIO *in;
3019 mtext = mimelen ? fs_get(mimelen+1) : NULL;
3020 btext = fs_get(bodylen+1);
3021 result = 0;
3023 flag = 1; /* silence all failures */
3024 for(i = 1; result == 0 && i < TOTAL_SIGFLTR; i++){
3025 if((in = BIO_new(BIO_s_mem())) == NULL)
3026 return -1;
3028 (void) BIO_reset(in);
3030 if(i+1 == TOTAL_SIGFLTR)
3031 flag = nflag;
3033 if(mimelen)
3034 strncpy(mtext, mimetext, mlen = mimelen);
3035 strncpy(btext, bodytext, blen = bodylen);
3036 for(j = 0; j < TOTAL_FILTERS; j++)
3037 if((i >> j) & 1)
3038 (sig_filter[j].filter)(&mtext, &mlen, &btext, &blen);
3039 if(mtext != NULL)
3040 BIO_write(in, mtext, mlen);
3041 BIO_write(in, btext, blen);
3042 result = do_signature_verify(p7, in, NULL, flag);
3043 BIO_free(in);
3045 if(mtext) fs_give((void **)&mtext);
3046 if(btext) fs_give((void **)&btext);
3047 return result;
3051 * Given a multipart body of type multipart/signed, attempt to verify it.
3052 * Returns non-zero if the body was changed.
3054 static int
3055 do_detached_signature_verify(BODY *b, long msgno, char *section)
3057 PKCS7 *p7 = NULL;
3058 BIO *in = NULL;
3059 PART *p;
3060 int result, modified_the_body = 0;
3061 int flag; /* 1 silent, 0 not silent */
3062 unsigned long mimelen, bodylen;
3063 char newSec[100], *mimetext, *bodytext;
3064 char *what_we_did;
3065 SIZEDTEXT *st;
3067 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"));
3069 smime_init();
3070 mimetext = bodytext = NULL;
3072 /* if it was signed and then encrypted, use the decrypted text
3073 * to check the validity of the signature
3075 if(b->sparep){
3076 if(get_body_sparep_type(b->sparep) == SizedText){
3077 /* bodytext includes mimetext */
3078 st = (SIZEDTEXT *) get_body_sparep_data(b->sparep);
3079 bodytext = (char *) st->data;
3080 bodylen = st->size;
3081 mimetext = NULL;
3082 mimelen = 0L;
3085 else{
3086 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3087 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3088 if(mimetext)
3089 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3091 if(mimetext == NULL || bodytext == NULL)
3092 return modified_the_body;
3095 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3097 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
3098 || (in = BIO_new(BIO_s_mem())) == NULL)
3099 return modified_the_body;
3101 (void) BIO_reset(in);
3102 if(mimetext != NULL)
3103 BIO_write(in, mimetext, mimelen);
3104 BIO_write(in, bodytext, bodylen);
3106 smime_extract_and_save_cert(p7);
3108 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3109 flag = (mimelen == 0 || !IS_REMOTE(ps_global->mail_stream->mailbox))
3110 ? 0 : 1;
3111 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, flag);
3112 if(result < 0)
3113 return modified_the_body;
3114 if(result == 0
3115 && mimelen > 0 /* do not do this for encrypted messages */
3116 && IS_REMOTE(ps_global->mail_stream->mailbox)){
3117 char *fetch;
3118 unsigned long hlen, tlen;
3119 STORE_S *msg_so;
3121 BIO_free(in);
3122 if((in = BIO_new(BIO_s_mem())) != NULL
3123 && (fetch = mail_fetch_header(ps_global->mail_stream, msgno, NULL,
3124 NULL, &hlen, FT_PEEK)) != NULL
3125 && (msg_so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL
3126 && so_nputs(msg_so, fetch, (long) hlen)
3127 && (fetch = pine_mail_fetch_text(ps_global->mail_stream, msgno, NULL,
3128 &tlen, FT_PEEK)) != NULL
3129 && so_nputs(msg_so, fetch, tlen)){
3130 STRING bs;
3131 char *h = (char *) so_text(msg_so);
3132 char *bstart = strstr(h, "\r\n\r\n");
3133 ENVELOPE *env;
3134 BODY *body, *tmpB;
3136 bstart += 4;
3137 INIT(&bs, mail_string, bstart, tlen);
3138 rfc822_parse_msg_full(&env, &body, h, bstart-h-4, &bs, BADHOST, 0, 0);
3139 mail_free_envelope(&env);
3141 mail_free_body_part(&b->nested.part);
3142 tmpB = mail_body_section(body, (unsigned char *) section);
3143 if(MIME_MSG(tmpB->type, tmpB->subtype))
3144 b->nested.part = tmpB->nested.msg->body->nested.part;
3145 else
3146 b->nested.part = tmpB->nested.part;
3147 create_local_cache(bstart, bstart, &b->nested.part->body, 1);
3148 modified_the_body = 1;
3150 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3152 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3154 if(mimetext)
3155 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3157 if (mimetext == NULL || bodytext == NULL)
3158 return modified_the_body;
3160 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3162 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL)
3163 return modified_the_body;
3165 (void) BIO_reset(in);
3166 BIO_write(in, mimetext, mimelen);
3167 BIO_write(in, bodytext, bodylen);
3168 so_give(&msg_so);
3170 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3171 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, 0);
3172 if(result < 0)
3173 return modified_the_body;
3179 BIO_free(in);
3180 if(b->subtype)
3181 fs_give((void**) &b->subtype);
3183 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3184 b->encoding = ENC8BIT;
3186 if(b->description)
3187 fs_give ((void**) &b->description);
3189 what_we_did = result ? _("This message was cryptographically signed.") :
3190 _("This message was cryptographically signed but the signature could not be verified.");
3192 b->description = cpystr(what_we_did);
3194 b->sparep = create_body_sparep(P7Type, p7);
3196 p = b->nested.part;
3198 /* p is signed plaintext */
3199 if(p && p->next)
3200 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
3202 modified_the_body = 1;
3204 return modified_the_body;
3208 PERSONAL_CERT *
3209 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
3211 PERSONAL_CERT *x = NULL;
3213 if(ps_global->smime){
3214 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
3215 X509 *mine;
3217 mine = x->cert;
3219 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,X509_get_issuer_name(mine)) &&
3220 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,X509_get_serialNumber(mine))){
3221 break;
3226 return x;
3230 static PERSONAL_CERT *
3231 find_certificate_matching_pkcs7(PKCS7 *p7)
3233 int i;
3234 STACK_OF(PKCS7_RECIP_INFO) *recips;
3235 PERSONAL_CERT *x = NULL;
3237 recips = p7->d.enveloped->recipientinfo;
3239 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
3240 PKCS7_RECIP_INFO *ri;
3242 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
3244 if((x=find_certificate_matching_recip_info(ri))!=0){
3245 break;
3249 return x;
3252 /* decrypt an encrypted file.
3253 Args: fp - the path to the encrypted file.
3254 rv - a code that tells the caller what happened inside the function
3255 pcert - a personal certificate that was used to encrypt this file
3256 Returns the decoded text allocated in a char *, whose memory must be
3257 freed by caller
3260 char *
3261 decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc)
3263 PKCS7 *p7 = NULL;
3264 char *text, *tmp;
3265 BIO *in = NULL, *out = NULL;
3266 long unsigned int len;
3267 void *ret;
3269 if(rv) *rv = -1; /* assume failure */
3270 if(pc == NULL || (text = read_file(fp, 0)) == NULL || *text == '\0')
3271 return NULL;
3273 tmp = strchr(text + strlen("-----BEGIN PKCS7-----") + strlen(NEWLINE), '-');
3274 if(tmp != NULL) *tmp = '\0';
3275 tmp = text + strlen("-----BEGIN PKCS7-----") + strlen(NEWLINE);
3277 ret = rfc822_base64((unsigned char *)tmp, strlen(tmp), &len);
3279 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
3280 p7 = d2i_PKCS7_bio(in, NULL);
3281 BIO_free(in);
3284 if (text) fs_give((void **)&text);
3285 if (ret) fs_give((void **)&ret);
3287 if (rv) *rv = pc->key == NULL ? -1 : 1;
3289 out = BIO_new(BIO_s_mem());
3290 (void) BIO_reset(out);
3292 if(PKCS7_decrypt(p7, pc->key, pc->cert, out, 0) != 0){
3293 len = BIO_get_mem_data(out, &tmp);
3294 text = fs_get((len+1)*sizeof(char));
3295 strncpy(text, tmp, len);
3296 text[len] = '\0';
3297 BIO_free(out);
3298 } else
3299 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3300 (char *) openssl_error_string());
3301 PKCS7_free(p7);
3303 return text;
3307 * Try to decode (decrypt or verify a signature) a PKCS7 body
3308 * Returns non-zero if something was changed.
3310 static int
3311 do_decoding(BODY *b, long msgno, const char *section)
3313 int modified_the_body = 0;
3314 BIO *out = NULL;
3315 PKCS7 *p7 = NULL;
3316 X509 *recip = NULL;
3317 EVP_PKEY *key = NULL;
3318 PERSONAL_CERT *pcert = NULL;
3319 char *what_we_did = "";
3320 char null[1];
3322 dprint((9, "do_decoding(msgno=%ld type=%d subtype=%s section=%s)", msgno, b->type, b->subtype ? b->subtype : "NULL", (section && *section) ? section : (section != NULL) ? "Top" : "NULL"));
3323 null[0] = '\0';
3324 smime_init();
3327 * Extract binary data from part to an in-memory store
3330 if(b->sparep){
3331 if(get_body_sparep_type(b->sparep) == P7Type)
3332 p7 = (PKCS7*) get_body_sparep_data(b->sparep);
3334 else{
3335 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
3336 if(!p7){
3337 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
3338 (char*) openssl_error_string());
3339 goto end;
3343 * Save the PKCS7 object for later dealings by the user interface.
3344 * It will be cleaned up when the body is garbage collected.
3346 b->sparep = create_body_sparep(P7Type, p7);
3349 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
3351 if(PKCS7_type_is_signed(p7)){
3352 int sigok;
3354 out = BIO_new(BIO_s_mem());
3355 (void) BIO_reset(out);
3356 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3358 sigok = do_signature_verify(p7, NULL, out, 0);
3360 what_we_did = sigok ? _("This message was cryptographically signed.") :
3361 _("This message was cryptographically signed but the signature could not be verified.");
3363 /* make sure it's null terminated */
3364 BIO_write(out, null, 1);
3366 else if(!PKCS7_type_is_enveloped(p7)){
3367 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
3368 goto end;
3370 else{ /* It *is* enveloped */
3371 int decrypt_result;
3373 what_we_did = _("This message was encrypted.");
3375 /* now need to find a cert that can decrypt this */
3376 pcert = find_certificate_matching_pkcs7(p7);
3378 if(!pcert){
3379 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3380 goto end;
3383 recip = pcert->cert;
3385 if(!load_private_key(pcert)
3386 && ps_global->smime
3387 && ps_global->smime->need_passphrase
3388 && !ps_global->smime->already_auto_asked){
3389 /* Couldn't load key with blank password, ask user */
3390 ps_global->smime->already_auto_asked = 1;
3391 if(pith_opt_smime_get_passphrase){
3392 (*pith_opt_smime_get_passphrase)();
3393 load_private_key(pcert);
3397 key = pcert->key;
3398 if(!key)
3399 goto end;
3401 out = BIO_new(BIO_s_mem());
3402 (void) BIO_reset(out);
3403 BIO_puts(out, "MIME-Version: 1.0\r\n");
3405 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
3407 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3408 forget_private_keys();
3410 if(!decrypt_result){
3411 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3412 (char*) openssl_error_string());
3413 goto end; }
3415 BIO_write(out, null, 1);
3419 * We've now produced a flattened MIME object in BIO out.
3420 * It needs to be turned back into a BODY.
3423 if(out){
3424 BODY *body;
3425 ENVELOPE *env;
3426 char *h = NULL;
3427 char *bstart;
3428 STRING s;
3429 BUF_MEM *bptr = NULL;
3430 int we_free = 0;
3432 BIO_get_mem_ptr(out, &bptr);
3433 if(bptr)
3434 h = bptr->data;
3436 /* look for start of body */
3437 bstart = strstr(h, "\r\n\r\n");
3439 if(!bstart){
3441 * Some clients do not canonicalize before encrypting, so
3442 * look for "\n\n" instead.
3444 bstart = strstr(h, "\n\n");
3445 if(bstart){
3446 int lines;
3447 char *s, *t;
3448 for(lines = 0, bstart = h; (bstart = strchr(bstart, '\n')) != NULL;
3449 bstart++, lines++);
3450 h = t = fs_get(strlen(bptr->data) + lines + 1);
3451 we_free++;
3452 for(s = bptr->data; *s != '\0'; s++)
3453 if(*s == '\n' && *(s-1) != '\r'){
3454 *t++ = '\r';
3455 *t++ = '\n';
3457 else
3458 *t++ = *s;
3459 *t = '\0';
3460 bstart = strstr(h, "\r\n\r\n");
3464 if(!bstart){
3465 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
3467 else{
3468 SIZEDTEXT *st;
3469 bstart += 4; /* skip over CRLF*2 */
3471 INIT(&s, mail_string, bstart, strlen(bstart));
3472 rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
3473 mail_free_envelope(&env); /* Don't care about this */
3475 if(body->type == TYPEMULTIPART
3476 && !strucmp(body->subtype, "SIGNED")){
3477 char *cookie = NULL;
3478 PARAMETER *param;
3479 for (param = body->parameter; param && !cookie; param = param->next)
3480 if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value;
3481 if(cookie != NULL){
3482 st = fs_get(sizeof(SIZEDTEXT));
3483 st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */
3484 st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4);
3485 body->sparep = create_body_sparep(SizedText, (void *)st);
3487 else
3488 q_status_message(SM_ORDER, 3, 3, _("Couldn't find cookie in attachment list."));
3490 body->mime.offset = 0;
3491 body->mime.text.size = 0;
3494 * Now convert original body (application/pkcs7-mime)
3495 * to a multipart body with one sub-part (the decrypted body).
3496 * Note that the sub-part may also be multipart!
3499 b->type = TYPEMULTIPART;
3500 if(b->subtype)
3501 fs_give((void **) &b->subtype);
3504 * This subtype is used in mailview.c to annotate the display of
3505 * encrypted or signed messages. We know for sure then that it's a PKCS7
3506 * part because the sparep field is set to the PKCS7 object (see above).
3508 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3509 b->encoding = ENC8BIT;
3511 if(b->description)
3512 fs_give((void **) &b->description);
3514 b->description = cpystr(what_we_did);
3516 if(b->disposition.type)
3517 fs_give((void **) &b->disposition.type);
3519 if(b->contents.text.data)
3520 fs_give((void **) &b->contents.text.data);
3522 if(b->parameter)
3523 mail_free_body_parameter(&b->parameter);
3525 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3526 b->nested.part = fs_get(sizeof(PART));
3527 b->nested.part->body = *body;
3528 b->nested.part->next = NULL;
3530 fs_give((void**) &body);
3533 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3534 * the decrypted data. Otherwise, it'll try to load it from the original
3535 * data. Eek.
3537 create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body, 0);
3539 modified_the_body = 1;
3541 if(we_free)
3542 fs_give((void **) &h);
3545 end:
3546 if(out)
3547 BIO_free(out);
3549 return modified_the_body;
3554 * Recursively handle PKCS7 bodies in our message.
3556 * Returns non-zero if some fiddling was done.
3558 static int
3559 do_fiddle_smime_message(BODY *b, long msgno, char *section)
3561 int modified_the_body = 0;
3563 if(!b)
3564 return 0;
3566 dprint((9, "do_fiddle_smime_message(msgno=%ld type=%d subtype=%s section=%s)", msgno, b->type, b->subtype ? b->subtype : "NULL", (section && *section) ? section : (section != NULL) ? "Top" : "NULL"));
3568 if(is_pkcs7_body(b)){
3570 if(do_decoding(b, msgno, section)){
3572 * b should now be a multipart message:
3573 * fiddle it too in case it's been multiply-encrypted!
3576 /* fallthru */
3577 modified_the_body = 1;
3581 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
3583 PART *p;
3584 int partNum;
3585 char newSec[100];
3587 if(MIME_MULT_SIGNED(b->type, b->subtype)){
3591 * Ahah. We have a multipart signed entity.
3593 * Multipart/signed
3594 * part 1 (signed thing)
3595 * part 2 (the pkcs7 signature)
3597 * We're going to convert that to
3599 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3600 * part 1 (signed thing)
3601 * part 2 has been freed
3603 * We also extract the signature from part 2 and save it
3604 * in the multipart body->sparep, and we add a description
3605 * in the multipart body->description.
3608 * The results of a decrypted message will be similar. It
3609 * will be
3611 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3612 * part 1 (decrypted thing)
3615 modified_the_body += do_detached_signature_verify(b, msgno, section);
3617 else if(MIME_MSG(b->type, b->subtype)){
3618 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
3620 else{
3622 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
3623 /* Append part number to the section string */
3625 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
3627 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
3632 return modified_the_body;
3637 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3638 * Returns non-zero if something was changed.
3641 fiddle_smime_message(BODY *b, long msgno)
3643 return do_fiddle_smime_message(b, msgno, "");
3647 /********************************************************************************/
3651 * Output a string in a distinctive style
3653 void
3654 gf_puts_uline(char *txt, gf_o_t pc)
3656 pc(TAG_EMBED); pc(TAG_BOLDON);
3657 gf_puts(txt, pc);
3658 pc(TAG_EMBED); pc(TAG_BOLDOFF);
3661 /* get_chain_for_cert: error and level are mandatory arguments */
3662 STACK_OF(X509) *
3663 get_chain_for_cert(X509 *cert, int *error, int *level)
3665 STACK_OF(X509) *chain = NULL;
3666 X509_STORE_CTX *ctx;
3667 X509 *x, *xtmp;
3668 int rc; /* return code */
3670 *level = -1;
3671 *error = 0;
3672 ERR_clear_error();
3673 if((s_cert_store != NULL) && (ctx = X509_STORE_CTX_new()) != NULL){
3674 X509_STORE_set_flags(s_cert_store, 0);
3675 if(!X509_STORE_CTX_init(ctx, s_cert_store, cert, NULL))
3676 *error = X509_STORE_CTX_get_error(ctx);
3677 else if((chain = sk_X509_new_null()) != NULL){
3678 for(x = cert; ; x = xtmp){
3679 if(++*level > 0)
3680 sk_X509_push(chain, X509_dup(x));
3681 rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x);
3682 if(rc < 0)
3683 *error = 1;
3684 if(rc <= 0)
3685 break;
3686 if(!X509_check_issued(xtmp, xtmp))
3687 break;
3690 X509_STORE_CTX_free(ctx);
3692 return chain;
3697 * Sign a message. Called from call_mailer in send.c.
3699 * This takes the header for the outgoing message as well as a pointer
3700 * to the current body (which may be reallocated).
3701 * The last argument (BODY **bp) is an argument that tells Alpine
3702 * if the body has 8 bit. if *bp is not null we compute two signatures
3703 * one for the quoted-printable encoded message, and another for the
3704 * 8bit encoded message. We return the signature for the 8bit encoded
3705 * part in p2->body.mime.text.data.
3706 * The reason why we compute two signatures is so that we can decide
3707 * which one to use later, and we only do it in the case that *bp is
3708 * not null. If we did not do this, then we might not be able to sign
3709 * a message until we log in to the smtp server, so instead of doing
3710 * that, we get ready for any possible situation we might find.
3713 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp)
3715 STORE_S *outs = NULL;
3716 STORE_S *outs_2 = NULL;
3717 BODY *body = *bodyP;
3718 BODY *newBody = NULL;
3719 PART *p1 = NULL;
3720 PART *p2 = NULL;
3721 PERSONAL_CERT *pcert;
3722 BIO *in = NULL;
3723 BIO *in_2 = NULL;
3724 BIO *out = NULL;
3725 BIO *out_2 = NULL;
3726 PKCS7 *p7 = NULL;
3727 PKCS7 *p7_2 = NULL;
3728 STACK_OF(X509) *chain;
3729 const EVP_MD *md = EVP_sha256(); /* use this digest instead of sha1 */
3730 int result = 0, error;
3731 int flags = dont_detach ? 0 : PKCS7_DETACHED;
3732 int level;
3734 dprint((9, "sign_outgoing_message()"));
3736 smime_init();
3738 /* Look for a private key matching the sender address... */
3740 pcert = match_personal_cert(header->env);
3742 if(!pcert){
3743 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
3744 goto end;
3747 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
3748 /* Couldn't load key with blank password, try again */
3749 if(pith_opt_smime_get_passphrase){
3750 (*pith_opt_smime_get_passphrase)();
3751 load_private_key(pcert);
3755 if(!pcert->key)
3756 goto end;
3758 if(((chain = get_chain_for_cert(pcert->cert, &error, &level)) != NULL && error)
3759 || level == 0){
3760 sk_X509_pop_free(chain, X509_free);
3761 chain = NULL;
3764 if(error)
3765 q_status_message(SM_ORDER, 1, 1,
3766 _("Not all certificates needed to verify signature included in signed message"));
3768 in = body_to_bio(body);
3770 flags |= PKCS7_PARTIAL;
3771 if((p7 = PKCS7_sign(NULL, NULL, chain, in, flags)) != NULL
3772 && PKCS7_sign_add_signer(p7, pcert->cert, pcert->key, md, flags))
3773 PKCS7_final(p7, in, flags);
3775 if(bp && *bp){
3776 int i, save_encoding;
3778 for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++);
3780 if(i > ENCMAX){ /* no empty encoding slots! */
3781 *bp = NULL;
3783 else {
3784 save_encoding = (*bp)->encoding;
3785 body_encodings[(*bp)->encoding = i] = body_encodings[ENC8BIT];
3787 in_2 = body_to_bio(body);
3789 body_encodings[i] = NULL;
3790 (*bp)->encoding = save_encoding;
3794 if(bp && *bp){
3795 if((p7_2 = PKCS7_sign(NULL, NULL, chain, in_2, flags)) != NULL
3796 && PKCS7_sign_add_signer(p7_2, pcert->cert, pcert->key, md, flags))
3797 PKCS7_final(p7_2, in_2, flags);
3800 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3801 forget_private_keys();
3803 if(chain)
3804 sk_X509_pop_free(chain, X509_free);
3806 if(!p7){
3807 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
3808 goto end;
3811 outs = so_get(BioType, NULL, EDIT_ACCESS);
3812 out = bio_from_store(outs);
3814 i2d_PKCS7_bio(out, p7);
3815 (void) BIO_flush(out);
3817 so_seek(outs, 0, SEEK_SET);
3819 if(bp && *bp && p7_2){
3820 outs_2 = so_get(BioType, NULL, EDIT_ACCESS);
3821 out_2 = bio_from_store(outs_2);
3823 i2d_PKCS7_bio(out_2, p7_2);
3824 (void) BIO_flush(out_2);
3826 so_seek(outs_2, 0, SEEK_SET);
3829 if((flags&PKCS7_DETACHED)==0){
3831 /* the simple case: the signed data is in the pkcs7 object */
3833 newBody = mail_newbody();
3835 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3837 newBody->contents.text.data = (unsigned char *) outs;
3838 *bodyP = newBody;
3840 result = 1;
3842 else{
3845 * OK.
3846 * We have to create a new body as follows:
3848 * multipart/signed; blah blah blah
3849 * reference to existing body
3851 * pkcs7 object
3854 newBody = mail_newbody();
3856 newBody->type = TYPEMULTIPART;
3857 newBody->subtype = cpystr("signed");
3858 newBody->encoding = ENC7BIT;
3860 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
3861 set_parameter(&newBody->parameter, "micalg", "sha-256");
3863 p1 = mail_newbody_part();
3864 p2 = mail_newbody_part();
3867 * This is nasty. We're just copying the body in here,
3868 * but since our newBody is freed at the end of call_mailer,
3869 * we mustn't let this body (the original one) be freed twice.
3871 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
3873 p1->next = p2;
3875 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL);
3876 p2->body.mime.text.data = (unsigned char *) outs_2;
3877 p2->body.contents.text.data = (unsigned char *) outs;
3879 newBody->nested.part = p1;
3881 *bodyP = newBody;
3883 result = 1;
3886 end:
3888 PKCS7_free(p7);
3889 BIO_free(in);
3891 if(bp && *bp){
3892 if(p7_2) PKCS7_free(p7_2);
3893 BIO_free(in_2);
3896 dprint((9, "sign_outgoing_message returns %d", result));
3897 return result;
3901 SMIME_STUFF_S *
3902 new_smime_struct(void)
3904 SMIME_STUFF_S *ret = NULL;
3906 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
3907 memset((void *) ret, 0, sizeof(*ret));
3908 ret->publictype = Nada;
3910 return ret;
3914 static void
3915 free_smime_struct(SMIME_STUFF_S **smime)
3917 if(smime && *smime){
3918 if((*smime)->passphrase_emailaddr){
3919 int i;
3920 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
3921 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
3922 fs_give((void **) (*smime)->passphrase_emailaddr);
3925 if((*smime)->publicpath)
3926 fs_give((void **) &(*smime)->publicpath);
3928 if((*smime)->publiccertlist)
3929 free_certlist(&(*smime)->publiccertlist);
3931 if((*smime)->backuppubliccertlist)
3932 free_certlist(&(*smime)->backuppubliccertlist);
3934 if((*smime)->cacertlist)
3935 free_certlist(&(*smime)->cacertlist);
3937 if((*smime)->backupcacertlist)
3938 free_certlist(&(*smime)->backupcacertlist);
3940 if((*smime)->privatecertlist)
3941 free_certlist(&(*smime)->privatecertlist);
3943 if((*smime)->backupprivatecertlist)
3944 free_certlist(&(*smime)->backupprivatecertlist);
3946 if((*smime)->publiccontent)
3947 fs_give((void **) &(*smime)->publiccontent);
3949 if((*smime)->privatepath)
3950 fs_give((void **) &(*smime)->privatepath);
3952 if((*smime)->personal_certs){
3953 PERSONAL_CERT *pc;
3955 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
3956 free_personal_certs(&pc);
3957 (*smime)->personal_certs = NULL;
3960 if((*smime)->privatecontent)
3961 fs_give((void **) &(*smime)->privatecontent);
3963 if((*smime)->capath)
3964 fs_give((void **) &(*smime)->capath);
3966 if((*smime)->cacontent)
3967 fs_give((void **) &(*smime)->cacontent);
3969 fs_give((void **) smime);
3973 #endif /* SMIME */