* Replace body.c by body.obj in pith/makefile.wnt to fix
[alpine.git] / pith / smime.c
blob9cab8ee2010d35bb6fd6d394addb7c80cb47048a
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: smime.c 1176 2008-09-29 21:16:42Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2018 Eduardo Chappa
8 * Copyright 2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
20 * This is based on a contribution from Jonathan Paisley
22 * File: smime.c
23 * Author: paisleyj@dcs.gla.ac.uk
24 * Date: 01/2001
28 #include "../pith/headers.h"
30 #ifdef SMIME
32 #include "../pith/osdep/canaccess.h"
33 #include "../pith/helptext.h"
34 #include "../pith/store.h"
35 #include "../pith/status.h"
36 #include "../pith/detach.h"
37 #include "../pith/conf.h"
38 #include "../pith/smkeys.h"
39 #include "../pith/smime.h"
40 #include "../pith/mailpart.h"
41 #include "../pith/reply.h"
42 #include "../pith/tempfile.h"
43 #include "../pith/readfile.h"
44 #include "../pith/remote.h"
45 #include "../pith/body.h"
46 #ifdef PASSFILE
47 #include "../pith/imap.h"
48 #endif /* PASSFILE */
50 #include <openssl/buffer.h>
51 #include <openssl/x509v3.h>
52 #include <openssl/evp.h>
54 /* internal prototypes */
55 static void forget_private_keys(void);
56 static int app_RAND_load_file(const char *file);
57 static void openssl_extra_randomness(void);
58 static int app_RAND_write_file(const char *file);
59 static const char *openssl_error_string(void);
60 static int load_private_key(PERSONAL_CERT *pcert);
61 static void create_local_cache(char *h, char *base, BODY *b, int type);
62 static long rfc822_output_func(void *b, char *string);
63 static void setup_pkcs7_body_for_signature(BODY *b, char *description,
64 char *type, char *filename, char *smime_type);
65 static BIO *body_to_bio(BODY *body);
66 static BIO *bio_from_store(STORE_S *store);
67 static STORE_S *get_part_contents(long msgno, const char *section);
68 static PKCS7 *get_pkcs7_from_part(long msgno, const char *section);
69 static int do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent);
70 static int do_detached_signature_verify(BODY *b, long msgno, char *section);
71 static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7);
72 static int do_decoding(BODY *b, long msgno, const char *section);
73 static void free_smime_struct(SMIME_STUFF_S **smime);
74 static void setup_storage_locations(void);
75 static int copy_container_to_dir(WhichCerts which);
76 static int do_fiddle_smime_message(BODY *b, long msgno, char *section);
77 void setup_privatekey_storage(void);
78 int smime_extract_and_save_cert(PKCS7 *p7, int check_cert);
79 int same_cert(X509 *, X509 *);
80 #ifdef PASSFILE
81 int load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile, char **certfile, EVP_PKEY **pkey, X509 **pcert);
82 #endif /* PASSFILE */
83 EVP_PKEY *load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *);
84 void smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen);
85 void smime_remove_folding_space(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen);
86 int smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag);
88 int (*pith_opt_smime_get_passphrase)(void);
89 int (*pith_smime_import_certificate)(char *, char *, char *, size_t);
90 int (*pith_smime_enter_password)(char *, char *, size_t);
91 int (*pith_smime_confirm_save)(char *);
93 static X509_STORE *s_cert_store;
95 /* State management for randomness functions below */
96 static int seeded = 0;
98 #ifdef PASSFILE
99 /*
100 * This code does not work in Windows, because of the PASSFILE thing, so
101 * I did not try to fix it. If you think it does need to be applied to
102 * the Windows version of alpine, there are more changes that are needed
103 * than fixing this function in this module. E. Chappa 09/28/17.
105 * load key from pathkeydir and cert from pathcertdir. It chooses the first
106 * key/certificate pair that matches. Delete pairs that you do not want used,
107 * if you do not want them selected. All parameters must be non-null.
108 * Memory freed by caller.
109 * Return values:
110 * -1 : user cancelled load
111 * 0 : load was successful
112 * 1 : there was an error in the loading.
115 load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile,
116 char **certfile, EVP_PKEY **pkey, X509 **pcert)
118 char buf[MAXPATH+1], pathkey[MAXPATH+1], prompt[MAILTMPLEN];
119 DIR *dirp;
120 struct dirent *d;
121 int b = 0, ret = 1; /* assume error */
123 if(pathkeydir == NULL || pathcertdir == NULL || keyfile == NULL
124 || pkey == NULL || certfile == NULL || pcert == NULL)
125 return 1;
127 *keyfile = NULL;
128 *certfile = NULL;
129 *pkey = NULL;
130 *pcert = NULL;
132 if((dirp = opendir(pathkeydir)) != NULL){
133 while(b == 0 && (d=readdir(dirp)) != NULL){
134 size_t ll;
136 if((ll=strlen(d->d_name)) && ll > 4){
137 if(!strcmp(d->d_name+ll-4, ".key")){
138 strncpy(buf, d->d_name, sizeof(buf));
139 buf[sizeof(buf)-1] = '\0';
140 build_path(pathkey, pathkeydir, buf, sizeof(pathkey));
141 buf[strlen(buf)-4] = '\0';
142 snprintf(prompt, sizeof(prompt),
143 _("Enter password of key <%s> to unlock password file: "), buf);
144 if((*pkey = load_pkey_with_prompt(pathkey, NULL, prompt, &ret)) != NULL){
145 if(load_cert_for_key(pathcertdir, *pkey, certfile, pcert)){
146 b = 1; /* break */
147 *keyfile = cpystr(buf);
148 } else {
149 EVP_PKEY_free(*pkey);
150 *pkey = NULL;
151 q_status_message1(SM_ORDER, 0, 2,
152 _("Cannot find certificate that matches key <%s>. Continuing..."), buf);
158 closedir(dirp);
160 return ret;
164 /* setup a key and certificate to encrypt and decrypt a password file.
165 * These files will be saved in the .alpine-smime/.pwd directory, but its
166 * location can be setup in the command line with the -pwdcertdir option.
167 * Here are the rules:
169 * Check if the .alpine-smime/.pwd (or -pwdcertdir directory) exists,
170 * if not create it. If we are successful, move to the next step
172 * - If the user has a key/cert pair, in the .alpine-smime/.pwd dir
173 * setup is successful;
174 * - if the user does not have a key/cert pair, look to see if
175 * ps_global->smime->personal_certs is already setup, if so, use it.
176 * - if ps_global->smime->personal_certs is not set up, see if we can
177 * find a certificate/cert pair in the default locations at compilation
178 * time. (~/.alpine-smime/private and ~/.alpine-smime/public
179 * - if none of this is successful, create a key/certificate pair
180 * (TODO: implement this)
181 * - in any other case, setup is not successful.
183 * If setup is successful, setup ps_global->pwdcert.
184 * If any of this fails, ps_global->pwdcert will be null.
185 * Ok, that should do it.
187 * return values: 0 - everything is normal
188 * 1 - User could not unlock key or no key in directory.
189 * 2 - User cancelled to create self signed certificate
190 * -1 - we do not know which directory to use
191 * -2 - "-pwdcertdir" was given by user, but directory does not exist
192 * -3 - "DF_PASSWORD_DIR" exists but it is not a directory!!??
193 * -4 - we tried to create DF_PASSWORD_DIR but failed.
194 * -5 - password directory exists, but it is empty
198 setup_pwdcert(void **pwdcert)
200 int rv;
201 int we_inited = 0;
202 int setup_dir = 0; /* make it non zero if we know which dir to use */
203 struct stat sbuf;
204 char pathdir[MAXPATH+1], pathkey[MAXPATH+1], fpath[MAXPATH+1], pathcert[MAXPATH+1];
205 char fpath2[MAXPATH+1], prompt[MAILTMPLEN];
206 char *keyfile, *certfile, *text;
207 EVP_PKEY *pkey = NULL;
208 X509 *pcert = NULL;
209 PERSONAL_CERT *pc, *pc2 = NULL;
210 static int was_here = 0;
212 if(pwdcert == NULL || was_here == 1)
213 return -1;
215 was_here++;
216 if(ps_global->pwdcertdir){
217 if(our_stat(ps_global->pwdcertdir, &sbuf) == 0
218 && ((sbuf.st_mode & S_IFMT) == S_IFDIR)){
219 setup_dir++;
220 strncpy(pathdir, ps_global->pwdcertdir, sizeof(pathdir));
221 pathdir[sizeof(pathdir)-1] = '\0';
223 else rv = -2;
224 } else {
225 smime_path(DF_PASSWORD_DIR, pathdir, sizeof(pathdir));
226 if(our_stat(pathdir, &sbuf) == 0){
227 if((sbuf.st_mode & S_IFMT) == S_IFDIR)
228 setup_dir++;
229 else rv = -3;
230 } else if(can_access(pathdir, ACCESS_EXISTS) != 0
231 && our_mkpath(pathdir, 0700) == 0)
232 setup_dir++;
233 else rv = -4;
236 if(setup_dir == 0){
237 was_here = 0;
238 return rv;
241 if(load_key_and_cert(pathdir, pathdir, &keyfile, &certfile, &pkey, &pcert) < 0){
242 was_here = 0;
243 return 1;
246 if(ps_global->pwdcertdir == NULL){ /* save the result of pwdcertdir */
247 ps_global->pwdcertdir = cpystr(pathdir);
248 /* if the user gave a pwdcertdir and there is nothing there, do not
249 * continue. Let the user initialize on their own this directory.
251 if(certfile == NULL || keyfile == NULL){
252 was_here = 0;
253 return -5;
257 if(certfile && keyfile){
258 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
259 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
260 pc->name = keyfile;
261 pc->key = pkey;
262 pc->cert = pcert;
263 pc->cname = certfile;
264 *pwdcert = (void *) pc;
265 was_here = 0;
266 return 0;
269 /* look to see if there are any certificates lying around, first
270 * we try to load ps_global->smime to see if that has information
271 * we can use. If we are the process filling the smime structure
272 * we deinit at the end, since this might not do a full init.
274 if(ps_global && ps_global->smime && !ps_global->smime->inited){
275 we_inited++;
276 smime_init();
279 /* at this point ps_global->smime->inited == 1 */
280 if(ps_global->smime && ps_global->smime->personal_certs != NULL){
281 pc = (PERSONAL_CERT *) ps_global->smime->personal_certs;
282 if(ps_global->smime->privatetype == Directory){
283 build_path(pathkey, ps_global->smime->privatepath, pc->name, sizeof(pathkey));
284 strncat(pathkey, ".key", 4);
285 pathkey[sizeof(pathkey)-1] = '\0';
286 text = NULL;
287 } else if (ps_global->smime->privatetype == Container){
288 if(pc->keytext == NULL){ /* we should *never* be here, but just in case */
289 if(ps_global->smime->privatecontent != NULL){
290 char tmp[MAILTMPLEN], *s, *t, c;
291 snprintf(tmp, sizeof(tmp), "%s%s", EMAILADDRLEADER, pc->name);
292 tmp[sizeof(tmp)-1] = '\0';
293 if((s = strstr(ps_global->smime->privatecontent, tmp)) != NULL){
294 if((t = strstr(s+strlen(tmp), EMAILADDRLEADER)) != NULL){
295 c = *t;
296 *t = '\0';
297 pc->keytext = cpystr(s + strlen(tmp) + strlen(NEWLINE));
298 *t = c;
300 else
301 pc->keytext = cpystr(s + strlen(tmp) + strlen(NEWLINE));
305 if(pc->keytext != NULL) /* we should go straigth here */
306 text = pc->keytext;
307 } else if (ps_global->smime->privatetype == Keychain){
308 pathkey[0] = '\0'; /* no apple key chain support yet */
309 text = NULL;
311 if((pathkey && *pathkey) || text){
312 snprintf(prompt, sizeof(prompt),
313 _("Enter password of key <%s> to unlock password file: "), pc->name);
315 if((pkey = load_pkey_with_prompt(pathkey, text, prompt, NULL)) != NULL){
316 pc2 = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
317 memset((void *)pc2, 0, sizeof(PERSONAL_CERT));
318 pc2->name = cpystr(pc->name);
319 pc2->key = pkey;
320 pc2->cert = X509_dup(pc->cert);
322 /* now copy the keys and certs, starting by the key... */
323 build_path(fpath, pathdir, pc->name, sizeof(fpath));
324 strncat(fpath, ".key", 4);
325 fpath[sizeof(fpath)-1] = '\0';
326 if(our_stat(fpath, &sbuf) == 0){ /* if fpath exists */
327 if((sbuf.st_mode & S_IFMT) == S_IFREG) /* and is a regular file */
328 setup_dir++; /* we are done */
329 } else if(ps_global->smime->privatetype == Directory){
330 if(our_copy(fpath, pathkey) == 0)
331 setup_dir++;
332 } else if(ps_global->smime->privatetype == Container){
333 BIO *out;
334 if((out = BIO_new_file(fpath, "w")) != NULL){
335 if(BIO_puts(out, pc->keytext) > 0)
336 setup_dir++;
337 BIO_free(out);
339 } else if(ps_global->smime->privatetype == Keychain){
340 /* add support for Apple Mac OS X */
344 /* successful copy of key, now continue with certificate */
345 if(setup_dir){
346 setup_dir = 0;
348 build_path(pathkey, ps_global->smime->publicpath, pc->name, sizeof(pathkey));
349 strncat(pathkey, ".crt", 4);
350 pathkey[sizeof(pathkey)-1] = '\0';
352 build_path(fpath, pathdir, pc->name, sizeof(fpath));
353 strncat(fpath, ".crt", 4);
354 fpath[sizeof(fpath)-1] = '\0';
356 if(our_stat(fpath, &sbuf) == 0){
357 if((sbuf.st_mode & S_IFMT) == S_IFREG)
358 setup_dir++;
360 else if(ps_global->smime->privatetype == Directory){
361 if(our_copy(fpath, pathkey) == 0)
362 setup_dir++;
363 } else if(ps_global->smime->privatetype == Container) {
364 BIO *out;
365 if((out = BIO_new_file(fpath, "w")) != NULL){
366 if(PEM_write_bio_X509(out, pc->cert))
367 setup_dir++;
368 BIO_free(out);
370 } else if (ps_global->smime->privatetype == Keychain) {
371 /* add support for Mac OS X */
375 if(setup_dir){
376 *pwdcert = (void *) pc2;
377 was_here = 0;
378 return 0;
380 else if(pc2 != NULL)
381 free_personal_certs(&pc2);
382 } /* if (pathkey...) */
383 } /* if(ps_global->smime->personal_certs) */
386 if(setup_dir == 0){
387 /* PATHCERTDIR(Private) must be null, so create a path */
388 set_current_val(&ps_global->vars[V_PRIVATEKEY_DIR], TRUE, TRUE);
389 smime_path(ps_global->VAR_PRIVATEKEY_DIR, pathkey, sizeof(pathkey));
391 /* PATHCERTDIR(Public) must be null, so create a path */
392 set_current_val(&ps_global->vars[V_PUBLICCERT_DIR], TRUE, TRUE);
393 smime_path(ps_global->VAR_PUBLICCERT_DIR, pathcert, sizeof(pathcert));
395 /* BUG: this does not support local containers */
396 load_key_and_cert(pathkey, pathcert, &keyfile, &certfile, &pkey, &pcert);
398 if(certfile && keyfile){
399 build_path(fpath, pathdir, keyfile, sizeof(fpath));
400 strncat(fpath, ".key", 4);
401 fpath[sizeof(fpath)-1] = '\0';
403 build_path(fpath2, pathkey, keyfile, sizeof(fpath));
404 strncat(fpath2, ".key", 4);
405 fpath2[sizeof(fpath2)-1] = '\0';
407 if(our_copy(fpath, fpath2) == 0)
408 setup_dir++;
410 if(setup_dir){
411 setup_dir = 0;
413 build_path(fpath, pathdir, certfile, sizeof(fpath));
414 build_path(fpath2, pathcert, certfile, sizeof(fpath2));
416 if(our_copy(fpath, fpath2) == 0)
417 setup_dir++;
422 if(keyfile && certfile){
423 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
424 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
425 pc->name = keyfile;
426 pc->key = pkey;
427 pc->cert = pcert;
428 *pwdcert = (void *) pc;
429 fs_give((void **)&certfile);
430 was_here = 0;
431 return 0;
434 was_here = 0;
435 if(we_inited)
436 smime_deinit();
437 return 0;
439 #endif /* PASSFILE */
441 /* smime_expunge_cert.
442 * Return values: < 0 there was an error.
443 * >=0 the number of messages expunged
446 smime_expunge_cert(WhichCerts ctype)
448 int count, removed;
449 CertList *cl, *dummy, *data;
450 char *path, buf[MAXPATH+1];
451 char *contents;
453 if(DATACERT(ctype)== NULL)
454 return -1;
456 /* data cert is the way we unify certificate management across
457 * functions, but it is not where we really save the information in the
458 * case ctype is equal to Private. What we will do is to update the
459 * datacert, and in the case of ctype equal to Private use the updated
460 * certdata to update the personal_certs data.
463 path = PATHCERTDIR(ctype);
465 if(path){
466 /* add a fake certificate at the beginning of the list */
467 dummy = fs_get(sizeof(CertList));
468 memset((void *)dummy, 0, sizeof(CertList));
469 dummy->next = DATACERT(ctype);
471 for(cl = dummy, count = 0; cl && cl->next;){
472 if(cl->next->data.deleted == 0){
473 cl = cl->next;
474 continue;
477 removed = 1; /* assume success */
478 if(SMHOLDERTYPE(ctype) == Directory){
479 build_path(buf, path, cl->next->name, sizeof(buf));
480 if(ctype == Private && strlen(buf) + strlen(EXTCERT(Private)) < sizeof(buf)){
481 strncat(buf, EXTCERT(Private), 4);
482 buf[sizeof(buf)-1] = '\0';
485 if(our_unlink(buf) < 0){
486 q_status_message1(SM_ORDER, 3, 3, _("Error removing certificate %s"), cl->next->name);
487 cl = cl->next;
488 removed = 0;
491 else if(SMHOLDERTYPE(ctype) == Container){
492 char *prefix= ctype == CACert ? CACERTSTORELEADER : EMAILADDRLEADER;
493 char tmp[MAILTMPLEN], *s, *t;
495 contents = CONTENTCERTLIST(ctype);
496 snprintf(tmp, sizeof(tmp), "%s%s", prefix, cl->next->name);
497 tmp[sizeof(tmp) - 1] = '\0';
498 if((s = strstr(contents, tmp)) != NULL){
499 if((t = strstr(s+strlen(tmp), prefix)) == NULL)
500 *s = '\0';
501 else
502 memmove(s, t, strlen(t)+1);
503 fs_resize((void **)&contents, strlen(contents)+1);
504 switch(ctype){
505 case Private: ps_global->smime->privatecontent = contents; break;
506 case Public : ps_global->smime->publiccontent = contents; break;
507 case CACert : ps_global->smime->cacontent = contents; break;
508 default : break;
511 else
512 removed = 0;
513 } else { /* unhandled case */
516 if(removed > 0){
517 count++; /* count it! */
518 data = cl->next;
519 cl->next = data->next;
520 if(data->name) fs_give((void **)&data->name);
521 fs_give((void **)&data);
524 } else
525 q_status_message(SM_ORDER, 3, 3, _("Error expunging certificate"));
527 switch(ctype){
528 case Private: ps_global->smime->privatecertlist = dummy->next; break;
529 case Public : ps_global->smime->publiccertlist = dummy->next; break;
530 case CACert : ps_global->smime->cacertlist = dummy->next; break;
531 default : break;
533 fs_give((void **)&dummy);
534 if(SMHOLDERTYPE(ctype) == Container){
535 if(copy_dir_to_container(ctype, contents) < 0)
536 count = 0;
538 if(count > 0){
539 q_status_message2(SM_ORDER, 3, 3, _("Removed %s certificate%s"), comatose(count), plural(count));
541 else
542 q_status_message(SM_ORDER, 3, 3, _("Error: No certificates were removed"));
543 return count;
546 void
547 mark_cert_deleted(WhichCerts ctype, int num, unsigned state)
549 CertList *cl;
550 int i;
552 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
553 cl->data.deleted = state;
556 unsigned
557 get_cert_deleted(WhichCerts ctype, int num)
559 CertList *cl;
560 int i;
562 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
563 return (cl && cl->data.deleted) ? 1 : 0;
566 EVP_PKEY *
567 load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *ret)
569 EVP_PKEY *pkey;
570 int rc = 0; /* rc == 1, cancel, rc == 0 success */
571 char pass[MAILTMPLEN+1];
572 BIO *in;
574 /* attempt to load with empty password */
575 in = text ? BIO_new_mem_buf(text, strlen(text)) : BIO_new_file(fpath, "r");
576 if(in != NULL){
577 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, "");
578 if(pkey != NULL) return pkey;
579 } else return NULL;
581 if(pith_smime_enter_password)
582 while(pkey == NULL && rc != 1){
583 do {
584 rc = (*pith_smime_enter_password)(prompt, (char *)pass, sizeof(pass));
585 } while (rc!=0 && rc!=1 && rc>0);
587 (void) BIO_reset(in);
588 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, (char *)pass);
591 BIO_free(in);
593 if(ret) *ret = rc == 1 ? -1 : pkey != NULL ? 0 : 1;
594 return pkey;
597 /* This is a tool for conf_screen, The return value must be zero when
598 * nothing changed, so if there is a failure in the import return 0
599 * and return 1 when we succeeded.\
600 * We call this function in two ways:
601 * either fname is null or not. If they fname is null, so is p_cert.
602 * if p_cert is not null, it is the PERSONAL_CERT structure of fname if this
603 * is available, otherwise we will fill it up here.
606 import_certificate(WhichCerts ctype, PERSONAL_CERT *p_cert, char *fname)
608 int r = 1, rc;
609 char filename[MAXPATH+1], full_filename[MAXPATH+1], buf[MAXPATH+1];
610 char *what;
612 if(pith_smime_import_certificate == NULL
613 || pith_smime_enter_password == NULL){
614 q_status_message(SM_ORDER, 0, 2,
615 _("import of certificates not implemented yet!"));
616 return 0;
619 if(fname == NULL){
620 what = ctype == Public || ctype == CACert ? "certificate" : "key";
621 r = (*pith_smime_import_certificate)(filename, full_filename, what, sizeof(filename) - 20);
623 if(r < 0)
624 return 0;
625 } else {
626 char *s;
627 strncpy(full_filename, fname, sizeof(full_filename));
628 if((s = strrchr(full_filename, '/')) != NULL)
629 strncpy(filename, s+1, sizeof(filename));
632 /* we are trying to import a new key for the password file. First we ask for the
633 * private key. Once this is loaded, we make a reasonable attempt to find the
634 * public key in the same directory as the key was loaded from. We do this by
635 * looking for a file with the correct public certificate name, then we look
636 * in the same private key, and if not, we ask the user for its location. If all
637 * of this works, we import the key and public to the password directory.
639 #ifdef PASSFILE
640 if(ctype == Password){
641 char PrivateKeyPath[MAXPATH+1], PublicCertPath[MAXPATH+1], s[MAXPATH+1];
642 char full_name_key[MAXPATH+1], full_name_cert[MAXPATH+1];
643 char *use_this_file;
644 char prompt[500];
645 EVP_PKEY *key = p_cert ? p_cert->key : NULL;
647 rc = 1; /* assume success :) */
648 if(strlen(filename) > 4){
649 strncpy(s, filename, sizeof(s));
650 s[sizeof(s)-1] = '\0';
651 if(!strcmp(s + strlen(s) - strlen(EXTCERT(Private)), EXTCERT(Private)))
652 s[strlen(s) - strlen(EXTCERT(Private))] = '\0';
653 else
654 rc = 0;
655 } else rc = 0;
657 if(rc == 0){
658 q_status_message(SM_ORDER, 1, 3, _("Error in key name. Check file extension"));
659 return 0;
662 snprintf(prompt, sizeof(prompt), _("Enter passphrase to unlock new key <%s>: "), filename);
663 prompt[sizeof(prompt)-1] = '\0';
664 if(key != NULL
665 || (key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){
666 BIO *ins = NULL;
667 X509 *cert = p_cert ? p_cert->cert : NULL, *cert2;
669 strncpy(full_name_key, full_filename, sizeof(full_filename));
670 full_name_key[sizeof(full_name_key)-1] = '\0';
672 build_path(buf, PATHCERTDIR(ctype), s, sizeof(buf));
674 strncpy(PrivateKeyPath, buf, sizeof(PrivateKeyPath));
675 PrivateKeyPath[sizeof(PrivateKeyPath)-1] = '\0';
676 if(strlen(PrivateKeyPath) + 4 < sizeof(PrivateKeyPath)){
677 strncat(PrivateKeyPath, EXTCERT(Private), 4);
678 PrivateKeyPath[sizeof(PrivateKeyPath)-1] = '\0';
681 /* remove .key extension and replace it with .crt extension */
682 strncpy(full_name_cert, full_name_key, sizeof(full_name_key));
683 full_name_cert[sizeof(full_name_cert)-1] = '\0';
684 full_name_cert[strlen(full_name_cert) - strlen(EXTCERT(Private))] = '\0';
685 strncat(full_name_cert, EXTCERT(Public), 4);
686 full_name_cert[sizeof(full_name_cert)-1] = '\0';
689 /* set up path to location where we will save public cert */
690 strncpy(PublicCertPath, buf, sizeof(PublicCertPath));
691 PublicCertPath[sizeof(PublicCertPath)-1] = '\0';
692 if(strlen(PublicCertPath) + 4 < sizeof(PublicCertPath)){
693 strncat(PublicCertPath, EXTCERT(Public), 4);
694 PublicCertPath[sizeof(PublicCertPath)-1] = '\0';
696 /* attempt #1, use provided certificate,
697 * assumption is that full_name_cert is the file that this
698 * certificate derives from (which is obtained by substitution
699 * of .key extension in key by .crt extension)
701 if(cert != NULL) /* attempt #1 */
702 use_this_file = &full_name_cert[0];
703 else if((ins = BIO_new_file(full_name_cert, "r")) != NULL){
704 /* attempt #2 to guess public cert name, use .crt extension */
705 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
706 use_this_file = &full_name_cert[0];
709 else{ /* attempt #3 to guess public cert name: use the original key */
710 if((ins = BIO_new_file(full_name_key, "r")) != NULL){
711 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
712 use_this_file = &full_name_key[0];
715 else {
716 int done = 0;
717 /* attempt #4, ask the user */
718 do {
719 r = (*pith_smime_import_certificate)(filename, use_this_file, "certificate", sizeof(filename) - 20);
720 if(r < 0){
721 if(ins != NULL) BIO_free(ins);
722 if(cert != NULL) X509_free(cert);
723 return 0;
725 if((ins = BIO_new_file(use_this_file, "r")) != NULL){
726 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL)
727 done++;
728 else
729 q_status_message(SM_ORDER, 1, 3, _("Error parsing certificate"));
731 else
732 q_status_message(SM_ORDER, 1, 3, _("Error reading certificate"));
733 } while (done == 0);
736 if(ins != NULL){
737 if(cert != NULL){ /* check that certificate matches key */
738 if(!X509_check_private_key(cert, key)){
739 rc = 0;
740 q_status_message(SM_ORDER, 1, 3, _("Certificate does not match key"));
742 else
743 rc = 1; /* Success! */
745 else
746 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
748 if(rc == 1){ /* if everything has been successful,
749 * copy the files to their final destination */
750 if(our_copy(PrivateKeyPath, full_filename) == 0){ /* <-- save the private key */
751 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
752 if(our_copy(PublicCertPath, use_this_file) == 0){
753 char tmp[MAILTMPLEN];
754 FILE *fp;
756 if(!passfile_name(ps_global->pinerc, tmp, sizeof(tmp))
757 || !(fp = our_fopen(tmp, "rb"))){
758 q_status_message(SM_ORDER, 1, 3, _("Error reading password file!"));
759 rc = 0;
761 else {
762 char tmp2[MAILTMPLEN];
763 int encrypted = 0;
764 char *text;
765 PERSONAL_CERT *pwdcert, *pc = p_cert;
767 pwdcert = (PERSONAL_CERT *) ps_global->pwdcert;
768 if(pwdcert == NULL)
769 setup_pwdcert((void **)&pwdcert);
771 tmp2[0] = '\0';
772 fgets(tmp2, sizeof(tmp2), fp);
773 fclose(fp);
774 if(strcmp(tmp2, "-----BEGIN PKCS7-----\n")){
775 if(encrypt_file((char *)tmp, NULL, pwdcert))
776 encrypted++;
778 else
779 encrypted++;
781 if(encrypted){
782 text = decrypt_file((char *)tmp, NULL, pwdcert);
783 if(text != NULL){
784 if(pc == NULL){
785 pc = fs_get(sizeof(PERSONAL_CERT));
786 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
787 filename[strlen(filename)-strlen(EXTCERT(Private))] = '\0';
788 pc->name = cpystr(filename);
789 snprintf(buf, sizeof(buf), "%s%s", filename, EXTCERT(Public));
790 buf[sizeof(buf)-1] = '\0';
791 pc->cname = cpystr(buf);
792 pc->key = key;
793 pc->cert = cert;
796 if(encrypt_file((char *)tmp, text, pc)){ /* we did it! */
797 build_path(buf, PATHCERTDIR(ctype), pwdcert->name, sizeof(buf));
798 strncat(buf, EXTCERT(Private), 4);
799 buf[sizeof(buf)-1] = '\0';
800 if(strcmp(PrivateKeyPath, buf)){
801 if (unlink(buf) < 0)
802 q_status_message(SM_ORDER, 1, 3, _("Failed to remove old key"));
804 build_path(buf, PATHCERTDIR(ctype), pwdcert->cname, sizeof(buf));
805 if(strcmp(PublicCertPath, buf)){
806 if(unlink(buf) < 0)
807 q_status_message(SM_ORDER, 1, 3, _("Failed to remove old certificate"));
809 free_personal_certs((PERSONAL_CERT **)&ps_global->pwdcert);
810 ps_global->pwdcert = pc;
811 rc = 1;
812 q_status_message(SM_ORDER, 1, 3, _("Password file reencrypted"));
813 } else {
814 q_status_message(SM_ORDER, 1, 3, _("Failed to reencrypt password file"));
815 rc = 0;
817 } else {
818 q_status_message(SM_ORDER, 1, 3, _("Error decrypting Password file"));
820 } else {
821 q_status_message(SM_ORDER, 1, 3, _("Password file not encrypted and coulr not encrypt"));
822 rc = 0;
826 else{
827 q_status_message(SM_ORDER, 1, 3, _("Error saving public certificate"));
828 if(our_unlink(PrivateKeyPath) < 0)
829 q_status_message(SM_ORDER, 1, 3, _("Error while cleaning private key"));
830 rc = 0;
833 else{
834 rc = 0;
835 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
837 if(ins != NULL) BIO_free(ins);
838 if(rc == 0 && cert != NULL) X509_free(cert);
840 } else {
841 rc = 0;
842 q_status_message(SM_ORDER, 1, 3, _("Error unlocking private key"));
845 return rc;
847 #endif /* PASSFILE */
849 smime_init();
850 ps_global->mangled_screen = 1;
852 if (ctype == Private){
853 char prompt[500], *s, *t;
854 EVP_PKEY *key = NULL;
856 if(!ps_global->smime->privatecertlist){
857 ps_global->smime->privatecertlist = fs_get(sizeof(CertList));
858 memset((void *)DATACERT(ctype), 0, sizeof(CertList));
861 for(s = t = filename; (t = strstr(s, ".key")) != NULL; s = t + 1);
862 if(s) *(s-1) = 0;
864 snprintf(prompt, sizeof(prompt), _("Enter passphrase for <%s>: "), filename);
865 prompt[sizeof(prompt)-1] = '\0';
866 if((key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){
867 if(SMHOLDERTYPE(ctype) == Directory){
868 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
869 if(strcmp(buf + strlen(buf) - 4, EXTCERT(ctype)) != 0 && strlen(buf) + 4 < sizeof(buf)){
870 strncat(buf, EXTCERT(ctype), 4);
871 buf[sizeof(buf)-1] = '\0';
873 rc = our_copy(buf, full_filename);
875 else /* if(SMHOLDERTYPE(ctype) == Container){ */
876 rc = add_file_to_container(ctype, full_filename, NULL);
877 if(rc == 0)
878 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
879 else
880 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
881 if(ps_global->smime->publiccertlist)
882 ps_global->smime->publiccertlist->data.renew = 1;
884 else
885 q_status_message(SM_ORDER, 1, 3, _("Problem unlocking key (not a certificate or wrong password)"));
886 } else if (ctype == CACert){
887 BIO *ins;
888 X509 *cert;
890 if((ins = BIO_new_file(full_filename, "r")) != NULL){
891 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
892 if(SMHOLDERTYPE(ctype) == Directory){
893 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
894 if(strcmp(buf + strlen(buf) - 4, ".crt") != 0 && strlen(buf) + 4 < sizeof(buf)){
895 strncat(buf, EXTCERT(ctype), 4);
896 buf[sizeof(buf)-1] = '\0';
899 rc = our_copy(buf, full_filename);
901 else /* if(SMHOLDERTYPE(ctype) == Container){ */
902 rc = add_file_to_container(ctype, full_filename, NULL);
903 if(rc == 0)
904 q_status_message(SM_ORDER, 1, 3, _("Certificate saved"));
905 else
906 q_status_message(SM_ORDER, 1, 3, _("Error saving certificate"));
907 X509_free(cert); /* not needed anymore */
909 else
910 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
911 BIO_free(ins);
913 renew_store();
914 } else { /* ctype == Public. save certificate, but first validate that it is one */
915 BIO *ins;
916 X509 *cert;
918 if((ins = BIO_new_file(full_filename, "r")) != NULL){
919 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
920 if(SMHOLDERTYPE(ctype) == Directory){
921 char **email;
923 if((email = get_x509_subject_email(cert)) != NULL){
924 int i;
925 for(i = 0; email[i] != NULL; i++){
926 save_cert_for(email[i], cert, Public);
927 fs_give((void **)&email[i]);
929 fs_give((void **)email);
931 if(strcmp(filename + strlen(filename) - 4, ".crt") == 0)
932 filename[strlen(filename) - 4] = '\0';
933 save_cert_for(filename, cert, Public);
935 else /* if(SMHOLDERTYPE(ctype) == Container){ */
936 add_file_to_container(ctype, full_filename, NULL);
937 X509_free(cert);
938 if(ps_global->smime->publiccertlist)
939 ps_global->smime->publiccertlist->data.renew = 1;
941 else
942 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
943 BIO_free(ins);
946 if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1;
947 return 1;
950 /* itype: information type to add: 0 - public, 1 - private.
951 * Memory freed by caller
953 BIO *
954 print_private_key_information(char *email, int itype)
956 BIO *out;
957 PERSONAL_CERT *pc;
959 if(ps_global->smime == NULL
960 || ps_global->smime->personal_certs == NULL
961 || (itype != 0 && itype != 1))
962 return NULL;
964 for(pc = ps_global->smime->personal_certs;
965 pc != NULL && strcmp(pc->name, email) != 0; pc = pc->next);
966 if(pc->key == NULL
967 && !load_private_key(pc)
968 && ps_global->smime
969 && ps_global->smime->need_passphrase){
970 if (*pith_opt_smime_get_passphrase)
971 (*pith_opt_smime_get_passphrase)();
972 load_private_key(pc);
975 if(pc->key == NULL)
976 return NULL;
978 out = BIO_new(BIO_s_mem());
979 if(itype == 0) /* 0 means public */
980 EVP_PKEY_print_public(out, pc->key, 0, NULL);
981 else if (itype == 1) /* 1 means private */
982 EVP_PKEY_print_private(out, pc->key, 0, NULL);
984 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
985 forget_private_keys();
987 return out;
991 * Forget any cached private keys
993 static void
994 forget_private_keys(void)
996 PERSONAL_CERT *pcert;
997 size_t len;
998 volatile char *p;
1000 dprint((9, "forget_private_keys()"));
1001 if(ps_global->smime){
1002 ps_global->smime->already_auto_asked = 0;
1003 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
1004 pcert;
1005 pcert=pcert->next){
1007 if(pcert->key){
1008 EVP_PKEY_free(pcert->key);
1009 pcert->key = NULL;
1013 ps_global->smime->entered_passphrase = 0;
1014 len = sizeof(ps_global->smime->passphrase);
1015 p = ps_global->smime->passphrase;
1017 while(len-- > 0)
1018 *p++ = '\0';
1022 /* modelled after signature_path in reply.c, but uses home dir instead of the
1023 * directory where the .pinerc is located, since according to documentation,
1024 * the .alpine-smime directories are subdirectories of the home directory
1026 int smime_path(char *rpath, char *fpath, size_t len)
1028 *fpath = '\0';
1029 if(rpath && *rpath){
1030 size_t spl = strlen(rpath);
1032 if(IS_REMOTE(rpath)){
1033 if(spl < len - 1)
1034 strncpy(fpath, rpath, len-1);
1035 fpath[len-1] = '\0';
1037 else if(is_absolute_path(rpath)){
1038 strncpy(fpath, rpath, len-1);
1039 fpath[len-1] = '\0';
1040 fnexpand(fpath, len);
1042 else if(ps_global->VAR_OPER_DIR){
1043 if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
1044 build_path(fpath, ps_global->VAR_OPER_DIR, rpath, len);
1046 else if(ps_global->home_dir){
1047 if(strlen(ps_global->home_dir) + spl < len - 1)
1048 build_path(fpath, ps_global->home_dir, rpath, len);
1051 return fpath && *fpath ? 1 : 0;
1057 * taken from openssl/apps/app_rand.c
1059 static int
1060 app_RAND_load_file(const char *file)
1062 #define RANDBUFLEN 200
1063 char buffer[RANDBUFLEN];
1065 if(file == NULL)
1066 file = RAND_file_name(buffer, RANDBUFLEN);
1068 if(file == NULL || !RAND_load_file(file, -1)){
1069 if(RAND_status() == 0){
1070 dprint((1, "unable to load 'random state'\n"));
1071 dprint((1, "This means that the random number generator has not been seeded\n"));
1072 dprint((1, "with much random data.\n"));
1075 return 0;
1078 seeded = 1;
1079 return 1;
1084 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
1086 static void
1087 openssl_extra_randomness(void)
1089 #if !defined(WIN32)
1090 int fd;
1091 unsigned long i;
1092 char *tf = NULL;
1093 char tmp[MAXPATH];
1094 struct stat sbuf;
1095 /* if system doesn't have /dev/urandom */
1096 if(stat ("/dev/urandom", &sbuf)){
1097 tmp[0] = '0';
1098 tf = temp_nam(NULL, NULL);
1099 if(tf){
1100 strncpy(tmp, tf, sizeof(tmp));
1101 tmp[sizeof(tmp)-1] = '\0';
1102 fs_give((void **) &tf);
1105 if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
1106 i = (unsigned long) tmp;
1107 else{
1108 unlink(tmp); /* don't need the file */
1109 fstat(fd, &sbuf); /* get information about the file */
1110 i = sbuf.st_ino; /* remember its inode */
1111 close(fd); /* or its descriptor */
1113 /* not great but it'll have to do */
1114 snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
1115 tcp_serverhost (),i,
1116 (unsigned long) (time (0) ^ gethostid ()),
1117 (unsigned long) getpid ());
1118 RAND_seed(tmp, strlen(tmp));
1120 #endif
1124 /* taken from openssl/apps/app_rand.c */
1125 static int
1126 app_RAND_write_file(const char *file)
1128 char buffer[200];
1130 if(!seeded)
1132 * If we did not manage to read the seed file,
1133 * we should not write a low-entropy seed file back --
1134 * it would suppress a crucial warning the next time
1135 * we want to use it.
1137 return 0;
1139 if(file == NULL)
1140 file = RAND_file_name(buffer, sizeof buffer);
1142 if(file == NULL || !RAND_write_file(file)){
1143 dprint((1, "unable to write 'random state'\n"));
1144 return 0;
1147 return 1;
1150 CertList *
1151 certlist_from_personal_certs(PERSONAL_CERT *pc)
1153 CertList *cl;
1154 X509 *x;
1156 if(pc == NULL)
1157 return NULL;
1159 if((x = get_cert_for(pc->name, Public, 1)) != NULL)
1160 cl = smime_X509_to_cert_info(x, pc->name);
1161 cl->next = certlist_from_personal_certs(pc->next);
1163 return cl;
1166 void
1167 renew_cert_data(CertList **data, WhichCerts ctype)
1169 smime_init();
1170 if(ctype == Private){
1171 if(data){
1172 PERSONAL_CERT *pc = (PERSONAL_CERT *)ps_global->smime->personal_certs;
1173 if(*data)
1174 free_certlist(data);
1175 free_personal_certs(&pc);
1176 setup_privatekey_storage();
1177 *data = certlist_from_personal_certs((PERSONAL_CERT *)ps_global->smime->personal_certs);
1178 if(data && *data){
1179 resort_certificates(data, ctype);
1180 RENEWCERT(*data) = 0;
1182 ps_global->smime->privatecertlist = *data;
1184 if(ps_global->smime->privatecertlist)
1185 RENEWCERT(ps_global->smime->privatecertlist) = 0;
1186 } else {
1187 X509_LOOKUP *lookup = NULL;
1188 X509_STORE *store = NULL;
1190 if((store = X509_STORE_new()) != NULL){
1191 if((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL){
1192 X509_STORE_free(store);
1193 store = NULL;
1194 } else{
1195 free_certlist(data);
1196 if(SMHOLDERTYPE(ctype) == Directory)
1197 add_certs_in_dir(lookup, PATHCERTDIR(ctype), EXTCERT(ctype), data);
1198 else /* if(SMHOLDERTYPE(ctype) == Container) */
1199 *data = mem_to_certlist(CONTENTCERTLIST(ctype), ctype);
1200 if(data && *data){
1201 resort_certificates(data, ctype);
1202 RENEWCERT(*data) = 0;
1204 if(ctype == Public)
1205 ps_global->smime->publiccertlist = *data;
1206 else
1207 ps_global->smime->cacertlist = *data;
1211 setup_certs_backup_by_type(ctype);
1214 void
1215 smime_reinit(void)
1217 smime_deinit();
1218 smime_init();
1221 /* Installed as an atexit() handler to save the random data */
1222 void
1223 smime_deinit(void)
1225 dprint((9, "smime_deinit()"));
1226 app_RAND_write_file(NULL);
1227 free_smime_struct(&ps_global->smime);
1230 /* we renew the store when it has changed */
1231 void renew_store(void)
1233 if(ps_global->smime->inited){
1234 if(s_cert_store != NULL)
1235 X509_STORE_free(s_cert_store);
1236 s_cert_store = get_ca_store();
1240 /* Initialise openssl stuff if needed */
1241 void
1242 smime_init(void)
1244 if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
1246 dprint((9, "smime_init()"));
1247 if(!ps_global->smime)
1248 ps_global->smime = new_smime_struct();
1250 setup_storage_locations();
1252 s_cert_store = get_ca_store();
1253 setup_certs_backup_by_type(CACert);
1255 #ifdef OPENSSL_1_1_0
1256 OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS|OPENSSL_INIT_ADD_ALL_DIGESTS|OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
1257 #else
1258 OpenSSL_add_all_algorithms();
1259 ERR_load_crypto_strings();
1260 #endif /* OPENSSL_1_1_0 */
1262 app_RAND_load_file(NULL);
1263 openssl_extra_randomness();
1264 ps_global->smime->inited = 1;
1267 ERR_clear_error();
1271 /* validate a certificate. Return value : 0 for no error, -1 for error.
1272 * In the latter case, set the openssl smime error in *error.
1274 int smime_validate_cert(X509 *cert, long *error)
1276 X509_STORE_CTX *csc;
1278 ERR_clear_error();
1279 *error = 0;
1280 if((s_cert_store != NULL) && (csc = X509_STORE_CTX_new()) != NULL){
1281 X509_STORE_set_flags(s_cert_store, 0);
1282 if(X509_STORE_CTX_init(csc,s_cert_store,cert,NULL)
1283 && X509_verify_cert(csc) <= 0)
1284 *error = X509_STORE_CTX_get_error(csc);
1285 X509_STORE_CTX_free(csc);
1287 return *error ? -1 : 0;
1290 PERSONAL_CERT *
1291 get_personal_certs(char *path)
1293 PERSONAL_CERT *result = NULL;
1294 char buf2[MAXPATH], *fname;
1295 X509 *cert;
1296 size_t ll;
1297 #ifndef _WINDOWS
1298 struct dirent *d;
1299 DIR *dirp;
1300 #else /* _WINDOWS */
1301 struct _finddata_t dbuf;
1302 char buf[_MAX_PATH + 4];
1303 long findrv;
1304 #endif /* _WINDOWS */
1306 ps_global->smime->privatepath = cpystr(path);
1308 #ifndef _WINDOWS
1309 dirp = opendir(path);
1310 if(dirp){
1311 while((d=readdir(dirp)) != NULL){
1312 fname = d->d_name;
1313 #else /* _WINDOWS */
1314 snprintf(buf, sizeof(buf), "%s%s*.*", path, (path[strlen(path)-1] == '\\') ? "" : "\\");
1315 buf[sizeof(buf)-1] = '\0';
1316 if((findrv = _findfirst(buf, &dbuf)) < 0)
1317 return(NULL);
1319 do {
1320 fname = fname_to_utf8(dbuf.name);
1321 #endif
1322 if((ll=strlen(fname)) && ll > 4 && !strcmp(fname+ll-4, ".key")){
1324 /* copy file name to temp buffer */
1325 strncpy(buf2, fname, sizeof(buf2)-1);
1326 buf2[sizeof(buf2)-1] = '\0';
1327 /* chop off ".key" trailier */
1328 buf2[strlen(buf2)-4] = '\0';
1329 /* Look for certificate */
1330 cert = get_cert_for(buf2, Public, 1);
1332 if(cert){
1333 PERSONAL_CERT *pc;
1335 /* create a new PERSONAL_CERT, fill it in */
1337 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1338 pc->cert = cert;
1339 pc->name = cpystr(buf2);
1340 strncat(buf2, EXTCERT(Public), 4);
1341 pc->cname = cpystr(buf2);
1343 /* Try to load the key with an empty password */
1344 pc->key = load_key(pc, "", SM_NORMALCERT);
1346 pc->next = result;
1347 result = pc;
1350 #ifndef _WINDOWS
1352 closedir(dirp);
1354 #else /* _WINDOWS */
1355 } while(_findnext(findrv, &dbuf) == 0);
1356 _findclose(findrv);
1357 #endif /* !_WINDOWS */
1358 return result;
1362 void
1363 setup_privatekey_storage(void)
1365 char path[MAXPATH+1], *contents;
1366 int privatekeycontainer = 0;
1368 /* private keys in a container */
1369 if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
1371 privatekeycontainer = 1;
1372 contents = NULL;
1373 path[0] = '\0';
1374 if(!smime_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
1375 privatekeycontainer = 0;
1377 if(privatekeycontainer && !IS_REMOTE(path)
1378 && ps_global->VAR_OPER_DIR
1379 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1380 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1381 /* TRANSLATORS: First arg is the directory name, second is
1382 the file user wants to read but can't. */
1383 _("Can't read file outside %s: %s"),
1384 ps_global->VAR_OPER_DIR, path);
1385 privatekeycontainer = 0;
1388 if(privatekeycontainer
1389 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1390 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1392 !(contents = read_file(path, READ_FROM_LOCALE)))
1393 privatekeycontainer = 0;
1396 if(privatekeycontainer && path[0]){
1397 ps_global->smime->privatetype = Container;
1398 ps_global->smime->privatepath = cpystr(path);
1400 if(contents){
1401 ps_global->smime->privatecontent = contents;
1402 ps_global->smime->personal_certs = mem_to_personal_certs(contents);
1407 /* private keys in a directory of files */
1408 if(!privatekeycontainer){
1409 ps_global->smime->privatetype = Directory;
1411 path[0] = '\0';
1412 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1413 && !IS_REMOTE(path)))
1414 ps_global->smime->privatetype = Nada;
1415 else if(can_access(path, ACCESS_EXISTS)){
1416 if(our_mkpath(path, 0700)){
1417 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1418 ps_global->smime->privatetype = Nada;
1422 if(ps_global->smime->privatetype == Directory)
1423 ps_global->smime->personal_certs = get_personal_certs(path);
1425 setup_certs_backup_by_type(Private);
1430 static void
1431 setup_storage_locations(void)
1433 int publiccertcontainer = 0, cacertcontainer = 0;
1434 char path[MAXPATH+1], *contents;
1436 if(!ps_global->smime)
1437 return;
1439 #ifdef APPLEKEYCHAIN
1440 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1441 ps_global->smime->publictype = Keychain;
1443 else{
1444 #endif /* APPLEKEYCHAIN */
1445 /* Public certificates in a container */
1446 if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
1448 publiccertcontainer = 1;
1449 contents = NULL;
1450 path[0] = '\0';
1451 if(!smime_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
1452 publiccertcontainer = 0;
1454 if(publiccertcontainer && !IS_REMOTE(path)
1455 && ps_global->VAR_OPER_DIR
1456 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1457 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1458 /* TRANSLATORS: First arg is the directory name, second is
1459 the file user wants to read but can't. */
1460 _("Can't read file outside %s: %s"),
1461 ps_global->VAR_OPER_DIR, path);
1462 publiccertcontainer = 0;
1465 if(publiccertcontainer
1466 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1467 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1469 !(contents = read_file(path, READ_FROM_LOCALE)))
1470 publiccertcontainer = 0;
1473 if(publiccertcontainer && path[0]){
1474 ps_global->smime->publictype = Container;
1475 ps_global->smime->publicpath = cpystr(path);
1477 if(contents){
1478 ps_global->smime->publiccontent = contents;
1479 ps_global->smime->publiccertlist = mem_to_certlist(contents, Public);
1484 /* Public certificates in a directory of files */
1485 if(!publiccertcontainer){
1486 ps_global->smime->publictype = Directory;
1488 path[0] = '\0';
1489 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1490 && !IS_REMOTE(path)))
1491 ps_global->smime->publictype = Nada;
1492 else if(can_access(path, ACCESS_EXISTS)){
1493 if(our_mkpath(path, 0700)){
1494 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1495 ps_global->smime->publictype = Nada;
1499 if(ps_global->smime->publictype == Directory)
1500 ps_global->smime->publicpath = cpystr(path);
1503 #ifdef APPLEKEYCHAIN
1505 #endif /* APPLEKEYCHAIN */
1507 setup_privatekey_storage();
1509 /* extra cacerts in a container */
1510 if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
1512 cacertcontainer = 1;
1513 contents = NULL;
1514 path[0] = '\0';
1515 if(!smime_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
1516 cacertcontainer = 0;
1518 if(cacertcontainer && !IS_REMOTE(path)
1519 && ps_global->VAR_OPER_DIR
1520 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1521 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1522 /* TRANSLATORS: First arg is the directory name, second is
1523 the file user wants to read but can't. */
1524 _("Can't read file outside %s: %s"),
1525 ps_global->VAR_OPER_DIR, path);
1526 cacertcontainer = 0;
1529 if(cacertcontainer
1530 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1531 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1533 !(contents = read_file(path, READ_FROM_LOCALE)))
1534 cacertcontainer = 0;
1537 if(cacertcontainer && path[0]){
1538 ps_global->smime->catype = Container;
1539 ps_global->smime->capath = cpystr(path);
1540 ps_global->smime->cacontent = contents;
1541 if(contents)
1542 ps_global->smime->cacertlist = mem_to_certlist(contents, CACert);
1546 if(!cacertcontainer){
1547 ps_global->smime->catype = Directory;
1549 path[0] = '\0';
1550 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1551 && !IS_REMOTE(path)))
1552 ps_global->smime->catype = Nada;
1553 else if(can_access(path, ACCESS_EXISTS)){
1554 if(our_mkpath(path, 0700)){
1555 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1556 ps_global->smime->catype = Nada;
1560 if(ps_global->smime->catype == Directory)
1561 ps_global->smime->capath = cpystr(path);
1567 copy_publiccert_dir_to_container(void)
1569 return(copy_dir_to_container(Public, NULL));
1574 copy_publiccert_container_to_dir(void)
1576 return(copy_container_to_dir(Public));
1581 copy_privatecert_dir_to_container(void)
1583 return(copy_dir_to_container(Private, NULL));
1588 copy_privatecert_container_to_dir(void)
1590 return(copy_container_to_dir(Private));
1595 copy_cacert_dir_to_container(void)
1597 return(copy_dir_to_container(CACert, NULL));
1602 copy_cacert_container_to_dir(void)
1604 return(copy_container_to_dir(CACert));
1607 /* Add the contents of a file to a container. Do not check the content
1608 * of the file, just add it using the format for that container. The
1609 * caller must check the format, so that there is no data corruption
1610 * in the future.
1611 * return value: 0 - success,
1612 * != 0 - failure.
1615 add_file_to_container(WhichCerts ctype, char *fpath, char *altname)
1617 char *sep = (ctype == Public || ctype == Private)
1618 ? EMAILADDRLEADER : CACERTSTORELEADER;
1619 char *content = ctype == Public ? ps_global->smime->publiccontent
1620 : (ctype == Private ? ps_global->smime->privatecontent
1621 : ps_global->smime->cacontent);
1622 char *name;
1623 char *s;
1624 unsigned char c;
1625 struct stat sbuf;
1626 STORE_S *in = NULL;
1627 int rv = -1; /* assume error */
1629 if(our_stat(fpath, &sbuf) < 0
1630 || (in = so_get(FileStar, fpath, READ_ACCESS | READ_FROM_LOCALE)) == NULL)
1631 goto endadd;
1633 if(altname != NULL)
1634 name = altname;
1635 else if((name = strrchr(fpath, '/')) != NULL){
1636 size_t ll;
1637 if((ll = strlen(++name)) > 4 && strucmp(name + ll - 4, EXTCERT(ctype)) == 0)
1638 name[ll-strlen(EXTCERT(ctype))] = '\0';
1640 else
1641 goto endadd;
1643 if(content){
1644 fs_resize((void **)&content, strlen(content) + strlen(sep) + strlen(name) + sbuf.st_size + 2*strlen(NEWLINE) + 1); /* 1 = \0*/
1645 s = content;
1646 content += strlen(content);
1648 else{
1649 s = content = fs_get(strlen(sep) + strlen(name) + sbuf.st_size + strlen(NEWLINE) + 1); /* 1 = \0 */
1650 *content = '\0';
1652 strncat(content, sep, strlen(sep));
1653 strncat(content, name, strlen(name));
1654 content += strlen(content);
1655 #ifdef _WINDOWS
1656 *content++ = '\r';
1657 #endif /* _WINDOWS */
1658 *content++ = '\n';
1660 while(so_readc(&c, in))
1661 *content++ = (char) c;
1662 *content = '\0';
1664 switch(ctype){
1665 case Private: ps_global->smime->privatecontent = s; break;
1666 case Public : ps_global->smime->publiccontent = s; break;
1667 case CACert : ps_global->smime->cacontent = s; break;
1668 default : break;
1671 rv = copy_dir_to_container(ctype, s);
1673 endadd:
1674 if(in) so_give(&in);
1676 return rv;
1681 * returns 0 on success, -1 on failure
1682 * contents is an argument which tells this function to write the value
1683 * of this variable instead of reading the contents of the directory.
1684 * If the var contents is not null use its value as the value of the
1685 * container.
1688 copy_dir_to_container(WhichCerts which, char *contents)
1690 int ret = 0, container = 0;
1691 BIO *bio_out = NULL, *bio_in = NULL;
1692 char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
1693 char *tempfile = NULL, fpath[MAXPATH+1], *fname;
1694 size_t ll;
1695 #ifndef _WINDOWS
1696 DIR *dirp;
1697 struct dirent *d;
1698 #else /* _WINDOWS */
1699 struct _finddata_t dbuf;
1700 char buf[_MAX_PATH + 4];
1701 long findrv;
1702 #endif /* _WINDOWS */
1703 REMDATA_S *rd = NULL;
1704 char *configdir = NULL;
1705 char *configpath = NULL;
1706 char *configcontainer = NULL;
1707 char *filesuffix = NULL;
1708 char *ret_dir = NULL;
1710 dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1711 smime_init();
1713 srcpath[0] = '\0';
1714 dstpath[0] = '\0';
1715 file[0] = '\0';
1716 emailaddr[0] = '\0';
1718 if(which == Public){
1719 configdir = ps_global->VAR_PUBLICCERT_DIR;
1720 configpath = ps_global->smime->publicpath;
1721 configcontainer = cpystr(DF_PUBLIC_CONTAINER);
1722 filesuffix = ".crt";
1724 else if(which == Private){
1725 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1726 configpath = ps_global->smime->privatepath;
1727 configcontainer = cpystr(DF_PRIVATE_CONTAINER);
1728 filesuffix = ".key";
1730 else if(which == CACert){
1731 configdir = ps_global->VAR_CACERT_DIR;
1732 configpath = ps_global->smime->capath;
1733 configcontainer = cpystr(DF_CA_CONTAINER);
1734 filesuffix = ".crt";
1736 container = SMHOLDERTYPE(which) == Container;
1738 if(!(configdir && configdir[0])){
1739 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1740 return -1;
1743 if(!(configpath && configpath[0])){
1744 #ifdef APPLEKEYCHAIN
1745 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1746 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1747 return -1;
1749 #endif /* APPLEKEYCHAIN */
1750 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1751 return -1;
1754 if(!(filesuffix && strlen(filesuffix) == 4)){
1755 return -1;
1760 * If there is a legit directory to read from set up the
1761 * container file to write to.
1763 if(smime_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
1765 if(IS_REMOTE(configpath)){
1766 rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
1767 NULL, "Error: ",
1768 _("Can't access remote smime configuration."));
1769 if(!rd)
1770 return -1;
1772 (void) rd_read_metadata(rd);
1774 if(rd->access == MaybeRorW){
1775 if(rd->read_status == 'R')
1776 rd->access = ReadOnly;
1777 else
1778 rd->access = ReadWrite;
1781 if(rd->access != NoExists){
1783 rd_check_remvalid(rd, 1L);
1786 * If the cached info says it is readonly but
1787 * it looks like it's been fixed now, change it to readwrite.
1789 if(rd->read_status == 'R'){
1790 rd_check_readonly_access(rd);
1791 if(rd->read_status == 'W'){
1792 rd->access = ReadWrite;
1793 rd->flags |= REM_OUTOFDATE;
1795 else
1796 rd->access = ReadOnly;
1800 if(rd->flags & REM_OUTOFDATE){
1801 if(rd_update_local(rd) != 0){
1803 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1804 rd_close_remdata(&rd);
1805 return -1;
1808 else
1809 rd_open_remote(rd);
1811 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1812 rd_close_remdata(&rd);
1813 return -1;
1816 rd->flags |= DO_REMTRIM;
1818 strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
1819 dstpath[sizeof(dstpath)-1] = '\0';
1821 else{
1822 strncpy(dstpath, configpath, sizeof(dstpath)-1);
1823 dstpath[sizeof(dstpath)-1] = '\0';
1827 * dstpath is either the local Container file or the local cache file
1828 * for the remote Container file.
1830 tempfile = tempfile_in_same_dir(dstpath, "az", &ret_dir);
1834 * If there is a legit directory to read from and a tempfile
1835 * to write to we continue.
1837 if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
1839 if(contents != NULL){
1840 if(BIO_puts(bio_out, contents) < 0)
1841 ret = -1;
1843 else {
1844 #ifndef _WINDOWS
1845 if((dirp = opendir(srcpath)) != NULL){
1847 while((d=readdir(dirp)) && !ret){
1848 fname = d->d_name;
1849 #else /* _WINDOWS */
1850 snprintf(buf, sizeof(buf), "%s%s*.*", srcpath, (srcpath[strlen(srcpath)-1] == '\\') ? "" : "\\");
1851 buf[sizeof(buf)-1] = '\0';
1852 if((findrv = _findfirst(buf, &dbuf)) < 0)
1853 return -1;
1856 fname = fname_to_utf8(dbuf.name);
1857 #endif /* ! _WINDOWS */
1858 if((ll=strlen(fname)) && ll > 4 && !strcmp(fname+ll-4, filesuffix)){
1860 /* copy file name to temp buffer */
1861 strncpy(emailaddr, fname, sizeof(emailaddr)-1);
1862 emailaddr[sizeof(emailaddr)-1] = '\0';
1863 /* chop off suffix trailier */
1864 emailaddr[strlen(emailaddr)-4] = 0;
1867 * This is the separator between the contents of
1868 * different files.
1870 if(which == CACert){
1871 if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
1872 && (BIO_puts(bio_out, emailaddr) > 0)
1873 && (BIO_puts(bio_out, NEWLINE) > 0)))
1874 ret = -1;
1876 else{
1877 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1878 && (BIO_puts(bio_out, emailaddr) > 0)
1879 && (BIO_puts(bio_out, NEWLINE) > 0)))
1880 ret = -1;
1883 /* read then write contents of file */
1884 build_path(file, srcpath, fname, sizeof(file));
1885 if(!(bio_in = BIO_new_file(file, "r")))
1886 ret = -1;
1888 if(!ret){
1889 int good_stuff = 0;
1891 while(BIO_gets(bio_in, line, sizeof(line)) > 0){
1892 if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
1893 good_stuff = 1;
1895 if(good_stuff)
1896 BIO_puts(bio_out, line);
1898 if(strncmp("-----END", line, strlen("-----END")) == 0)
1899 good_stuff = 0;
1903 BIO_free(bio_in);
1905 #ifndef _WINDOWS
1907 closedir(dirp);
1909 #else /* _WINDOWS */
1910 } while (_findnext(findrv, &dbuf) == 0);
1911 _findclose(findrv);
1912 #endif /* ! _WINDOWS */
1915 BIO_free(bio_out);
1917 if(!ret){
1918 if(container && configpath && *configpath){
1919 strncpy(fpath, configpath, sizeof(fpath));
1920 fpath[sizeof(fpath) - 1] = '\0';
1922 else if(ret_dir){
1923 if(strlen(dstpath) + strlen(configcontainer) - strlen(ret_dir) + 1 < sizeof(dstpath))
1924 snprintf(fpath, sizeof(fpath), "%s%c%s",
1925 dstpath, tempfile[strlen(ret_dir)], configcontainer);
1926 else
1927 ret = -1;
1929 else ret = -1;
1931 if(!ret){
1932 if(!IS_REMOTE(configpath)){
1933 if(rename_file(tempfile, fpath) < 0){
1934 q_status_message2(SM_ORDER, 3, 3,
1935 _("Can't rename %s to %s"), tempfile, fpath);
1936 ret = -1;
1937 } else q_status_message1(SM_ORDER, 3, 3,
1938 _("saved container to %s"), fpath);
1940 else { /* if the container is remote, copy it */
1941 int e;
1942 char datebuf[200];
1944 if(rd != NULL && rename_file(tempfile, rd->lf) < 0){
1945 q_status_message2(SM_ORDER, 3, 3,
1946 _("Can't rename %s to %s"), tempfile, rd->lf);
1947 ret = -1;
1950 datebuf[0] = '\0';
1952 if((e = rd_update_remote(rd, datebuf)) != 0){
1953 if(e == -1){
1954 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1955 _("Error opening temporary smime file %s: %s"),
1956 rd->lf, error_description(errno));
1957 dprint((1,
1958 "write_remote_smime: error opening temp file %s\n",
1959 rd->lf ? rd->lf : "?"));
1961 else{
1962 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1963 _("Error copying to %s: %s"),
1964 rd->rn, error_description(errno));
1965 dprint((1,
1966 "write_remote_smime: error copying from %s to %s\n",
1967 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1970 q_status_message(SM_ORDER | SM_DING, 5, 5,
1971 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1973 else{
1974 rd_update_metadata(rd, datebuf);
1975 rd->read_status = 'W';
1978 rd_close_remdata(&rd);
1984 if(tempfile)
1985 fs_give((void **) &tempfile);
1987 if(ret_dir)
1988 fs_give((void **) &ret_dir);
1990 if(configcontainer)
1991 fs_give((void **) &configcontainer);
1993 return ret;
1998 * returns 0 on success, -1 on failure
2001 copy_container_to_dir(WhichCerts which)
2003 char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
2004 char iobuf[4096];
2005 char *contents = NULL;
2006 char *leader = NULL;
2007 char *filesuffix = NULL;
2008 char *configdir = NULL;
2009 char *configpath = NULL;
2010 char *tempfile = NULL;
2011 char *p, *q, *line, *name, *certtext, *save_p;
2012 int len;
2013 BIO *in, *out;
2015 dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
2016 smime_init();
2018 path[0] = '\0';
2020 if(which == Public){
2021 leader = EMAILADDRLEADER;
2022 contents = ps_global->smime->publiccontent;
2023 configdir = ps_global->VAR_PUBLICCERT_DIR;
2024 configpath = ps_global->smime->publicpath;
2025 filesuffix = ".crt";
2026 if(!(configpath && configpath[0])){
2027 #ifdef APPLEKEYCHAIN
2028 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
2029 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
2030 return -1;
2032 #endif /* APPLEKEYCHAIN */
2033 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2034 return -1;
2037 fs_give((void **) &ps_global->smime->publicpath);
2039 path[0] = '\0';
2040 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
2041 && !IS_REMOTE(path))){
2042 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2043 return -1;
2046 if(can_access(path, ACCESS_EXISTS)){
2047 if(our_mkpath(path, 0700)){
2048 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2049 return -1;
2053 ps_global->smime->publicpath = cpystr(path);
2054 configpath = ps_global->smime->publicpath;
2056 else if(which == Private){
2057 leader = EMAILADDRLEADER;
2058 contents = ps_global->smime->privatecontent;
2059 configdir = ps_global->VAR_PRIVATEKEY_DIR;
2060 configpath = ps_global->smime->privatepath;
2061 filesuffix = ".key";
2062 if(!(configpath && configpath[0])){
2063 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2064 return -1;
2067 fs_give((void **) &ps_global->smime->privatepath);
2069 path[0] = '\0';
2070 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
2071 && !IS_REMOTE(path))){
2072 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2073 return -1;
2076 if(can_access(path, ACCESS_EXISTS)){
2077 if(our_mkpath(path, 0700)){
2078 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2079 return -1;
2083 ps_global->smime->privatepath = cpystr(path);
2084 configpath = ps_global->smime->privatepath;
2086 else if(which == CACert){
2087 leader = CACERTSTORELEADER;
2088 contents = ps_global->smime->cacontent;
2089 configdir = ps_global->VAR_CACERT_DIR;
2090 configpath = ps_global->smime->capath;
2091 filesuffix = ".crt";
2092 if(!(configpath && configpath[0])){
2093 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2094 return -1;
2097 fs_give((void **) &ps_global->smime->capath);
2099 path[0] = '\0';
2100 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
2101 && !IS_REMOTE(path))){
2102 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2103 return -1;
2106 if(can_access(path, ACCESS_EXISTS)){
2107 if(our_mkpath(path, 0700)){
2108 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2109 return -1;
2113 ps_global->smime->capath = cpystr(path);
2114 configpath = ps_global->smime->capath;
2117 if(!(configdir && configdir[0])){
2118 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
2119 return -1;
2122 if(!(configpath && configpath[0])){
2123 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2124 return -1;
2127 if(!(filesuffix && strlen(filesuffix) == 4)){
2128 return -1;
2132 if(contents && *contents){
2133 for(p = contents; *p != '\0';){
2134 line = p;
2136 while(*p && *p != '\n')
2137 p++;
2139 save_p = NULL;
2140 if(*p == '\n'){
2141 save_p = p;
2142 *p++ = '\0';
2145 if(strncmp(leader, line, strlen(leader)) == 0){
2146 name = line + strlen(leader);
2147 certtext = p;
2148 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
2149 if((q = strstr(certtext, leader)) != NULL){
2150 p = q;
2152 else{ /* end of file */
2153 q = certtext + strlen(certtext);
2154 p = q;
2157 strncpy(buf, name, sizeof(buf)-5);
2158 buf[sizeof(buf)-5] = '\0';
2159 strncat(buf, filesuffix, 5);
2160 build_path(file, configpath, buf, sizeof(file));
2162 in = BIO_new_mem_buf(certtext, q-certtext);
2163 if(in){
2164 tempfile = tempfile_in_same_dir(file, "az", NULL);
2165 out = NULL;
2166 if(tempfile)
2167 out = BIO_new_file(tempfile, "w");
2169 if(out){
2170 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
2171 BIO_write(out, iobuf, len);
2173 BIO_free(out);
2175 if(rename_file(tempfile, file) < 0){
2176 q_status_message2(SM_ORDER, 3, 3,
2177 _("Can't rename %s to %s"),
2178 tempfile, file);
2179 return -1;
2182 fs_give((void **) &tempfile);
2185 BIO_free(in);
2190 if(save_p)
2191 *save_p = '\n';
2195 return 0;
2199 #ifdef APPLEKEYCHAIN
2202 copy_publiccert_container_to_keychain(void)
2204 /* NOT IMPLEMNTED */
2205 return -1;
2209 copy_publiccert_keychain_to_container(void)
2211 /* NOT IMPLEMNTED */
2212 return -1;
2215 #endif /* APPLEKEYCHAIN */
2219 * Get a pointer to a string describing the most recent OpenSSL error.
2220 * It's statically allocated, so don't change or attempt to free it.
2222 static const char *
2223 openssl_error_string(void)
2225 char *errs;
2226 const char *data = NULL;
2227 long errn;
2229 errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2230 errs = (char*) ERR_reason_error_string(errn);
2232 if(errs)
2233 return errs;
2234 else if(data)
2235 return data;
2237 return "unknown error";
2241 /* Return true if the body looks like a PKCS7 object */
2243 is_pkcs7_body(BODY *body)
2245 int result;
2247 result = body->type==TYPEAPPLICATION &&
2248 body->subtype &&
2249 (strucmp(body->subtype,"pkcs7-mime")==0 ||
2250 strucmp(body->subtype,"x-pkcs7-mime")==0 ||
2251 strucmp(body->subtype,"pkcs7-signature")==0 ||
2252 strucmp(body->subtype,"x-pkcs7-signature")==0);
2254 return result;
2259 * Recursively stash a pointer to the decrypted data in our
2260 * manufactured body.
2261 * parameters: type: call of type 1, save the base and header for multipart messages
2262 call of type 0, do not save the base and header for multipart messages
2264 static void
2265 create_local_cache(char *h, char *base, BODY *b, int type)
2267 if(b->type==TYPEMULTIPART){
2268 PART *p;
2270 if(type == 1){
2271 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2272 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2273 } else if(type == 0){
2275 * We don't really want to copy the real body contents. It shouldn't be
2276 * used, and in the case of a message with attachments, we'll be
2277 * duplicating the files multiple times.
2279 cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
2281 for(p=b->nested.part; p; p=p->next)
2282 create_local_cache(h, base, (BODY *) p, type);
2285 else{
2286 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2287 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2292 static long
2293 rfc822_output_func(void *b, char *string)
2295 BIO *bio = (BIO *) b;
2297 return(string ? *string ? (BIO_puts(bio, string) > 0 ? 1L : 0L)
2298 : (BIO_puts(bio, string) >= 0 ? 1L : 0L)
2299 : 0L);
2304 * Attempt to load the private key for the given PERSONAL_CERT.
2305 * This sets the appropriate passphrase globals in order to
2306 * interact with the user correctly.
2308 static int
2309 load_private_key(PERSONAL_CERT *pcert)
2311 if(!pcert->key){
2313 /* Try empty password by default */
2314 char *password = "";
2316 if(ps_global->smime
2317 && (ps_global->smime->need_passphrase
2318 || ps_global->smime->entered_passphrase)){
2319 /* We've already been in here and discovered we need a different password */
2321 if(ps_global->smime->entered_passphrase)
2322 password = (char *) ps_global->smime->passphrase; /* already entered */
2323 else
2324 return 0;
2327 ERR_clear_error();
2329 if(!(pcert->key = load_key(pcert, password, SM_NORMALCERT))){
2330 long err = ERR_get_error();
2332 /* Couldn't load key... */
2334 if(ps_global->smime && ps_global->smime->entered_passphrase){
2336 /* The user got the password wrong maybe? */
2338 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
2339 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
2340 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
2341 else
2342 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2344 /* This passphrase is no good; forget it */
2345 ps_global->smime->entered_passphrase = 0;
2348 if(ps_global->smime){
2349 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2350 ps_global->smime->need_passphrase = 1;
2351 if(ps_global->smime->passphrase_emailaddr){
2352 int i;
2353 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
2354 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
2355 fs_give((void **) ps_global->smime->passphrase_emailaddr);
2358 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
2361 return 0;
2363 else{
2364 /* This key will be cached, so we won't be called again */
2365 if(ps_global->smime){
2366 ps_global->smime->entered_passphrase = 0;
2367 ps_global->smime->need_passphrase = 0;
2371 return 1;
2374 return 0;
2378 static void
2379 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename, char *smime_type)
2381 b->type = TYPEAPPLICATION;
2382 b->subtype = cpystr(type);
2383 b->encoding = ENCBINARY;
2384 b->description = cpystr(description);
2386 b->disposition.type = cpystr("attachment");
2387 set_parameter(&b->disposition.parameter, "filename", filename);
2389 set_parameter(&b->parameter, "name", filename);
2390 if(smime_type && *smime_type)
2391 set_parameter(&b->parameter, "smime-type", smime_type);
2396 * Look for a personal certificate matching the
2397 * given address
2399 PERSONAL_CERT *
2400 match_personal_cert_to_email(ADDRESS *a)
2402 PERSONAL_CERT *pcert = NULL;
2403 char buf[MAXPATH];
2404 char **email;
2405 int i, done;
2407 if(!a || !a->mailbox || !a->host)
2408 return NULL;
2410 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2412 if(ps_global->smime){
2413 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
2414 pcert;
2415 pcert=pcert->next){
2417 if(!pcert->cert)
2418 continue;
2420 email = get_x509_subject_email(pcert->cert);
2422 done = 0;
2423 if(email != NULL){
2424 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
2425 if(email[i] != NULL) done++;
2426 for(i = 0; email[i] != NULL; i++)
2427 fs_give((void **)&email[i]);
2428 fs_give((void **)email);
2431 if(done > 0)
2432 break;
2436 return pcert;
2441 * Look for a personal certificate matching the from
2442 * (or reply_to? in the given envelope)
2444 PERSONAL_CERT *
2445 match_personal_cert(ENVELOPE *env)
2447 PERSONAL_CERT *pcert;
2449 pcert = match_personal_cert_to_email(env->reply_to);
2450 if(!pcert)
2451 pcert = match_personal_cert_to_email(env->from);
2453 return pcert;
2458 * Flatten the given body into its MIME representation.
2459 * Return the result in a BIO.
2461 static BIO *
2462 body_to_bio(BODY *body)
2464 BIO *bio = NULL;
2465 int len;
2467 bio = BIO_new(BIO_s_mem());
2468 if(!bio)
2469 return NULL;
2471 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
2472 pine_write_body_header(body, rfc822_output_func, bio);
2473 pine_rfc822_output_body(body, rfc822_output_func, bio);
2476 * Now need to truncate by two characters since the above
2477 * appends CRLF.
2479 if((len=BIO_ctrl_pending(bio)) > 1){
2480 BUF_MEM *biobuf = NULL;
2482 BIO_get_mem_ptr(bio, &biobuf);
2483 if(biobuf){
2484 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
2488 return bio;
2492 static BIO *
2493 bio_from_store(STORE_S *store)
2495 BIO *ret = NULL;
2497 if(store && store->src == BioType && store->txt){
2498 ret = (BIO *) store->txt;
2501 return(ret);
2505 * Encrypt file; given a path (char *) fp, replace the file
2506 * by an encrypted version of it. If (char *) text is not null, then
2507 * replace the text of (char *) fp by the encrypted version of (char *) text.
2508 * certpath is the FULL path to the file containing the certificate used for
2509 * encryption.
2510 * return value: 0 - failed to encrypt; 1 - success!
2513 encrypt_file(char *fp, char *text, PERSONAL_CERT *pc)
2515 const EVP_CIPHER *cipher = NULL;
2516 STACK_OF(X509) *encerts = NULL;
2517 BIO *out = NULL;
2518 PKCS7 *p7 = NULL;
2519 int rv = 0;
2521 if(pc == NULL)
2522 return 0;
2524 cipher = EVP_aes_256_cbc();
2525 encerts = sk_X509_new_null();
2527 sk_X509_push(encerts, X509_dup(pc->cert));
2529 if(text){
2530 if((out = BIO_new(BIO_s_mem())) != NULL){
2531 (void) BIO_reset(out);
2532 BIO_puts(out, text);
2535 else if((out = BIO_new_file(fp, "rb")) != NULL)
2536 BIO_read_filename(out, fp);
2538 if((p7 = PKCS7_encrypt(encerts, out, cipher, 0)) != NULL){
2539 BIO_set_close(out, BIO_CLOSE);
2540 BIO_free(out);
2541 if((out = BIO_new_file(fp, "w")) != NULL){
2542 BIO_reset(out);
2543 rv = PEM_write_bio_PKCS7(out, p7);
2544 BIO_flush(out);
2548 if(out != NULL)
2549 BIO_free(out);
2550 PKCS7_free(p7);
2551 sk_X509_pop_free(encerts, X509_free);
2553 return rv;
2557 * Encrypt a message on the way out. Called from call_mailer in send.c
2558 * The body may be reallocated.
2561 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
2563 PKCS7 *p7 = NULL;
2564 BIO *in = NULL;
2565 BIO *out = NULL;
2566 const EVP_CIPHER *cipher = NULL;
2567 STACK_OF(X509) *encerts = NULL;
2568 STORE_S *outs = NULL;
2569 PINEFIELD *pf;
2570 ADDRESS *a;
2571 BODY *body = *bodyP;
2572 BODY *newBody = NULL;
2573 int result = 0;
2574 X509 *cert;
2575 char buf[MAXPATH];
2577 dprint((9, "encrypt_outgoing_message()"));
2578 smime_init();
2580 cipher = EVP_aes_256_cbc();
2582 encerts = sk_X509_new_null();
2584 /* Look for a certificate for each of the recipients */
2585 for(pf = header->local; pf && pf->name; pf = pf->next)
2586 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
2587 for(a=*pf->addr; a; a=a->next){
2588 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2590 if((cert = get_cert_for(buf, Public, 1)) != NULL){
2591 sk_X509_push(encerts,cert);
2592 }else{
2593 q_status_message2(SM_ORDER, 1, 1,
2594 _("Unable to find certificate for <%s@%s>"),
2595 a->mailbox, a->host);
2596 goto end;
2601 /* add the sender's certificate so that they can decrypt the message too */
2602 for(a=header->env->from; a ; a = a->next){
2603 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2605 if((cert = get_cert_for(buf, Public, 1)) != NULL
2606 && sk_X509_find(encerts, cert) == -1)
2607 sk_X509_push(encerts,cert);
2610 in = body_to_bio(body);
2612 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
2614 outs = so_get(BioType, NULL, EDIT_ACCESS);
2615 out = bio_from_store(outs);
2617 i2d_PKCS7_bio(out, p7);
2618 (void) BIO_flush(out);
2620 so_seek(outs, 0, SEEK_SET);
2622 newBody = mail_newbody();
2624 newBody->type = TYPEAPPLICATION;
2625 newBody->subtype = cpystr("pkcs7-mime");
2626 newBody->encoding = ENCBINARY;
2628 newBody->disposition.type = cpystr("attachment");
2629 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
2631 newBody->description = cpystr("S/MIME Encrypted Message");
2632 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
2633 set_parameter(&newBody->parameter, "name", "smime.p7m");
2635 newBody->contents.text.data = (unsigned char *) outs;
2637 *bodyP = newBody;
2639 result = 1;
2641 end:
2643 BIO_free(in);
2644 PKCS7_free(p7);
2645 sk_X509_pop_free(encerts, X509_free);
2647 dprint((9, "encrypt_outgoing_message returns %d", result));
2648 return result;
2653 Get (and decode) the body of the given section of msg
2655 static STORE_S*
2656 get_part_contents(long msgno, const char *section)
2658 long len;
2659 gf_io_t pc;
2660 STORE_S *store = NULL;
2661 char *err;
2663 store = so_get(CharStar, NULL, EDIT_ACCESS);
2664 if(store){
2665 gf_set_so_writec(&pc,store);
2667 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
2669 gf_clear_so_writec(store);
2671 so_seek(store, 0, SEEK_SET);
2673 if(err)
2674 so_give(&store);
2677 return store;
2681 static PKCS7 *
2682 get_pkcs7_from_part(long msgno,const char *section)
2684 STORE_S *store = NULL;
2685 PKCS7 *p7 = NULL;
2686 BIO *in = NULL;
2688 store = get_part_contents(msgno, section);
2690 if(store){
2691 if(store->src == CharStar){
2692 int len;
2695 * We're reaching inside the STORE_S structure. We should
2696 * probably have a way to get the length, instead.
2698 len = (int) (store->eod - store->dp);
2699 in = BIO_new_mem_buf(store->txt, len);
2701 else{ /* just copy it */
2702 unsigned char c;
2704 in = BIO_new(BIO_s_mem());
2705 (void) BIO_reset(in);
2707 so_seek(store, 0L, 0);
2708 while(so_readc(&c, store)){
2709 BIO_write(in, &c, 1);
2713 if(in){
2714 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2715 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
2716 /* error */
2719 BIO_free(in);
2722 so_give(&store);
2725 return p7;
2728 int same_cert(X509 *x, X509 *cert)
2730 char bufcert[256], bufx[256];
2731 int rv = 0;
2733 get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert), ":");
2734 get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx), ":");
2735 if(strcmp(bufx, bufcert) == 0)
2736 rv = 1;
2738 return rv;
2742 /* extract and save certificates from a PKCS7 package. The ctype variable
2743 * tells us if we want to extract it to a public/ or a ca/ directory. The
2744 * later makes sense only for recoverable errors (errors that can be fixed
2745 * by saving to the ca/ directory before we verify the signature).
2746 * Return value:
2747 * 0 - no errors (in public/) no need to try again,
2748 * or validated self signed certificate (in ca/)
2749 * < 0 - certificate error is not recoverable, don't even think about it.
2752 int smime_extract_and_save_cert(PKCS7 *p7, int check_cert)
2754 STACK_OF(X509) *signers;
2755 X509 *x, *cert;
2756 char **email;
2757 int i, j;
2758 long error;
2760 if((signers = PKCS7_get0_signers(p7, NULL, 0)) == NULL)
2761 return -1;
2763 for(i = 0; i < sk_X509_num(signers); i++){
2764 if((x = sk_X509_value(signers,i)) == NULL)
2765 continue;
2767 if((email = get_x509_subject_email(x)) != NULL){
2768 for(j = 0; email[j] != NULL; j++){
2769 if((cert = get_cert_for(email[j], Public, 1)) == NULL
2770 || same_cert(x, cert) == 0){
2771 if(check_cert == 0
2772 || smime_validate_cert(x, &error) == 0
2773 || (*pith_smime_confirm_save)(email[j]) == 1)
2774 save_cert_for(email[j], x, Public);
2776 if(cert != NULL)
2777 X509_free(cert);
2778 fs_give((void **) &email[j]);
2780 fs_give((void **) email);
2783 sk_X509_free(signers);
2785 return 0;
2789 * Try to verify a signature.
2791 * p7 - the pkcs7 object to verify
2792 * in - the plain data to verify (NULL if not detached)
2793 * out - BIO to which to write the opaque data
2794 * silent - if non zero, do not print errors, only print success.
2796 static int
2797 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
2799 STACK_OF(X509) *otherCerts = NULL;
2800 CertList *cl;
2801 int result, flags;
2802 const char *data;
2803 long err;
2805 if(!s_cert_store){
2806 if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
2807 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2809 return -1;
2812 flags = F_ON(F_USE_CERT_STORE_ONLY, ps_global) ? PKCS7_NOINTERN : 0;
2814 if(ps_global->smime->publiccertlist == NULL){
2815 renew_cert_data(&ps_global->smime->publiccertlist, Public);
2816 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next){
2817 if(cl->x509_cert == NULL){
2818 char *s = strrchr(cl->name, '.');
2819 *s = '\0';
2820 cl->x509_cert = get_cert_for(cl->name, Public, 1);
2821 *s = '.';
2826 if(ps_global->smime->publiccertlist){
2827 otherCerts = sk_X509_new_null();
2828 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next)
2829 if(cl->x509_cert != NULL)
2830 sk_X509_push(otherCerts, X509_dup(cl->x509_cert));
2833 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, flags);
2835 sk_X509_pop_free(otherCerts, X509_free);
2837 if(result){
2838 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
2840 else{
2841 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2843 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
2845 /* Retry verification so we can get the plain text */
2846 /* Might be better to reimplement PKCS7_verify here? */
2848 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY);
2850 if (!silent) q_status_message1(SM_ORDER | SM_DING, 3, 3,
2851 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
2854 return result;
2857 /* Big comment, explaining the mess that exists out there, and how we deal
2858 with it, and also how we solve the problems that are created this way.
2860 When Alpine sends a message, it constructs that message, computes the
2861 signature, but then it forgets the message it signed and reconstructs it
2862 again. Since it signs a message containing a notice about "mime aware
2863 tools", but it does not send that we do not include that in the part
2864 that is signed, and that takes care of much of the problems.
2866 Another problem is what is received from the servers. All servers tested
2867 seem to transmit the message that was signed intact and Alpine can check
2868 the signature correctly. That is not a problem. The problem arises when
2869 the message includes attachments. In this case different servers send
2870 different things, so it will be up to us to figure out what is the text
2871 that was actually signed. Confused? here is the story:
2873 When a message containing and attachment is sent by Alpine, UW-IMAP,
2874 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2875 that was sent by Alpine, but GMX.com, Exchange, and probably other
2876 servers add a trailing \r\n in the message, so when validating the
2877 signature, these messages will not validate. There are several things
2878 that can be done.
2880 1. Add a trailing \r\n to any message that contains attachments, sign that
2881 and send that. In this way, all messages will validate with all
2882 servers.
2884 2. Compatibility mode: If a message has an attachment, contains a trailing
2885 \r\n and does not validate (sent by an earlier version of Alpine),
2886 remove the trailing \r\n and try to revalidate again.
2888 3. We do not add \r\n to validate a message that we sent, because that
2889 would only work in Alpine, and not in any other client. That would
2890 not be a good thing to do.
2892 PART II
2894 Now we have to deal with encrypted and signed messages. The problem is
2895 that c-client makes all its pointers point to "on disk" content, but
2896 since we decrypted the data earlier, we have to make sure of two things.
2897 One is that we saved that data (so we do not have to decrypt it again)
2898 and second that we can use it.
2900 In order to save the data we use create_local_cache, so that we do not
2901 have to redecrypt the message. Once this is saved, c-client functions will
2902 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2904 PART III
2906 When we are trying to verify messages with detached signatures, some
2907 imap servers send incorrect information in the mail_fetch_mime call. By
2908 incorrect I mean that this is not fetched directly from the message, but
2909 it is read from the message, processed, and then the processed part is
2910 sent to us, so this text might not agree with what is in the message,
2911 and so the validation of the signature might fail. However, the good
2912 news is that the message validates if saved to a local folder. This
2913 means that if normal validation does not work we can make it work by
2914 saving the message locally and validating that. This is implemented
2915 below, and causes delay in the display of the message. I am considering
2916 at this time not to do this automatically, but wait for the user to tell
2917 us to do it for them by means of a command available in the
2918 mail_view_screen. This might help in other situations, where a message
2919 is supposed to have an attachment, but it can not be seen in the
2920 processed text. Nevertheless, at this time, this is automatic, and is
2921 causing a delay in the processing of the message, but it is validating
2922 correctly all messages.
2924 PART IV
2926 When the user sends a message as encrypted and signed, this code used to
2927 encrypt first, and then sign the pkcs7 body, but it turns out that some
2928 other clients can not handle these messages. While we could argue that the
2929 other clients need to improve, we will support reading messages in both
2930 ways, and will send messages using this technique; that is, signed first,
2931 encrypted second. It seems that all tested clients support this way, so it
2932 should be safe to do so.
2935 typedef struct smime_filter_s {
2936 void (*filter)();
2937 } SMIME_FILTER_S;
2939 SMIME_FILTER_S sig_filter[] = {
2940 {smime_remove_trailing_crlf},
2941 {smime_remove_folding_space}
2944 #define TOTAL_FILTERS (sizeof(sig_filter)/sizeof(sig_filter[0]))
2945 #define TOTAL_SIGFLTR (1 << TOTAL_FILTERS) /* not good, keep filters to a low number */
2947 void
2948 smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen,
2949 char **bodytext, unsigned long *bodylen)
2951 if(*bodylen > 2 && !strncmp(*bodytext+*bodylen-2, "\r\n", 2))
2952 *bodylen -= 2;
2955 void
2956 smime_remove_folding_space(char **mimetext, unsigned long *mimelen,
2957 char **bodytext, unsigned long *bodylen)
2959 char *s = NULL, *t;
2960 unsigned long mlen = *mimelen;
2962 if(*mimetext){
2963 for (s = t = *mimetext; t - *mimetext < *mimelen; ){
2964 if(*t == '\r' && *(t+1) == '\n' && (*(t+2) == '\t' || *(t+2) == ' ')){
2965 *s++ = ' ';
2966 t += 3;
2967 mlen -= 2;
2969 else
2970 *s++ = *t++;
2972 *mimelen = mlen;
2977 smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag)
2979 int result, i, j, flag;
2980 char *mtext, *btext;
2981 unsigned long mlen, blen;
2982 BIO *in;
2984 mtext = mimelen ? fs_get(mimelen+1) : NULL;
2985 btext = fs_get(bodylen+1);
2986 result = 0;
2988 flag = 1; /* silence all failures */
2989 for(i = 1; result == 0 && i < TOTAL_SIGFLTR; i++){
2990 if((in = BIO_new(BIO_s_mem())) == NULL)
2991 return -1;
2993 (void) BIO_reset(in);
2995 if(i+1 == TOTAL_SIGFLTR)
2996 flag = nflag;
2998 if(mimelen)
2999 strncpy(mtext, mimetext, mlen = mimelen);
3000 strncpy(btext, bodytext, blen = bodylen);
3001 for(j = 0; j < TOTAL_FILTERS; j++)
3002 if((i >> j) & 1)
3003 (sig_filter[j].filter)(&mtext, &mlen, &btext, &blen);
3004 if(mtext != NULL)
3005 BIO_write(in, mtext, mlen);
3006 BIO_write(in, btext, blen);
3007 result = do_signature_verify(p7, in, NULL, flag);
3008 BIO_free(in);
3010 if(mtext) fs_give((void **)&mtext);
3011 if(btext) fs_give((void **)&btext);
3012 return result;
3016 * Given a multipart body of type multipart/signed, attempt to verify it.
3017 * Returns non-zero if the body was changed.
3019 static int
3020 do_detached_signature_verify(BODY *b, long msgno, char *section)
3022 PKCS7 *p7 = NULL;
3023 BIO *in = NULL;
3024 PART *p;
3025 int result, modified_the_body = 0;
3026 int flag; /* 1 silent, 0 not silent */
3027 int saved = 0;
3028 unsigned long mimelen, bodylen;
3029 char newSec[100], *mimetext, *bodytext;
3030 char *what_we_did;
3031 SIZEDTEXT *st;
3033 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"));
3035 smime_init();
3037 /* if it was signed and then encrypted, use the decrypted text
3038 * to check the validity of the signature
3040 if(b->sparep){
3041 if(get_body_sparep_type(b->sparep) == SizedText){
3042 /* bodytext includes mimetext */
3043 st = (SIZEDTEXT *) get_body_sparep_data(b->sparep);
3044 bodytext = (char *) st->data;
3045 bodylen = st->size;
3046 mimetext = NULL;
3047 mimelen = 0L;
3050 else{
3051 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3052 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3053 if(mimetext)
3054 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3056 if(mimetext == NULL || bodytext == NULL)
3057 return modified_the_body;
3060 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3062 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
3063 || (in = BIO_new(BIO_s_mem())) == NULL)
3064 return modified_the_body;
3066 (void) BIO_reset(in);
3067 if(mimetext != NULL)
3068 BIO_write(in, mimetext, mimelen);
3069 BIO_write(in, bodytext, bodylen);
3071 saved = smime_extract_and_save_cert(p7, F_ON(F_USE_CERT_STORE_ONLY, ps_global));
3072 if(saved < 0 && F_ON(F_USE_CERT_STORE_ONLY, ps_global))
3073 return modified_the_body;
3075 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3076 flag = (mimelen == 0 || !IS_REMOTE(ps_global->mail_stream->mailbox))
3077 ? 0 : 1;
3078 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, flag);
3079 if(result < 0)
3080 return modified_the_body;
3081 if(result == 0
3082 && mimelen > 0 /* do not do this for encrypted messages */
3083 && IS_REMOTE(ps_global->mail_stream->mailbox)){
3084 char *fetch;
3085 unsigned long hlen, tlen;
3086 STORE_S *msg_so;
3088 BIO_free(in);
3089 if((in = BIO_new(BIO_s_mem())) != NULL
3090 && (fetch = mail_fetch_header(ps_global->mail_stream, msgno, NULL,
3091 NULL, &hlen, FT_PEEK)) != NULL
3092 && (msg_so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL
3093 && so_nputs(msg_so, fetch, (long) hlen)
3094 && (fetch = pine_mail_fetch_text(ps_global->mail_stream, msgno, NULL,
3095 &tlen, FT_PEEK)) != NULL
3096 && so_nputs(msg_so, fetch, tlen)){
3097 STRING bs;
3098 char *h = (char *) so_text(msg_so);
3099 char *bstart = strstr(h, "\r\n\r\n");
3100 ENVELOPE *env;
3101 BODY *body, *tmpB;
3103 bstart += 4;
3104 INIT(&bs, mail_string, bstart, tlen);
3105 rfc822_parse_msg_full(&env, &body, h, bstart-h-4, &bs, BADHOST, 0, 0);
3106 mail_free_envelope(&env);
3108 mail_free_body_part(&b->nested.part);
3109 tmpB = mail_body_section(body, (unsigned char *) section);
3110 if(MIME_MSG(tmpB->type, tmpB->subtype))
3111 b->nested.part = tmpB->nested.msg->body->nested.part;
3112 else
3113 b->nested.part = tmpB->nested.part;
3114 create_local_cache(bstart, bstart, &b->nested.part->body, 1);
3115 modified_the_body = 1;
3117 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3119 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3121 if(mimetext)
3122 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3124 if (mimetext == NULL || bodytext == NULL)
3125 return modified_the_body;
3127 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3129 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL)
3130 return modified_the_body;
3132 (void) BIO_reset(in);
3133 BIO_write(in, mimetext, mimelen);
3134 BIO_write(in, bodytext, bodylen);
3135 so_give(&msg_so);
3137 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3138 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, 0);
3139 if(result < 0)
3140 return modified_the_body;
3146 BIO_free(in);
3147 if(b->subtype)
3148 fs_give((void**) &b->subtype);
3150 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3151 b->encoding = ENC8BIT;
3153 if(b->description)
3154 fs_give ((void**) &b->description);
3156 what_we_did = result ? _("This message was cryptographically signed.") :
3157 _("This message was cryptographically signed but the signature could not be verified.");
3159 b->description = cpystr(what_we_did);
3161 b->sparep = create_body_sparep(P7Type, p7);
3163 p = b->nested.part;
3165 /* p is signed plaintext */
3166 if(p && p->next)
3167 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
3169 modified_the_body = 1;
3171 return modified_the_body;
3175 PERSONAL_CERT *
3176 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
3178 PERSONAL_CERT *x = NULL;
3180 if(ps_global->smime){
3181 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
3182 X509 *mine;
3184 mine = x->cert;
3186 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,X509_get_issuer_name(mine)) &&
3187 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,X509_get_serialNumber(mine))){
3188 break;
3193 return x;
3197 static PERSONAL_CERT *
3198 find_certificate_matching_pkcs7(PKCS7 *p7)
3200 int i;
3201 STACK_OF(PKCS7_RECIP_INFO) *recips;
3202 PERSONAL_CERT *x = NULL;
3204 recips = p7->d.enveloped->recipientinfo;
3206 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
3207 PKCS7_RECIP_INFO *ri;
3209 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
3211 if((x=find_certificate_matching_recip_info(ri))!=0){
3212 break;
3216 return x;
3219 /* decrypt an encrypted file.
3220 Args: fp - the path to the encrypted file.
3221 rv - a code that tells the caller what happened inside the function
3222 pcert - a personal certificate that was used to encrypt this file
3223 Returns the decoded text allocated in a char *, whose memory must be
3224 freed by caller
3227 char *
3228 decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc)
3230 PKCS7 *p7 = NULL;
3231 char *text, *tmp;
3232 BIO *in = NULL, *out = NULL;
3233 int i, j;
3234 long unsigned int len;
3235 void *ret;
3237 if(pc == NULL || (text = read_file(fp, 0)) == NULL || *text == '\0')
3238 return NULL;
3240 tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1);
3241 for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0'
3242 && text[i] != '-'; j++, i++)
3243 tmp[j] = text[i];
3244 tmp[j] = '\0';
3246 ret = rfc822_base64((unsigned char *)tmp, strlen(tmp), &len);
3248 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
3249 p7 = d2i_PKCS7_bio(in, NULL);
3250 BIO_free(in);
3253 if (text) fs_give((void **)&text);
3254 if (ret) fs_give((void **)&ret);
3256 if (rv) *rv = pc->key == NULL ? -1 : 1;
3258 out = BIO_new(BIO_s_mem());
3259 (void) BIO_reset(out);
3261 if(PKCS7_decrypt(p7, pc->key, pc->cert, out, 0) != 0){
3262 BIO_get_mem_data(out, &tmp);
3263 text = cpystr(tmp);
3264 BIO_free(out);
3265 } else
3266 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3267 (char *) openssl_error_string());
3268 PKCS7_free(p7);
3270 return text;
3274 * Try to decode (decrypt or verify a signature) a PKCS7 body
3275 * Returns non-zero if something was changed.
3277 static int
3278 do_decoding(BODY *b, long msgno, const char *section)
3280 int modified_the_body = 0;
3281 BIO *out = NULL;
3282 PKCS7 *p7 = NULL;
3283 X509 *recip = NULL;
3284 EVP_PKEY *key = NULL;
3285 PERSONAL_CERT *pcert = NULL;
3286 char *what_we_did = "";
3287 char null[1];
3289 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"));
3290 null[0] = '\0';
3291 smime_init();
3294 * Extract binary data from part to an in-memory store
3297 if(b->sparep){
3298 if(get_body_sparep_type(b->sparep) == P7Type)
3299 p7 = (PKCS7*) get_body_sparep_data(b->sparep);
3301 else{
3302 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
3303 if(!p7){
3304 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
3305 (char*) openssl_error_string());
3306 goto end;
3310 * Save the PKCS7 object for later dealings by the user interface.
3311 * It will be cleaned up when the body is garbage collected.
3313 b->sparep = create_body_sparep(P7Type, p7);
3316 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
3318 if(PKCS7_type_is_signed(p7)){
3319 int sigok;
3321 out = BIO_new(BIO_s_mem());
3322 (void) BIO_reset(out);
3323 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3325 sigok = do_signature_verify(p7, NULL, out, 0);
3327 what_we_did = sigok ? _("This message was cryptographically signed.") :
3328 _("This message was cryptographically signed but the signature could not be verified.");
3330 /* make sure it's null terminated */
3331 BIO_write(out, null, 1);
3333 else if(!PKCS7_type_is_enveloped(p7)){
3334 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
3335 goto end;
3337 else{ /* It *is* enveloped */
3338 int decrypt_result;
3340 what_we_did = _("This message was encrypted.");
3342 /* now need to find a cert that can decrypt this */
3343 pcert = find_certificate_matching_pkcs7(p7);
3345 if(!pcert){
3346 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3347 goto end;
3350 recip = pcert->cert;
3352 if(!load_private_key(pcert)
3353 && ps_global->smime
3354 && ps_global->smime->need_passphrase
3355 && !ps_global->smime->already_auto_asked){
3356 /* Couldn't load key with blank password, ask user */
3357 ps_global->smime->already_auto_asked = 1;
3358 if(pith_opt_smime_get_passphrase){
3359 (*pith_opt_smime_get_passphrase)();
3360 load_private_key(pcert);
3364 key = pcert->key;
3365 if(!key)
3366 goto end;
3368 out = BIO_new(BIO_s_mem());
3369 (void) BIO_reset(out);
3370 BIO_puts(out, "MIME-Version: 1.0\r\n");
3372 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
3374 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3375 forget_private_keys();
3377 if(!decrypt_result){
3378 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3379 (char*) openssl_error_string());
3380 goto end; }
3382 BIO_write(out, null, 1);
3386 * We've now produced a flattened MIME object in BIO out.
3387 * It needs to be turned back into a BODY.
3390 if(out){
3391 BODY *body;
3392 ENVELOPE *env;
3393 char *h = NULL;
3394 char *bstart;
3395 STRING s;
3396 BUF_MEM *bptr = NULL;
3398 BIO_get_mem_ptr(out, &bptr);
3399 if(bptr)
3400 h = bptr->data;
3402 /* look for start of body */
3403 bstart = strstr(h, "\r\n\r\n");
3405 if(!bstart){
3406 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
3408 else{
3409 SIZEDTEXT *st;
3410 bstart += 4; /* skip over CRLF*2 */
3412 INIT(&s, mail_string, bstart, strlen(bstart));
3413 rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
3414 mail_free_envelope(&env); /* Don't care about this */
3416 if(body->type == TYPEMULTIPART
3417 && !strucmp(body->subtype, "SIGNED")){
3418 char *cookie = NULL;
3419 PARAMETER *param;
3420 for (param = body->parameter; param && !cookie; param = param->next)
3421 if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value;
3422 if(cookie != NULL){
3423 st = fs_get(sizeof(SIZEDTEXT));
3424 st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */
3425 st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4);
3426 body->sparep = create_body_sparep(SizedText, (void *)st);
3428 else
3429 q_status_message(SM_ORDER, 3, 3, _("Couldn't find cookie in attachment list."));
3431 body->mime.offset = 0;
3432 body->mime.text.size = 0;
3435 * Now convert original body (application/pkcs7-mime)
3436 * to a multipart body with one sub-part (the decrypted body).
3437 * Note that the sub-part may also be multipart!
3440 b->type = TYPEMULTIPART;
3441 if(b->subtype)
3442 fs_give((void**) &b->subtype);
3445 * This subtype is used in mailview.c to annotate the display of
3446 * encrypted or signed messages. We know for sure then that it's a PKCS7
3447 * part because the sparep field is set to the PKCS7 object (see above).
3449 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3450 b->encoding = ENC8BIT;
3452 if(b->description)
3453 fs_give((void**) &b->description);
3455 b->description = cpystr(what_we_did);
3457 if(b->disposition.type)
3458 fs_give((void **) &b->disposition.type);
3460 if(b->contents.text.data)
3461 fs_give((void **) &b->contents.text.data);
3463 if(b->parameter)
3464 mail_free_body_parameter(&b->parameter);
3466 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3467 b->nested.part = fs_get(sizeof(PART));
3468 b->nested.part->body = *body;
3469 b->nested.part->next = NULL;
3471 fs_give((void**) &body);
3474 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3475 * the decrypted data. Otherwise, it'll try to load it from the original
3476 * data. Eek.
3478 create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body, 0);
3480 modified_the_body = 1;
3484 end:
3485 if(out)
3486 BIO_free(out);
3488 return modified_the_body;
3493 * Recursively handle PKCS7 bodies in our message.
3495 * Returns non-zero if some fiddling was done.
3497 static int
3498 do_fiddle_smime_message(BODY *b, long msgno, char *section)
3500 int modified_the_body = 0;
3502 if(!b)
3503 return 0;
3505 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"));
3507 if(is_pkcs7_body(b)){
3509 if(do_decoding(b, msgno, section)){
3511 * b should now be a multipart message:
3512 * fiddle it too in case it's been multiply-encrypted!
3515 /* fallthru */
3516 modified_the_body = 1;
3520 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
3522 PART *p;
3523 int partNum;
3524 char newSec[100];
3526 if(MIME_MULT_SIGNED(b->type, b->subtype)){
3530 * Ahah. We have a multipart signed entity.
3532 * Multipart/signed
3533 * part 1 (signed thing)
3534 * part 2 (the pkcs7 signature)
3536 * We're going to convert that to
3538 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3539 * part 1 (signed thing)
3540 * part 2 has been freed
3542 * We also extract the signature from part 2 and save it
3543 * in the multipart body->sparep, and we add a description
3544 * in the multipart body->description.
3547 * The results of a decrypted message will be similar. It
3548 * will be
3550 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3551 * part 1 (decrypted thing)
3554 modified_the_body += do_detached_signature_verify(b, msgno, section);
3556 else if(MIME_MSG(b->type, b->subtype)){
3557 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
3559 else{
3561 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
3562 /* Append part number to the section string */
3564 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
3566 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
3571 return modified_the_body;
3576 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3577 * Returns non-zero if something was changed.
3580 fiddle_smime_message(BODY *b, long msgno)
3582 return do_fiddle_smime_message(b, msgno, "");
3586 /********************************************************************************/
3590 * Output a string in a distinctive style
3592 void
3593 gf_puts_uline(char *txt, gf_io_t pc)
3595 pc(TAG_EMBED); pc(TAG_BOLDON);
3596 gf_puts(txt, pc);
3597 pc(TAG_EMBED); pc(TAG_BOLDOFF);
3600 /* get_chain_for_cert: error and level are mandatory arguments */
3601 STACK_OF(X509) *
3602 get_chain_for_cert(X509 *cert, int *error, int *level)
3604 STACK_OF(X509) *chain = NULL;
3605 X509_STORE_CTX *ctx;
3606 X509 *x, *xtmp;
3607 int rc; /* return code */
3609 *level = -1;
3610 *error = 0;
3611 ERR_clear_error();
3612 if((s_cert_store != NULL) && (ctx = X509_STORE_CTX_new()) != NULL){
3613 X509_STORE_set_flags(s_cert_store, 0);
3614 if(!X509_STORE_CTX_init(ctx, s_cert_store, cert, NULL))
3615 *error = X509_STORE_CTX_get_error(ctx);
3616 else if((chain = sk_X509_new_null()) != NULL){
3617 for(x = cert; ; x = xtmp){
3618 if(++*level > 0)
3619 sk_X509_push(chain, X509_dup(x));
3620 rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x);
3621 if(rc < 0)
3622 *error = 1;
3623 if(rc <= 0)
3624 break;
3625 if(!X509_check_issued(xtmp, xtmp))
3626 break;
3629 X509_STORE_CTX_free(ctx);
3631 return chain;
3636 * Sign a message. Called from call_mailer in send.c.
3638 * This takes the header for the outgoing message as well as a pointer
3639 * to the current body (which may be reallocated).
3640 * The last argument (BODY **bp) is an argument that tells Alpine
3641 * if the body has 8 bit. if *bp is not null we compute two signatures
3642 * one for the quoted-printable encoded message, and another for the
3643 * 8bit encoded message. We return the signature for the 8bit encoded
3644 * part in p2->body.mime.text.data.
3645 * The reason why we compute two signatures is so that we can decide
3646 * which one to use later, and we only do it in the case that *bp is
3647 * not null. If we did not do this, then we might not be able to sign
3648 * a message until we log in to the smtp server, so instead of doing
3649 * that, we get ready for any possible situation we might find.
3652 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp)
3654 STORE_S *outs = NULL;
3655 STORE_S *outs_2 = NULL;
3656 BODY *body = *bodyP;
3657 BODY *newBody = NULL;
3658 PART *p1 = NULL;
3659 PART *p2 = NULL;
3660 PERSONAL_CERT *pcert;
3661 BIO *in = NULL;
3662 BIO *in_2 = NULL;
3663 BIO *out = NULL;
3664 BIO *out_2 = NULL;
3665 PKCS7 *p7 = NULL;
3666 PKCS7 *p7_2 = NULL;
3667 STACK_OF(X509) *chain;
3668 const EVP_MD *md = EVP_sha256(); /* use this digest instead of sha1 */
3669 int result = 0, error;
3670 int flags = dont_detach ? 0 : PKCS7_DETACHED;
3671 int level;
3673 dprint((9, "sign_outgoing_message()"));
3675 smime_init();
3677 /* Look for a private key matching the sender address... */
3679 pcert = match_personal_cert(header->env);
3681 if(!pcert){
3682 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
3683 goto end;
3686 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
3687 /* Couldn't load key with blank password, try again */
3688 if(pith_opt_smime_get_passphrase){
3689 (*pith_opt_smime_get_passphrase)();
3690 load_private_key(pcert);
3694 if(!pcert->key)
3695 goto end;
3697 if(((chain = get_chain_for_cert(pcert->cert, &error, &level)) != NULL && error)
3698 || level == 0){
3699 sk_X509_pop_free(chain, X509_free);
3700 chain = NULL;
3703 if(error)
3704 q_status_message(SM_ORDER, 1, 1,
3705 _("Not all certificates needed to verify signature included in signed message"));
3707 in = body_to_bio(body);
3709 flags |= PKCS7_PARTIAL;
3710 if((p7 = PKCS7_sign(NULL, NULL, chain, in, flags)) != NULL
3711 && PKCS7_sign_add_signer(p7, pcert->cert, pcert->key, md, flags))
3712 PKCS7_final(p7, in, flags);
3714 if(bp && *bp){
3715 int i, save_encoding;
3717 for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++);
3719 if(i > ENCMAX){ /* no empty encoding slots! */
3720 *bp = NULL;
3722 else {
3723 save_encoding = (*bp)->encoding;
3724 body_encodings[(*bp)->encoding = i] = body_encodings[ENC8BIT];
3726 in_2 = body_to_bio(body);
3728 body_encodings[i] = NULL;
3729 (*bp)->encoding = save_encoding;
3733 if(bp && *bp){
3734 if((p7_2 = PKCS7_sign(NULL, NULL, chain, in_2, flags)) != NULL
3735 && PKCS7_sign_add_signer(p7_2, pcert->cert, pcert->key, md, flags))
3736 PKCS7_final(p7_2, in_2, flags);
3739 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3740 forget_private_keys();
3742 if(chain)
3743 sk_X509_pop_free(chain, X509_free);
3745 if(!p7){
3746 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
3747 goto end;
3750 outs = so_get(BioType, NULL, EDIT_ACCESS);
3751 out = bio_from_store(outs);
3753 i2d_PKCS7_bio(out, p7);
3754 (void) BIO_flush(out);
3756 so_seek(outs, 0, SEEK_SET);
3758 if(bp && *bp && p7_2){
3759 outs_2 = so_get(BioType, NULL, EDIT_ACCESS);
3760 out_2 = bio_from_store(outs_2);
3762 i2d_PKCS7_bio(out_2, p7_2);
3763 (void) BIO_flush(out_2);
3765 so_seek(outs_2, 0, SEEK_SET);
3768 if((flags&PKCS7_DETACHED)==0){
3770 /* the simple case: the signed data is in the pkcs7 object */
3772 newBody = mail_newbody();
3774 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3776 newBody->contents.text.data = (unsigned char *) outs;
3777 *bodyP = newBody;
3779 result = 1;
3781 else{
3784 * OK.
3785 * We have to create a new body as follows:
3787 * multipart/signed; blah blah blah
3788 * reference to existing body
3790 * pkcs7 object
3793 newBody = mail_newbody();
3795 newBody->type = TYPEMULTIPART;
3796 newBody->subtype = cpystr("signed");
3797 newBody->encoding = ENC7BIT;
3799 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
3800 set_parameter(&newBody->parameter, "micalg", "sha-256");
3802 p1 = mail_newbody_part();
3803 p2 = mail_newbody_part();
3806 * This is nasty. We're just copying the body in here,
3807 * but since our newBody is freed at the end of call_mailer,
3808 * we mustn't let this body (the original one) be freed twice.
3810 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
3812 p1->next = p2;
3814 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL);
3815 p2->body.mime.text.data = (unsigned char *) outs_2;
3816 p2->body.contents.text.data = (unsigned char *) outs;
3818 newBody->nested.part = p1;
3820 *bodyP = newBody;
3822 result = 1;
3825 end:
3827 PKCS7_free(p7);
3828 BIO_free(in);
3830 if(bp && *bp){
3831 if(p7_2) PKCS7_free(p7_2);
3832 BIO_free(in_2);
3835 dprint((9, "sign_outgoing_message returns %d", result));
3836 return result;
3840 SMIME_STUFF_S *
3841 new_smime_struct(void)
3843 SMIME_STUFF_S *ret = NULL;
3845 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
3846 memset((void *) ret, 0, sizeof(*ret));
3847 ret->publictype = Nada;
3849 return ret;
3853 static void
3854 free_smime_struct(SMIME_STUFF_S **smime)
3856 if(smime && *smime){
3857 if((*smime)->passphrase_emailaddr){
3858 int i;
3859 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
3860 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
3861 fs_give((void **) (*smime)->passphrase_emailaddr);
3864 if((*smime)->publicpath)
3865 fs_give((void **) &(*smime)->publicpath);
3867 if((*smime)->publiccertlist)
3868 free_certlist(&(*smime)->publiccertlist);
3870 if((*smime)->backuppubliccertlist)
3871 free_certlist(&(*smime)->backuppubliccertlist);
3873 if((*smime)->cacertlist)
3874 free_certlist(&(*smime)->cacertlist);
3876 if((*smime)->backupcacertlist)
3877 free_certlist(&(*smime)->backupcacertlist);
3879 if((*smime)->privatecertlist)
3880 free_certlist(&(*smime)->privatecertlist);
3882 if((*smime)->backupprivatecertlist)
3883 free_certlist(&(*smime)->backupprivatecertlist);
3885 if((*smime)->publiccontent)
3886 fs_give((void **) &(*smime)->publiccontent);
3888 if((*smime)->privatepath)
3889 fs_give((void **) &(*smime)->privatepath);
3891 if((*smime)->personal_certs){
3892 PERSONAL_CERT *pc;
3894 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
3895 free_personal_certs(&pc);
3896 (*smime)->personal_certs = NULL;
3899 if((*smime)->privatecontent)
3900 fs_give((void **) &(*smime)->privatecontent);
3902 if((*smime)->capath)
3903 fs_give((void **) &(*smime)->capath);
3905 if((*smime)->cacontent)
3906 fs_give((void **) &(*smime)->cacontent);
3908 fs_give((void **) smime);
3912 #endif /* SMIME */