* NTLM authentication support with the ntlm library, in Unix systems.
[alpine.git] / pith / smime.c
blob90aa8321861c8bc0c383392952fb9953a0ce0d93
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);
2983 result = 0;
2985 flag = 1; /* silence all failures */
2986 for(i = 1; result == 0 && i < TOTAL_SIGFLTR; i++){
2987 if((in = BIO_new(BIO_s_mem())) == NULL)
2988 return -1;
2990 (void) BIO_reset(in);
2992 if(i+1 == TOTAL_SIGFLTR)
2993 flag = nflag;
2995 if(mimelen)
2996 strncpy(mtext, mimetext, mlen = mimelen);
2997 strncpy(btext, bodytext, blen = bodylen);
2998 for(j = 0; j < TOTAL_FILTERS; j++)
2999 if((i >> j) & 1)
3000 (sig_filter[j].filter)(&mtext, &mlen, &btext, &blen);
3001 if(mtext != NULL)
3002 BIO_write(in, mtext, mlen);
3003 BIO_write(in, btext, blen);
3004 result = do_signature_verify(p7, in, NULL, flag);
3005 BIO_free(in);
3007 if(mtext) fs_give((void **)&mtext);
3008 if(btext) fs_give((void **)&btext);
3009 return result;
3013 * Given a multipart body of type multipart/signed, attempt to verify it.
3014 * Returns non-zero if the body was changed.
3016 static int
3017 do_detached_signature_verify(BODY *b, long msgno, char *section)
3019 PKCS7 *p7 = NULL;
3020 BIO *in = NULL;
3021 PART *p;
3022 int result, modified_the_body = 0;
3023 int flag; /* 1 silent, 0 not silent */
3024 int saved = 0;
3025 unsigned long mimelen, bodylen;
3026 char newSec[100], *mimetext, *bodytext;
3027 char *what_we_did;
3028 SIZEDTEXT *st;
3030 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"));
3032 smime_init();
3034 /* if it was signed and then encrypted, use the decrypted text
3035 * to check the validity of the signature
3037 if(b->sparep){
3038 if(get_smime_sparep_type(b->sparep) == SizedText){
3039 /* bodytext includes mimetext */
3040 st = (SIZEDTEXT *) get_smime_sparep_data(b->sparep);
3041 bodytext = (char *) st->data;
3042 bodylen = st->size;
3043 mimetext = NULL;
3044 mimelen = 0L;
3047 else{
3048 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3049 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3050 if(mimetext)
3051 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3053 if(mimetext == NULL || bodytext == NULL)
3054 return modified_the_body;
3057 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3059 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
3060 || (in = BIO_new(BIO_s_mem())) == NULL)
3061 return modified_the_body;
3063 (void) BIO_reset(in);
3064 if(mimetext != NULL)
3065 BIO_write(in, mimetext, mimelen);
3066 BIO_write(in, bodytext, bodylen);
3068 saved = smime_extract_and_save_cert(p7, F_ON(F_USE_CERT_STORE_ONLY, ps_global));
3069 if(saved < 0 && F_ON(F_USE_CERT_STORE_ONLY, ps_global))
3070 return modified_the_body;
3072 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3073 flag = (mimelen == 0 || !IS_REMOTE(ps_global->mail_stream->mailbox))
3074 ? 0 : 1;
3075 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, flag);
3076 if(result < 0)
3077 return modified_the_body;
3078 if(result == 0
3079 && mimelen > 0 /* do not do this for encrypted messages */
3080 && IS_REMOTE(ps_global->mail_stream->mailbox)){
3081 char *fetch;
3082 unsigned long hlen, tlen;
3083 STORE_S *msg_so;
3085 BIO_free(in);
3086 if((in = BIO_new(BIO_s_mem())) != NULL
3087 && (fetch = mail_fetch_header(ps_global->mail_stream, msgno, NULL,
3088 NULL, &hlen, FT_PEEK)) != NULL
3089 && (msg_so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL
3090 && so_nputs(msg_so, fetch, (long) hlen)
3091 && (fetch = pine_mail_fetch_text(ps_global->mail_stream, msgno, NULL,
3092 &tlen, FT_PEEK)) != NULL
3093 && so_nputs(msg_so, fetch, tlen)){
3094 STRING bs;
3095 char *h = (char *) so_text(msg_so);
3096 char *bstart = strstr(h, "\r\n\r\n");
3097 ENVELOPE *env;
3098 BODY *body, *tmpB;
3100 bstart += 4;
3101 INIT(&bs, mail_string, bstart, tlen);
3102 rfc822_parse_msg_full(&env, &body, h, bstart-h-4, &bs, BADHOST, 0, 0);
3103 mail_free_envelope(&env);
3105 mail_free_body_part(&b->nested.part);
3106 tmpB = mail_body_section(body, (unsigned char *) section);
3107 if(MIME_MSG(tmpB->type, tmpB->subtype))
3108 b->nested.part = tmpB->nested.msg->body->nested.part;
3109 else
3110 b->nested.part = tmpB->nested.part;
3111 create_local_cache(bstart, bstart, &b->nested.part->body, 1);
3112 modified_the_body = 1;
3114 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3116 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3118 if(mimetext)
3119 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3121 if (mimetext == NULL || bodytext == NULL)
3122 return modified_the_body;
3124 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3126 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL)
3127 return modified_the_body;
3129 (void) BIO_reset(in);
3130 BIO_write(in, mimetext, mimelen);
3131 BIO_write(in, bodytext, bodylen);
3132 so_give(&msg_so);
3134 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3135 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, 0);
3136 if(result < 0)
3137 return modified_the_body;
3143 BIO_free(in);
3144 if(b->subtype)
3145 fs_give((void**) &b->subtype);
3147 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3148 b->encoding = ENC8BIT;
3150 if(b->description)
3151 fs_give ((void**) &b->description);
3153 what_we_did = result ? _("This message was cryptographically signed.") :
3154 _("This message was cryptographically signed but the signature could not be verified.");
3156 b->description = cpystr(what_we_did);
3158 b->sparep = create_smime_sparep(P7Type, p7);
3160 p = b->nested.part;
3162 /* p is signed plaintext */
3163 if(p && p->next)
3164 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
3166 modified_the_body = 1;
3168 return modified_the_body;
3172 PERSONAL_CERT *
3173 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
3175 PERSONAL_CERT *x = NULL;
3177 if(ps_global->smime){
3178 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
3179 X509 *mine;
3181 mine = x->cert;
3183 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,X509_get_issuer_name(mine)) &&
3184 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,X509_get_serialNumber(mine))){
3185 break;
3190 return x;
3194 static PERSONAL_CERT *
3195 find_certificate_matching_pkcs7(PKCS7 *p7)
3197 int i;
3198 STACK_OF(PKCS7_RECIP_INFO) *recips;
3199 PERSONAL_CERT *x = NULL;
3201 recips = p7->d.enveloped->recipientinfo;
3203 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
3204 PKCS7_RECIP_INFO *ri;
3206 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
3208 if((x=find_certificate_matching_recip_info(ri))!=0){
3209 break;
3213 return x;
3216 /* decrypt an encrypted file.
3217 Args: fp - the path to the encrypted file.
3218 rv - a code that tells the caller what happened inside the function
3219 pcert - a personal certificate that was used to encrypt this file
3220 Returns the decoded text allocated in a char *, whose memory must be
3221 freed by caller
3224 char *
3225 decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc)
3227 PKCS7 *p7 = NULL;
3228 char *text, *tmp;
3229 BIO *in = NULL, *out = NULL;
3230 int i, j;
3231 long unsigned int len;
3232 void *ret;
3234 if(pc == NULL || (text = read_file(fp, 0)) == NULL || *text == '\0')
3235 return NULL;
3237 tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1);
3238 for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0'
3239 && text[i] != '-'; j++, i++)
3240 tmp[j] = text[i];
3241 tmp[j] = '\0';
3243 ret = rfc822_base64((unsigned char *)tmp, strlen(tmp), &len);
3245 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
3246 p7 = d2i_PKCS7_bio(in, NULL);
3247 BIO_free(in);
3250 if (text) fs_give((void **)&text);
3251 if (ret) fs_give((void **)&ret);
3253 if (rv) *rv = pc->key == NULL ? -1 : 1;
3255 out = BIO_new(BIO_s_mem());
3256 (void) BIO_reset(out);
3258 if(PKCS7_decrypt(p7, pc->key, pc->cert, out, 0) != 0){
3259 BIO_get_mem_data(out, &tmp);
3260 text = cpystr(tmp);
3261 BIO_free(out);
3262 } else
3263 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3264 (char *) openssl_error_string());
3265 PKCS7_free(p7);
3267 return text;
3271 * Try to decode (decrypt or verify a signature) a PKCS7 body
3272 * Returns non-zero if something was changed.
3274 static int
3275 do_decoding(BODY *b, long msgno, const char *section)
3277 int modified_the_body = 0;
3278 BIO *out = NULL;
3279 PKCS7 *p7 = NULL;
3280 X509 *recip = NULL;
3281 EVP_PKEY *key = NULL;
3282 PERSONAL_CERT *pcert = NULL;
3283 char *what_we_did = "";
3284 char null[1];
3286 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"));
3287 null[0] = '\0';
3288 smime_init();
3291 * Extract binary data from part to an in-memory store
3294 if(b->sparep){
3295 if(get_smime_sparep_type(b->sparep) == P7Type)
3296 p7 = (PKCS7*) get_smime_sparep_data(b->sparep);
3298 else{
3299 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
3300 if(!p7){
3301 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
3302 (char*) openssl_error_string());
3303 goto end;
3307 * Save the PKCS7 object for later dealings by the user interface.
3308 * It will be cleaned up when the body is garbage collected.
3310 b->sparep = create_smime_sparep(P7Type, p7);
3313 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
3315 if(PKCS7_type_is_signed(p7)){
3316 int sigok;
3318 out = BIO_new(BIO_s_mem());
3319 (void) BIO_reset(out);
3320 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3322 sigok = do_signature_verify(p7, NULL, out, 0);
3324 what_we_did = sigok ? _("This message was cryptographically signed.") :
3325 _("This message was cryptographically signed but the signature could not be verified.");
3327 /* make sure it's null terminated */
3328 BIO_write(out, null, 1);
3330 else if(!PKCS7_type_is_enveloped(p7)){
3331 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
3332 goto end;
3334 else{ /* It *is* enveloped */
3335 int decrypt_result;
3337 what_we_did = _("This message was encrypted.");
3339 /* now need to find a cert that can decrypt this */
3340 pcert = find_certificate_matching_pkcs7(p7);
3342 if(!pcert){
3343 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3344 goto end;
3347 recip = pcert->cert;
3349 if(!load_private_key(pcert)
3350 && ps_global->smime
3351 && ps_global->smime->need_passphrase
3352 && !ps_global->smime->already_auto_asked){
3353 /* Couldn't load key with blank password, ask user */
3354 ps_global->smime->already_auto_asked = 1;
3355 if(pith_opt_smime_get_passphrase){
3356 (*pith_opt_smime_get_passphrase)();
3357 load_private_key(pcert);
3361 key = pcert->key;
3362 if(!key)
3363 goto end;
3365 out = BIO_new(BIO_s_mem());
3366 (void) BIO_reset(out);
3367 BIO_puts(out, "MIME-Version: 1.0\r\n");
3369 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
3371 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3372 forget_private_keys();
3374 if(!decrypt_result){
3375 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3376 (char*) openssl_error_string());
3377 goto end; }
3379 BIO_write(out, null, 1);
3383 * We've now produced a flattened MIME object in BIO out.
3384 * It needs to be turned back into a BODY.
3387 if(out){
3388 BODY *body;
3389 ENVELOPE *env;
3390 char *h = NULL;
3391 char *bstart;
3392 STRING s;
3393 BUF_MEM *bptr = NULL;
3395 BIO_get_mem_ptr(out, &bptr);
3396 if(bptr)
3397 h = bptr->data;
3399 /* look for start of body */
3400 bstart = strstr(h, "\r\n\r\n");
3402 if(!bstart){
3403 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
3405 else{
3406 SIZEDTEXT *st;
3407 bstart += 4; /* skip over CRLF*2 */
3409 INIT(&s, mail_string, bstart, strlen(bstart));
3410 rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
3411 mail_free_envelope(&env); /* Don't care about this */
3413 if(body->type == TYPEMULTIPART
3414 && !strucmp(body->subtype, "SIGNED")){
3415 char *cookie = NULL;
3416 PARAMETER *param;
3417 for (param = body->parameter; param && !cookie; param = param->next)
3418 if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value;
3419 if(cookie != NULL){
3420 st = fs_get(sizeof(SIZEDTEXT));
3421 st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */
3422 st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4);
3423 body->sparep = create_smime_sparep(SizedText, (void *)st);
3425 else
3426 q_status_message(SM_ORDER, 3, 3, _("Couldn't find cookie in attachment list."));
3428 body->mime.offset = 0;
3429 body->mime.text.size = 0;
3432 * Now convert original body (application/pkcs7-mime)
3433 * to a multipart body with one sub-part (the decrypted body).
3434 * Note that the sub-part may also be multipart!
3437 b->type = TYPEMULTIPART;
3438 if(b->subtype)
3439 fs_give((void**) &b->subtype);
3442 * This subtype is used in mailview.c to annotate the display of
3443 * encrypted or signed messages. We know for sure then that it's a PKCS7
3444 * part because the sparep field is set to the PKCS7 object (see above).
3446 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3447 b->encoding = ENC8BIT;
3449 if(b->description)
3450 fs_give((void**) &b->description);
3452 b->description = cpystr(what_we_did);
3454 if(b->disposition.type)
3455 fs_give((void **) &b->disposition.type);
3457 if(b->contents.text.data)
3458 fs_give((void **) &b->contents.text.data);
3460 if(b->parameter)
3461 mail_free_body_parameter(&b->parameter);
3463 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3464 b->nested.part = fs_get(sizeof(PART));
3465 b->nested.part->body = *body;
3466 b->nested.part->next = NULL;
3468 fs_give((void**) &body);
3471 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3472 * the decrypted data. Otherwise, it'll try to load it from the original
3473 * data. Eek.
3475 create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body, 0);
3477 modified_the_body = 1;
3481 end:
3482 if(out)
3483 BIO_free(out);
3485 return modified_the_body;
3490 * Recursively handle PKCS7 bodies in our message.
3492 * Returns non-zero if some fiddling was done.
3494 static int
3495 do_fiddle_smime_message(BODY *b, long msgno, char *section)
3497 int modified_the_body = 0;
3499 if(!b)
3500 return 0;
3502 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"));
3504 if(is_pkcs7_body(b)){
3506 if(do_decoding(b, msgno, section)){
3508 * b should now be a multipart message:
3509 * fiddle it too in case it's been multiply-encrypted!
3512 /* fallthru */
3513 modified_the_body = 1;
3517 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
3519 PART *p;
3520 int partNum;
3521 char newSec[100];
3523 if(MIME_MULT_SIGNED(b->type, b->subtype)){
3527 * Ahah. We have a multipart signed entity.
3529 * Multipart/signed
3530 * part 1 (signed thing)
3531 * part 2 (the pkcs7 signature)
3533 * We're going to convert that to
3535 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3536 * part 1 (signed thing)
3537 * part 2 has been freed
3539 * We also extract the signature from part 2 and save it
3540 * in the multipart body->sparep, and we add a description
3541 * in the multipart body->description.
3544 * The results of a decrypted message will be similar. It
3545 * will be
3547 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3548 * part 1 (decrypted thing)
3551 modified_the_body += do_detached_signature_verify(b, msgno, section);
3553 else if(MIME_MSG(b->type, b->subtype)){
3554 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
3556 else{
3558 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
3559 /* Append part number to the section string */
3561 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
3563 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
3568 return modified_the_body;
3573 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3574 * Returns non-zero if something was changed.
3577 fiddle_smime_message(BODY *b, long msgno)
3579 return do_fiddle_smime_message(b, msgno, "");
3583 /********************************************************************************/
3587 * Output a string in a distinctive style
3589 void
3590 gf_puts_uline(char *txt, gf_io_t pc)
3592 pc(TAG_EMBED); pc(TAG_BOLDON);
3593 gf_puts(txt, pc);
3594 pc(TAG_EMBED); pc(TAG_BOLDOFF);
3597 /* get_chain_for_cert: error and level are mandatory arguments */
3598 STACK_OF(X509) *
3599 get_chain_for_cert(X509 *cert, int *error, int *level)
3601 STACK_OF(X509) *chain = NULL;
3602 X509_STORE_CTX *ctx;
3603 X509 *x, *xtmp;
3604 int rc; /* return code */
3606 *level = -1;
3607 *error = 0;
3608 ERR_clear_error();
3609 if((s_cert_store != NULL) && (ctx = X509_STORE_CTX_new()) != NULL){
3610 X509_STORE_set_flags(s_cert_store, 0);
3611 if(!X509_STORE_CTX_init(ctx, s_cert_store, cert, NULL))
3612 *error = X509_STORE_CTX_get_error(ctx);
3613 else if((chain = sk_X509_new_null()) != NULL){
3614 for(x = cert; ; x = xtmp){
3615 if(++*level > 0)
3616 sk_X509_push(chain, X509_dup(x));
3617 rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x);
3618 if(rc < 0)
3619 *error = 1;
3620 if(rc <= 0)
3621 break;
3622 if(!X509_check_issued(xtmp, xtmp))
3623 break;
3626 X509_STORE_CTX_free(ctx);
3628 return chain;
3633 * Sign a message. Called from call_mailer in send.c.
3635 * This takes the header for the outgoing message as well as a pointer
3636 * to the current body (which may be reallocated).
3637 * The last argument (BODY **bp) is an argument that tells Alpine
3638 * if the body has 8 bit. if *bp is not null we compute two signatures
3639 * one for the quoted-printable encoded message, and another for the
3640 * 8bit encoded message. We return the signature for the 8bit encoded
3641 * part in p2->body.mime.text.data.
3642 * The reason why we compute two signatures is so that we can decide
3643 * which one to use later, and we only do it in the case that *bp is
3644 * not null. If we did not do this, then we might not be able to sign
3645 * a message until we log in to the smtp server, so instead of doing
3646 * that, we get ready for any possible situation we might find.
3649 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp)
3651 STORE_S *outs = NULL;
3652 STORE_S *outs_2 = NULL;
3653 BODY *body = *bodyP;
3654 BODY *newBody = NULL;
3655 PART *p1 = NULL;
3656 PART *p2 = NULL;
3657 PERSONAL_CERT *pcert;
3658 BIO *in = NULL;
3659 BIO *in_2 = NULL;
3660 BIO *out = NULL;
3661 BIO *out_2 = NULL;
3662 PKCS7 *p7 = NULL;
3663 PKCS7 *p7_2 = NULL;
3664 STACK_OF(X509) *chain;
3665 const EVP_MD *md = EVP_sha256(); /* use this digest instead of sha1 */
3666 int result = 0, error;
3667 int flags = dont_detach ? 0 : PKCS7_DETACHED;
3668 int level;
3670 dprint((9, "sign_outgoing_message()"));
3672 smime_init();
3674 /* Look for a private key matching the sender address... */
3676 pcert = match_personal_cert(header->env);
3678 if(!pcert){
3679 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
3680 goto end;
3683 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
3684 /* Couldn't load key with blank password, try again */
3685 if(pith_opt_smime_get_passphrase){
3686 (*pith_opt_smime_get_passphrase)();
3687 load_private_key(pcert);
3691 if(!pcert->key)
3692 goto end;
3694 if(((chain = get_chain_for_cert(pcert->cert, &error, &level)) != NULL && error)
3695 || level == 0){
3696 sk_X509_pop_free(chain, X509_free);
3697 chain = NULL;
3700 if(error)
3701 q_status_message(SM_ORDER, 1, 1,
3702 _("Not all certificates needed to verify signature included in signed message"));
3704 in = body_to_bio(body);
3706 flags |= PKCS7_PARTIAL;
3707 if((p7 = PKCS7_sign(NULL, NULL, chain, in, flags)) != NULL
3708 && PKCS7_sign_add_signer(p7, pcert->cert, pcert->key, md, flags))
3709 PKCS7_final(p7, in, flags);
3711 if(bp && *bp){
3712 int i, save_encoding;
3714 for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++);
3716 if(i > ENCMAX){ /* no empty encoding slots! */
3717 *bp = NULL;
3719 else {
3720 save_encoding = (*bp)->encoding;
3721 body_encodings[(*bp)->encoding = i] = body_encodings[ENC8BIT];
3723 in_2 = body_to_bio(body);
3725 body_encodings[i] = NULL;
3726 (*bp)->encoding = save_encoding;
3730 if(bp && *bp){
3731 if((p7_2 = PKCS7_sign(NULL, NULL, chain, in_2, flags)) != NULL
3732 && PKCS7_sign_add_signer(p7_2, pcert->cert, pcert->key, md, flags))
3733 PKCS7_final(p7_2, in_2, flags);
3736 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3737 forget_private_keys();
3739 if(chain)
3740 sk_X509_pop_free(chain, X509_free);
3742 if(!p7){
3743 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
3744 goto end;
3747 outs = so_get(BioType, NULL, EDIT_ACCESS);
3748 out = bio_from_store(outs);
3750 i2d_PKCS7_bio(out, p7);
3751 (void) BIO_flush(out);
3753 so_seek(outs, 0, SEEK_SET);
3755 if(bp && *bp && p7_2){
3756 outs_2 = so_get(BioType, NULL, EDIT_ACCESS);
3757 out_2 = bio_from_store(outs_2);
3759 i2d_PKCS7_bio(out_2, p7_2);
3760 (void) BIO_flush(out_2);
3762 so_seek(outs_2, 0, SEEK_SET);
3765 if((flags&PKCS7_DETACHED)==0){
3767 /* the simple case: the signed data is in the pkcs7 object */
3769 newBody = mail_newbody();
3771 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3773 newBody->contents.text.data = (unsigned char *) outs;
3774 *bodyP = newBody;
3776 result = 1;
3778 else{
3781 * OK.
3782 * We have to create a new body as follows:
3784 * multipart/signed; blah blah blah
3785 * reference to existing body
3787 * pkcs7 object
3790 newBody = mail_newbody();
3792 newBody->type = TYPEMULTIPART;
3793 newBody->subtype = cpystr("signed");
3794 newBody->encoding = ENC7BIT;
3796 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
3797 set_parameter(&newBody->parameter, "micalg", "sha-256");
3799 p1 = mail_newbody_part();
3800 p2 = mail_newbody_part();
3803 * This is nasty. We're just copying the body in here,
3804 * but since our newBody is freed at the end of call_mailer,
3805 * we mustn't let this body (the original one) be freed twice.
3807 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
3809 p1->next = p2;
3811 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL);
3812 p2->body.mime.text.data = (unsigned char *) outs_2;
3813 p2->body.contents.text.data = (unsigned char *) outs;
3815 newBody->nested.part = p1;
3817 *bodyP = newBody;
3819 result = 1;
3822 end:
3824 PKCS7_free(p7);
3825 BIO_free(in);
3827 if(bp && *bp){
3828 if(p7_2) PKCS7_free(p7_2);
3829 BIO_free(in_2);
3832 dprint((9, "sign_outgoing_message returns %d", result));
3833 return result;
3837 SMIME_STUFF_S *
3838 new_smime_struct(void)
3840 SMIME_STUFF_S *ret = NULL;
3842 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
3843 memset((void *) ret, 0, sizeof(*ret));
3844 ret->publictype = Nada;
3846 return ret;
3850 static void
3851 free_smime_struct(SMIME_STUFF_S **smime)
3853 if(smime && *smime){
3854 if((*smime)->passphrase_emailaddr){
3855 int i;
3856 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
3857 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
3858 fs_give((void **) (*smime)->passphrase_emailaddr);
3861 if((*smime)->publicpath)
3862 fs_give((void **) &(*smime)->publicpath);
3864 if((*smime)->publiccertlist)
3865 free_certlist(&(*smime)->publiccertlist);
3867 if((*smime)->backuppubliccertlist)
3868 free_certlist(&(*smime)->backuppubliccertlist);
3870 if((*smime)->cacertlist)
3871 free_certlist(&(*smime)->cacertlist);
3873 if((*smime)->backupcacertlist)
3874 free_certlist(&(*smime)->backupcacertlist);
3876 if((*smime)->privatecertlist)
3877 free_certlist(&(*smime)->privatecertlist);
3879 if((*smime)->backupprivatecertlist)
3880 free_certlist(&(*smime)->backupprivatecertlist);
3882 if((*smime)->publiccontent)
3883 fs_give((void **) &(*smime)->publiccontent);
3885 if((*smime)->privatepath)
3886 fs_give((void **) &(*smime)->privatepath);
3888 if((*smime)->personal_certs){
3889 PERSONAL_CERT *pc;
3891 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
3892 free_personal_certs(&pc);
3893 (*smime)->personal_certs = NULL;
3896 if((*smime)->privatecontent)
3897 fs_give((void **) &(*smime)->privatecontent);
3899 if((*smime)->capath)
3900 fs_give((void **) &(*smime)->capath);
3902 if((*smime)->cacontent)
3903 fs_give((void **) &(*smime)->cacontent);
3905 fs_give((void **) smime);
3909 #endif /* SMIME */