* Fix crash when a CA certificate failed to load, by protecting some calls
[alpine.git] / pith / smime.c
blob50c0dcc1fad71eca8ebb49fb86ba1816d9ba3517
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-2017 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 #ifdef PASSFILE
46 #include "../pith/imap.h"
47 #endif /* PASSFILE */
49 #include <openssl/buffer.h>
50 #include <openssl/x509v3.h>
51 #include <openssl/evp.h>
53 /* internal prototypes */
54 static void forget_private_keys(void);
55 static int app_RAND_load_file(const char *file);
56 static void openssl_extra_randomness(void);
57 static int app_RAND_write_file(const char *file);
58 static const char *openssl_error_string(void);
59 static int load_private_key(PERSONAL_CERT *pcert);
60 static void create_local_cache(char *h, char *base, BODY *b, int type);
61 static long rfc822_output_func(void *b, char *string);
62 static void setup_pkcs7_body_for_signature(BODY *b, char *description,
63 char *type, char *filename, char *smime_type);
64 static BIO *body_to_bio(BODY *body);
65 static BIO *bio_from_store(STORE_S *store);
66 static STORE_S *get_part_contents(long msgno, const char *section);
67 static PKCS7 *get_pkcs7_from_part(long msgno, const char *section);
68 static int do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent);
69 static int do_detached_signature_verify(BODY *b, long msgno, char *section);
70 static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7);
71 static int do_decoding(BODY *b, long msgno, const char *section);
72 static void free_smime_struct(SMIME_STUFF_S **smime);
73 static void setup_storage_locations(void);
74 static int copy_container_to_dir(WhichCerts which);
75 static int do_fiddle_smime_message(BODY *b, long msgno, char *section);
76 void setup_privatekey_storage(void);
77 int smime_extract_and_save_cert(PKCS7 *p7, int check_cert);
78 int same_cert(X509 *, X509 *);
79 #ifdef PASSFILE
80 int load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile, char **certfile, EVP_PKEY **pkey, X509 **pcert);
81 #endif /* PASSFILE */
82 EVP_PKEY *load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *);
83 void smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen);
84 void smime_remove_folding_space(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen);
85 int smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag);
87 int (*pith_opt_smime_get_passphrase)(void);
88 int (*pith_smime_import_certificate)(char *, char *, char *, size_t);
89 int (*pith_smime_enter_password)(char *, char *, size_t);
90 int (*pith_smime_confirm_save)(char *);
92 static X509_STORE *s_cert_store;
94 /* State management for randomness functions below */
95 static int seeded = 0;
97 void *
98 create_smime_sparep(SpareType stype, void *s)
100 SMIME_SPARE_S *rv;
102 rv = fs_get(sizeof(SMIME_SPARE_S));
103 rv->sptype = stype;
104 rv->data = s;
105 return (void *) rv;
108 SpareType
109 get_smime_sparep_type(void *s)
111 return ((SMIME_SPARE_S *)s)->sptype;
114 void *
115 get_smime_sparep_data(void *s)
117 return ((SMIME_SPARE_S *)s)->data;
121 #ifdef PASSFILE
123 * load key from pathkeydir and cert from pathcertdir. It chooses the first
124 * key/certificate pair that matches. Delete pairs that you do not want used,
125 * if you do not want them selected. All parameters must be non-null.
126 * Memory freed by caller.
127 * Return values:
128 * -1 : user cancelled load
129 * 0 : load was successful
130 * 1 : there was an error in the loading.
133 load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile,
134 char **certfile, EVP_PKEY **pkey, X509 **pcert)
136 char buf[MAXPATH+1], pathkey[MAXPATH+1], prompt[MAILTMPLEN];
137 DIR *dirp;
138 struct dirent *d;
139 int b = 0, ret = 1; /* assume error */
141 if(pathkeydir == NULL || pathcertdir == NULL || keyfile == NULL
142 || pkey == NULL || certfile == NULL || pcert == NULL)
143 return 1;
145 *keyfile = NULL;
146 *certfile = NULL;
147 *pkey = NULL;
148 *pcert = NULL;
150 if((dirp = opendir(pathkeydir)) != NULL){
151 while(b == 0 && (d=readdir(dirp)) != NULL){
152 size_t ll;
154 if((ll=strlen(d->d_name)) && ll > 4){
155 if(!strcmp(d->d_name+ll-4, ".key")){
156 strncpy(buf, d->d_name, sizeof(buf));
157 buf[sizeof(buf)-1] = '\0';
158 build_path(pathkey, pathkeydir, buf, sizeof(pathkey));
159 buf[strlen(buf)-4] = '\0';
160 snprintf(prompt, sizeof(prompt),
161 _("Enter password of key <%s> to unlock password file: "), buf);
162 if((*pkey = load_pkey_with_prompt(pathkey, NULL, prompt, &ret)) != NULL){
163 if(load_cert_for_key(pathcertdir, *pkey, certfile, pcert)){
164 b = 1; /* break */
165 *keyfile = cpystr(buf);
166 } else {
167 EVP_PKEY_free(*pkey);
168 *pkey = NULL;
169 q_status_message1(SM_ORDER, 0, 2,
170 _("Cannot find certificate that matches key <%s>. Continuing..."), buf);
176 closedir(dirp);
178 return ret;
182 /* setup a key and certificate to encrypt and decrypt a password file.
183 * These files will be saved in the .alpine-smime/.pwd directory, but its
184 * location can be setup in the command line with the -pwdcertdir option.
185 * Here are the rules:
187 * Check if the .alpine-smime/.pwd (or -pwdcertdir directory) exists,
188 * if not create it. If we are successful, move to the next step
190 * - If the user has a key/cert pair, in the .alpine-smime/.pwd dir
191 * setup is successful;
192 * - if the user does not have a key/cert pair, look to see if
193 * ps_global->smime->personal_certs is already setup, if so, use it.
194 * - if ps_global->smime->personal_certs is not set up, see if we can
195 * find a certificate/cert pair in the default locations at compilation
196 * time. (~/.alpine-smime/private and ~/.alpine-smime/public
197 * - if none of this is successful, create a key/certificate pair
198 * (TODO: implement this)
199 * - in any other case, setup is not successful.
201 * If setup is successful, setup ps_global->pwdcert.
202 * If any of this fails, ps_global->pwdcert will be null.
203 * Ok, that should do it.
205 * return values: 0 - everything is normal
206 * 1 - User could not unlock key or no key in directory.
207 * 2 - User cancelled to create self signed certificate
208 * -1 - we do not know which directory to use
209 * -2 - "-pwdcertdir" was given by user, but directory does not exist
210 * -3 - "DF_PASSWORD_DIR" exists but it is not a directory!!??
211 * -4 - we tried to create DF_PASSWORD_DIR but failed.
212 * -5 - password directory exists, but it is empty
216 setup_pwdcert(void **pwdcert)
218 int rv;
219 int we_inited = 0;
220 int setup_dir = 0; /* make it non zero if we know which dir to use */
221 struct stat sbuf;
222 char pathdir[MAXPATH+1], pathkey[MAXPATH+1], fpath[MAXPATH+1], pathcert[MAXPATH+1];
223 char fpath2[MAXPATH+1], prompt[MAILTMPLEN];
224 char *keyfile, *certfile, *text;
225 EVP_PKEY *pkey = NULL;
226 X509 *pcert = NULL;
227 PERSONAL_CERT *pc, *pc2 = NULL;
228 static int was_here = 0;
230 if(pwdcert == NULL || was_here == 1)
231 return -1;
233 was_here++;
234 if(ps_global->pwdcertdir){
235 if(our_stat(ps_global->pwdcertdir, &sbuf) == 0
236 && ((sbuf.st_mode & S_IFMT) == S_IFDIR)){
237 setup_dir++;
238 strncpy(pathdir, ps_global->pwdcertdir, sizeof(pathdir));
239 pathdir[sizeof(pathdir)-1] = '\0';
241 else rv = -2;
242 } else {
243 smime_path(DF_PASSWORD_DIR, pathdir, sizeof(pathdir));
244 if(our_stat(pathdir, &sbuf) == 0){
245 if((sbuf.st_mode & S_IFMT) == S_IFDIR)
246 setup_dir++;
247 else rv = -3;
248 } else if(can_access(pathdir, ACCESS_EXISTS) != 0
249 && our_mkpath(pathdir, 0700) == 0)
250 setup_dir++;
251 else rv = -4;
254 if(setup_dir == 0){
255 was_here = 0;
256 return rv;
259 if(load_key_and_cert(pathdir, pathdir, &keyfile, &certfile, &pkey, &pcert) < 0){
260 was_here = 0;
261 return 1;
264 if(ps_global->pwdcertdir == NULL){ /* save the result of pwdcertdir */
265 ps_global->pwdcertdir = cpystr(pathdir);
266 /* if the user gave a pwdcertdir and there is nothing there, do not
267 * continue. Let the user initialize on their own this directory.
269 if(certfile == NULL || keyfile == NULL){
270 was_here = 0;
271 return -5;
275 if(certfile && keyfile){
276 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
277 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
278 pc->name = keyfile;
279 pc->key = pkey;
280 pc->cert = pcert;
281 pc->cname = certfile;
282 *pwdcert = (void *) pc;
283 was_here = 0;
284 return 0;
287 /* look to see if there are any certificates lying around, first
288 * we try to load ps_global->smime to see if that has information
289 * we can use. If we are the process filling the smime structure
290 * we deinit at the end, since this might not do a full init.
292 if(ps_global && ps_global->smime && !ps_global->smime->inited){
293 we_inited++;
294 smime_init();
297 /* at this point ps_global->smime->inited == 1 */
298 if(ps_global->smime && ps_global->smime->personal_certs != NULL){
299 pc = (PERSONAL_CERT *) ps_global->smime->personal_certs;
300 if(ps_global->smime->privatetype == Directory){
301 build_path(pathkey, ps_global->smime->privatepath, pc->name, sizeof(pathkey));
302 strncat(pathkey, ".key", 4);
303 pathkey[sizeof(pathkey)-1] = '\0';
304 text = NULL;
305 } else if (ps_global->smime->privatetype == Container){
306 if(pc->keytext == NULL){ /* we should *never* be here, but just in case */
307 if(ps_global->smime->privatecontent != NULL){
308 char tmp[MAILTMPLEN], *s, *t, c;
309 snprintf(tmp, sizeof(tmp), "%s%s", EMAILADDRLEADER, pc->name);
310 tmp[sizeof(tmp)-1] = '\0';
311 if((s = strstr(ps_global->smime->privatecontent, tmp)) != NULL){
312 if((t = strstr(s+strlen(tmp), EMAILADDRLEADER)) != NULL){
313 c = *t;
314 *t = '\0';
315 pc->keytext = cpystr(s + strlen(tmp) + 1); /* 1 = strlen("\n") */
316 *t = c;
318 else
319 pc->keytext = cpystr(s + strlen(tmp) + 1); /* 1 = strlen("\n") */
323 if(pc->keytext != NULL) /* we should go straigth here */
324 text = pc->keytext;
325 } else if (ps_global->smime->privatetype == Keychain){
326 pathkey[0] = '\0'; /* no apple key chain support yet */
327 text = NULL;
329 if((pathkey && *pathkey) || text){
330 snprintf(prompt, sizeof(prompt),
331 _("Enter password of key <%s> to unlock password file: "), pc->name);
333 if((pkey = load_pkey_with_prompt(pathkey, text, prompt, NULL)) != NULL){
334 pc2 = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
335 memset((void *)pc2, 0, sizeof(PERSONAL_CERT));
336 pc2->name = cpystr(pc->name);
337 pc2->key = pkey;
338 pc2->cert = X509_dup(pc->cert);
340 /* now copy the keys and certs, starting by the key... */
341 build_path(fpath, pathdir, pc->name, sizeof(fpath));
342 strncat(fpath, ".key", 4);
343 fpath[sizeof(fpath)-1] = '\0';
344 if(our_stat(fpath, &sbuf) == 0){ /* if fpath exists */
345 if((sbuf.st_mode & S_IFMT) == S_IFREG) /* and is a regular file */
346 setup_dir++; /* we are done */
347 } else if(ps_global->smime->privatetype == Directory){
348 if(our_copy(fpath, pathkey) == 0)
349 setup_dir++;
350 } else if(ps_global->smime->privatetype == Container){
351 BIO *out;
352 if((out = BIO_new_file(fpath, "w")) != NULL){
353 if(BIO_puts(out, pc->keytext) > 0)
354 setup_dir++;
355 BIO_free(out);
357 } else if(ps_global->smime->privatetype == Keychain){
358 /* add support for Apple Mac OS X */
362 /* successful copy of key, now continue with certificate */
363 if(setup_dir){
364 setup_dir = 0;
366 build_path(pathkey, ps_global->smime->publicpath, pc->name, sizeof(pathkey));
367 strncat(pathkey, ".crt", 4);
368 pathkey[sizeof(pathkey)-1] = '\0';
370 build_path(fpath, pathdir, pc->name, sizeof(fpath));
371 strncat(fpath, ".crt", 4);
372 fpath[sizeof(fpath)-1] = '\0';
374 if(our_stat(fpath, &sbuf) == 0){
375 if((sbuf.st_mode & S_IFMT) == S_IFREG)
376 setup_dir++;
378 else if(ps_global->smime->privatetype == Directory){
379 if(our_copy(fpath, pathkey) == 0)
380 setup_dir++;
381 } else if(ps_global->smime->privatetype == Container) {
382 BIO *out;
383 if((out = BIO_new_file(fpath, "w")) != NULL){
384 if(PEM_write_bio_X509(out, pc->cert))
385 setup_dir++;
386 BIO_free(out);
388 } else if (ps_global->smime->privatetype == Keychain) {
389 /* add support for Mac OS X */
393 if(setup_dir){
394 *pwdcert = (void *) pc2;
395 was_here = 0;
396 return 0;
398 else if(pc2 != NULL)
399 free_personal_certs(&pc2);
400 } /* if (pathkey...) */
401 } /* if(ps_global->smime->personal_certs) */
404 if(setup_dir == 0){
405 /* PATHCERTDIR(Private) must be null, so create a path */
406 set_current_val(&ps_global->vars[V_PRIVATEKEY_DIR], TRUE, TRUE);
407 smime_path(ps_global->VAR_PRIVATEKEY_DIR, pathkey, sizeof(pathkey));
409 /* PATHCERTDIR(Public) must be null, so create a path */
410 set_current_val(&ps_global->vars[V_PUBLICCERT_DIR], TRUE, TRUE);
411 smime_path(ps_global->VAR_PUBLICCERT_DIR, pathcert, sizeof(pathcert));
413 /* BUG: this does not support local containers */
414 load_key_and_cert(pathkey, pathcert, &keyfile, &certfile, &pkey, &pcert);
416 if(certfile && keyfile){
417 build_path(fpath, pathdir, keyfile, sizeof(fpath));
418 strncat(fpath, ".key", 4);
419 fpath[sizeof(fpath)-1] = '\0';
421 build_path(fpath2, pathkey, keyfile, sizeof(fpath));
422 strncat(fpath2, ".key", 4);
423 fpath2[sizeof(fpath2)-1] = '\0';
425 if(our_copy(fpath, fpath2) == 0)
426 setup_dir++;
428 if(setup_dir){
429 setup_dir = 0;
431 build_path(fpath, pathdir, certfile, sizeof(fpath));
432 build_path(fpath2, pathcert, certfile, sizeof(fpath2));
434 if(our_copy(fpath, fpath2) == 0)
435 setup_dir++;
440 if(keyfile && certfile){
441 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
442 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
443 pc->name = keyfile;
444 pc->key = pkey;
445 pc->cert = pcert;
446 *pwdcert = (void *) pc;
447 fs_give((void **)&certfile);
448 was_here = 0;
449 return 0;
452 was_here = 0;
453 if(we_inited)
454 smime_deinit();
455 return 0;
457 #endif /* PASSFILE */
459 /* smime_expunge_cert.
460 * Return values: < 0 there was an error.
461 * >=0 the number of messages expunged
464 smime_expunge_cert(WhichCerts ctype)
466 int count, removed;
467 CertList *cl, *dummy, *data;
468 char *path, buf[MAXPATH+1];
469 char *contents;
471 if(DATACERT(ctype)== NULL)
472 return -1;
474 /* data cert is the way we unify certificate management across
475 * functions, but it is not where we really save the information in the
476 * case ctype is equal to Private. What we will do is to update the
477 * datacert, and in the case of ctype equal to Private use the updated
478 * certdata to update the personal_certs data.
481 path = PATHCERTDIR(ctype);
483 if(path){
484 /* add a fake certificate at the beginning of the list */
485 dummy = fs_get(sizeof(CertList));
486 memset((void *)dummy, 0, sizeof(CertList));
487 dummy->next = DATACERT(ctype);
489 for(cl = dummy, count = 0; cl && cl->next;){
490 if(cl->next->data.deleted == 0){
491 cl = cl->next;
492 continue;
495 removed = 1; /* assume success */
496 if(SMHOLDERTYPE(ctype) == Directory){
497 build_path(buf, path, cl->next->name, sizeof(buf));
498 if(ctype == Private && strlen(buf) + strlen(EXTCERT(Private)) < sizeof(buf)){
499 strncat(buf, EXTCERT(Private), 4);
500 buf[sizeof(buf)-1] = '\0';
503 if(our_unlink(buf) < 0){
504 q_status_message1(SM_ORDER, 3, 3, _("Error removing certificate %s"), cl->next->name);
505 cl = cl->next;
506 removed = 0;
509 else if(SMHOLDERTYPE(ctype) == Container){
510 char *prefix= ctype == CACert ? CACERTSTORELEADER : EMAILADDRLEADER;
511 char tmp[MAILTMPLEN], *s, *t;
513 contents = CONTENTCERTLIST(ctype);
514 snprintf(tmp, sizeof(tmp), "%s%s", prefix, cl->next->name);
515 tmp[sizeof(tmp) - 1] = '\0';
516 if((s = strstr(contents, tmp)) != NULL){
517 if((t = strstr(s+strlen(tmp), prefix)) == NULL)
518 *s = '\0';
519 else
520 memmove(s, t, strlen(t)+1);
521 fs_resize((void **)&contents, strlen(contents)+1);
522 switch(ctype){
523 case Private: ps_global->smime->privatecontent = contents; break;
524 case Public : ps_global->smime->publiccontent = contents; break;
525 case CACert : ps_global->smime->cacontent = contents; break;
526 default : break;
529 else
530 removed = 0;
531 } else { /* unhandled case */
534 if(removed > 0){
535 count++; /* count it! */
536 data = cl->next;
537 cl->next = data->next;
538 if(data->name) fs_give((void **)&data->name);
539 fs_give((void **)&data);
542 } else
543 q_status_message(SM_ORDER, 3, 3, _("Error expunging certificate"));
545 switch(ctype){
546 case Private: ps_global->smime->privatecertlist = dummy->next; break;
547 case Public : ps_global->smime->publiccertlist = dummy->next; break;
548 case CACert : ps_global->smime->cacertlist = dummy->next; break;
549 default : break;
551 fs_give((void **)&dummy);
552 if(SMHOLDERTYPE(ctype) == Container){
553 if(copy_dir_to_container(ctype, contents) < 0)
554 count = 0;
556 if(count > 0){
557 q_status_message2(SM_ORDER, 3, 3, _("Removed %s certificate%s"), comatose(count), plural(count));
559 else
560 q_status_message(SM_ORDER, 3, 3, _("Error: No certificates were removed"));
561 return count;
564 void
565 mark_cert_deleted(WhichCerts ctype, int num, unsigned state)
567 CertList *cl;
568 int i;
570 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
571 cl->data.deleted = state;
574 unsigned
575 get_cert_deleted(WhichCerts ctype, int num)
577 CertList *cl;
578 int i;
580 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
581 return (cl && cl->data.deleted) ? 1 : 0;
584 EVP_PKEY *
585 load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *ret)
587 EVP_PKEY *pkey;
588 int rc = 0; /* rc == 1, cancel, rc == 0 success */
589 char pass[MAILTMPLEN+1];
590 BIO *in;
592 /* attempt to load with empty password */
593 in = text ? BIO_new_mem_buf(text, strlen(text)) : BIO_new_file(fpath, "r");
594 if(in != NULL){
595 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, "");
596 if(pkey != NULL) return pkey;
597 } else return NULL;
599 if(pith_smime_enter_password)
600 while(pkey == NULL && rc != 1){
601 do {
602 rc = (*pith_smime_enter_password)(prompt, (char *)pass, sizeof(pass));
603 } while (rc!=0 && rc!=1 && rc>0);
605 (void) BIO_reset(in);
606 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, (char *)pass);
609 BIO_free(in);
611 if(ret) *ret = rc == 1 ? -1 : pkey != NULL ? 0 : 1;
612 return pkey;
615 /* This is a tool for conf_screen, The return value must be zero when
616 * nothing changed, so if there is a failure in the import return 0
617 * and return 1 when we succeeded.\
618 * We call this function in two ways:
619 * either fname is null or not. If they fname is null, so is p_cert.
620 * if p_cert is not null, it is the PERSONAL_CERT structure of fname if this
621 * is available, otherwise we will fill it up here.
624 import_certificate(WhichCerts ctype, PERSONAL_CERT *p_cert, char *fname)
626 int r = 1, rc;
627 char filename[MAXPATH+1], full_filename[MAXPATH+1], buf[MAXPATH+1];
628 char *what;
630 if(pith_smime_import_certificate == NULL
631 || pith_smime_enter_password == NULL){
632 q_status_message(SM_ORDER, 0, 2,
633 _("import of certificates not implemented yet!"));
634 return 0;
637 if(fname == NULL){
638 what = ctype == Public || ctype == CACert ? "certificate" : "key";
639 r = (*pith_smime_import_certificate)(filename, full_filename, what, sizeof(filename) - 20);
641 if(r < 0)
642 return 0;
643 } else {
644 char *s;
645 strncpy(full_filename, fname, sizeof(full_filename));
646 if((s = strrchr(full_filename, '/')) != NULL)
647 strncpy(filename, s+1, sizeof(filename));
650 /* we are trying to import a new key for the password file. First we ask for the
651 * private key. Once this is loaded, we make a reasonable attempt to find the
652 * public key in the same directory as the key was loaded from. We do this by
653 * looking for a file with the correct public certificate name, then we look
654 * in the same private key, and if not, we ask the user for its location. If all
655 * of this works, we import the key and public to the password directory.
657 #ifdef PASSFILE
658 if(ctype == Password){
659 char PrivateKeyPath[MAXPATH+1], PublicCertPath[MAXPATH+1], s[MAXPATH+1];
660 char full_name_key[MAXPATH+1], full_name_cert[MAXPATH+1];
661 char *use_this_file;
662 char prompt[500];
663 EVP_PKEY *key = p_cert ? p_cert->key : NULL;
665 rc = 1; /* assume success :) */
666 if(strlen(filename) > 4){
667 strncpy(s, filename, sizeof(s));
668 s[sizeof(s)-1] = '\0';
669 if(!strcmp(s + strlen(s) - strlen(EXTCERT(Private)), EXTCERT(Private)))
670 s[strlen(s) - strlen(EXTCERT(Private))] = '\0';
671 else
672 rc = 0;
673 } else rc = 0;
675 if(rc == 0){
676 q_status_message(SM_ORDER, 1, 3, _("Error in key name. Check file extension"));
677 return 0;
680 snprintf(prompt, sizeof(prompt), _("Enter passphrase to unlock new key <%s>: "), filename);
681 prompt[sizeof(prompt)-1] = '\0';
682 if(key != NULL
683 || (key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){
684 BIO *ins = NULL;
685 X509 *cert = p_cert ? p_cert->cert : NULL, *cert2;
687 strncpy(full_name_key, full_filename, sizeof(full_filename));
688 full_name_key[sizeof(full_name_key)-1] = '\0';
690 build_path(buf, PATHCERTDIR(ctype), s, sizeof(buf));
692 strncpy(PrivateKeyPath, buf, sizeof(PrivateKeyPath));
693 PrivateKeyPath[sizeof(PrivateKeyPath)-1] = '\0';
694 if(strlen(PrivateKeyPath) + 4 < sizeof(PrivateKeyPath)){
695 strncat(PrivateKeyPath, EXTCERT(Private), 4);
696 PrivateKeyPath[sizeof(PrivateKeyPath)-1] = '\0';
699 /* remove .key extension and replace it with .crt extension */
700 strncpy(full_name_cert, full_name_key, sizeof(full_name_key));
701 full_name_cert[sizeof(full_name_cert)-1] = '\0';
702 full_name_cert[strlen(full_name_cert) - strlen(EXTCERT(Private))] = '\0';
703 strncat(full_name_cert, EXTCERT(Public), 4);
704 full_name_cert[sizeof(full_name_cert)-1] = '\0';
707 /* set up path to location where we will save public cert */
708 strncpy(PublicCertPath, buf, sizeof(PublicCertPath));
709 PublicCertPath[sizeof(PublicCertPath)-1] = '\0';
710 if(strlen(PublicCertPath) + 4 < sizeof(PublicCertPath)){
711 strncat(PublicCertPath, EXTCERT(Public), 4);
712 PublicCertPath[sizeof(PublicCertPath)-1] = '\0';
714 /* attempt #1, use provided certificate,
715 * assumption is that full_name_cert is the file that this
716 * certificate derives from (which is obtained by substitution
717 * of .key extension in key by .crt extension)
719 if(cert != NULL) /* attempt #1 */
720 use_this_file = &full_name_cert[0];
721 else if((ins = BIO_new_file(full_name_cert, "r")) != NULL){
722 /* attempt #2 to guess public cert name, use .crt extension */
723 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
724 use_this_file = &full_name_cert[0];
727 else{ /* attempt #3 to guess public cert name: use the original key */
728 if((ins = BIO_new_file(full_name_key, "r")) != NULL){
729 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
730 use_this_file = &full_name_key[0];
733 else {
734 int done = 0;
735 /* attempt #4, ask the user */
736 do {
737 r = (*pith_smime_import_certificate)(filename, use_this_file, "certificate", sizeof(filename) - 20);
738 if(r < 0){
739 if(ins != NULL) BIO_free(ins);
740 if(cert != NULL) X509_free(cert);
741 return 0;
743 if((ins = BIO_new_file(use_this_file, "r")) != NULL){
744 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL)
745 done++;
746 else
747 q_status_message(SM_ORDER, 1, 3, _("Error parsing certificate"));
749 else
750 q_status_message(SM_ORDER, 1, 3, _("Error reading certificate"));
751 } while (done == 0);
754 if(ins != NULL){
755 if(cert != NULL){ /* check that certificate matches key */
756 if(!X509_check_private_key(cert, key)){
757 rc = 0;
758 q_status_message(SM_ORDER, 1, 3, _("Certificate does not match key"));
760 else
761 rc = 1; /* Success! */
763 else
764 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
766 if(rc == 1){ /* if everything has been successful,
767 * copy the files to their final destination */
768 if(our_copy(PrivateKeyPath, full_filename) == 0){ /* <-- save the private key */
769 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
770 if(our_copy(PublicCertPath, use_this_file) == 0){
771 char tmp[MAILTMPLEN];
772 FILE *fp;
774 if(!passfile_name(ps_global->pinerc, tmp, sizeof(tmp))
775 || !(fp = our_fopen(tmp, "rb"))){
776 q_status_message(SM_ORDER, 1, 3, _("Error reading password file!"));
777 rc = 0;
779 else {
780 char tmp2[MAILTMPLEN];
781 int encrypted = 0;
782 char *text;
783 PERSONAL_CERT *pwdcert, *pc = p_cert;
785 pwdcert = (PERSONAL_CERT *) ps_global->pwdcert;
786 if(pwdcert == NULL)
787 setup_pwdcert((void **)&pwdcert);
789 tmp2[0] = '\0';
790 fgets(tmp2, sizeof(tmp2), fp);
791 fclose(fp);
792 if(strcmp(tmp2, "-----BEGIN PKCS7-----\n")){
793 if(encrypt_file((char *)tmp, NULL, pwdcert))
794 encrypted++;
796 else
797 encrypted++;
799 if(encrypted){
800 text = decrypt_file((char *)tmp, NULL, pwdcert);
801 if(text != NULL){
802 if(pc == NULL){
803 pc = fs_get(sizeof(PERSONAL_CERT));
804 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
805 filename[strlen(filename)-strlen(EXTCERT(Private))] = '\0';
806 pc->name = cpystr(filename);
807 snprintf(buf, sizeof(buf), "%s%s", filename, EXTCERT(Public));
808 buf[sizeof(buf)-1] = '\0';
809 pc->cname = cpystr(buf);
810 pc->key = key;
811 pc->cert = cert;
814 if(encrypt_file((char *)tmp, text, pc)){ /* we did it! */
815 build_path(buf, PATHCERTDIR(ctype), pwdcert->name, sizeof(buf));
816 strncat(buf, EXTCERT(Private), 4);
817 buf[sizeof(buf)-1] = '\0';
818 if(strcmp(PrivateKeyPath, buf)){
819 if (unlink(buf) < 0)
820 q_status_message(SM_ORDER, 1, 3, _("Failed to remove old key"));
822 build_path(buf, PATHCERTDIR(ctype), pwdcert->cname, sizeof(buf));
823 if(strcmp(PublicCertPath, buf)){
824 if(unlink(buf) < 0)
825 q_status_message(SM_ORDER, 1, 3, _("Failed to remove old certificate"));
827 free_personal_certs((PERSONAL_CERT **)&ps_global->pwdcert);
828 ps_global->pwdcert = pc;
829 rc = 1;
830 q_status_message(SM_ORDER, 1, 3, _("Password file reencrypted"));
831 } else {
832 q_status_message(SM_ORDER, 1, 3, _("Failed to reencrypt password file"));
833 rc = 0;
835 } else {
836 q_status_message(SM_ORDER, 1, 3, _("Error decrypting Password file"));
838 } else {
839 q_status_message(SM_ORDER, 1, 3, _("Password file not encrypted and coulr not encrypt"));
840 rc = 0;
844 else{
845 q_status_message(SM_ORDER, 1, 3, _("Error saving public certificate"));
846 if(our_unlink(PrivateKeyPath) < 0)
847 q_status_message(SM_ORDER, 1, 3, _("Error while cleaning private key"));
848 rc = 0;
851 else{
852 rc = 0;
853 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
855 if(ins != NULL) BIO_free(ins);
856 if(rc == 0 && cert != NULL) X509_free(cert);
858 } else {
859 rc = 0;
860 q_status_message(SM_ORDER, 1, 3, _("Error unlocking private key"));
863 return rc;
865 #endif /* PASSFILE */
867 smime_init();
868 ps_global->mangled_screen = 1;
870 if (ctype == Private){
871 char prompt[500], *s, *t;
872 EVP_PKEY *key = NULL;
874 if(!ps_global->smime->privatecertlist){
875 ps_global->smime->privatecertlist = fs_get(sizeof(CertList));
876 memset((void *)DATACERT(ctype), 0, sizeof(CertList));
879 for(s = t = filename; (t = strstr(s, ".key")) != NULL; s = t + 1);
880 if(s) *(s-1) = 0;
882 snprintf(prompt, sizeof(prompt), _("Enter passphrase for <%s>: "), filename);
883 prompt[sizeof(prompt)-1] = '\0';
884 if((key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){
885 if(SMHOLDERTYPE(ctype) == Directory){
886 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
887 if(strcmp(buf + strlen(buf) - 4, EXTCERT(ctype)) != 0 && strlen(buf) + 4 < sizeof(buf)){
888 strncat(buf, EXTCERT(ctype), 4);
889 buf[sizeof(buf)-1] = '\0';
891 rc = our_copy(buf, full_filename);
893 else /* if(SMHOLDERTYPE(ctype) == Container){ */
894 rc = add_file_to_container(ctype, full_filename, NULL);
895 if(rc == 0)
896 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
897 else
898 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
899 if(ps_global->smime->publiccertlist)
900 ps_global->smime->publiccertlist->data.renew = 1;
902 else
903 q_status_message(SM_ORDER, 1, 3, _("Problem unlocking key (not a certificate or wrong password)"));
904 } else if (ctype == CACert){
905 BIO *ins;
906 X509 *cert;
908 if((ins = BIO_new_file(full_filename, "r")) != NULL){
909 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
910 if(SMHOLDERTYPE(ctype) == Directory){
911 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
912 if(strcmp(buf + strlen(buf) - 4, ".crt") != 0 && strlen(buf) + 4 < sizeof(buf)){
913 strncat(buf, EXTCERT(ctype), 4);
914 buf[sizeof(buf)-1] = '\0';
917 rc = our_copy(buf, full_filename);
919 else /* if(SMHOLDERTYPE(ctype) == Container){ */
920 rc = add_file_to_container(ctype, full_filename, NULL);
921 if(rc == 0)
922 q_status_message(SM_ORDER, 1, 3, _("Certificate saved"));
923 else
924 q_status_message(SM_ORDER, 1, 3, _("Error saving certificate"));
925 X509_free(cert); /* not needed anymore */
927 else
928 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
929 BIO_free(ins);
931 renew_store();
932 } else { /* ctype == Public. save certificate, but first validate that it is one */
933 BIO *ins;
934 X509 *cert;
936 if((ins = BIO_new_file(full_filename, "r")) != NULL){
937 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
938 if(SMHOLDERTYPE(ctype) == Directory){
939 char **email;
941 if((email = get_x509_subject_email(cert)) != NULL){
942 int i;
943 for(i = 0; email[i] != NULL; i++){
944 save_cert_for(email[i], cert, Public);
945 fs_give((void **)&email[i]);
947 fs_give((void **)email);
949 if(strcmp(filename + strlen(filename) - 4, ".crt") == 0)
950 filename[strlen(filename) - 4] = '\0';
951 save_cert_for(filename, cert, Public);
953 else /* if(SMHOLDERTYPE(ctype) == Container){ */
954 add_file_to_container(ctype, full_filename, NULL);
955 X509_free(cert);
956 if(ps_global->smime->publiccertlist)
957 ps_global->smime->publiccertlist->data.renew = 1;
959 else
960 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
961 BIO_free(ins);
964 if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1;
965 return 1;
968 /* itype: information type to add: 0 - public, 1 - private.
969 * Memory freed by caller
971 BIO *
972 print_private_key_information(char *email, int itype)
974 BIO *out;
975 PERSONAL_CERT *pc;
977 if(ps_global->smime == NULL
978 || ps_global->smime->personal_certs == NULL
979 || (itype != 0 && itype != 1))
980 return NULL;
982 for(pc = ps_global->smime->personal_certs;
983 pc != NULL && strcmp(pc->name, email) != 0; pc = pc->next);
984 if(pc->key == NULL
985 && !load_private_key(pc)
986 && ps_global->smime
987 && ps_global->smime->need_passphrase){
988 if (*pith_opt_smime_get_passphrase)
989 (*pith_opt_smime_get_passphrase)();
990 load_private_key(pc);
993 if(pc->key == NULL)
994 return NULL;
996 out = BIO_new(BIO_s_mem());
997 if(itype == 0) /* 0 means public */
998 EVP_PKEY_print_public(out, pc->key, 0, NULL);
999 else if (itype == 1) /* 1 means private */
1000 EVP_PKEY_print_private(out, pc->key, 0, NULL);
1002 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
1003 forget_private_keys();
1005 return out;
1009 * Forget any cached private keys
1011 static void
1012 forget_private_keys(void)
1014 PERSONAL_CERT *pcert;
1015 size_t len;
1016 volatile char *p;
1018 dprint((9, "forget_private_keys()"));
1019 if(ps_global->smime){
1020 ps_global->smime->already_auto_asked = 0;
1021 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
1022 pcert;
1023 pcert=pcert->next){
1025 if(pcert->key){
1026 EVP_PKEY_free(pcert->key);
1027 pcert->key = NULL;
1031 ps_global->smime->entered_passphrase = 0;
1032 len = sizeof(ps_global->smime->passphrase);
1033 p = ps_global->smime->passphrase;
1035 while(len-- > 0)
1036 *p++ = '\0';
1040 /* modelled after signature_path in reply.c, but uses home dir instead of the
1041 * directory where the .pinerc is located, since according to documentation,
1042 * the .alpine-smime directories are subdirectories of the home directory
1044 int smime_path(char *rpath, char *fpath, size_t len)
1046 *fpath = '\0';
1047 if(rpath && *rpath){
1048 size_t spl = strlen(rpath);
1050 if(IS_REMOTE(rpath)){
1051 if(spl < len - 1)
1052 strncpy(fpath, rpath, len-1);
1053 fpath[len-1] = '\0';
1055 else if(is_absolute_path(rpath)){
1056 strncpy(fpath, rpath, len-1);
1057 fpath[len-1] = '\0';
1058 fnexpand(fpath, len);
1060 else if(ps_global->VAR_OPER_DIR){
1061 if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
1062 build_path(fpath, ps_global->VAR_OPER_DIR, rpath, len);
1064 else if(ps_global->home_dir){
1065 if(strlen(ps_global->home_dir) + spl < len - 1)
1066 build_path(fpath, ps_global->home_dir, rpath, len);
1069 return fpath && *fpath ? 1 : 0;
1075 * taken from openssl/apps/app_rand.c
1077 static int
1078 app_RAND_load_file(const char *file)
1080 #define RANDBUFLEN 200
1081 char buffer[RANDBUFLEN];
1083 if(file == NULL)
1084 file = RAND_file_name(buffer, RANDBUFLEN);
1086 if(file == NULL || !RAND_load_file(file, -1)){
1087 if(RAND_status() == 0){
1088 dprint((1, "unable to load 'random state'\n"));
1089 dprint((1, "This means that the random number generator has not been seeded\n"));
1090 dprint((1, "with much random data.\n"));
1093 return 0;
1096 seeded = 1;
1097 return 1;
1102 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
1104 static void
1105 openssl_extra_randomness(void)
1107 #if !defined(WIN32)
1108 int fd;
1109 unsigned long i;
1110 char *tf = NULL;
1111 char tmp[MAXPATH];
1112 struct stat sbuf;
1113 /* if system doesn't have /dev/urandom */
1114 if(stat ("/dev/urandom", &sbuf)){
1115 tmp[0] = '0';
1116 tf = temp_nam(NULL, NULL);
1117 if(tf){
1118 strncpy(tmp, tf, sizeof(tmp));
1119 tmp[sizeof(tmp)-1] = '\0';
1120 fs_give((void **) &tf);
1123 if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
1124 i = (unsigned long) tmp;
1125 else{
1126 unlink(tmp); /* don't need the file */
1127 fstat(fd, &sbuf); /* get information about the file */
1128 i = sbuf.st_ino; /* remember its inode */
1129 close(fd); /* or its descriptor */
1131 /* not great but it'll have to do */
1132 snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
1133 tcp_serverhost (),i,
1134 (unsigned long) (time (0) ^ gethostid ()),
1135 (unsigned long) getpid ());
1136 RAND_seed(tmp, strlen(tmp));
1138 #endif
1142 /* taken from openssl/apps/app_rand.c */
1143 static int
1144 app_RAND_write_file(const char *file)
1146 char buffer[200];
1148 if(!seeded)
1150 * If we did not manage to read the seed file,
1151 * we should not write a low-entropy seed file back --
1152 * it would suppress a crucial warning the next time
1153 * we want to use it.
1155 return 0;
1157 if(file == NULL)
1158 file = RAND_file_name(buffer, sizeof buffer);
1160 if(file == NULL || !RAND_write_file(file)){
1161 dprint((1, "unable to write 'random state'\n"));
1162 return 0;
1165 return 1;
1168 CertList *
1169 certlist_from_personal_certs(PERSONAL_CERT *pc)
1171 CertList *cl;
1172 X509 *x;
1174 if(pc == NULL)
1175 return NULL;
1177 if((x = get_cert_for(pc->name, Public, 1)) != NULL)
1178 cl = smime_X509_to_cert_info(x, pc->name);
1179 cl->next = certlist_from_personal_certs(pc->next);
1181 return cl;
1184 void
1185 renew_cert_data(CertList **data, WhichCerts ctype)
1187 smime_init();
1188 if(ctype == Private){
1189 if(data){
1190 PERSONAL_CERT *pc = (PERSONAL_CERT *)ps_global->smime->personal_certs;
1191 if(*data)
1192 free_certlist(data);
1193 free_personal_certs(&pc);
1194 setup_privatekey_storage();
1195 *data = certlist_from_personal_certs((PERSONAL_CERT *)ps_global->smime->personal_certs);
1196 if(data && *data){
1197 resort_certificates(data, ctype);
1198 RENEWCERT(*data) = 0;
1200 ps_global->smime->privatecertlist = *data;
1202 if(ps_global->smime->privatecertlist)
1203 RENEWCERT(ps_global->smime->privatecertlist) = 0;
1204 } else {
1205 X509_LOOKUP *lookup = NULL;
1206 X509_STORE *store = NULL;
1208 if((store = X509_STORE_new()) != NULL){
1209 if((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL){
1210 X509_STORE_free(store);
1211 store = NULL;
1212 } else{
1213 free_certlist(data);
1214 if(SMHOLDERTYPE(ctype) == Directory)
1215 add_certs_in_dir(lookup, PATHCERTDIR(ctype), EXTCERT(ctype), data);
1216 else /* if(SMHOLDERTYPE(ctype) == Container) */
1217 *data = mem_to_certlist(CONTENTCERTLIST(ctype), ctype);
1218 if(data && *data){
1219 resort_certificates(data, ctype);
1220 RENEWCERT(*data) = 0;
1222 if(ctype == Public)
1223 ps_global->smime->publiccertlist = *data;
1224 else
1225 ps_global->smime->cacertlist = *data;
1229 setup_certs_backup_by_type(ctype);
1232 void
1233 smime_reinit(void)
1235 smime_deinit();
1236 smime_init();
1239 /* Installed as an atexit() handler to save the random data */
1240 void
1241 smime_deinit(void)
1243 dprint((9, "smime_deinit()"));
1244 app_RAND_write_file(NULL);
1245 free_smime_struct(&ps_global->smime);
1248 /* we renew the store when it has changed */
1249 void renew_store(void)
1251 if(ps_global->smime->inited){
1252 if(s_cert_store != NULL)
1253 X509_STORE_free(s_cert_store);
1254 s_cert_store = get_ca_store();
1258 /* Initialise openssl stuff if needed */
1259 void
1260 smime_init(void)
1262 if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
1264 dprint((9, "smime_init()"));
1265 if(!ps_global->smime)
1266 ps_global->smime = new_smime_struct();
1268 setup_storage_locations();
1270 s_cert_store = get_ca_store();
1271 setup_certs_backup_by_type(CACert);
1273 #ifdef OPENSSL_1_1_0
1274 OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS|OPENSSL_INIT_ADD_ALL_DIGESTS|OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
1275 #else
1276 OpenSSL_add_all_algorithms();
1277 ERR_load_crypto_strings();
1278 #endif /* OPENSSL_1_1_0 */
1280 app_RAND_load_file(NULL);
1281 openssl_extra_randomness();
1282 ps_global->smime->inited = 1;
1285 ERR_clear_error();
1289 /* validate a certificate. Return value : 0 for no error, -1 for error.
1290 * In the latter case, set the openssl smime error in *error.
1292 int smime_validate_cert(X509 *cert, long *error)
1294 X509_STORE_CTX *csc;
1296 ERR_clear_error();
1297 *error = 0;
1298 if((s_cert_store != NULL) && (csc = X509_STORE_CTX_new()) != NULL){
1299 X509_STORE_set_flags(s_cert_store, 0);
1300 if(X509_STORE_CTX_init(csc,s_cert_store,cert,NULL)
1301 && X509_verify_cert(csc) <= 0)
1302 *error = X509_STORE_CTX_get_error(csc);
1303 X509_STORE_CTX_free(csc);
1305 return *error ? -1 : 0;
1308 PERSONAL_CERT *
1309 get_personal_certs(char *path)
1311 PERSONAL_CERT *result = NULL;
1312 char buf2[MAXPATH];
1313 struct dirent *d;
1314 DIR *dirp;
1316 ps_global->smime->privatepath = cpystr(path);
1317 dirp = opendir(path);
1318 if(dirp){
1319 while((d=readdir(dirp)) != NULL){
1320 X509 *cert;
1321 size_t ll;
1323 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, ".key")){
1325 /* copy file name to temp buffer */
1326 strncpy(buf2, d->d_name, sizeof(buf2)-1);
1327 buf2[sizeof(buf2)-1] = '\0';
1328 /* chop off ".key" trailier */
1329 buf2[strlen(buf2)-4] = '\0';
1330 /* Look for certificate */
1331 cert = get_cert_for(buf2, Public, 1);
1333 if(cert){
1334 PERSONAL_CERT *pc;
1336 /* create a new PERSONAL_CERT, fill it in */
1338 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1339 pc->cert = cert;
1340 pc->name = cpystr(buf2);
1341 strncat(buf2, EXTCERT(Public), 4);
1342 pc->cname = cpystr(buf2);
1344 /* Try to load the key with an empty password */
1345 pc->key = load_key(pc, "", SM_NORMALCERT);
1347 pc->next = result;
1348 result = pc;
1352 closedir(dirp);
1354 return result;
1358 void
1359 setup_privatekey_storage(void)
1361 char path[MAXPATH+1], *contents;
1362 int privatekeycontainer = 0;
1364 /* private keys in a container */
1365 if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
1367 privatekeycontainer = 1;
1368 contents = NULL;
1369 path[0] = '\0';
1370 if(!smime_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
1371 privatekeycontainer = 0;
1373 if(privatekeycontainer && !IS_REMOTE(path)
1374 && ps_global->VAR_OPER_DIR
1375 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1376 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1377 /* TRANSLATORS: First arg is the directory name, second is
1378 the file user wants to read but can't. */
1379 _("Can't read file outside %s: %s"),
1380 ps_global->VAR_OPER_DIR, path);
1381 privatekeycontainer = 0;
1384 if(privatekeycontainer
1385 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1386 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1388 !(contents = read_file(path, READ_FROM_LOCALE)))
1389 privatekeycontainer = 0;
1392 if(privatekeycontainer && path[0]){
1393 ps_global->smime->privatetype = Container;
1394 ps_global->smime->privatepath = cpystr(path);
1396 if(contents){
1397 ps_global->smime->privatecontent = contents;
1398 ps_global->smime->personal_certs = mem_to_personal_certs(contents);
1403 /* private keys in a directory of files */
1404 if(!privatekeycontainer){
1405 ps_global->smime->privatetype = Directory;
1407 path[0] = '\0';
1408 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1409 && !IS_REMOTE(path)))
1410 ps_global->smime->privatetype = Nada;
1411 else if(can_access(path, ACCESS_EXISTS)){
1412 if(our_mkpath(path, 0700)){
1413 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1414 ps_global->smime->privatetype = Nada;
1418 if(ps_global->smime->privatetype == Directory)
1419 ps_global->smime->personal_certs = get_personal_certs(path);
1421 setup_certs_backup_by_type(Private);
1426 static void
1427 setup_storage_locations(void)
1429 int publiccertcontainer = 0, cacertcontainer = 0;
1430 char path[MAXPATH+1], *contents;
1432 if(!ps_global->smime)
1433 return;
1435 #ifdef APPLEKEYCHAIN
1436 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1437 ps_global->smime->publictype = Keychain;
1439 else{
1440 #endif /* APPLEKEYCHAIN */
1441 /* Public certificates in a container */
1442 if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
1444 publiccertcontainer = 1;
1445 contents = NULL;
1446 path[0] = '\0';
1447 if(!smime_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
1448 publiccertcontainer = 0;
1450 if(publiccertcontainer && !IS_REMOTE(path)
1451 && ps_global->VAR_OPER_DIR
1452 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1453 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1454 /* TRANSLATORS: First arg is the directory name, second is
1455 the file user wants to read but can't. */
1456 _("Can't read file outside %s: %s"),
1457 ps_global->VAR_OPER_DIR, path);
1458 publiccertcontainer = 0;
1461 if(publiccertcontainer
1462 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1463 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1465 !(contents = read_file(path, READ_FROM_LOCALE)))
1466 publiccertcontainer = 0;
1469 if(publiccertcontainer && path[0]){
1470 ps_global->smime->publictype = Container;
1471 ps_global->smime->publicpath = cpystr(path);
1473 if(contents){
1474 ps_global->smime->publiccontent = contents;
1475 ps_global->smime->publiccertlist = mem_to_certlist(contents, Public);
1480 /* Public certificates in a directory of files */
1481 if(!publiccertcontainer){
1482 ps_global->smime->publictype = Directory;
1484 path[0] = '\0';
1485 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1486 && !IS_REMOTE(path)))
1487 ps_global->smime->publictype = Nada;
1488 else if(can_access(path, ACCESS_EXISTS)){
1489 if(our_mkpath(path, 0700)){
1490 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1491 ps_global->smime->publictype = Nada;
1495 if(ps_global->smime->publictype == Directory)
1496 ps_global->smime->publicpath = cpystr(path);
1499 #ifdef APPLEKEYCHAIN
1501 #endif /* APPLEKEYCHAIN */
1503 setup_privatekey_storage();
1505 /* extra cacerts in a container */
1506 if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
1508 cacertcontainer = 1;
1509 contents = NULL;
1510 path[0] = '\0';
1511 if(!smime_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
1512 cacertcontainer = 0;
1514 if(cacertcontainer && !IS_REMOTE(path)
1515 && ps_global->VAR_OPER_DIR
1516 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1517 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1518 /* TRANSLATORS: First arg is the directory name, second is
1519 the file user wants to read but can't. */
1520 _("Can't read file outside %s: %s"),
1521 ps_global->VAR_OPER_DIR, path);
1522 cacertcontainer = 0;
1525 if(cacertcontainer
1526 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1527 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1529 !(contents = read_file(path, READ_FROM_LOCALE)))
1530 cacertcontainer = 0;
1533 if(cacertcontainer && path[0]){
1534 ps_global->smime->catype = Container;
1535 ps_global->smime->capath = cpystr(path);
1536 ps_global->smime->cacontent = contents;
1537 if(contents)
1538 ps_global->smime->cacertlist = mem_to_certlist(contents, CACert);
1542 if(!cacertcontainer){
1543 ps_global->smime->catype = Directory;
1545 path[0] = '\0';
1546 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1547 && !IS_REMOTE(path)))
1548 ps_global->smime->catype = Nada;
1549 else if(can_access(path, ACCESS_EXISTS)){
1550 if(our_mkpath(path, 0700)){
1551 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1552 ps_global->smime->catype = Nada;
1556 if(ps_global->smime->catype == Directory)
1557 ps_global->smime->capath = cpystr(path);
1563 copy_publiccert_dir_to_container(void)
1565 return(copy_dir_to_container(Public, NULL));
1570 copy_publiccert_container_to_dir(void)
1572 return(copy_container_to_dir(Public));
1577 copy_privatecert_dir_to_container(void)
1579 return(copy_dir_to_container(Private, NULL));
1584 copy_privatecert_container_to_dir(void)
1586 return(copy_container_to_dir(Private));
1591 copy_cacert_dir_to_container(void)
1593 return(copy_dir_to_container(CACert, NULL));
1598 copy_cacert_container_to_dir(void)
1600 return(copy_container_to_dir(CACert));
1603 /* Add the contents of a file to a container. Do not check the content
1604 * of the file, just add it using the format for that container. The
1605 * caller must check the format, so that there is no data corruption
1606 * in the future.
1607 * return value: 0 - success,
1608 * != 0 - failure.
1611 add_file_to_container(WhichCerts ctype, char *fpath, char *altname)
1613 char *sep = (ctype == Public || ctype == Private)
1614 ? EMAILADDRLEADER : CACERTSTORELEADER;
1615 char *content = ctype == Public ? ps_global->smime->publiccontent
1616 : (ctype == Private ? ps_global->smime->privatecontent
1617 : ps_global->smime->cacontent);
1618 char *name;
1619 char *s;
1620 unsigned char c;
1621 struct stat sbuf;
1622 STORE_S *in = NULL;
1623 int rv = -1; /* assume error */
1625 if(our_stat(fpath, &sbuf) < 0
1626 || (in = so_get(FileStar, fpath, READ_ACCESS | READ_FROM_LOCALE)) == NULL)
1627 goto endadd;
1629 if(altname != NULL)
1630 name = altname;
1631 else if((name = strrchr(fpath, '/')) != NULL){
1632 size_t ll;
1633 if((ll = strlen(++name)) > 4 && strucmp(name + ll - 4, EXTCERT(ctype)) == 0)
1634 name[ll-strlen(EXTCERT(ctype))] = '\0';
1636 else
1637 goto endadd;
1639 if(content){
1640 fs_resize((void **)&content, strlen(content) + strlen(sep) + strlen(name) + sbuf.st_size + 3); /* 2 = \n + \n + \0*/
1641 s = content;
1642 content += strlen(content);
1644 else{
1645 s = content = fs_get(strlen(sep) + strlen(name) + sbuf.st_size + 1); /* 2 = \n + \0 */
1646 *content = '\0';
1648 strncat(content, sep, strlen(sep));
1649 strncat(content, name, strlen(name));
1650 content += strlen(content);
1651 *content++ = '\n';
1653 while(so_readc(&c, in))
1654 *content++ = (char) c;
1655 *content = '\0';
1657 switch(ctype){
1658 case Private: ps_global->smime->privatecontent = s; break;
1659 case Public : ps_global->smime->publiccontent = s; break;
1660 case CACert : ps_global->smime->cacontent = s; break;
1661 default : break;
1664 rv = copy_dir_to_container(ctype, s);
1666 endadd:
1667 if(in) so_give(&in);
1669 return rv;
1674 * returns 0 on success, -1 on failure
1675 * contents is an argument which tells this function to write the value
1676 * of this variable instead of reading the contents of the directory.
1677 * If the var contents is not null use its value as the value of the
1678 * container.
1681 copy_dir_to_container(WhichCerts which, char *contents)
1683 int ret = 0, container = 0;
1684 BIO *bio_out = NULL, *bio_in = NULL;
1685 char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
1686 char *tempfile = NULL, fpath[MAXPATH+1];
1687 DIR *dirp;
1688 struct dirent *d;
1689 REMDATA_S *rd = NULL;
1690 char *configdir = NULL;
1691 char *configpath = NULL;
1692 char *configcontainer = NULL;
1693 char *filesuffix = NULL;
1694 char *ret_dir = NULL;
1696 dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1697 smime_init();
1699 srcpath[0] = '\0';
1700 dstpath[0] = '\0';
1701 file[0] = '\0';
1702 emailaddr[0] = '\0';
1704 if(which == Public){
1705 configdir = ps_global->VAR_PUBLICCERT_DIR;
1706 configpath = ps_global->smime->publicpath;
1707 configcontainer = cpystr(DF_PUBLIC_CONTAINER);
1708 filesuffix = ".crt";
1710 else if(which == Private){
1711 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1712 configpath = ps_global->smime->privatepath;
1713 configcontainer = cpystr(DF_PRIVATE_CONTAINER);
1714 filesuffix = ".key";
1716 else if(which == CACert){
1717 configdir = ps_global->VAR_CACERT_DIR;
1718 configpath = ps_global->smime->capath;
1719 configcontainer = cpystr(DF_CA_CONTAINER);
1720 filesuffix = ".crt";
1722 container = SMHOLDERTYPE(which) == Container;
1724 if(!(configdir && configdir[0])){
1725 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1726 return -1;
1729 if(!(configpath && configpath[0])){
1730 #ifdef APPLEKEYCHAIN
1731 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1732 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1733 return -1;
1735 #endif /* APPLEKEYCHAIN */
1736 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1737 return -1;
1740 if(!(filesuffix && strlen(filesuffix) == 4)){
1741 return -1;
1746 * If there is a legit directory to read from set up the
1747 * container file to write to.
1749 if(smime_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
1751 if(IS_REMOTE(configpath)){
1752 rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
1753 NULL, "Error: ",
1754 _("Can't access remote smime configuration."));
1755 if(!rd)
1756 return -1;
1758 (void) rd_read_metadata(rd);
1760 if(rd->access == MaybeRorW){
1761 if(rd->read_status == 'R')
1762 rd->access = ReadOnly;
1763 else
1764 rd->access = ReadWrite;
1767 if(rd->access != NoExists){
1769 rd_check_remvalid(rd, 1L);
1772 * If the cached info says it is readonly but
1773 * it looks like it's been fixed now, change it to readwrite.
1775 if(rd->read_status == 'R'){
1776 rd_check_readonly_access(rd);
1777 if(rd->read_status == 'W'){
1778 rd->access = ReadWrite;
1779 rd->flags |= REM_OUTOFDATE;
1781 else
1782 rd->access = ReadOnly;
1786 if(rd->flags & REM_OUTOFDATE){
1787 if(rd_update_local(rd) != 0){
1789 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1790 rd_close_remdata(&rd);
1791 return -1;
1794 else
1795 rd_open_remote(rd);
1797 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1798 rd_close_remdata(&rd);
1799 return -1;
1802 rd->flags |= DO_REMTRIM;
1804 strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
1805 dstpath[sizeof(dstpath)-1] = '\0';
1807 else{
1808 strncpy(dstpath, configpath, sizeof(dstpath)-1);
1809 dstpath[sizeof(dstpath)-1] = '\0';
1813 * dstpath is either the local Container file or the local cache file
1814 * for the remote Container file.
1816 tempfile = tempfile_in_same_dir(dstpath, "az", &ret_dir);
1820 * If there is a legit directory to read from and a tempfile
1821 * to write to we continue.
1823 if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
1825 if(contents != NULL){
1826 if(BIO_puts(bio_out, contents) < 0)
1827 ret = -1;
1829 else {
1830 if((dirp = opendir(srcpath)) != NULL){
1832 while((d=readdir(dirp)) && !ret){
1833 size_t ll;
1835 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, filesuffix)){
1837 /* copy file name to temp buffer */
1838 strncpy(emailaddr, d->d_name, sizeof(emailaddr)-1);
1839 emailaddr[sizeof(emailaddr)-1] = '\0';
1840 /* chop off suffix trailier */
1841 emailaddr[strlen(emailaddr)-4] = 0;
1844 * This is the separator between the contents of
1845 * different files.
1847 if(which == CACert){
1848 if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
1849 && (BIO_puts(bio_out, emailaddr) > 0)
1850 && (BIO_puts(bio_out, "\n") > 0)))
1851 ret = -1;
1853 else{
1854 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1855 && (BIO_puts(bio_out, emailaddr) > 0)
1856 && (BIO_puts(bio_out, "\n") > 0)))
1857 ret = -1;
1860 /* read then write contents of file */
1861 build_path(file, srcpath, d->d_name, sizeof(file));
1862 if(!(bio_in = BIO_new_file(file, "r")))
1863 ret = -1;
1865 if(!ret){
1866 int good_stuff = 0;
1868 while(BIO_gets(bio_in, line, sizeof(line)) > 0){
1869 if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
1870 good_stuff = 1;
1872 if(good_stuff)
1873 BIO_puts(bio_out, line);
1875 if(strncmp("-----END", line, strlen("-----END")) == 0)
1876 good_stuff = 0;
1880 BIO_free(bio_in);
1884 closedir(dirp);
1888 BIO_free(bio_out);
1890 if(!ret){
1891 if(container && configpath && *configpath){
1892 strncpy(fpath, configpath, sizeof(fpath));
1893 fpath[sizeof(fpath) - 1] = '\0';
1895 else if(ret_dir){
1896 if(strlen(dstpath) + strlen(configcontainer) - strlen(ret_dir) + 1 < sizeof(dstpath))
1897 snprintf(fpath, sizeof(fpath), "%s%c%s",
1898 dstpath, tempfile[strlen(ret_dir)], configcontainer);
1899 else
1900 ret = -1;
1902 else ret = -1;
1904 if(!ret){
1905 if(!IS_REMOTE(configpath)){
1906 if(rename_file(tempfile, fpath) < 0){
1907 q_status_message2(SM_ORDER, 3, 3,
1908 _("Can't rename %s to %s"), tempfile, fpath);
1909 ret = -1;
1910 } else q_status_message1(SM_ORDER, 3, 3,
1911 _("saved container to %s"), fpath);
1913 else { /* if the container is remote, copy it */
1914 int e;
1915 char datebuf[200];
1917 if(rd != NULL && rename_file(tempfile, rd->lf) < 0){
1918 q_status_message2(SM_ORDER, 3, 3,
1919 _("Can't rename %s to %s"), tempfile, rd->lf);
1920 ret = -1;
1923 datebuf[0] = '\0';
1925 if((e = rd_update_remote(rd, datebuf)) != 0){
1926 if(e == -1){
1927 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1928 _("Error opening temporary smime file %s: %s"),
1929 rd->lf, error_description(errno));
1930 dprint((1,
1931 "write_remote_smime: error opening temp file %s\n",
1932 rd->lf ? rd->lf : "?"));
1934 else{
1935 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1936 _("Error copying to %s: %s"),
1937 rd->rn, error_description(errno));
1938 dprint((1,
1939 "write_remote_smime: error copying from %s to %s\n",
1940 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1943 q_status_message(SM_ORDER | SM_DING, 5, 5,
1944 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1946 else{
1947 rd_update_metadata(rd, datebuf);
1948 rd->read_status = 'W';
1951 rd_close_remdata(&rd);
1957 if(tempfile)
1958 fs_give((void **) &tempfile);
1960 if(ret_dir)
1961 fs_give((void **) &ret_dir);
1963 if(configcontainer)
1964 fs_give((void **) &configcontainer);
1966 return ret;
1971 * returns 0 on success, -1 on failure
1974 copy_container_to_dir(WhichCerts which)
1976 char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
1977 char iobuf[4096];
1978 char *contents = NULL;
1979 char *leader = NULL;
1980 char *filesuffix = NULL;
1981 char *configdir = NULL;
1982 char *configpath = NULL;
1983 char *tempfile = NULL;
1984 char *p, *q, *line, *name, *certtext, *save_p;
1985 int len;
1986 BIO *in, *out;
1988 dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1989 smime_init();
1991 path[0] = '\0';
1993 if(which == Public){
1994 leader = EMAILADDRLEADER;
1995 contents = ps_global->smime->publiccontent;
1996 configdir = ps_global->VAR_PUBLICCERT_DIR;
1997 configpath = ps_global->smime->publicpath;
1998 filesuffix = ".crt";
1999 if(!(configpath && configpath[0])){
2000 #ifdef APPLEKEYCHAIN
2001 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
2002 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
2003 return -1;
2005 #endif /* APPLEKEYCHAIN */
2006 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2007 return -1;
2010 fs_give((void **) &ps_global->smime->publicpath);
2012 path[0] = '\0';
2013 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
2014 && !IS_REMOTE(path))){
2015 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2016 return -1;
2019 if(can_access(path, ACCESS_EXISTS)){
2020 if(our_mkpath(path, 0700)){
2021 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2022 return -1;
2026 ps_global->smime->publicpath = cpystr(path);
2027 configpath = ps_global->smime->publicpath;
2029 else if(which == Private){
2030 leader = EMAILADDRLEADER;
2031 contents = ps_global->smime->privatecontent;
2032 configdir = ps_global->VAR_PRIVATEKEY_DIR;
2033 configpath = ps_global->smime->privatepath;
2034 filesuffix = ".key";
2035 if(!(configpath && configpath[0])){
2036 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2037 return -1;
2040 fs_give((void **) &ps_global->smime->privatepath);
2042 path[0] = '\0';
2043 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
2044 && !IS_REMOTE(path))){
2045 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2046 return -1;
2049 if(can_access(path, ACCESS_EXISTS)){
2050 if(our_mkpath(path, 0700)){
2051 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2052 return -1;
2056 ps_global->smime->privatepath = cpystr(path);
2057 configpath = ps_global->smime->privatepath;
2059 else if(which == CACert){
2060 leader = CACERTSTORELEADER;
2061 contents = ps_global->smime->cacontent;
2062 configdir = ps_global->VAR_CACERT_DIR;
2063 configpath = ps_global->smime->capath;
2064 filesuffix = ".crt";
2065 if(!(configpath && configpath[0])){
2066 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2067 return -1;
2070 fs_give((void **) &ps_global->smime->capath);
2072 path[0] = '\0';
2073 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
2074 && !IS_REMOTE(path))){
2075 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2076 return -1;
2079 if(can_access(path, ACCESS_EXISTS)){
2080 if(our_mkpath(path, 0700)){
2081 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2082 return -1;
2086 ps_global->smime->capath = cpystr(path);
2087 configpath = ps_global->smime->capath;
2090 if(!(configdir && configdir[0])){
2091 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
2092 return -1;
2095 if(!(configpath && configpath[0])){
2096 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2097 return -1;
2100 if(!(filesuffix && strlen(filesuffix) == 4)){
2101 return -1;
2105 if(contents && *contents){
2106 for(p = contents; *p != '\0';){
2107 line = p;
2109 while(*p && *p != '\n')
2110 p++;
2112 save_p = NULL;
2113 if(*p == '\n'){
2114 save_p = p;
2115 *p++ = '\0';
2118 if(strncmp(leader, line, strlen(leader)) == 0){
2119 name = line + strlen(leader);
2120 certtext = p;
2121 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
2122 if((q = strstr(certtext, leader)) != NULL){
2123 p = q;
2125 else{ /* end of file */
2126 q = certtext + strlen(certtext);
2127 p = q;
2130 strncpy(buf, name, sizeof(buf)-5);
2131 buf[sizeof(buf)-5] = '\0';
2132 strncat(buf, filesuffix, 5);
2133 build_path(file, configpath, buf, sizeof(file));
2135 in = BIO_new_mem_buf(certtext, q-certtext);
2136 if(in){
2137 tempfile = tempfile_in_same_dir(file, "az", NULL);
2138 out = NULL;
2139 if(tempfile)
2140 out = BIO_new_file(tempfile, "w");
2142 if(out){
2143 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
2144 BIO_write(out, iobuf, len);
2146 BIO_free(out);
2148 if(rename_file(tempfile, file) < 0){
2149 q_status_message2(SM_ORDER, 3, 3,
2150 _("Can't rename %s to %s"),
2151 tempfile, file);
2152 return -1;
2155 fs_give((void **) &tempfile);
2158 BIO_free(in);
2163 if(save_p)
2164 *save_p = '\n';
2168 return 0;
2172 #ifdef APPLEKEYCHAIN
2175 copy_publiccert_container_to_keychain(void)
2177 /* NOT IMPLEMNTED */
2178 return -1;
2182 copy_publiccert_keychain_to_container(void)
2184 /* NOT IMPLEMNTED */
2185 return -1;
2188 #endif /* APPLEKEYCHAIN */
2192 * Get a pointer to a string describing the most recent OpenSSL error.
2193 * It's statically allocated, so don't change or attempt to free it.
2195 static const char *
2196 openssl_error_string(void)
2198 char *errs;
2199 const char *data = NULL;
2200 long errn;
2202 errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2203 errs = (char*) ERR_reason_error_string(errn);
2205 if(errs)
2206 return errs;
2207 else if(data)
2208 return data;
2210 return "unknown error";
2214 /* Return true if the body looks like a PKCS7 object */
2216 is_pkcs7_body(BODY *body)
2218 int result;
2220 result = body->type==TYPEAPPLICATION &&
2221 body->subtype &&
2222 (strucmp(body->subtype,"pkcs7-mime")==0 ||
2223 strucmp(body->subtype,"x-pkcs7-mime")==0 ||
2224 strucmp(body->subtype,"pkcs7-signature")==0 ||
2225 strucmp(body->subtype,"x-pkcs7-signature")==0);
2227 return result;
2232 * Recursively stash a pointer to the decrypted data in our
2233 * manufactured body.
2234 * parameters: type: call of type 1, save the base and header for multipart messages
2235 call of type 0, do not save the base and header for multipart messages
2237 static void
2238 create_local_cache(char *h, char *base, BODY *b, int type)
2240 if(b->type==TYPEMULTIPART){
2241 PART *p;
2243 if(type == 1){
2244 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2245 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2246 } else if(type == 0){
2248 * We don't really want to copy the real body contents. It shouldn't be
2249 * used, and in the case of a message with attachments, we'll be
2250 * duplicating the files multiple times.
2252 cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
2254 for(p=b->nested.part; p; p=p->next)
2255 create_local_cache(h, base, (BODY *) p, type);
2258 else{
2259 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2260 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2265 static long
2266 rfc822_output_func(void *b, char *string)
2268 BIO *bio = (BIO *) b;
2270 return(string ? *string ? (BIO_puts(bio, string) > 0 ? 1L : 0L)
2271 : (BIO_puts(bio, string) >= 0 ? 1L : 0L)
2272 : 0L);
2277 * Attempt to load the private key for the given PERSONAL_CERT.
2278 * This sets the appropriate passphrase globals in order to
2279 * interact with the user correctly.
2281 static int
2282 load_private_key(PERSONAL_CERT *pcert)
2284 if(!pcert->key){
2286 /* Try empty password by default */
2287 char *password = "";
2289 if(ps_global->smime
2290 && (ps_global->smime->need_passphrase
2291 || ps_global->smime->entered_passphrase)){
2292 /* We've already been in here and discovered we need a different password */
2294 if(ps_global->smime->entered_passphrase)
2295 password = (char *) ps_global->smime->passphrase; /* already entered */
2296 else
2297 return 0;
2300 ERR_clear_error();
2302 if(!(pcert->key = load_key(pcert, password, SM_NORMALCERT))){
2303 long err = ERR_get_error();
2305 /* Couldn't load key... */
2307 if(ps_global->smime && ps_global->smime->entered_passphrase){
2309 /* The user got the password wrong maybe? */
2311 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
2312 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
2313 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
2314 else
2315 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2317 /* This passphrase is no good; forget it */
2318 ps_global->smime->entered_passphrase = 0;
2321 if(ps_global->smime){
2322 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2323 ps_global->smime->need_passphrase = 1;
2324 if(ps_global->smime->passphrase_emailaddr){
2325 int i;
2326 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
2327 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
2328 fs_give((void **) ps_global->smime->passphrase_emailaddr);
2331 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
2334 return 0;
2336 else{
2337 /* This key will be cached, so we won't be called again */
2338 if(ps_global->smime){
2339 ps_global->smime->entered_passphrase = 0;
2340 ps_global->smime->need_passphrase = 0;
2344 return 1;
2347 return 0;
2351 static void
2352 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename, char *smime_type)
2354 b->type = TYPEAPPLICATION;
2355 b->subtype = cpystr(type);
2356 b->encoding = ENCBINARY;
2357 b->description = cpystr(description);
2359 b->disposition.type = cpystr("attachment");
2360 set_parameter(&b->disposition.parameter, "filename", filename);
2362 set_parameter(&b->parameter, "name", filename);
2363 if(smime_type && *smime_type)
2364 set_parameter(&b->parameter, "smime-type", smime_type);
2369 * Look for a personal certificate matching the
2370 * given address
2372 PERSONAL_CERT *
2373 match_personal_cert_to_email(ADDRESS *a)
2375 PERSONAL_CERT *pcert = NULL;
2376 char buf[MAXPATH];
2377 char **email;
2378 int i, done;
2380 if(!a || !a->mailbox || !a->host)
2381 return NULL;
2383 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2385 if(ps_global->smime){
2386 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
2387 pcert;
2388 pcert=pcert->next){
2390 if(!pcert->cert)
2391 continue;
2393 email = get_x509_subject_email(pcert->cert);
2395 done = 0;
2396 if(email != NULL){
2397 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
2398 if(email[i] != NULL) done++;
2399 for(i = 0; email[i] != NULL; i++)
2400 fs_give((void **)&email[i]);
2401 fs_give((void **)email);
2404 if(done > 0)
2405 break;
2409 return pcert;
2414 * Look for a personal certificate matching the from
2415 * (or reply_to? in the given envelope)
2417 PERSONAL_CERT *
2418 match_personal_cert(ENVELOPE *env)
2420 PERSONAL_CERT *pcert;
2422 pcert = match_personal_cert_to_email(env->reply_to);
2423 if(!pcert)
2424 pcert = match_personal_cert_to_email(env->from);
2426 return pcert;
2431 * Flatten the given body into its MIME representation.
2432 * Return the result in a BIO.
2434 static BIO *
2435 body_to_bio(BODY *body)
2437 BIO *bio = NULL;
2438 int len;
2440 bio = BIO_new(BIO_s_mem());
2441 if(!bio)
2442 return NULL;
2444 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
2445 pine_write_body_header(body, rfc822_output_func, bio);
2446 pine_rfc822_output_body(body, rfc822_output_func, bio);
2449 * Now need to truncate by two characters since the above
2450 * appends CRLF.
2452 if((len=BIO_ctrl_pending(bio)) > 1){
2453 BUF_MEM *biobuf = NULL;
2455 BIO_get_mem_ptr(bio, &biobuf);
2456 if(biobuf){
2457 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
2461 return bio;
2465 static BIO *
2466 bio_from_store(STORE_S *store)
2468 BIO *ret = NULL;
2470 if(store && store->src == BioType && store->txt){
2471 ret = (BIO *) store->txt;
2474 return(ret);
2478 * Encrypt file; given a path (char *) fp, replace the file
2479 * by an encrypted version of it. If (char *) text is not null, then
2480 * replace the text of (char *) fp by the encrypted version of (char *) text.
2481 * certpath is the FULL path to the file containing the certificate used for
2482 * encryption.
2483 * return value: 0 - failed to encrypt; 1 - success!
2486 encrypt_file(char *fp, char *text, PERSONAL_CERT *pc)
2488 const EVP_CIPHER *cipher = NULL;
2489 STACK_OF(X509) *encerts = NULL;
2490 BIO *out = NULL;
2491 PKCS7 *p7 = NULL;
2492 int rv = 0;
2494 if(pc == NULL)
2495 return 0;
2497 cipher = EVP_aes_256_cbc();
2498 encerts = sk_X509_new_null();
2500 sk_X509_push(encerts, X509_dup(pc->cert));
2502 if(text){
2503 if((out = BIO_new(BIO_s_mem())) != NULL){
2504 (void) BIO_reset(out);
2505 BIO_puts(out, text);
2508 else if((out = BIO_new_file(fp, "rb")) != NULL)
2509 BIO_read_filename(out, fp);
2511 if((p7 = PKCS7_encrypt(encerts, out, cipher, 0)) != NULL){
2512 BIO_set_close(out, BIO_CLOSE);
2513 BIO_free(out);
2514 if((out = BIO_new_file(fp, "w")) != NULL){
2515 BIO_reset(out);
2516 rv = PEM_write_bio_PKCS7(out, p7);
2517 BIO_flush(out);
2521 if(out != NULL)
2522 BIO_free(out);
2523 PKCS7_free(p7);
2524 sk_X509_pop_free(encerts, X509_free);
2526 return rv;
2530 * Encrypt a message on the way out. Called from call_mailer in send.c
2531 * The body may be reallocated.
2534 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
2536 PKCS7 *p7 = NULL;
2537 BIO *in = NULL;
2538 BIO *out = NULL;
2539 const EVP_CIPHER *cipher = NULL;
2540 STACK_OF(X509) *encerts = NULL;
2541 STORE_S *outs = NULL;
2542 PINEFIELD *pf;
2543 ADDRESS *a;
2544 BODY *body = *bodyP;
2545 BODY *newBody = NULL;
2546 int result = 0;
2547 X509 *cert;
2548 char buf[MAXPATH];
2550 dprint((9, "encrypt_outgoing_message()"));
2551 smime_init();
2553 cipher = EVP_aes_256_cbc();
2555 encerts = sk_X509_new_null();
2557 /* Look for a certificate for each of the recipients */
2558 for(pf = header->local; pf && pf->name; pf = pf->next)
2559 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
2560 for(a=*pf->addr; a; a=a->next){
2561 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2563 if((cert = get_cert_for(buf, Public, 1)) != NULL){
2564 sk_X509_push(encerts,cert);
2565 }else{
2566 q_status_message2(SM_ORDER, 1, 1,
2567 _("Unable to find certificate for <%s@%s>"),
2568 a->mailbox, a->host);
2569 goto end;
2574 /* add the sender's certificate so that they can decrypt the message too */
2575 for(a=header->env->from; a ; a = a->next){
2576 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2578 if((cert = get_cert_for(buf, Public, 1)) != NULL
2579 && sk_X509_find(encerts, cert) == -1)
2580 sk_X509_push(encerts,cert);
2583 in = body_to_bio(body);
2585 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
2587 outs = so_get(BioType, NULL, EDIT_ACCESS);
2588 out = bio_from_store(outs);
2590 i2d_PKCS7_bio(out, p7);
2591 (void) BIO_flush(out);
2593 so_seek(outs, 0, SEEK_SET);
2595 newBody = mail_newbody();
2597 newBody->type = TYPEAPPLICATION;
2598 newBody->subtype = cpystr("pkcs7-mime");
2599 newBody->encoding = ENCBINARY;
2601 newBody->disposition.type = cpystr("attachment");
2602 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
2604 newBody->description = cpystr("S/MIME Encrypted Message");
2605 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
2606 set_parameter(&newBody->parameter, "name", "smime.p7m");
2608 newBody->contents.text.data = (unsigned char *) outs;
2610 *bodyP = newBody;
2612 result = 1;
2614 end:
2616 BIO_free(in);
2617 PKCS7_free(p7);
2618 sk_X509_pop_free(encerts, X509_free);
2620 dprint((9, "encrypt_outgoing_message returns %d", result));
2621 return result;
2626 Get (and decode) the body of the given section of msg
2628 static STORE_S*
2629 get_part_contents(long msgno, const char *section)
2631 long len;
2632 gf_io_t pc;
2633 STORE_S *store = NULL;
2634 char *err;
2636 store = so_get(CharStar, NULL, EDIT_ACCESS);
2637 if(store){
2638 gf_set_so_writec(&pc,store);
2640 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
2642 gf_clear_so_writec(store);
2644 so_seek(store, 0, SEEK_SET);
2646 if(err)
2647 so_give(&store);
2650 return store;
2654 static PKCS7 *
2655 get_pkcs7_from_part(long msgno,const char *section)
2657 STORE_S *store = NULL;
2658 PKCS7 *p7 = NULL;
2659 BIO *in = NULL;
2661 store = get_part_contents(msgno, section);
2663 if(store){
2664 if(store->src == CharStar){
2665 int len;
2668 * We're reaching inside the STORE_S structure. We should
2669 * probably have a way to get the length, instead.
2671 len = (int) (store->eod - store->dp);
2672 in = BIO_new_mem_buf(store->txt, len);
2674 else{ /* just copy it */
2675 unsigned char c;
2677 in = BIO_new(BIO_s_mem());
2678 (void) BIO_reset(in);
2680 so_seek(store, 0L, 0);
2681 while(so_readc(&c, store)){
2682 BIO_write(in, &c, 1);
2686 if(in){
2687 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2688 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
2689 /* error */
2692 BIO_free(in);
2695 so_give(&store);
2698 return p7;
2701 int same_cert(X509 *x, X509 *cert)
2703 char bufcert[256], bufx[256];
2704 int rv = 0;
2706 get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert), ":");
2707 get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx), ":");
2708 if(strcmp(bufx, bufcert) == 0)
2709 rv = 1;
2711 return rv;
2715 /* extract and save certificates from a PKCS7 package. The ctype variable
2716 * tells us if we want to extract it to a public/ or a ca/ directory. The
2717 * later makes sense only for recoverable errors (errors that can be fixed
2718 * by saving to the ca/ directory before we verify the signature).
2719 * Return value:
2720 * 0 - no errors (in public/) no need to try again,
2721 * or validated self signed certificate (in ca/)
2722 * < 0 - certificate error is not recoverable, don't even think about it.
2725 int smime_extract_and_save_cert(PKCS7 *p7, int check_cert)
2727 STACK_OF(X509) *signers;
2728 X509 *x, *cert;
2729 char **email;
2730 int i, j;
2731 long error;
2733 if((signers = PKCS7_get0_signers(p7, NULL, 0)) == NULL)
2734 return -1;
2736 for(i = 0; i < sk_X509_num(signers); i++){
2737 if((x = sk_X509_value(signers,i)) == NULL)
2738 continue;
2740 if((email = get_x509_subject_email(x)) != NULL){
2741 for(j = 0; email[j] != NULL; j++){
2742 if((cert = get_cert_for(email[j], Public, 1)) == NULL
2743 || same_cert(x, cert) == 0){
2744 if(check_cert == 0
2745 || smime_validate_cert(x, &error) == 0
2746 || (*pith_smime_confirm_save)(email[j]) == 1)
2747 save_cert_for(email[j], x, Public);
2749 if(cert != NULL)
2750 X509_free(cert);
2751 fs_give((void **) &email[j]);
2753 fs_give((void **) email);
2756 sk_X509_free(signers);
2758 return 0;
2762 * Try to verify a signature.
2764 * p7 - the pkcs7 object to verify
2765 * in - the plain data to verify (NULL if not detached)
2766 * out - BIO to which to write the opaque data
2767 * silent - if non zero, do not print errors, only print success.
2769 static int
2770 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
2772 STACK_OF(X509) *otherCerts = NULL;
2773 CertList *cl;
2774 int result, flags;
2775 const char *data;
2776 long err;
2778 if(!s_cert_store){
2779 if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
2780 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2782 return -1;
2785 flags = F_ON(F_USE_CERT_STORE_ONLY, ps_global) ? PKCS7_NOINTERN : 0;
2787 if(ps_global->smime->publiccertlist == NULL){
2788 renew_cert_data(&ps_global->smime->publiccertlist, Public);
2789 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next){
2790 if(cl->x509_cert == NULL){
2791 char *s = strrchr(cl->name, '.');
2792 *s = '\0';
2793 cl->x509_cert = get_cert_for(cl->name, Public, 1);
2794 *s = '.';
2799 if(ps_global->smime->publiccertlist){
2800 otherCerts = sk_X509_new_null();
2801 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next)
2802 if(cl->x509_cert != NULL)
2803 sk_X509_push(otherCerts, X509_dup(cl->x509_cert));
2806 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, flags);
2808 sk_X509_pop_free(otherCerts, X509_free);
2810 if(result){
2811 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
2813 else{
2814 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2816 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
2818 /* Retry verification so we can get the plain text */
2819 /* Might be better to reimplement PKCS7_verify here? */
2821 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY);
2823 if (!silent) q_status_message1(SM_ORDER | SM_DING, 3, 3,
2824 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
2827 return result;
2831 void
2832 free_smime_body_sparep(void **sparep)
2834 char *s;
2835 SIZEDTEXT *st;
2836 if(sparep && *sparep){
2837 switch(get_smime_sparep_type(*sparep)){
2838 case P7Type: PKCS7_free((PKCS7 *) get_smime_sparep_data(*sparep));
2839 break;
2840 case CharType: s = (char *)get_smime_sparep_data(*sparep);
2841 fs_give((void **) &s);
2842 break;
2843 case SizedText : st = (SIZEDTEXT *)get_smime_sparep_data(*sparep);
2844 fs_give((void **) &st->data);
2845 fs_give((void **) &st);
2846 break;
2847 default : break;
2849 ((SMIME_SPARE_S *)(*sparep))->data = NULL;
2850 fs_give(sparep);
2854 /* Big comment, explaining the mess that exists out there, and how we deal
2855 with it, and also how we solve the problems that are created this way.
2857 When Alpine sends a message, it constructs that message, computes the
2858 signature, but then it forgets the message it signed and reconstructs it
2859 again. Since it signs a message containing a notice about "mime aware
2860 tools", but it does not send that we do not include that in the part
2861 that is signed, and that takes care of much of the problems.
2863 Another problem is what is received from the servers. All servers tested
2864 seem to transmit the message that was signed intact and Alpine can check
2865 the signature correctly. That is not a problem. The problem arises when
2866 the message includes attachments. In this case different servers send
2867 different things, so it will be up to us to figure out what is the text
2868 that was actually signed. Confused? here is the story:
2870 When a message containing and attachment is sent by Alpine, UW-IMAP,
2871 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2872 that was sent by Alpine, but GMX.com, Exchange, and probably other
2873 servers add a trailing \r\n in the message, so when validating the
2874 signature, these messages will not validate. There are several things
2875 that can be done.
2877 1. Add a trailing \r\n to any message that contains attachments, sign that
2878 and send that. In this way, all messages will validate with all
2879 servers.
2881 2. Compatibility mode: If a message has an attachment, contains a trailing
2882 \r\n and does not validate (sent by an earlier version of Alpine),
2883 remove the trailing \r\n and try to revalidate again.
2885 3. We do not add \r\n to validate a message that we sent, because that
2886 would only work in Alpine, and not in any other client. That would
2887 not be a good thing to do.
2889 PART II
2891 Now we have to deal with encrypted and signed messages. The problem is
2892 that c-client makes all its pointers point to "on disk" content, but
2893 since we decrypted the data earlier, we have to make sure of two things.
2894 One is that we saved that data (so we do not have to decrypt it again)
2895 and second that we can use it.
2897 In order to save the data we use create_local_cache, so that we do not
2898 have to redecrypt the message. Once this is saved, c-client functions will
2899 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2901 PART III
2903 When we are trying to verify messages with detached signatures, some
2904 imap servers send incorrect information in the mail_fetch_mime call. By
2905 incorrect I mean that this is not fetched directly from the message, but
2906 it is read from the message, processed, and then the processed part is
2907 sent to us, so this text might not agree with what is in the message,
2908 and so the validation of the signature might fail. However, the good
2909 news is that the message validates if saved to a local folder. This
2910 means that if normal validation does not work we can make it work by
2911 saving the message locally and validating that. This is implemented
2912 below, and causes delay in the display of the message. I am considering
2913 at this time not to do this automatically, but wait for the user to tell
2914 us to do it for them by means of a command available in the
2915 mail_view_screen. This might help in other situations, where a message
2916 is supposed to have an attachment, but it can not be seen in the
2917 processed text. Nevertheless, at this time, this is automatic, and is
2918 causing a delay in the processing of the message, but it is validating
2919 correctly all messages.
2921 PART IV
2923 When the user sends a message as encrypted and signed, this code used to
2924 encrypt first, and then sign the pkcs7 body, but it turns out that some
2925 other clients can not handle these messages. While we could argue that the
2926 other clients need to improve, we will support reading messages in both
2927 ways, and will send messages using this technique; that is, signed first,
2928 encrypted second. It seems that all tested clients support this way, so it
2929 should be safe to do so.
2932 typedef struct smime_filter_s {
2933 void (*filter)();
2934 } SMIME_FILTER_S;
2936 SMIME_FILTER_S sig_filter[] = {
2937 {smime_remove_trailing_crlf},
2938 {smime_remove_folding_space}
2941 #define TOTAL_FILTERS (sizeof(sig_filter)/sizeof(sig_filter[0]))
2942 #define TOTAL_SIGFLTR (1 << TOTAL_FILTERS) /* not good, keep filters to a low number */
2944 void
2945 smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen,
2946 char **bodytext, unsigned long *bodylen)
2948 if(*bodylen > 2 && !strncmp(*bodytext+*bodylen-2, "\r\n", 2))
2949 *bodylen -= 2;
2952 void
2953 smime_remove_folding_space(char **mimetext, unsigned long *mimelen,
2954 char **bodytext, unsigned long *bodylen)
2956 char *s = NULL, *t;
2957 unsigned long mlen = *mimelen;
2959 if(*mimetext){
2960 for (s = t = *mimetext; t - *mimetext < *mimelen; ){
2961 if(*t == '\r' && *(t+1) == '\n' && (*(t+2) == '\t' || *(t+2) == ' ')){
2962 *s++ = ' ';
2963 t += 3;
2964 mlen -= 2;
2966 else
2967 *s++ = *t++;
2969 *mimelen = mlen;
2974 smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag)
2976 int result, i, j, flag;
2977 char *mtext, *btext;
2978 unsigned long mlen, blen;
2979 BIO *in;
2981 mtext = mimelen ? fs_get(mimelen+1) : NULL;
2982 btext = fs_get(bodylen+1);
2984 flag = 1; /* silence all failures */
2985 for(i = 1; result == 0 && i < TOTAL_SIGFLTR; i++){
2986 if((in = BIO_new(BIO_s_mem())) == NULL)
2987 return -1;
2989 (void) BIO_reset(in);
2991 if(i+1 == TOTAL_SIGFLTR)
2992 flag = nflag;
2994 if(mimelen)
2995 strncpy(mtext, mimetext, mlen = mimelen);
2996 strncpy(btext, bodytext, blen = bodylen);
2997 for(j = 0; j < TOTAL_FILTERS; j++)
2998 if((i >> j) & 1)
2999 (sig_filter[j].filter)(&mtext, &mlen, &btext, &blen);
3000 if(mtext != NULL)
3001 BIO_write(in, mtext, mlen);
3002 BIO_write(in, btext, blen);
3003 result = do_signature_verify(p7, in, NULL, flag);
3004 BIO_free(in);
3006 if(mtext) fs_give((void **)&mtext);
3007 if(btext) fs_give((void **)&btext);
3008 return result;
3012 * Given a multipart body of type multipart/signed, attempt to verify it.
3013 * Returns non-zero if the body was changed.
3015 static int
3016 do_detached_signature_verify(BODY *b, long msgno, char *section)
3018 PKCS7 *p7 = NULL;
3019 BIO *in = NULL;
3020 PART *p;
3021 int result, modified_the_body = 0;
3022 int flag; /* 1 silent, 0 not silent */
3023 int saved = 0;
3024 unsigned long mimelen, bodylen;
3025 char newSec[100], *mimetext, *bodytext;
3026 char *what_we_did;
3027 SIZEDTEXT *st;
3029 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"));
3031 smime_init();
3033 /* if it was signed and then encrypted, use the decrypted text
3034 * to check the validity of the signature
3036 if(b->sparep){
3037 if(get_smime_sparep_type(b->sparep) == SizedText){
3038 /* bodytext includes mimetext */
3039 st = (SIZEDTEXT *) get_smime_sparep_data(b->sparep);
3040 bodytext = (char *) st->data;
3041 bodylen = st->size;
3042 mimetext = NULL;
3043 mimelen = 0L;
3046 else{
3047 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3048 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3049 if(mimetext)
3050 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3052 if(mimetext == NULL || bodytext == NULL)
3053 return modified_the_body;
3056 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3058 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
3059 || (in = BIO_new(BIO_s_mem())) == NULL)
3060 return modified_the_body;
3062 (void) BIO_reset(in);
3063 if(mimetext != NULL)
3064 BIO_write(in, mimetext, mimelen);
3065 BIO_write(in, bodytext, bodylen);
3067 saved = smime_extract_and_save_cert(p7, F_ON(F_USE_CERT_STORE_ONLY, ps_global));
3068 if(saved < 0 && F_ON(F_USE_CERT_STORE_ONLY, ps_global))
3069 return modified_the_body;
3071 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3072 flag = (mimelen == 0 || !IS_REMOTE(ps_global->mail_stream->mailbox))
3073 ? 0 : 1;
3074 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, flag);
3075 if(result < 0)
3076 return modified_the_body;
3077 if(result == 0
3078 && mimelen > 0 /* do not do this for encrypted messages */
3079 && IS_REMOTE(ps_global->mail_stream->mailbox)){
3080 char *fetch;
3081 unsigned long hlen, tlen;
3082 STORE_S *msg_so;
3084 BIO_free(in);
3085 if((in = BIO_new(BIO_s_mem())) != NULL
3086 && (fetch = mail_fetch_header(ps_global->mail_stream, msgno, NULL,
3087 NULL, &hlen, FT_PEEK)) != NULL
3088 && (msg_so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL
3089 && so_nputs(msg_so, fetch, (long) hlen)
3090 && (fetch = pine_mail_fetch_text(ps_global->mail_stream, msgno, NULL,
3091 &tlen, FT_PEEK)) != NULL
3092 && so_nputs(msg_so, fetch, tlen)){
3093 STRING bs;
3094 char *h = (char *) so_text(msg_so);
3095 char *bstart = strstr(h, "\r\n\r\n");
3096 ENVELOPE *env;
3097 BODY *body, *tmpB;
3099 bstart += 4;
3100 INIT(&bs, mail_string, bstart, tlen);
3101 rfc822_parse_msg_full(&env, &body, h, bstart-h-4, &bs, BADHOST, 0, 0);
3102 mail_free_envelope(&env);
3104 mail_free_body_part(&b->nested.part);
3105 tmpB = mail_body_section(body, (unsigned char *) section);
3106 if(MIME_MSG(tmpB->type, tmpB->subtype))
3107 b->nested.part = tmpB->nested.msg->body->nested.part;
3108 else
3109 b->nested.part = tmpB->nested.part;
3110 create_local_cache(bstart, bstart, &b->nested.part->body, 1);
3111 modified_the_body = 1;
3113 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3115 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3117 if(mimetext)
3118 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3120 if (mimetext == NULL || bodytext == NULL)
3121 return modified_the_body;
3123 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3125 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL)
3126 return modified_the_body;
3128 (void) BIO_reset(in);
3129 BIO_write(in, mimetext, mimelen);
3130 BIO_write(in, bodytext, bodylen);
3131 so_give(&msg_so);
3133 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3134 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, 0);
3135 if(result < 0)
3136 return modified_the_body;
3142 BIO_free(in);
3143 if(b->subtype)
3144 fs_give((void**) &b->subtype);
3146 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3147 b->encoding = ENC8BIT;
3149 if(b->description)
3150 fs_give ((void**) &b->description);
3152 what_we_did = result ? _("This message was cryptographically signed.") :
3153 _("This message was cryptographically signed but the signature could not be verified.");
3155 b->description = cpystr(what_we_did);
3157 b->sparep = create_smime_sparep(P7Type, p7);
3159 p = b->nested.part;
3161 /* p is signed plaintext */
3162 if(p && p->next)
3163 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
3165 modified_the_body = 1;
3167 return modified_the_body;
3171 PERSONAL_CERT *
3172 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
3174 PERSONAL_CERT *x = NULL;
3176 if(ps_global->smime){
3177 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
3178 X509 *mine;
3180 mine = x->cert;
3182 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,X509_get_issuer_name(mine)) &&
3183 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,X509_get_serialNumber(mine))){
3184 break;
3189 return x;
3193 static PERSONAL_CERT *
3194 find_certificate_matching_pkcs7(PKCS7 *p7)
3196 int i;
3197 STACK_OF(PKCS7_RECIP_INFO) *recips;
3198 PERSONAL_CERT *x = NULL;
3200 recips = p7->d.enveloped->recipientinfo;
3202 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
3203 PKCS7_RECIP_INFO *ri;
3205 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
3207 if((x=find_certificate_matching_recip_info(ri))!=0){
3208 break;
3212 return x;
3215 /* decrypt an encrypted file.
3216 Args: fp - the path to the encrypted file.
3217 rv - a code that tells the caller what happened inside the function
3218 pcert - a personal certificate that was used to encrypt this file
3219 Returns the decoded text allocated in a char *, whose memory must be
3220 freed by caller
3223 char *
3224 decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc)
3226 PKCS7 *p7 = NULL;
3227 char *text, *tmp;
3228 BIO *in = NULL, *out = NULL;
3229 int i, j;
3230 long unsigned int len;
3231 void *ret;
3233 if(pc == NULL || (text = read_file(fp, 0)) == NULL || *text == '\0')
3234 return NULL;
3236 tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1);
3237 for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0'
3238 && text[i] != '-'; j++, i++)
3239 tmp[j] = text[i];
3240 tmp[j] = '\0';
3242 ret = rfc822_base64((unsigned char *)tmp, strlen(tmp), &len);
3244 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
3245 p7 = d2i_PKCS7_bio(in, NULL);
3246 BIO_free(in);
3249 if (text) fs_give((void **)&text);
3250 if (ret) fs_give((void **)&ret);
3252 if (rv) *rv = pc->key == NULL ? -1 : 1;
3254 out = BIO_new(BIO_s_mem());
3255 (void) BIO_reset(out);
3257 if(PKCS7_decrypt(p7, pc->key, pc->cert, out, 0) != 0){
3258 BIO_get_mem_data(out, &tmp);
3259 text = cpystr(tmp);
3260 BIO_free(out);
3261 } else
3262 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3263 (char *) openssl_error_string());
3264 PKCS7_free(p7);
3266 return text;
3270 * Try to decode (decrypt or verify a signature) a PKCS7 body
3271 * Returns non-zero if something was changed.
3273 static int
3274 do_decoding(BODY *b, long msgno, const char *section)
3276 int modified_the_body = 0;
3277 BIO *out = NULL;
3278 PKCS7 *p7 = NULL;
3279 X509 *recip = NULL;
3280 EVP_PKEY *key = NULL;
3281 PERSONAL_CERT *pcert = NULL;
3282 char *what_we_did = "";
3283 char null[1];
3285 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"));
3286 null[0] = '\0';
3287 smime_init();
3290 * Extract binary data from part to an in-memory store
3293 if(b->sparep){
3294 if(get_smime_sparep_type(b->sparep) == P7Type)
3295 p7 = (PKCS7*) get_smime_sparep_data(b->sparep);
3297 else{
3298 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
3299 if(!p7){
3300 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
3301 (char*) openssl_error_string());
3302 goto end;
3306 * Save the PKCS7 object for later dealings by the user interface.
3307 * It will be cleaned up when the body is garbage collected.
3309 b->sparep = create_smime_sparep(P7Type, p7);
3312 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
3314 if(PKCS7_type_is_signed(p7)){
3315 int sigok;
3317 out = BIO_new(BIO_s_mem());
3318 (void) BIO_reset(out);
3319 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3321 sigok = do_signature_verify(p7, NULL, out, 0);
3323 what_we_did = sigok ? _("This message was cryptographically signed.") :
3324 _("This message was cryptographically signed but the signature could not be verified.");
3326 /* make sure it's null terminated */
3327 BIO_write(out, null, 1);
3329 else if(!PKCS7_type_is_enveloped(p7)){
3330 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
3331 goto end;
3333 else{ /* It *is* enveloped */
3334 int decrypt_result;
3336 what_we_did = _("This message was encrypted.");
3338 /* now need to find a cert that can decrypt this */
3339 pcert = find_certificate_matching_pkcs7(p7);
3341 if(!pcert){
3342 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3343 goto end;
3346 recip = pcert->cert;
3348 if(!load_private_key(pcert)
3349 && ps_global->smime
3350 && ps_global->smime->need_passphrase
3351 && !ps_global->smime->already_auto_asked){
3352 /* Couldn't load key with blank password, ask user */
3353 ps_global->smime->already_auto_asked = 1;
3354 if(pith_opt_smime_get_passphrase){
3355 (*pith_opt_smime_get_passphrase)();
3356 load_private_key(pcert);
3360 key = pcert->key;
3361 if(!key)
3362 goto end;
3364 out = BIO_new(BIO_s_mem());
3365 (void) BIO_reset(out);
3366 BIO_puts(out, "MIME-Version: 1.0\r\n");
3368 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
3370 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3371 forget_private_keys();
3373 if(!decrypt_result){
3374 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3375 (char*) openssl_error_string());
3376 goto end; }
3378 BIO_write(out, null, 1);
3382 * We've now produced a flattened MIME object in BIO out.
3383 * It needs to be turned back into a BODY.
3386 if(out){
3387 BODY *body;
3388 ENVELOPE *env;
3389 char *h = NULL;
3390 char *bstart;
3391 STRING s;
3392 BUF_MEM *bptr = NULL;
3394 BIO_get_mem_ptr(out, &bptr);
3395 if(bptr)
3396 h = bptr->data;
3398 /* look for start of body */
3399 bstart = strstr(h, "\r\n\r\n");
3401 if(!bstart){
3402 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
3404 else{
3405 SIZEDTEXT *st;
3406 bstart += 4; /* skip over CRLF*2 */
3408 INIT(&s, mail_string, bstart, strlen(bstart));
3409 rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
3410 mail_free_envelope(&env); /* Don't care about this */
3412 if(body->type == TYPEMULTIPART
3413 && !strucmp(body->subtype, "SIGNED")){
3414 char *cookie = NULL;
3415 PARAMETER *param;
3416 for (param = body->parameter; param && !cookie; param = param->next)
3417 if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value;
3418 if(cookie != NULL){
3419 st = fs_get(sizeof(SIZEDTEXT));
3420 st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */
3421 st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4);
3422 body->sparep = create_smime_sparep(SizedText, (void *)st);
3424 else
3425 q_status_message(SM_ORDER, 3, 3, _("Couldn't find cookie in attachment list."));
3427 body->mime.offset = 0;
3428 body->mime.text.size = 0;
3431 * Now convert original body (application/pkcs7-mime)
3432 * to a multipart body with one sub-part (the decrypted body).
3433 * Note that the sub-part may also be multipart!
3436 b->type = TYPEMULTIPART;
3437 if(b->subtype)
3438 fs_give((void**) &b->subtype);
3441 * This subtype is used in mailview.c to annotate the display of
3442 * encrypted or signed messages. We know for sure then that it's a PKCS7
3443 * part because the sparep field is set to the PKCS7 object (see above).
3445 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3446 b->encoding = ENC8BIT;
3448 if(b->description)
3449 fs_give((void**) &b->description);
3451 b->description = cpystr(what_we_did);
3453 if(b->disposition.type)
3454 fs_give((void **) &b->disposition.type);
3456 if(b->contents.text.data)
3457 fs_give((void **) &b->contents.text.data);
3459 if(b->parameter)
3460 mail_free_body_parameter(&b->parameter);
3462 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3463 b->nested.part = fs_get(sizeof(PART));
3464 b->nested.part->body = *body;
3465 b->nested.part->next = NULL;
3467 fs_give((void**) &body);
3470 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3471 * the decrypted data. Otherwise, it'll try to load it from the original
3472 * data. Eek.
3474 create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body, 0);
3476 modified_the_body = 1;
3480 end:
3481 if(out)
3482 BIO_free(out);
3484 return modified_the_body;
3489 * Recursively handle PKCS7 bodies in our message.
3491 * Returns non-zero if some fiddling was done.
3493 static int
3494 do_fiddle_smime_message(BODY *b, long msgno, char *section)
3496 int modified_the_body = 0;
3498 if(!b)
3499 return 0;
3501 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"));
3503 if(is_pkcs7_body(b)){
3505 if(do_decoding(b, msgno, section)){
3507 * b should now be a multipart message:
3508 * fiddle it too in case it's been multiply-encrypted!
3511 /* fallthru */
3512 modified_the_body = 1;
3516 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
3518 PART *p;
3519 int partNum;
3520 char newSec[100];
3522 if(MIME_MULT_SIGNED(b->type, b->subtype)){
3526 * Ahah. We have a multipart signed entity.
3528 * Multipart/signed
3529 * part 1 (signed thing)
3530 * part 2 (the pkcs7 signature)
3532 * We're going to convert that to
3534 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3535 * part 1 (signed thing)
3536 * part 2 has been freed
3538 * We also extract the signature from part 2 and save it
3539 * in the multipart body->sparep, and we add a description
3540 * in the multipart body->description.
3543 * The results of a decrypted message will be similar. It
3544 * will be
3546 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3547 * part 1 (decrypted thing)
3550 modified_the_body += do_detached_signature_verify(b, msgno, section);
3552 else if(MIME_MSG(b->type, b->subtype)){
3553 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
3555 else{
3557 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
3558 /* Append part number to the section string */
3560 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
3562 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
3567 return modified_the_body;
3572 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3573 * Returns non-zero if something was changed.
3576 fiddle_smime_message(BODY *b, long msgno)
3578 return do_fiddle_smime_message(b, msgno, "");
3582 /********************************************************************************/
3586 * Output a string in a distinctive style
3588 void
3589 gf_puts_uline(char *txt, gf_io_t pc)
3591 pc(TAG_EMBED); pc(TAG_BOLDON);
3592 gf_puts(txt, pc);
3593 pc(TAG_EMBED); pc(TAG_BOLDOFF);
3596 /* get_chain_for_cert: error and level are mandatory arguments */
3597 STACK_OF(X509) *
3598 get_chain_for_cert(X509 *cert, int *error, int *level)
3600 STACK_OF(X509) *chain = NULL;
3601 X509_STORE_CTX *ctx;
3602 X509 *x, *xtmp;
3603 int rc; /* return code */
3605 *level = -1;
3606 *error = 0;
3607 ERR_clear_error();
3608 if((s_cert_store != NULL) && (ctx = X509_STORE_CTX_new()) != NULL){
3609 X509_STORE_set_flags(s_cert_store, 0);
3610 if(!X509_STORE_CTX_init(ctx, s_cert_store, cert, NULL))
3611 *error = X509_STORE_CTX_get_error(ctx);
3612 else if((chain = sk_X509_new_null()) != NULL){
3613 for(x = cert; ; x = xtmp){
3614 if(++*level > 0)
3615 sk_X509_push(chain, X509_dup(x));
3616 rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x);
3617 if(rc < 0)
3618 *error = 1;
3619 if(rc <= 0)
3620 break;
3621 if(!X509_check_issued(xtmp, xtmp))
3622 break;
3625 X509_STORE_CTX_free(ctx);
3627 return chain;
3632 * Sign a message. Called from call_mailer in send.c.
3634 * This takes the header for the outgoing message as well as a pointer
3635 * to the current body (which may be reallocated).
3636 * The last argument (BODY **bp) is an argument that tells Alpine
3637 * if the body has 8 bit. if *bp is not null we compute two signatures
3638 * one for the quoted-printable encoded message, and another for the
3639 * 8bit encoded message. We return the signature for the 8bit encoded
3640 * part in p2->body.mime.text.data.
3641 * The reason why we compute two signatures is so that we can decide
3642 * which one to use later, and we only do it in the case that *bp is
3643 * not null. If we did not do this, then we might not be able to sign
3644 * a message until we log in to the smtp server, so instead of doing
3645 * that, we get ready for any possible situation we might find.
3648 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp)
3650 STORE_S *outs = NULL;
3651 STORE_S *outs_2 = NULL;
3652 BODY *body = *bodyP;
3653 BODY *newBody = NULL;
3654 PART *p1 = NULL;
3655 PART *p2 = NULL;
3656 PERSONAL_CERT *pcert;
3657 BIO *in = NULL;
3658 BIO *in_2 = NULL;
3659 BIO *out = NULL;
3660 BIO *out_2 = NULL;
3661 PKCS7 *p7 = NULL;
3662 PKCS7 *p7_2 = NULL;
3663 STACK_OF(X509) *chain;
3664 const EVP_MD *md = EVP_sha256(); /* use this digest instead of sha1 */
3665 int result = 0, error;
3666 int flags = dont_detach ? 0 : PKCS7_DETACHED;
3667 int level;
3669 dprint((9, "sign_outgoing_message()"));
3671 smime_init();
3673 /* Look for a private key matching the sender address... */
3675 pcert = match_personal_cert(header->env);
3677 if(!pcert){
3678 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
3679 goto end;
3682 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
3683 /* Couldn't load key with blank password, try again */
3684 if(pith_opt_smime_get_passphrase){
3685 (*pith_opt_smime_get_passphrase)();
3686 load_private_key(pcert);
3690 if(!pcert->key)
3691 goto end;
3693 if(((chain = get_chain_for_cert(pcert->cert, &error, &level)) != NULL && error)
3694 || level == 0){
3695 sk_X509_pop_free(chain, X509_free);
3696 chain = NULL;
3699 if(error)
3700 q_status_message(SM_ORDER, 1, 1,
3701 _("Not all certificates needed to verify signature included in signed message"));
3703 in = body_to_bio(body);
3705 flags |= PKCS7_PARTIAL;
3706 if((p7 = PKCS7_sign(NULL, NULL, chain, in, flags)) != NULL
3707 && PKCS7_sign_add_signer(p7, pcert->cert, pcert->key, md, flags))
3708 PKCS7_final(p7, in, flags);
3710 if(bp && *bp){
3711 int i, save_encoding;
3713 for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++);
3715 if(i > ENCMAX){ /* no empty encoding slots! */
3716 *bp = NULL;
3718 else {
3719 save_encoding = (*bp)->encoding;
3720 body_encodings[(*bp)->encoding = i] = body_encodings[ENC8BIT];
3722 in_2 = body_to_bio(body);
3724 body_encodings[i] = NULL;
3725 (*bp)->encoding = save_encoding;
3729 if(bp && *bp){
3730 if((p7_2 = PKCS7_sign(NULL, NULL, chain, in_2, flags)) != NULL
3731 && PKCS7_sign_add_signer(p7_2, pcert->cert, pcert->key, md, flags))
3732 PKCS7_final(p7_2, in_2, flags);
3735 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3736 forget_private_keys();
3738 if(chain)
3739 sk_X509_pop_free(chain, X509_free);
3741 if(!p7){
3742 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
3743 goto end;
3746 outs = so_get(BioType, NULL, EDIT_ACCESS);
3747 out = bio_from_store(outs);
3749 i2d_PKCS7_bio(out, p7);
3750 (void) BIO_flush(out);
3752 so_seek(outs, 0, SEEK_SET);
3754 if(bp && *bp && p7_2){
3755 outs_2 = so_get(BioType, NULL, EDIT_ACCESS);
3756 out_2 = bio_from_store(outs_2);
3758 i2d_PKCS7_bio(out_2, p7_2);
3759 (void) BIO_flush(out_2);
3761 so_seek(outs_2, 0, SEEK_SET);
3764 if((flags&PKCS7_DETACHED)==0){
3766 /* the simple case: the signed data is in the pkcs7 object */
3768 newBody = mail_newbody();
3770 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3772 newBody->contents.text.data = (unsigned char *) outs;
3773 *bodyP = newBody;
3775 result = 1;
3777 else{
3780 * OK.
3781 * We have to create a new body as follows:
3783 * multipart/signed; blah blah blah
3784 * reference to existing body
3786 * pkcs7 object
3789 newBody = mail_newbody();
3791 newBody->type = TYPEMULTIPART;
3792 newBody->subtype = cpystr("signed");
3793 newBody->encoding = ENC7BIT;
3795 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
3796 set_parameter(&newBody->parameter, "micalg", "sha-256");
3798 p1 = mail_newbody_part();
3799 p2 = mail_newbody_part();
3802 * This is nasty. We're just copying the body in here,
3803 * but since our newBody is freed at the end of call_mailer,
3804 * we mustn't let this body (the original one) be freed twice.
3806 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
3808 p1->next = p2;
3810 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL);
3811 p2->body.mime.text.data = (unsigned char *) outs_2;
3812 p2->body.contents.text.data = (unsigned char *) outs;
3814 newBody->nested.part = p1;
3816 *bodyP = newBody;
3818 result = 1;
3821 end:
3823 PKCS7_free(p7);
3824 BIO_free(in);
3826 if(bp && *bp){
3827 if(p7_2) PKCS7_free(p7_2);
3828 BIO_free(in_2);
3831 dprint((9, "sign_outgoing_message returns %d", result));
3832 return result;
3836 SMIME_STUFF_S *
3837 new_smime_struct(void)
3839 SMIME_STUFF_S *ret = NULL;
3841 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
3842 memset((void *) ret, 0, sizeof(*ret));
3843 ret->publictype = Nada;
3845 return ret;
3849 static void
3850 free_smime_struct(SMIME_STUFF_S **smime)
3852 if(smime && *smime){
3853 if((*smime)->passphrase_emailaddr){
3854 int i;
3855 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
3856 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
3857 fs_give((void **) (*smime)->passphrase_emailaddr);
3860 if((*smime)->publicpath)
3861 fs_give((void **) &(*smime)->publicpath);
3863 if((*smime)->publiccertlist)
3864 free_certlist(&(*smime)->publiccertlist);
3866 if((*smime)->backuppubliccertlist)
3867 free_certlist(&(*smime)->backuppubliccertlist);
3869 if((*smime)->cacertlist)
3870 free_certlist(&(*smime)->cacertlist);
3872 if((*smime)->backupcacertlist)
3873 free_certlist(&(*smime)->backupcacertlist);
3875 if((*smime)->privatecertlist)
3876 free_certlist(&(*smime)->privatecertlist);
3878 if((*smime)->backupprivatecertlist)
3879 free_certlist(&(*smime)->backupprivatecertlist);
3881 if((*smime)->publiccontent)
3882 fs_give((void **) &(*smime)->publiccontent);
3884 if((*smime)->privatepath)
3885 fs_give((void **) &(*smime)->privatepath);
3887 if((*smime)->personal_certs){
3888 PERSONAL_CERT *pc;
3890 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
3891 free_personal_certs(&pc);
3892 (*smime)->personal_certs = NULL;
3895 if((*smime)->privatecontent)
3896 fs_give((void **) &(*smime)->privatecontent);
3898 if((*smime)->capath)
3899 fs_give((void **) &(*smime)->capath);
3901 if((*smime)->cacontent)
3902 fs_give((void **) &(*smime)->cacontent);
3904 fs_give((void **) smime);
3908 #endif /* SMIME */