* LibreSSL 2.2.2 does not have RAND_egd, so we eliminate that call (we were
[alpine.git] / pith / smime.c
blob4e9cad843a7a845bca69876b0bf790bf42722ef2
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-2015 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"
46 #include <openssl/buffer.h>
47 #include <openssl/x509v3.h>
49 /* internal prototypes */
50 static void forget_private_keys(void);
51 static int app_RAND_load_file(const char *file);
52 static void openssl_extra_randomness(void);
53 static int app_RAND_write_file(const char *file);
54 static const char *openssl_error_string(void);
55 static int load_private_key(PERSONAL_CERT *pcert);
56 static void create_local_cache(char *h, char *base, BODY *b, int type);
57 static long rfc822_output_func(void *b, char *string);
58 static void setup_pkcs7_body_for_signature(BODY *b, char *description,
59 char *type, char *filename, char *smime_type);
60 static BIO *body_to_bio(BODY *body);
61 static BIO *bio_from_store(STORE_S *store);
62 static STORE_S *get_part_contents(long msgno, const char *section);
63 static PKCS7 *get_pkcs7_from_part(long msgno, const char *section);
64 static int do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent);
65 static int do_detached_signature_verify(BODY *b, long msgno, char *section);
66 static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7);
67 static int do_decoding(BODY *b, long msgno, const char *section);
68 static void free_smime_struct(SMIME_STUFF_S **smime);
69 static void setup_storage_locations(void);
70 static int copy_container_to_dir(WhichCerts which);
71 static int do_fiddle_smime_message(BODY *b, long msgno, char *section);
72 void setup_privatekey_storage(void);
73 int smime_path(char *rpath, char *fpath, size_t len);
74 int smime_extract_and_save_cert(PKCS7 *p7);
75 int same_cert(X509 *, X509 *);
76 CertList * certlist_from_personal_certs(PERSONAL_CERT *pc);
77 #ifdef PASSFILE
78 int load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile, char **certfile, EVP_PKEY **pkey, X509 **pcert);
79 #endif /* PASSFILE */
80 EVP_PKEY *load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *);
81 void smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen);
82 void smime_remove_folding_space(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen);
83 int smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag);
85 int (*pith_opt_smime_get_passphrase)(void);
86 int (*pith_smime_import_certificate)(char *, char *, size_t);
87 int (*pith_smime_enter_password)(char *prompt, char *, size_t);
89 static X509_STORE *s_cert_store;
91 /* State management for randomness functions below */
92 static int seeded = 0;
94 void *
95 create_smime_sparep(SpareType stype, void *s)
97 SMIME_SPARE_S *rv;
99 rv = fs_get(sizeof(SMIME_SPARE_S));
100 rv->sptype = stype;
101 rv->data = s;
102 return (void *) rv;
105 SpareType
106 get_smime_sparep_type(void *s)
108 return ((SMIME_SPARE_S *)s)->sptype;
111 void *
112 get_smime_sparep_data(void *s)
114 return ((SMIME_SPARE_S *)s)->data;
118 #ifdef PASSFILE
120 * load key from pathkeydir and cert from pathcertdir. It chooses the first
121 * key/certificate pair that matches. Delete pairs that you do not want used,
122 * if you do not want them selected. All parameters must be non-null.
123 * Memory freed by caller.
124 * Return values:
125 * -1 : user cancelled load
126 * 0 : load was successful
127 * 1 : there was an error in the loading.
130 load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile,
131 char **certfile, EVP_PKEY **pkey, X509 **pcert)
133 char buf[MAXPATH+1], pathkey[MAXPATH+1], prompt[MAILTMPLEN];
134 DIR *dirp;
135 struct dirent *d;
136 int b = 0, ret = 1; /* assume error */
138 if(pathkeydir == NULL || pathcertdir == NULL || keyfile == NULL
139 || pkey == NULL || certfile == NULL || pcert == NULL)
140 return 1;
142 *keyfile = NULL;
143 *certfile = NULL;
144 *pkey = NULL;
145 *pcert = NULL;
147 if((dirp = opendir(pathkeydir)) != NULL){
148 while(b == 0 && (d=readdir(dirp)) != NULL){
149 size_t ll;
151 if((ll=strlen(d->d_name)) && ll > 4){
152 if(!strcmp(d->d_name+ll-4, ".key")){
153 strncpy(buf, d->d_name, sizeof(buf));
154 buf[sizeof(buf)-1] = '\0';
155 build_path(pathkey, pathkeydir, buf, sizeof(pathkey));
156 buf[strlen(buf)-4] = '\0';
157 snprintf(prompt, sizeof(prompt),
158 _("Enter password of key <%s> to unlock password file: "), buf);
159 if((*pkey = load_pkey_with_prompt(pathkey, NULL, prompt, &ret)) != NULL){
160 if(load_cert_for_key(pathcertdir, *pkey, certfile, pcert)){
161 b = 1; /* break */
162 *keyfile = cpystr(buf);
163 } else {
164 EVP_PKEY_free(*pkey);
165 *pkey = NULL;
166 q_status_message1(SM_ORDER, 0, 2,
167 _("Cannot find certificate that matches key <%s>. Continuing..."), buf);
173 closedir(dirp);
175 return ret;
179 /* setup a key and certificate to encrypt and decrypt a password file.
180 * These files will be saved in the .alpine-smime/.pwd directory, but its
181 * location can be setup in the command line with the -pwdcertdir option.
182 * Here are the rules:
184 * Check if the .alpine-smime/.pwd (or -pwdcertdir directory) exists,
185 * if not create it. If we are successful, move to the next step
187 * - If the user has a key/cert pair, in the .alpine-smime/.pwd dir
188 * setup is successful;
189 * - if the user does not have a key/cert pair, look to see if
190 * ps_global->smime->personal_certs is already setup, if so, use it.
191 * - if ps_global->smime->personal_certs is not set up, see if we can
192 * find a certificate/cert pair in the default locations at compilation
193 * time. (~/.alpine-smime/private and ~/.alpine-smime/public
194 * - if none of this is successful, create a key/certificate pair
195 * (TODO: implement this)
196 * - in any other case, setup is not successful.
198 * If setup is successful, setup ps_global->pwdcert.
199 * If any of this fails, ps_global->pwdcert will be null.
200 * Ok, that should do it.
202 void
203 setup_pwdcert(void **pwdcert)
205 int we_inited = 0;
206 int setup_dir = 0; /* make it non zero if we know which dir to use */
207 struct stat sbuf;
208 char pathdir[MAXPATH+1], pathkey[MAXPATH+1], fpath[MAXPATH+1], pathcert[MAXPATH+1];
209 char fpath2[MAXPATH+1], prompt[MAILTMPLEN];
210 char *keyfile, *certfile, *text;
211 EVP_PKEY *pkey = NULL;
212 X509 *pcert = NULL;
213 PERSONAL_CERT *pc, *pc2 = NULL;
215 if(pwdcert == NULL)
216 return;
218 if(ps_global->pwdcertdir){
219 if(our_stat(ps_global->pwdcertdir, &sbuf) == 0
220 && ((sbuf.st_mode & S_IFMT) == S_IFDIR)){
221 setup_dir++;
222 strncpy(pathdir, ps_global->pwdcertdir, sizeof(pathdir));
223 pathdir[sizeof(pathdir)-1] = '\0';
225 } else {
226 smime_path(DF_PASSWORD_DIR, pathdir, sizeof(pathdir));
227 if(our_stat(pathdir, &sbuf) == 0){
228 if((sbuf.st_mode & S_IFMT) == S_IFDIR)
229 setup_dir++;
230 } else if(can_access(pathdir, ACCESS_EXISTS) != 0
231 && our_mkpath(pathdir, 0700) == 0)
232 setup_dir++;
235 if(setup_dir == 0)
236 return;
238 if(load_key_and_cert(pathdir, pathdir, &keyfile, &certfile, &pkey, &pcert) < 0)
239 return;
241 if(certfile && keyfile){
242 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
243 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
244 pc->name = keyfile;
245 pc->key = pkey;
246 pc->cert = pcert;
247 *pwdcert = (void *) pc;
248 fs_give((void **)&certfile);
249 return;
252 /* if the user gave a pwdcertdir and there is nothing there, do not
253 * continue. Let the user initialize on their own this directory.
255 if(ps_global->pwdcertdir != NULL)
256 return;
258 /* look to see if there are any certificates lying around, first
259 * we try to load ps_global->smime to see if that has information
260 * we can use. If we are the process filling the smime structure
261 * we deinit at the end, since this might not do a full init.
263 if(ps_global && ps_global->smime && !ps_global->smime->inited){
264 we_inited++;
265 smime_init();
268 /* at this point ps_global->smime->inited == 1 */
269 if(ps_global->smime && ps_global->smime->personal_certs != NULL){
270 pc = (PERSONAL_CERT *) ps_global->smime->personal_certs;
271 if(ps_global->smime->privatetype == Directory){
272 build_path(pathkey, ps_global->smime->privatepath, pc->name, sizeof(pathkey));
273 strncat(pathkey, ".key", 4);
274 pathkey[sizeof(pathkey)-1] = '\0';
275 text = NULL;
276 } else if (ps_global->smime->privatetype == Container){
277 if(pc->keytext == NULL){ /* we should *never* be here, but just in case */
278 if(ps_global->smime->privatecontent != NULL){
279 char tmp[MAILTMPLEN], *s, *t, c;
280 snprintf(tmp, sizeof(tmp), "%s%s", EMAILADDRLEADER, pc->name);
281 tmp[sizeof(tmp)-1] = '\0';
282 if((s = strstr(ps_global->smime->privatecontent, tmp)) != NULL){
283 if((t = strstr(s+strlen(tmp), EMAILADDRLEADER)) != NULL){
284 c = *t;
285 *t = '\0';
286 pc->keytext = cpystr(s + strlen(tmp) + 1); /* 1 = strlen("\n") */
287 *t = c;
289 else
290 pc->keytext = cpystr(s + strlen(tmp) + 1); /* 1 = strlen("\n") */
294 if(pc->keytext != NULL) /* we should go straigth here */
295 text = pc->keytext;
296 } else if (ps_global->smime->privatetype == Keychain){
297 pathkey[0] = '\0'; /* no apple key chain support yet */
298 text = NULL;
300 if((pathkey && *pathkey) || text){
301 snprintf(prompt, sizeof(prompt),
302 _("Enter password of key <%s> to unlock password file: "), pc->name);
304 if((pkey = load_pkey_with_prompt(pathkey, text, prompt, NULL)) != NULL){
305 pc2 = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
306 memset((void *)pc2, 0, sizeof(PERSONAL_CERT));
307 pc2->name = cpystr(pc->name);
308 pc2->key = pkey;
309 pc2->cert = X509_dup(pc->cert);
311 /* now copy the keys and certs, starting by the key... */
312 build_path(fpath, pathdir, pc->name, sizeof(fpath));
313 strncat(fpath, ".key", 4);
314 fpath[sizeof(fpath)-1] = '\0';
315 if(our_stat(fpath, &sbuf) == 0){ /* if fpath exists */
316 if((sbuf.st_mode & S_IFMT) == S_IFREG) /* and is a regular file */
317 setup_dir++; /* we are done */
318 } else if(ps_global->smime->privatetype == Directory){
319 if(our_copy(fpath, pathkey) == 0)
320 setup_dir++;
321 } else if(ps_global->smime->privatetype == Container){
322 BIO *out;
323 if((out = BIO_new_file(fpath, "w")) != NULL){
324 if(BIO_puts(out, pc->keytext) > 0)
325 setup_dir++;
326 BIO_free(out);
328 } else if(ps_global->smime->privatetype == Keychain){
329 /* add support for Apple Mac OS X */
333 /* successful copy of key, now continue with certificate */
334 if(setup_dir){
335 setup_dir = 0;
337 build_path(pathkey, ps_global->smime->publicpath, pc->name, sizeof(pathkey));
338 strncat(pathkey, ".crt", 4);
339 pathkey[sizeof(pathkey)-1] = '\0';
341 build_path(fpath, pathdir, pc->name, sizeof(fpath));
342 strncat(fpath, ".crt", 4);
343 fpath[sizeof(fpath)-1] = '\0';
345 if(our_stat(fpath, &sbuf) == 0){
346 if((sbuf.st_mode & S_IFMT) == S_IFREG)
347 setup_dir++;
349 else if(ps_global->smime->privatetype == Directory){
350 if(our_copy(fpath, pathkey) == 0)
351 setup_dir++;
352 } else if(ps_global->smime->privatetype == Container) {
353 BIO *out;
354 if((out = BIO_new_file(fpath, "w")) != NULL){
355 if(PEM_write_bio_X509(out, pc->cert))
356 setup_dir++;
357 BIO_free(out);
359 } else if (ps_global->smime->privatetype == Keychain) {
360 /* add support for Mac OS X */
364 if(setup_dir){
365 *pwdcert = (void *) pc2;
366 return;
368 else if(pc2 != NULL)
369 free_personal_certs(&pc2);
370 } /* if (pathkey...) */
371 } /* if(ps_global->smime->personal_certs) */
374 if(setup_dir == 0){
375 /* PATHCERTDIR(Private) must be null, so create a path */
376 set_current_val(&ps_global->vars[V_PRIVATEKEY_DIR], TRUE, TRUE);
377 smime_path(ps_global->VAR_PRIVATEKEY_DIR, pathkey, sizeof(pathkey));
379 /* PATHCERTDIR(Public) must be null, so create a path */
380 set_current_val(&ps_global->vars[V_PUBLICCERT_DIR], TRUE, TRUE);
381 smime_path(ps_global->VAR_PUBLICCERT_DIR, pathcert, sizeof(pathcert));
383 /* BUG: this does not support local containers */
384 load_key_and_cert(pathkey, pathcert, &keyfile, &certfile, &pkey, &pcert);
386 if(certfile && keyfile){
387 build_path(fpath, pathdir, keyfile, sizeof(fpath));
388 strncat(fpath, ".key", 4);
389 fpath[sizeof(fpath)-1] = '\0';
391 build_path(fpath2, pathkey, keyfile, sizeof(fpath));
392 strncat(fpath2, ".key", 4);
393 fpath2[sizeof(fpath2)-1] = '\0';
395 if(our_copy(fpath, fpath2) == 0)
396 setup_dir++;
398 if(setup_dir){
399 setup_dir = 0;
401 build_path(fpath, pathdir, certfile, sizeof(fpath));
402 build_path(fpath2, pathcert, certfile, sizeof(fpath2));
404 if(our_copy(fpath, fpath2) == 0)
405 setup_dir++;
410 if(keyfile && certfile){
411 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
412 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
413 pc->name = keyfile;
414 pc->key = pkey;
415 pc->cert = pcert;
416 *pwdcert = (void *) pc;
417 fs_give((void **)&certfile);
418 return;
421 /* TODO: create self signed certificate
422 q_status_message(SM_ORDER, 2, 2,
423 _("No key/certificate pair found for password file encryption support"));
426 if(we_inited)
427 smime_deinit();
429 #endif /* PASSFILE */
431 /* smime_expunge_cert.
432 * Return values: < 0 there was an error.
433 * >=0 the number of messages expunged
436 smime_expunge_cert(WhichCerts ctype)
438 int count, removed;
439 CertList *cl, *dummy, *data;
440 char *path, buf[MAXPATH+1];
441 char *contents;
443 if(DATACERT(ctype)== NULL)
444 return -1;
446 /* data cert is the way we unify certificate management across
447 * functions, but it is not where we really save the information in the
448 * case ctype is equal to Private. What we will do is to update the
449 * datacert, and in the case of ctype equal to Private use the updated
450 * certdata to update the personal_certs data.
453 path = PATHCERTDIR(ctype);
455 if(path){
456 /* add a fake certificate at the beginning of the list */
457 dummy = fs_get(sizeof(CertList));
458 memset((void *)dummy, 0, sizeof(CertList));
459 dummy->next = DATACERT(ctype);
461 for(cl = dummy, count = 0; cl && cl->next;){
462 if(cl->next->data.deleted == 0){
463 cl = cl->next;
464 continue;
467 removed = 1; /* assume success */
468 if(SMHOLDERTYPE(ctype) == Directory){
469 build_path(buf, path, cl->next->name, sizeof(buf));
470 if(ctype == Private && strlen(buf) + strlen(EXTCERT(Private)) < sizeof(buf)){
471 strncat(buf, EXTCERT(Private), 4);
472 buf[sizeof(buf)-1] = '\0';
475 if(our_unlink(buf) < 0){
476 q_status_message1(SM_ORDER, 3, 3, _("Error removing certificate %s"), cl->next->name);
477 cl = cl->next;
478 removed = 0;
481 else if(SMHOLDERTYPE(ctype) == Container){
482 char *prefix= ctype == CACert ? CACERTSTORELEADER : EMAILADDRLEADER;
483 char tmp[MAILTMPLEN], *s, *t;
485 contents = CONTENTCERTLIST(ctype);
486 snprintf(tmp, sizeof(tmp), "%s%s", prefix, cl->next->name);
487 tmp[sizeof(tmp) - 1] = '\0';
488 if((s = strstr(contents, tmp)) != NULL){
489 if((t = strstr(s+strlen(tmp), prefix)) == NULL)
490 *s = '\0';
491 else
492 memmove(s, t, strlen(t)+1);
493 fs_resize((void **)&contents, strlen(contents)+1);
494 switch(ctype){
495 case Private: ps_global->smime->privatecontent = contents; break;
496 case Public : ps_global->smime->publiccontent = contents; break;
497 case CACert : ps_global->smime->cacontent = contents; break;
498 default : break;
501 else
502 removed = 0;
503 } else { /* unhandled case */
506 if(removed > 0){
507 count++; /* count it! */
508 data = cl->next;
509 cl->next = data->next;
510 if(data->name) fs_give((void **)&data->name);
511 fs_give((void **)&data);
514 } else
515 q_status_message(SM_ORDER, 3, 3, _("Error expunging certificate"));
517 switch(ctype){
518 case Private: ps_global->smime->privatecertlist = dummy->next; break;
519 case Public : ps_global->smime->publiccertlist = dummy->next; break;
520 case CACert : ps_global->smime->cacertlist = dummy->next; break;
521 default : break;
523 fs_give((void **)&dummy);
524 if(SMHOLDERTYPE(ctype) == Container){
525 if(copy_dir_to_container(ctype, contents) < 0)
526 count = 0;
528 if(count > 0){
529 q_status_message2(SM_ORDER, 3, 3, _("Removed %s certificate%s"), comatose(count), plural(count));
531 else
532 q_status_message(SM_ORDER, 3, 3, _("Error: No certificates were removed"));
533 return count;
536 void
537 mark_cert_deleted(WhichCerts ctype, int num, unsigned state)
539 CertList *cl;
540 int i;
542 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
543 cl->data.deleted = state;
546 unsigned
547 get_cert_deleted(WhichCerts ctype, int num)
549 CertList *cl;
550 int i;
552 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
553 return (cl && cl->data.deleted) ? 1 : 0;
556 EVP_PKEY *
557 load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *ret)
559 EVP_PKEY *pkey;
560 int rc = 0; /* rc == 1, cancel, rc == 0 success */
561 char pass[MAILTMPLEN+1];
562 BIO *in;
564 /* attempt to load with empty password */
565 in = text ? BIO_new_mem_buf(text, strlen(text)) : BIO_new_file(fpath, "r");
566 if(in != NULL){
567 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, "");
568 if(pkey != NULL) return pkey;
569 } else return NULL;
571 if(pith_smime_enter_password)
572 while(pkey == NULL && rc != 1){
573 do {
574 rc = (*pith_smime_enter_password)(prompt, (char *)pass, sizeof(pass));
575 } while (rc!=0 && rc!=1 && rc>0);
577 (void) BIO_reset(in);
578 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, (char *)pass);
581 BIO_free(in);
583 if(ret) *ret = rc == 1 ? -1 : pkey != NULL ? 0 : 1;
584 return pkey;
590 import_certificate(WhichCerts ctype)
592 int r = 1, rc;
593 char filename[MAXPATH+1], full_filename[MAXPATH+1], buf[MAXPATH+1];
595 if(pith_smime_import_certificate == NULL){
596 q_status_message(SM_ORDER, 0, 2,
597 _("import of certificates not implemented yet!"));
598 return -1;
601 smime_init();
603 r = (*pith_smime_import_certificate)(filename, full_filename, sizeof(filename) - 20);
605 ps_global->mangled_screen = 1;
607 if(r < 0)
608 return r;
609 else if (ctype == Private){
610 char prompt[500], *s, *t;
611 EVP_PKEY *key = NULL;
613 if(!ps_global->smime->privatecertlist){
614 ps_global->smime->privatecertlist = fs_get(sizeof(CertList));
615 memset((void *)DATACERT(ctype), 0, sizeof(CertList));
618 for(s = t = filename; (t = strstr(s, ".key")) != NULL; s = t + 1);
619 if(s) *(s-1) = 0;
621 snprintf(prompt, sizeof(prompt), _("Enter passphrase for <%s>: "), filename);
622 prompt[sizeof(prompt)-1] = '\0';
623 if((key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){
624 if(SMHOLDERTYPE(ctype) == Directory){
625 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
626 if(strcmp(buf + strlen(buf) - 4, EXTCERT(ctype)) != 0 && strlen(buf) + 4 < sizeof(buf)){
627 strncat(buf, EXTCERT(ctype), 4);
628 buf[sizeof(buf)-1] = '\0';
630 rc = our_copy(buf, full_filename);
632 else /* if(SMHOLDERTYPE(ctype) == Container){ */
633 rc = add_file_to_container(ctype, full_filename, NULL);
634 if(rc == 0)
635 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
636 else
637 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
638 if(ps_global->smime->publiccertlist)
639 ps_global->smime->publiccertlist->data.renew = 1;
641 else
642 q_status_message(SM_ORDER, 1, 3, _("Problem unlocking key (not a certificate and/or wrong password)"));
643 } else if (ctype == CACert){
644 BIO *ins;
645 X509 *cert;
647 if((ins = BIO_new_file(full_filename, "r")) != NULL){
648 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
649 if(SMHOLDERTYPE(ctype) == Directory){
650 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
651 if(strcmp(buf + strlen(buf) - 4, ".crt") != 0 && strlen(buf) + 4 < sizeof(buf)){
652 strncat(buf, EXTCERT(ctype), 4);
653 buf[sizeof(buf)-1] = '\0';
656 rc = our_copy(buf, full_filename);
658 else /* if(SMHOLDERTYPE(ctype) == Container){ */
659 rc = add_file_to_container(ctype, full_filename, NULL);
660 if(rc == 0)
661 q_status_message(SM_ORDER, 1, 3, _("Certificate saved"));
662 else
663 q_status_message(SM_ORDER, 1, 3, _("Error saving certificate"));
664 X509_free(cert); /* not needed anymore */
666 else
667 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
668 BIO_free(ins);
670 renew_store();
671 } else { /* ctype == Public. save certificate, but first validate that it is one */
672 BIO *ins;
673 X509 *cert;
675 if((ins = BIO_new_file(full_filename, "r")) != NULL){
676 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
677 if(SMHOLDERTYPE(ctype) == Directory){
678 char **email;
680 if((email = get_x509_subject_email(cert)) != NULL){
681 int i;
682 for(i = 0; email[i] != NULL; i++){
683 save_cert_for(email[i], cert, Public);
684 fs_give((void **)&email[i]);
686 fs_give((void **)email);
688 else
689 save_cert_for(filename, cert, Public);
691 else /* if(SMHOLDERTYPE(ctype) == Container){ */
692 add_file_to_container(ctype, full_filename, NULL);
693 X509_free(cert);
694 if(ps_global->smime->publiccertlist)
695 ps_global->smime->publiccertlist->data.renew = 1;
697 else
698 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
699 BIO_free(ins);
702 if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1;
703 return 0;
706 /* itype: information type to add: 0 - public, 1 - private.
707 * Memory freed by caller
709 BIO *
710 print_private_key_information(char *email, int itype)
712 BIO *out;
713 PERSONAL_CERT *pc;
715 if(ps_global->smime == NULL
716 || ps_global->smime->personal_certs == NULL
717 || (itype != 0 && itype != 1))
718 return NULL;
720 for(pc = ps_global->smime->personal_certs;
721 pc != NULL && strcmp(pc->name, email) != 0; pc = pc->next);
722 if(pc->key == NULL
723 && !load_private_key(pc)
724 && ps_global->smime
725 && ps_global->smime->need_passphrase){
726 if (*pith_opt_smime_get_passphrase)
727 (*pith_opt_smime_get_passphrase)();
728 load_private_key(pc);
731 if(pc->key == NULL)
732 return NULL;
734 out = BIO_new(BIO_s_mem());
735 if(itype == 0) /* 0 means public */
736 EVP_PKEY_print_public(out, pc->key, 0, NULL);
737 else if (itype == 1) /* 1 means private */
738 EVP_PKEY_print_private(out, pc->key, 0, NULL);
740 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
741 forget_private_keys();
743 return out;
747 * Forget any cached private keys
749 static void
750 forget_private_keys(void)
752 PERSONAL_CERT *pcert;
753 size_t len;
754 volatile char *p;
756 dprint((9, "forget_private_keys()"));
757 if(ps_global->smime){
758 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
759 pcert;
760 pcert=pcert->next){
762 if(pcert->key){
763 EVP_PKEY_free(pcert->key);
764 pcert->key = NULL;
768 ps_global->smime->entered_passphrase = 0;
769 len = sizeof(ps_global->smime->passphrase);
770 p = ps_global->smime->passphrase;
772 while(len-- > 0)
773 *p++ = '\0';
777 /* modelled after signature_path in reply.c, but uses home dir instead of the
778 * directory where the .pinerc is located, since according to documentation,
779 * the .alpine-smime directories are subdirectories of the home directory
781 int smime_path(char *rpath, char *fpath, size_t len)
783 *fpath = '\0';
784 if(rpath && *rpath){
785 size_t spl = strlen(rpath);
787 if(IS_REMOTE(rpath)){
788 if(spl < len - 1)
789 strncpy(fpath, rpath, len-1);
790 fpath[len-1] = '\0';
792 else if(is_absolute_path(rpath)){
793 strncpy(fpath, rpath, len-1);
794 fpath[len-1] = '\0';
795 fnexpand(fpath, len);
797 else if(ps_global->VAR_OPER_DIR){
798 if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
799 build_path(fpath, ps_global->VAR_OPER_DIR, rpath, len);
801 else if(ps_global->home_dir){
802 if(strlen(ps_global->home_dir) + spl < len - 1)
803 build_path(fpath, ps_global->home_dir, rpath, len);
806 return fpath && *fpath ? 1 : 0;
812 * taken from openssl/apps/app_rand.c
814 static int
815 app_RAND_load_file(const char *file)
817 #define RANDBUFLEN 200
818 char buffer[RANDBUFLEN];
820 if(file == NULL)
821 file = RAND_file_name(buffer, RANDBUFLEN);
823 if(file == NULL || !RAND_load_file(file, -1)){
824 if(RAND_status() == 0){
825 dprint((1, "unable to load 'random state'\n"));
826 dprint((1, "This means that the random number generator has not been seeded\n"));
827 dprint((1, "with much random data.\n"));
830 return 0;
833 seeded = 1;
834 return 1;
839 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
841 static void
842 openssl_extra_randomness(void)
844 #if !defined(WIN32)
845 int fd;
846 unsigned long i;
847 char *tf = NULL;
848 char tmp[MAXPATH];
849 struct stat sbuf;
850 /* if system doesn't have /dev/urandom */
851 if(stat ("/dev/urandom", &sbuf)){
852 tmp[0] = '0';
853 tf = temp_nam(NULL, NULL);
854 if(tf){
855 strncpy(tmp, tf, sizeof(tmp));
856 tmp[sizeof(tmp)-1] = '\0';
857 fs_give((void **) &tf);
860 if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
861 i = (unsigned long) tmp;
862 else{
863 unlink(tmp); /* don't need the file */
864 fstat(fd, &sbuf); /* get information about the file */
865 i = sbuf.st_ino; /* remember its inode */
866 close(fd); /* or its descriptor */
868 /* not great but it'll have to do */
869 snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
870 tcp_serverhost (),i,
871 (unsigned long) (time (0) ^ gethostid ()),
872 (unsigned long) getpid ());
873 RAND_seed(tmp, strlen(tmp));
875 #endif
879 /* taken from openssl/apps/app_rand.c */
880 static int
881 app_RAND_write_file(const char *file)
883 char buffer[200];
885 if(!seeded)
887 * If we did not manage to read the seed file,
888 * we should not write a low-entropy seed file back --
889 * it would suppress a crucial warning the next time
890 * we want to use it.
892 return 0;
894 if(file == NULL)
895 file = RAND_file_name(buffer, sizeof buffer);
897 if(file == NULL || !RAND_write_file(file)){
898 dprint((1, "unable to write 'random state'\n"));
899 return 0;
902 return 1;
905 CertList *
906 certlist_from_personal_certs(PERSONAL_CERT *pc)
908 CertList *cl;
909 X509 *x;
910 char buf[MAXPATH];
912 if(pc == NULL)
913 return NULL;
915 cl = fs_get(sizeof(CertList));
916 memset((void *)cl, 0, sizeof(CertList));
917 cl->name = cpystr(pc->name);
918 x = get_cert_for(pc->name, Public);
919 if(x){
920 if(x->cert_info){
921 cl->data.date_from = smime_get_date(x->cert_info->validity->notBefore);
922 cl->data.date_to = smime_get_date(x->cert_info->validity->notAfter);
923 get_fingerprint(x, EVP_md5(), buf, sizeof(buf), NULL);
924 cl->data.md5 = cpystr(buf);
926 X509_free(x);
928 cl->next = certlist_from_personal_certs(pc->next);
930 return cl;
933 void
934 renew_cert_data(CertList **data, WhichCerts ctype)
936 smime_init();
937 if(ctype == Private){
938 if(data){
939 PERSONAL_CERT *pc = (PERSONAL_CERT *)ps_global->smime->personal_certs;
940 if(*data)
941 free_certlist(data);
942 free_personal_certs(&pc);
943 setup_privatekey_storage();
944 *data = certlist_from_personal_certs((PERSONAL_CERT *)ps_global->smime->personal_certs);
945 if(data && *data){
946 resort_certificates(data, ctype);
947 RENEWCERT(*data) = 0;
949 ps_global->smime->privatecertlist = *data;
951 if(ps_global->smime->privatecertlist)
952 RENEWCERT(ps_global->smime->privatecertlist) = 0;
953 } else {
954 X509_LOOKUP *lookup = NULL;
955 X509_STORE *store = NULL;
957 if((store = X509_STORE_new()) != NULL){
958 if((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL){
959 X509_STORE_free(store);
960 store = NULL;
961 } else{
962 free_certlist(data);
963 if(SMHOLDERTYPE(ctype) == Directory)
964 add_certs_in_dir(lookup, PATHCERTDIR(ctype), EXTCERT(ctype), data);
965 else /* if(SMHOLDERTYPE(ctype) == Container) */
966 *data = mem_to_certlist(CONTENTCERTLIST(ctype), ctype);
967 if(data && *data){
968 resort_certificates(data, ctype);
969 RENEWCERT(*data) = 0;
971 if(ctype == Public)
972 ps_global->smime->publiccertlist = *data;
973 else
974 ps_global->smime->cacertlist = *data;
978 setup_certs_backup_by_type(ctype);
981 void
982 smime_reinit(void)
984 smime_deinit();
985 smime_init();
988 /* Installed as an atexit() handler to save the random data */
989 void
990 smime_deinit(void)
992 dprint((9, "smime_deinit()"));
993 app_RAND_write_file(NULL);
994 free_smime_struct(&ps_global->smime);
997 /* we renew the store when it has changed */
998 void renew_store(void)
1000 if(ps_global->smime->inited){
1001 if(s_cert_store != NULL)
1002 X509_STORE_free(s_cert_store);
1003 s_cert_store = get_ca_store();
1007 /* Initialise openssl stuff if needed */
1008 void
1009 smime_init(void)
1011 if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
1013 dprint((9, "smime_init()"));
1014 if(!ps_global->smime)
1015 ps_global->smime = new_smime_struct();
1017 setup_storage_locations();
1019 s_cert_store = get_ca_store();
1020 setup_certs_backup_by_type(CACert);
1022 OpenSSL_add_all_algorithms();
1023 ERR_load_crypto_strings();
1025 app_RAND_load_file(NULL);
1026 openssl_extra_randomness();
1027 ps_global->smime->inited = 1;
1030 ERR_clear_error();
1034 /* validate a certificate. Return value : 0 for no error, -1 for error.
1035 * In the latter case, set the openssl smime error in *error.
1037 int smime_validate_cert(X509 *cert, long *error)
1039 X509_STORE_CTX *csc;
1041 ERR_clear_error();
1042 *error = 0;
1043 if((csc = X509_STORE_CTX_new()) != NULL){
1044 X509_STORE_set_flags(s_cert_store, 0);
1045 if(X509_STORE_CTX_init(csc,s_cert_store,cert,NULL)
1046 && X509_verify_cert(csc) <= 0)
1047 *error = X509_STORE_CTX_get_error(csc);
1048 X509_STORE_CTX_free(csc);
1050 return *error ? -1 : 0;
1053 PERSONAL_CERT *
1054 get_personal_certs(char *path)
1056 PERSONAL_CERT *result = NULL;
1057 char buf2[MAXPATH];
1058 struct dirent *d;
1059 DIR *dirp;
1061 ps_global->smime->privatepath = cpystr(path);
1062 dirp = opendir(path);
1063 if(dirp){
1064 while((d=readdir(dirp)) != NULL){
1065 X509 *cert;
1066 size_t ll;
1068 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, ".key")){
1070 /* copy file name to temp buffer */
1071 strncpy(buf2, d->d_name, sizeof(buf2)-1);
1072 buf2[sizeof(buf2)-1] = '\0';
1073 /* chop off ".key" trailier */
1074 buf2[strlen(buf2)-4] = 0;
1075 /* Look for certificate */
1076 cert = get_cert_for(buf2, Public);
1078 if(cert){
1079 PERSONAL_CERT *pc;
1081 /* create a new PERSONAL_CERT, fill it in */
1083 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1084 pc->cert = cert;
1085 pc->name = cpystr(buf2);
1087 /* Try to load the key with an empty password */
1088 pc->key = load_key(pc, "", SM_NORMALCERT);
1090 pc->next = result;
1091 result = pc;
1095 closedir(dirp);
1097 return result;
1101 void
1102 setup_privatekey_storage(void)
1104 char path[MAXPATH+1], *contents;
1105 int privatekeycontainer = 0;
1107 /* private keys in a container */
1108 if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
1110 privatekeycontainer = 1;
1111 contents = NULL;
1112 path[0] = '\0';
1113 if(!smime_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
1114 privatekeycontainer = 0;
1116 if(privatekeycontainer && !IS_REMOTE(path)
1117 && ps_global->VAR_OPER_DIR
1118 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1119 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1120 /* TRANSLATORS: First arg is the directory name, second is
1121 the file user wants to read but can't. */
1122 _("Can't read file outside %s: %s"),
1123 ps_global->VAR_OPER_DIR, path);
1124 privatekeycontainer = 0;
1127 if(privatekeycontainer
1128 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1129 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1131 !(contents = read_file(path, READ_FROM_LOCALE)))
1132 privatekeycontainer = 0;
1135 if(privatekeycontainer && path[0]){
1136 ps_global->smime->privatetype = Container;
1137 ps_global->smime->privatepath = cpystr(path);
1139 if(contents){
1140 ps_global->smime->privatecontent = contents;
1141 ps_global->smime->personal_certs = mem_to_personal_certs(contents);
1146 /* private keys in a directory of files */
1147 if(!privatekeycontainer){
1148 ps_global->smime->privatetype = Directory;
1150 path[0] = '\0';
1151 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1152 && !IS_REMOTE(path)))
1153 ps_global->smime->privatetype = Nada;
1154 else if(can_access(path, ACCESS_EXISTS)){
1155 if(our_mkpath(path, 0700)){
1156 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1157 ps_global->smime->privatetype = Nada;
1161 if(ps_global->smime->privatetype == Directory)
1162 ps_global->smime->personal_certs = get_personal_certs(path);
1164 setup_certs_backup_by_type(Private);
1169 static void
1170 setup_storage_locations(void)
1172 int publiccertcontainer = 0, cacertcontainer = 0;
1173 char path[MAXPATH+1], *contents;
1175 if(!ps_global->smime)
1176 return;
1178 #ifdef APPLEKEYCHAIN
1179 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1180 ps_global->smime->publictype = Keychain;
1182 else{
1183 #endif /* APPLEKEYCHAIN */
1184 /* Public certificates in a container */
1185 if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
1187 publiccertcontainer = 1;
1188 contents = NULL;
1189 path[0] = '\0';
1190 if(!smime_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
1191 publiccertcontainer = 0;
1193 if(publiccertcontainer && !IS_REMOTE(path)
1194 && ps_global->VAR_OPER_DIR
1195 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1196 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1197 /* TRANSLATORS: First arg is the directory name, second is
1198 the file user wants to read but can't. */
1199 _("Can't read file outside %s: %s"),
1200 ps_global->VAR_OPER_DIR, path);
1201 publiccertcontainer = 0;
1204 if(publiccertcontainer
1205 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1206 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1208 !(contents = read_file(path, READ_FROM_LOCALE)))
1209 publiccertcontainer = 0;
1212 if(publiccertcontainer && path[0]){
1213 ps_global->smime->publictype = Container;
1214 ps_global->smime->publicpath = cpystr(path);
1216 if(contents){
1217 ps_global->smime->publiccontent = contents;
1218 ps_global->smime->publiccertlist = mem_to_certlist(contents, Public);
1223 /* Public certificates in a directory of files */
1224 if(!publiccertcontainer){
1225 ps_global->smime->publictype = Directory;
1227 path[0] = '\0';
1228 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1229 && !IS_REMOTE(path)))
1230 ps_global->smime->publictype = Nada;
1231 else if(can_access(path, ACCESS_EXISTS)){
1232 if(our_mkpath(path, 0700)){
1233 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1234 ps_global->smime->publictype = Nada;
1238 if(ps_global->smime->publictype == Directory)
1239 ps_global->smime->publicpath = cpystr(path);
1242 #ifdef APPLEKEYCHAIN
1244 #endif /* APPLEKEYCHAIN */
1246 setup_privatekey_storage();
1248 /* extra cacerts in a container */
1249 if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
1251 cacertcontainer = 1;
1252 contents = NULL;
1253 path[0] = '\0';
1254 if(!smime_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
1255 cacertcontainer = 0;
1257 if(cacertcontainer && !IS_REMOTE(path)
1258 && ps_global->VAR_OPER_DIR
1259 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1260 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1261 /* TRANSLATORS: First arg is the directory name, second is
1262 the file user wants to read but can't. */
1263 _("Can't read file outside %s: %s"),
1264 ps_global->VAR_OPER_DIR, path);
1265 cacertcontainer = 0;
1268 if(cacertcontainer
1269 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1270 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1272 !(contents = read_file(path, READ_FROM_LOCALE)))
1273 cacertcontainer = 0;
1276 if(cacertcontainer && path[0]){
1277 ps_global->smime->catype = Container;
1278 ps_global->smime->capath = cpystr(path);
1279 ps_global->smime->cacontent = contents;
1280 if(contents)
1281 ps_global->smime->cacertlist = mem_to_certlist(contents, CACert);
1285 if(!cacertcontainer){
1286 ps_global->smime->catype = Directory;
1288 path[0] = '\0';
1289 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1290 && !IS_REMOTE(path)))
1291 ps_global->smime->catype = Nada;
1292 else if(can_access(path, ACCESS_EXISTS)){
1293 if(our_mkpath(path, 0700)){
1294 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1295 ps_global->smime->catype = Nada;
1299 if(ps_global->smime->catype == Directory)
1300 ps_global->smime->capath = cpystr(path);
1306 copy_publiccert_dir_to_container(void)
1308 return(copy_dir_to_container(Public, NULL));
1313 copy_publiccert_container_to_dir(void)
1315 return(copy_container_to_dir(Public));
1320 copy_privatecert_dir_to_container(void)
1322 return(copy_dir_to_container(Private, NULL));
1327 copy_privatecert_container_to_dir(void)
1329 return(copy_container_to_dir(Private));
1334 copy_cacert_dir_to_container(void)
1336 return(copy_dir_to_container(CACert, NULL));
1341 copy_cacert_container_to_dir(void)
1343 return(copy_container_to_dir(CACert));
1346 /* Add the contents of a file to a container. Do not check the content
1347 * of the file, just add it using the format for that container. The
1348 * caller must check the format, so that there is no data corruption
1349 * in the future.
1350 * return value: 0 - success,
1351 * != 0 - failure.
1354 add_file_to_container(WhichCerts ctype, char *fpath, char *altname)
1356 char *sep = (ctype == Public || ctype == Private)
1357 ? EMAILADDRLEADER : CACERTSTORELEADER;
1358 char *content = ctype == Public ? ps_global->smime->publiccontent
1359 : (ctype == Private ? ps_global->smime->privatecontent
1360 : ps_global->smime->cacontent);
1361 char *name;
1362 char *s;
1363 unsigned char c;
1364 struct stat sbuf;
1365 STORE_S *in = NULL;
1366 int rv = -1; /* assume error */
1368 if(our_stat(fpath, &sbuf) < 0
1369 || (in = so_get(FileStar, fpath, READ_ACCESS | READ_FROM_LOCALE)) == NULL)
1370 goto endadd;
1372 if(altname != NULL)
1373 name = altname;
1374 else if((name = strrchr(fpath, '/')) != NULL){
1375 size_t ll;
1376 if((ll = strlen(++name)) > 4 && strucmp(name + ll - 4, EXTCERT(ctype)) == 0)
1377 name[ll-strlen(EXTCERT(ctype))] = '\0';
1379 else
1380 goto endadd;
1382 if(content){
1383 fs_resize((void **)&content, strlen(content) + strlen(sep) + strlen(name) + sbuf.st_size + 3); /* 2 = \n + \n + \0*/
1384 s = content;
1385 content += strlen(content);
1387 else{
1388 s = content = fs_get(strlen(sep) + strlen(name) + sbuf.st_size + 1); /* 2 = \n + \0 */
1389 *content = '\0';
1391 strncat(content, sep, strlen(sep));
1392 strncat(content, name, strlen(name));
1393 content += strlen(content);
1394 *content++ = '\n';
1396 while(so_readc(&c, in))
1397 *content++ = (char) c;
1398 *content = '\0';
1400 switch(ctype){
1401 case Private: ps_global->smime->privatecontent = s; break;
1402 case Public : ps_global->smime->publiccontent = s; break;
1403 case CACert : ps_global->smime->cacontent = s; break;
1404 default : break;
1407 rv = copy_dir_to_container(ctype, s);
1409 endadd:
1410 if(in) so_give(&in);
1412 return rv;
1417 * returns 0 on success, -1 on failure
1418 * contents is an argument which tells this function to write the value
1419 * of this variable instead of reading the contents of the directory.
1420 * If the var contents is not null use its value as the value of the
1421 * container.
1424 copy_dir_to_container(WhichCerts which, char *contents)
1426 int ret = 0, container = 0;
1427 BIO *bio_out = NULL, *bio_in = NULL;
1428 char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
1429 char *tempfile = NULL, fpath[MAXPATH+1];
1430 DIR *dirp;
1431 struct dirent *d;
1432 REMDATA_S *rd = NULL;
1433 char *configdir = NULL;
1434 char *configpath = NULL;
1435 char *configcontainer = NULL;
1436 char *filesuffix = NULL;
1437 char *ret_dir = NULL;
1439 dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1440 smime_init();
1442 srcpath[0] = '\0';
1443 dstpath[0] = '\0';
1444 file[0] = '\0';
1445 emailaddr[0] = '\0';
1447 if(which == Public){
1448 configdir = ps_global->VAR_PUBLICCERT_DIR;
1449 configpath = ps_global->smime->publicpath;
1450 configcontainer = cpystr(DF_PUBLIC_CONTAINER);
1451 filesuffix = ".crt";
1453 else if(which == Private){
1454 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1455 configpath = ps_global->smime->privatepath;
1456 configcontainer = cpystr(DF_PRIVATE_CONTAINER);
1457 filesuffix = ".key";
1459 else if(which == CACert){
1460 configdir = ps_global->VAR_CACERT_DIR;
1461 configpath = ps_global->smime->capath;
1462 configcontainer = cpystr(DF_CA_CONTAINER);
1463 filesuffix = ".crt";
1465 container = SMHOLDERTYPE(which) == Container;
1467 if(!(configdir && configdir[0])){
1468 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1469 return -1;
1472 if(!(configpath && configpath[0])){
1473 #ifdef APPLEKEYCHAIN
1474 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1475 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1476 return -1;
1478 #endif /* APPLEKEYCHAIN */
1479 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1480 return -1;
1483 if(!(filesuffix && strlen(filesuffix) == 4)){
1484 return -1;
1489 * If there is a legit directory to read from set up the
1490 * container file to write to.
1492 if(smime_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
1494 if(IS_REMOTE(configpath)){
1495 rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
1496 NULL, "Error: ",
1497 _("Can't access remote smime configuration."));
1498 if(!rd)
1499 return -1;
1501 (void) rd_read_metadata(rd);
1503 if(rd->access == MaybeRorW){
1504 if(rd->read_status == 'R')
1505 rd->access = ReadOnly;
1506 else
1507 rd->access = ReadWrite;
1510 if(rd->access != NoExists){
1512 rd_check_remvalid(rd, 1L);
1515 * If the cached info says it is readonly but
1516 * it looks like it's been fixed now, change it to readwrite.
1518 if(rd->read_status == 'R'){
1519 rd_check_readonly_access(rd);
1520 if(rd->read_status == 'W'){
1521 rd->access = ReadWrite;
1522 rd->flags |= REM_OUTOFDATE;
1524 else
1525 rd->access = ReadOnly;
1529 if(rd->flags & REM_OUTOFDATE){
1530 if(rd_update_local(rd) != 0){
1532 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1533 rd_close_remdata(&rd);
1534 return -1;
1537 else
1538 rd_open_remote(rd);
1540 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1541 rd_close_remdata(&rd);
1542 return -1;
1545 rd->flags |= DO_REMTRIM;
1547 strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
1548 dstpath[sizeof(dstpath)-1] = '\0';
1550 else{
1551 strncpy(dstpath, configpath, sizeof(dstpath)-1);
1552 dstpath[sizeof(dstpath)-1] = '\0';
1556 * dstpath is either the local Container file or the local cache file
1557 * for the remote Container file.
1559 tempfile = tempfile_in_same_dir(dstpath, "az", &ret_dir);
1563 * If there is a legit directory to read from and a tempfile
1564 * to write to we continue.
1566 if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
1568 if(contents != NULL){
1569 if(BIO_puts(bio_out, contents) < 0)
1570 ret = -1;
1572 else {
1573 if((dirp = opendir(srcpath)) != NULL){
1575 while((d=readdir(dirp)) && !ret){
1576 size_t ll;
1578 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, filesuffix)){
1580 /* copy file name to temp buffer */
1581 strncpy(emailaddr, d->d_name, sizeof(emailaddr)-1);
1582 emailaddr[sizeof(emailaddr)-1] = '\0';
1583 /* chop off suffix trailier */
1584 emailaddr[strlen(emailaddr)-4] = 0;
1587 * This is the separator between the contents of
1588 * different files.
1590 if(which == CACert){
1591 if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
1592 && (BIO_puts(bio_out, emailaddr) > 0)
1593 && (BIO_puts(bio_out, "\n") > 0)))
1594 ret = -1;
1596 else{
1597 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1598 && (BIO_puts(bio_out, emailaddr) > 0)
1599 && (BIO_puts(bio_out, "\n") > 0)))
1600 ret = -1;
1603 /* read then write contents of file */
1604 build_path(file, srcpath, d->d_name, sizeof(file));
1605 if(!(bio_in = BIO_new_file(file, "r")))
1606 ret = -1;
1608 if(!ret){
1609 int good_stuff = 0;
1611 while(BIO_gets(bio_in, line, sizeof(line)) > 0){
1612 if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
1613 good_stuff = 1;
1615 if(good_stuff)
1616 BIO_puts(bio_out, line);
1618 if(strncmp("-----END", line, strlen("-----END")) == 0)
1619 good_stuff = 0;
1623 BIO_free(bio_in);
1627 closedir(dirp);
1631 BIO_free(bio_out);
1633 if(!ret){
1634 if(container && configpath && *configpath){
1635 strncpy(fpath, configpath, sizeof(fpath));
1636 fpath[sizeof(fpath) - 1] = '\0';
1638 else if(ret_dir){
1639 if(strlen(dstpath) + strlen(configcontainer) - strlen(ret_dir) + 1 < sizeof(dstpath))
1640 snprintf(fpath, sizeof(fpath), "%s%c%s",
1641 dstpath, tempfile[strlen(ret_dir)], configcontainer);
1642 else
1643 ret = -1;
1645 else ret = -1;
1647 if(!ret){
1648 if(rename_file(tempfile, fpath) < 0){
1649 q_status_message2(SM_ORDER, 3, 3,
1650 _("Can't rename %s to %s"), tempfile, fpath);
1651 ret = -1;
1652 } else q_status_message1(SM_ORDER, 3, 3,
1653 _("saved container to %s"), fpath);
1656 /* if the container is remote, copy it */
1657 if(!ret && IS_REMOTE(configpath)){
1658 int e;
1659 char datebuf[200];
1661 datebuf[0] = '\0';
1663 if((e = rd_update_remote(rd, datebuf)) != 0){
1664 if(e == -1){
1665 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1666 _("Error opening temporary smime file %s: %s"),
1667 rd->lf, error_description(errno));
1668 dprint((1,
1669 "write_remote_smime: error opening temp file %s\n",
1670 rd->lf ? rd->lf : "?"));
1672 else{
1673 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1674 _("Error copying to %s: %s"),
1675 rd->rn, error_description(errno));
1676 dprint((1,
1677 "write_remote_smime: error copying from %s to %s\n",
1678 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1681 q_status_message(SM_ORDER | SM_DING, 5, 5,
1682 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1684 else{
1685 rd_update_metadata(rd, datebuf);
1686 rd->read_status = 'W';
1689 rd_close_remdata(&rd);
1694 if(tempfile)
1695 fs_give((void **) &tempfile);
1697 if(ret_dir)
1698 fs_give((void **) &ret_dir);
1700 if(configcontainer)
1701 fs_give((void **) &configcontainer);
1703 return ret;
1708 * returns 0 on success, -1 on failure
1711 copy_container_to_dir(WhichCerts which)
1713 char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
1714 char iobuf[4096];
1715 char *contents = NULL;
1716 char *leader = NULL;
1717 char *filesuffix = NULL;
1718 char *configdir = NULL;
1719 char *configpath = NULL;
1720 char *tempfile = NULL;
1721 char *p, *q, *line, *name, *certtext, *save_p;
1722 int len;
1723 BIO *in, *out;
1725 dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1726 smime_init();
1728 path[0] = '\0';
1730 if(which == Public){
1731 leader = EMAILADDRLEADER;
1732 contents = ps_global->smime->publiccontent;
1733 configdir = ps_global->VAR_PUBLICCERT_DIR;
1734 configpath = ps_global->smime->publicpath;
1735 filesuffix = ".crt";
1736 if(!(configpath && configpath[0])){
1737 #ifdef APPLEKEYCHAIN
1738 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1739 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1740 return -1;
1742 #endif /* APPLEKEYCHAIN */
1743 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1744 return -1;
1747 fs_give((void **) &ps_global->smime->publicpath);
1749 path[0] = '\0';
1750 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1751 && !IS_REMOTE(path))){
1752 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1753 return -1;
1756 if(can_access(path, ACCESS_EXISTS)){
1757 if(our_mkpath(path, 0700)){
1758 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1759 return -1;
1763 ps_global->smime->publicpath = cpystr(path);
1764 configpath = ps_global->smime->publicpath;
1766 else if(which == Private){
1767 leader = EMAILADDRLEADER;
1768 contents = ps_global->smime->privatecontent;
1769 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1770 configpath = ps_global->smime->privatepath;
1771 filesuffix = ".key";
1772 if(!(configpath && configpath[0])){
1773 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1774 return -1;
1777 fs_give((void **) &ps_global->smime->privatepath);
1779 path[0] = '\0';
1780 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1781 && !IS_REMOTE(path))){
1782 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1783 return -1;
1786 if(can_access(path, ACCESS_EXISTS)){
1787 if(our_mkpath(path, 0700)){
1788 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1789 return -1;
1793 ps_global->smime->privatepath = cpystr(path);
1794 configpath = ps_global->smime->privatepath;
1796 else if(which == CACert){
1797 leader = CACERTSTORELEADER;
1798 contents = ps_global->smime->cacontent;
1799 configdir = ps_global->VAR_CACERT_DIR;
1800 configpath = ps_global->smime->capath;
1801 filesuffix = ".crt";
1802 if(!(configpath && configpath[0])){
1803 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1804 return -1;
1807 fs_give((void **) &ps_global->smime->capath);
1809 path[0] = '\0';
1810 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1811 && !IS_REMOTE(path))){
1812 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1813 return -1;
1816 if(can_access(path, ACCESS_EXISTS)){
1817 if(our_mkpath(path, 0700)){
1818 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1819 return -1;
1823 ps_global->smime->capath = cpystr(path);
1824 configpath = ps_global->smime->capath;
1827 if(!(configdir && configdir[0])){
1828 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1829 return -1;
1832 if(!(configpath && configpath[0])){
1833 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1834 return -1;
1837 if(!(filesuffix && strlen(filesuffix) == 4)){
1838 return -1;
1842 if(contents && *contents){
1843 for(p = contents; *p != '\0';){
1844 line = p;
1846 while(*p && *p != '\n')
1847 p++;
1849 save_p = NULL;
1850 if(*p == '\n'){
1851 save_p = p;
1852 *p++ = '\0';
1855 if(strncmp(leader, line, strlen(leader)) == 0){
1856 name = line + strlen(leader);
1857 certtext = p;
1858 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
1859 if((q = strstr(certtext, leader)) != NULL){
1860 p = q;
1862 else{ /* end of file */
1863 q = certtext + strlen(certtext);
1864 p = q;
1867 strncpy(buf, name, sizeof(buf)-5);
1868 buf[sizeof(buf)-5] = '\0';
1869 strncat(buf, filesuffix, 5);
1870 build_path(file, configpath, buf, sizeof(file));
1872 in = BIO_new_mem_buf(certtext, q-certtext);
1873 if(in){
1874 tempfile = tempfile_in_same_dir(file, "az", NULL);
1875 out = NULL;
1876 if(tempfile)
1877 out = BIO_new_file(tempfile, "w");
1879 if(out){
1880 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1881 BIO_write(out, iobuf, len);
1883 BIO_free(out);
1885 if(rename_file(tempfile, file) < 0){
1886 q_status_message2(SM_ORDER, 3, 3,
1887 _("Can't rename %s to %s"),
1888 tempfile, file);
1889 return -1;
1892 fs_give((void **) &tempfile);
1895 BIO_free(in);
1900 if(save_p)
1901 *save_p = '\n';
1905 return 0;
1909 #ifdef APPLEKEYCHAIN
1912 copy_publiccert_container_to_keychain(void)
1914 /* NOT IMPLEMNTED */
1915 return -1;
1919 copy_publiccert_keychain_to_container(void)
1921 /* NOT IMPLEMNTED */
1922 return -1;
1925 #endif /* APPLEKEYCHAIN */
1929 * Get a pointer to a string describing the most recent OpenSSL error.
1930 * It's statically allocated, so don't change or attempt to free it.
1932 static const char *
1933 openssl_error_string(void)
1935 char *errs;
1936 const char *data = NULL;
1937 long errn;
1939 errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
1940 errs = (char*) ERR_reason_error_string(errn);
1942 if(errs)
1943 return errs;
1944 else if(data)
1945 return data;
1947 return "unknown error";
1951 /* Return true if the body looks like a PKCS7 object */
1953 is_pkcs7_body(BODY *body)
1955 int result;
1957 result = body->type==TYPEAPPLICATION &&
1958 body->subtype &&
1959 (strucmp(body->subtype,"pkcs7-mime")==0 ||
1960 strucmp(body->subtype,"x-pkcs7-mime")==0 ||
1961 strucmp(body->subtype,"pkcs7-signature")==0 ||
1962 strucmp(body->subtype,"x-pkcs7-signature")==0);
1964 return result;
1969 * Recursively stash a pointer to the decrypted data in our
1970 * manufactured body.
1971 * parameters: type: call of type 1, save the base and header for multipart messages
1972 call of type 0, do not save the base and header for multipart messages
1974 static void
1975 create_local_cache(char *h, char *base, BODY *b, int type)
1977 if(b->type==TYPEMULTIPART){
1978 PART *p;
1980 if(type == 1){
1981 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
1982 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
1983 } else if(type == 0){
1985 * We don't really want to copy the real body contents. It shouldn't be
1986 * used, and in the case of a message with attachments, we'll be
1987 * duplicating the files multiple times.
1989 cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
1991 for(p=b->nested.part; p; p=p->next)
1992 create_local_cache(h, base, (BODY *) p, type);
1995 else{
1996 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
1997 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2002 static long
2003 rfc822_output_func(void *b, char *string)
2005 BIO *bio = (BIO *) b;
2007 return(string ? *string ? (BIO_puts(bio, string) > 0 ? 1L : 0L)
2008 : (BIO_puts(bio, string) >= 0 ? 1L : 0L)
2009 : 0L);
2014 * Attempt to load the private key for the given PERSONAL_CERT.
2015 * This sets the appropriate passphrase globals in order to
2016 * interact with the user correctly.
2018 static int
2019 load_private_key(PERSONAL_CERT *pcert)
2021 if(!pcert->key){
2023 /* Try empty password by default */
2024 char *password = "";
2026 if(ps_global->smime
2027 && (ps_global->smime->need_passphrase
2028 || ps_global->smime->entered_passphrase)){
2029 /* We've already been in here and discovered we need a different password */
2031 if(ps_global->smime->entered_passphrase)
2032 password = (char *) ps_global->smime->passphrase; /* already entered */
2033 else
2034 return 0;
2037 ERR_clear_error();
2039 if(!(pcert->key = load_key(pcert, password, SM_NORMALCERT))){
2040 long err = ERR_get_error();
2042 /* Couldn't load key... */
2044 if(ps_global->smime && ps_global->smime->entered_passphrase){
2046 /* The user got the password wrong maybe? */
2048 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
2049 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
2050 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
2051 else
2052 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2054 /* This passphrase is no good; forget it */
2055 ps_global->smime->entered_passphrase = 0;
2058 if(ps_global->smime){
2059 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2060 ps_global->smime->need_passphrase = 1;
2061 if(ps_global->smime->passphrase_emailaddr){
2062 int i;
2063 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
2064 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
2065 fs_give((void **) ps_global->smime->passphrase_emailaddr);
2068 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
2071 return 0;
2073 else{
2074 /* This key will be cached, so we won't be called again */
2075 if(ps_global->smime){
2076 ps_global->smime->entered_passphrase = 0;
2077 ps_global->smime->need_passphrase = 0;
2081 return 1;
2084 return 0;
2088 static void
2089 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename, char *smime_type)
2091 b->type = TYPEAPPLICATION;
2092 b->subtype = cpystr(type);
2093 b->encoding = ENCBINARY;
2094 b->description = cpystr(description);
2096 b->disposition.type = cpystr("attachment");
2097 set_parameter(&b->disposition.parameter, "filename", filename);
2099 set_parameter(&b->parameter, "name", filename);
2100 if(smime_type && *smime_type)
2101 set_parameter(&b->parameter, "smime-type", smime_type);
2106 * Look for a personal certificate matching the
2107 * given address
2109 PERSONAL_CERT *
2110 match_personal_cert_to_email(ADDRESS *a)
2112 PERSONAL_CERT *pcert = NULL;
2113 char buf[MAXPATH];
2114 char **email;
2115 int i, done;
2117 if(!a || !a->mailbox || !a->host)
2118 return NULL;
2120 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2122 if(ps_global->smime){
2123 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
2124 pcert;
2125 pcert=pcert->next){
2127 if(!pcert->cert)
2128 continue;
2130 email = get_x509_subject_email(pcert->cert);
2132 done = 0;
2133 if(email != NULL){
2134 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
2135 if(email[i] != NULL) done++;
2136 for(i = 0; email[i] != NULL; i++)
2137 fs_give((void **)&email[i]);
2138 fs_give((void **)email);
2141 if(done > 0)
2142 break;
2146 return pcert;
2151 * Look for a personal certificate matching the from
2152 * (or reply_to? in the given envelope)
2154 PERSONAL_CERT *
2155 match_personal_cert(ENVELOPE *env)
2157 PERSONAL_CERT *pcert;
2159 pcert = match_personal_cert_to_email(env->reply_to);
2160 if(!pcert)
2161 pcert = match_personal_cert_to_email(env->from);
2163 return pcert;
2168 * Flatten the given body into its MIME representation.
2169 * Return the result in a BIO.
2171 static BIO *
2172 body_to_bio(BODY *body)
2174 BIO *bio = NULL;
2175 int len;
2177 bio = BIO_new(BIO_s_mem());
2178 if(!bio)
2179 return NULL;
2181 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
2182 pine_write_body_header(body, rfc822_output_func, bio);
2183 pine_rfc822_output_body(body, rfc822_output_func, bio);
2186 * Now need to truncate by two characters since the above
2187 * appends CRLF.
2189 if((len=BIO_ctrl_pending(bio)) > 1){
2190 BUF_MEM *biobuf = NULL;
2192 BIO_get_mem_ptr(bio, &biobuf);
2193 if(biobuf){
2194 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
2198 return bio;
2202 static BIO *
2203 bio_from_store(STORE_S *store)
2205 BIO *ret = NULL;
2207 if(store && store->src == BioType && store->txt){
2208 ret = (BIO *) store->txt;
2211 return(ret);
2215 * Encrypt file; given a path (char *) fp, replace the file
2216 * by an encrypted version of it. If (char *) text is not null, then
2217 * replace the text of (char *) fp by the encrypted version of (char *) text.
2218 * certpath is the FULL path to the file containing the certificate used for
2219 * encryption.
2222 encrypt_file(char *fp, char *text, PERSONAL_CERT *pc)
2224 const EVP_CIPHER *cipher = NULL;
2225 STACK_OF(X509) *encerts = NULL;
2226 BIO *out = NULL;
2227 PKCS7 *p7 = NULL;
2228 int rv = 0;
2230 if(pc == NULL)
2231 return 0;
2233 cipher = EVP_aes_256_cbc();
2234 encerts = sk_X509_new_null();
2236 sk_X509_push(encerts, X509_dup(pc->cert));
2238 if(text){
2239 if((out = BIO_new(BIO_s_mem())) == NULL)
2240 goto end;
2241 (void) BIO_reset(out);
2242 BIO_puts(out, text);
2244 else{
2245 if(!(out = BIO_new_file(fp, "rb")))
2246 goto end;
2248 BIO_read_filename(out, fp);
2251 if((p7 = PKCS7_encrypt(encerts, out, cipher, 0)) == NULL)
2252 goto end;
2253 BIO_set_close(out, BIO_CLOSE);
2254 BIO_free(out);
2255 if(!(out = BIO_new_file(fp, "w")))
2256 goto end;
2257 BIO_reset(out);
2258 rv = PEM_write_bio_PKCS7(out, p7);
2259 BIO_flush(out);
2261 end:
2262 if(out != NULL)
2263 BIO_free(out);
2264 PKCS7_free(p7);
2265 sk_X509_pop_free(encerts, X509_free);
2267 return rv;
2271 * Encrypt a message on the way out. Called from call_mailer in send.c
2272 * The body may be reallocated.
2275 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
2277 PKCS7 *p7 = NULL;
2278 BIO *in = NULL;
2279 BIO *out = NULL;
2280 const EVP_CIPHER *cipher = NULL;
2281 STACK_OF(X509) *encerts = NULL;
2282 STORE_S *outs = NULL;
2283 PINEFIELD *pf;
2284 ADDRESS *a;
2285 BODY *body = *bodyP;
2286 BODY *newBody = NULL;
2287 int result = 0;
2288 X509 *cert;
2289 char buf[MAXPATH];
2291 dprint((9, "encrypt_outgoing_message()"));
2292 smime_init();
2294 cipher = EVP_aes_256_cbc();
2296 encerts = sk_X509_new_null();
2298 /* Look for a certificate for each of the recipients */
2299 for(pf = header->local; pf && pf->name; pf = pf->next)
2300 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
2301 for(a=*pf->addr; a; a=a->next){
2302 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2304 if((cert = get_cert_for(buf, Public)) != NULL){
2305 sk_X509_push(encerts,cert);
2306 }else{
2307 q_status_message2(SM_ORDER, 1, 1,
2308 _("Unable to find certificate for <%s@%s>"),
2309 a->mailbox, a->host);
2310 goto end;
2315 /* add the sender's certificate so that they can decrypt the message too */
2316 for(a=header->env->from; a ; a = a->next){
2317 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2319 if((cert = get_cert_for(buf, Public)) != NULL
2320 && sk_X509_find(encerts, cert) == -1)
2321 sk_X509_push(encerts,cert);
2324 in = body_to_bio(body);
2326 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
2328 outs = so_get(BioType, NULL, EDIT_ACCESS);
2329 out = bio_from_store(outs);
2331 i2d_PKCS7_bio(out, p7);
2332 (void) BIO_flush(out);
2334 so_seek(outs, 0, SEEK_SET);
2336 newBody = mail_newbody();
2338 newBody->type = TYPEAPPLICATION;
2339 newBody->subtype = cpystr("pkcs7-mime");
2340 newBody->encoding = ENCBINARY;
2342 newBody->disposition.type = cpystr("attachment");
2343 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
2345 newBody->description = cpystr("S/MIME Encrypted Message");
2346 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
2347 set_parameter(&newBody->parameter, "name", "smime.p7m");
2349 newBody->contents.text.data = (unsigned char *) outs;
2351 *bodyP = newBody;
2353 result = 1;
2355 end:
2357 BIO_free(in);
2358 PKCS7_free(p7);
2359 sk_X509_pop_free(encerts, X509_free);
2361 dprint((9, "encrypt_outgoing_message returns %d", result));
2362 return result;
2367 Get (and decode) the body of the given section of msg
2369 static STORE_S*
2370 get_part_contents(long msgno, const char *section)
2372 long len;
2373 gf_io_t pc;
2374 STORE_S *store = NULL;
2375 char *err;
2377 store = so_get(CharStar, NULL, EDIT_ACCESS);
2378 if(store){
2379 gf_set_so_writec(&pc,store);
2381 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
2383 gf_clear_so_writec(store);
2385 so_seek(store, 0, SEEK_SET);
2387 if(err)
2388 so_give(&store);
2391 return store;
2395 static PKCS7 *
2396 get_pkcs7_from_part(long msgno,const char *section)
2398 STORE_S *store = NULL;
2399 PKCS7 *p7 = NULL;
2400 BIO *in = NULL;
2402 store = get_part_contents(msgno, section);
2404 if(store){
2405 if(store->src == CharStar){
2406 int len;
2409 * We're reaching inside the STORE_S structure. We should
2410 * probably have a way to get the length, instead.
2412 len = (int) (store->eod - store->dp);
2413 in = BIO_new_mem_buf(store->txt, len);
2415 else{ /* just copy it */
2416 unsigned char c;
2418 in = BIO_new(BIO_s_mem());
2419 (void) BIO_reset(in);
2421 so_seek(store, 0L, 0);
2422 while(so_readc(&c, store)){
2423 BIO_write(in, &c, 1);
2427 if(in){
2428 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2429 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
2430 /* error */
2433 BIO_free(in);
2436 so_give(&store);
2439 return p7;
2442 int same_cert(X509 *x, X509 *cert)
2444 char bufcert[256], bufx[256];
2445 int rv = 0;
2447 get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert), ":");
2448 get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx), ":");
2449 if(strcmp(bufx, bufcert) == 0)
2450 rv = 1;
2452 return rv;
2456 /* extract and save certificates from a PKCS7 package. The ctype variable
2457 * tells us if we want to extract it to a public/ or a ca/ directory. The
2458 * later makes sense only for recoverable errors (errors that can be fixed
2459 * by saving to the ca/ directory before we verify the signature).
2460 * Return value:
2461 * 0 - no errors (in public/) no need to try again,
2462 * or validated self signed certificate (in ca/)
2463 * < 0 - certificate error is not recoverable, don't even think about it.
2466 int smime_extract_and_save_cert(PKCS7 *p7)
2468 STACK_OF(X509) *signers;
2469 X509 *x, *cert;
2470 char **email;
2471 int i, j;
2473 if((signers = PKCS7_get0_signers(p7, NULL, 0)) == NULL)
2474 return -1;
2476 for(i = 0; i < sk_X509_num(signers); i++){
2477 if((x = sk_X509_value(signers,i)) == NULL)
2478 continue;
2480 if((email = get_x509_subject_email(x)) != NULL){
2481 for(j = 0; email[j] != NULL; j++){
2482 if((cert = get_cert_for(email[j], Public)) == NULL
2483 || same_cert(x, cert) == 0)
2484 save_cert_for(email[j], x, Public);
2485 X509_free(cert);
2486 fs_give((void **) &email[i]);
2488 fs_give((void **) email);
2491 sk_X509_free(signers);
2493 return 0;
2497 * Try to verify a signature.
2499 * p7 - the pkcs7 object to verify
2500 * in - the plain data to verify (NULL if not detached)
2501 * out - BIO to which to write the opaque data
2502 * silent - if non zero, do not print errors, only print success.
2504 static int
2505 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
2507 STACK_OF(X509) *otherCerts = NULL;
2508 CertList *cl;
2509 int result;
2510 int flags;
2511 const char *data;
2512 long err;
2514 if(!s_cert_store){
2515 if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
2516 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2518 return -1;
2521 smime_extract_and_save_cert(p7);
2523 flags = F_ON(F_USE_CERT_STORE_ONLY, ps_global) ? PKCS7_NOINTERN : 0;
2525 if(ps_global->smime->publiccertlist == NULL){
2526 renew_cert_data(&ps_global->smime->publiccertlist, Public);
2527 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next){
2528 if(cl->x509_cert == NULL){
2529 char *s = strrchr(cl->name, '.');
2530 *s = '\0';
2531 cl->x509_cert = get_cert_for(cl->name, Public);
2532 *s = '.';
2537 if(ps_global->smime->publiccertlist){
2538 otherCerts = sk_X509_new_null();
2539 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next)
2540 if(cl->x509_cert != NULL)
2541 sk_X509_push(otherCerts, X509_dup(cl->x509_cert));
2544 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, flags);
2546 sk_X509_pop_free(otherCerts, X509_free);
2548 if(result){
2549 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
2551 else{
2552 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2554 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
2556 /* Retry verification so we can get the plain text */
2557 /* Might be better to reimplement PKCS7_verify here? */
2559 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY);
2561 if (!silent) q_status_message1(SM_ORDER | SM_DING, 3, 3,
2562 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
2565 return result;
2569 void
2570 free_smime_body_sparep(void **sparep)
2572 char *s;
2573 SIZEDTEXT *st;
2574 if(sparep && *sparep){
2575 switch(get_smime_sparep_type(*sparep)){
2576 case P7Type: PKCS7_free((PKCS7 *) get_smime_sparep_data(*sparep));
2577 break;
2578 case CharType: s = (char *)get_smime_sparep_data(*sparep);
2579 fs_give((void **) &s);
2580 break;
2581 case SizedText : st = (SIZEDTEXT *)get_smime_sparep_data(*sparep);
2582 fs_give((void **) &st->data);
2583 fs_give((void **) &st);
2584 break;
2585 default : break;
2587 ((SMIME_SPARE_S *)(*sparep))->data = NULL;
2588 fs_give(sparep);
2592 /* Big comment, explaining the mess that exists out there, and how we deal
2593 with it, and also how we solve the problems that are created this way.
2595 When Alpine sends a message, it constructs that message, computes the
2596 signature, but then it forgets the message it signed and reconstructs it
2597 again. Since it signs a message containing a notice about "mime aware
2598 tools", but it does not send that we do not include that in the part
2599 that is signed, and that takes care of much of the problems.
2601 Another problem is what is received from the servers. All servers tested
2602 seem to transmit the message that was signed intact and Alpine can check
2603 the signature correctly. That is not a problem. The problem arises when
2604 the message includes attachments. In this case different servers send
2605 different things, so it will be up to us to figure out what is the text
2606 that was actually signed. Confused? here is the story:
2608 When a message containing and attachment is sent by Alpine, UW-IMAP,
2609 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2610 that was sent by Alpine, but GMX.com, Exchange, and probably other
2611 servers add a trailing \r\n in the message, so when validating the
2612 signature, these messages will not validate. There are several things
2613 that can be done.
2615 1. Add a trailing \r\n to any message that contains attachments, sign that
2616 and send that. In this way, all messages will validate with all
2617 servers.
2619 2. Compatibility mode: If a message has an attachment, contains a trailing
2620 \r\n and does not validate (sent by an earlier version of Alpine),
2621 remove the trailing \r\n and try to revalidate again.
2623 3. We do not add \r\n to validate a message that we sent, because that
2624 would only work in Alpine, and not in any other client. That would
2625 not be a good thing to do.
2627 PART II
2629 Now we have to deal with encrypted and signed messages. The problem is
2630 that c-client makes all its pointers point to "on disk" content, but
2631 since we decrypted the data earlier, we have to make sure of two things.
2632 One is that we saved that data (so we do not have to decrypt it again)
2633 and second that we can use it.
2635 In order to save the data we use create_local_cache, so that we do not
2636 have to redecrypt the message. Once this is saved, c-client functions will
2637 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2639 PART III
2641 When we are trying to verify messages with detached signatures, some
2642 imap servers send incorrect information in the mail_fetch_mime call. By
2643 incorrect I mean that this is not fetched directly from the message, but
2644 it is read from the message, processed, and then the processed part is
2645 sent to us, so this text might not agree with what is in the message,
2646 and so the validation of the signature might fail. However, the good
2647 news is that the message validates if saved to a local folder. This
2648 means that if normal validation does not work we can make it work by
2649 saving the message locally and validating that. This is implemented
2650 below, and causes delay in the display of the message. I am considering
2651 at this time not to do this automatically, but wait for the user to tell
2652 us to do it for them by means of a command available in the
2653 mail_view_screen. This might help in other situations, where a message
2654 is supposed to have an attachment, but it can not be seen in the
2655 processed text. Nevertheless, at this time, this is automatic, and is
2656 causing a delay in the processing of the message, but it is validating
2657 correctly all messages.
2659 PART IV
2661 When the user sends a message as encrypted and signed, this code used to
2662 encrypt first, and then sign the pkcs7 body, but it turns out that some
2663 other clients can not handle these messages. While we could argue that the
2664 other clients need to improve, we will support reading messages in both
2665 ways, and will send messages using this technique; that is, signed first,
2666 encrypted second. It seems that all tested clients support this way, so it
2667 should be safe to do so.
2670 typedef struct smime_filter_s {
2671 void (*filter)();
2672 } SMIME_FILTER_S;
2674 SMIME_FILTER_S sig_filter[] = {
2675 {smime_remove_trailing_crlf},
2676 {smime_remove_folding_space}
2679 #define TOTAL_FILTERS (sizeof(sig_filter)/sizeof(sig_filter[0]))
2680 #define TOTAL_SIGFLTR (1 << TOTAL_FILTERS) /* not good, keep filters to a low number */
2682 void
2683 smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen,
2684 char **bodytext, unsigned long *bodylen)
2686 if(*bodylen > 2 && !strncmp(*bodytext+*bodylen-2, "\r\n", 2))
2687 *bodylen -= 2;
2690 void
2691 smime_remove_folding_space(char **mimetext, unsigned long *mimelen,
2692 char **bodytext, unsigned long *bodylen)
2694 char *s = NULL, *t;
2695 unsigned long mlen = *mimelen;
2697 if(*mimetext){
2698 for (s = t = *mimetext; t - *mimetext < *mimelen; ){
2699 if(*t == '\r' && *(t+1) == '\n' && (*(t+2) == '\t' || *(t+2) == ' ')){
2700 *s++ = ' ';
2701 t += 3;
2702 mlen -= 2;
2704 else
2705 *s++ = *t++;
2707 *mimelen = mlen;
2712 smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag)
2714 int result, i, j, flag;
2715 char *mtext, *btext;
2716 unsigned long mlen, blen;
2717 BIO *in;
2719 mtext = mimelen ? fs_get(mimelen+1) : NULL;
2720 btext = fs_get(bodylen+1);
2722 flag = 1; /* silence all failures */
2723 for(i = 1; result == 0 && i < TOTAL_SIGFLTR; i++){
2724 if((in = BIO_new(BIO_s_mem())) == NULL)
2725 return -1;
2727 (void) BIO_reset(in);
2729 if(i+1 == TOTAL_SIGFLTR)
2730 flag = nflag;
2732 if(mimelen)
2733 strncpy(mtext, mimetext, mlen = mimelen);
2734 strncpy(btext, bodytext, blen = bodylen);
2735 for(j = 0; j < TOTAL_FILTERS; j++)
2736 if((i >> j) & 1)
2737 (sig_filter[j].filter)(&mtext, &mlen, &btext, &blen);
2738 if(mtext != NULL)
2739 BIO_write(in, mtext, mlen);
2740 BIO_write(in, btext, blen);
2741 result = do_signature_verify(p7, in, NULL, flag);
2742 BIO_free(in);
2744 if(mtext) fs_give((void **)&mtext);
2745 if(btext) fs_give((void **)&btext);
2746 return result;
2750 * Given a multipart body of type multipart/signed, attempt to verify it.
2751 * Returns non-zero if the body was changed.
2753 static int
2754 do_detached_signature_verify(BODY *b, long msgno, char *section)
2756 PKCS7 *p7 = NULL;
2757 BIO *in = NULL;
2758 PART *p;
2759 int result, modified_the_body = 0;
2760 int flag; /* 1 silent, 0 not silent */
2761 unsigned long mimelen, bodylen;
2762 char newSec[100], *mimetext, *bodytext;
2763 char *what_we_did;
2764 SIZEDTEXT *st;
2766 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"));
2768 smime_init();
2770 /* if it was signed and then encrypted, use the decrypted text
2771 * to check the validity of the signature
2773 if(b->sparep){
2774 if(get_smime_sparep_type(b->sparep) == SizedText){
2775 /* bodytext includes mimetext */
2776 st = (SIZEDTEXT *) get_smime_sparep_data(b->sparep);
2777 bodytext = (char *) st->data;
2778 bodylen = st->size;
2779 mimetext = NULL;
2780 mimelen = 0L;
2783 else{
2784 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
2785 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
2786 if(mimetext)
2787 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
2789 if(mimetext == NULL || bodytext == NULL)
2790 return modified_the_body;
2793 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
2795 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
2796 || (in = BIO_new(BIO_s_mem())) == NULL)
2797 return modified_the_body;
2799 (void) BIO_reset(in);
2800 if(mimetext != NULL)
2801 BIO_write(in, mimetext, mimelen);
2802 BIO_write(in, bodytext, bodylen);
2804 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
2805 flag = (mimelen == 0 || !IS_REMOTE(ps_global->mail_stream->mailbox))
2806 ? 0 : 1;
2807 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, flag);
2808 if(result < 0)
2809 return modified_the_body;
2810 if(result == 0
2811 && mimelen > 0 /* do not do this for encrypted messages */
2812 && IS_REMOTE(ps_global->mail_stream->mailbox)){
2813 char *fetch;
2814 unsigned long hlen, tlen;
2815 STORE_S *msg_so;
2817 BIO_free(in);
2818 if((in = BIO_new(BIO_s_mem())) != NULL
2819 && (fetch = mail_fetch_header(ps_global->mail_stream, msgno, NULL,
2820 NULL, &hlen, FT_PEEK)) != NULL
2821 && (msg_so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL
2822 && so_nputs(msg_so, fetch, (long) hlen)
2823 && (fetch = pine_mail_fetch_text(ps_global->mail_stream, msgno, NULL,
2824 &tlen, FT_PEEK)) != NULL
2825 && so_nputs(msg_so, fetch, tlen)){
2826 STRING bs;
2827 char *h = (char *) so_text(msg_so);
2828 char *bstart = strstr(h, "\r\n\r\n");
2829 ENVELOPE *env;
2830 BODY *body, *tmpB;
2832 bstart += 4;
2833 INIT(&bs, mail_string, bstart, tlen);
2834 rfc822_parse_msg_full(&env, &body, h, bstart-h-4, &bs, BADHOST, 0, 0);
2835 mail_free_envelope(&env);
2837 mail_free_body_part(&b->nested.part);
2838 tmpB = mail_body_section(body, (unsigned char *) section);
2839 if(MIME_MSG(tmpB->type, tmpB->subtype))
2840 b->nested.part = tmpB->nested.msg->body->nested.part;
2841 else
2842 b->nested.part = tmpB->nested.part;
2843 create_local_cache(bstart, bstart, &b->nested.part->body, 1);
2844 modified_the_body = 1;
2846 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
2848 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
2850 if(mimetext)
2851 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
2853 if (mimetext == NULL || bodytext == NULL)
2854 return modified_the_body;
2856 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
2858 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL)
2859 return modified_the_body;
2861 (void) BIO_reset(in);
2862 BIO_write(in, mimetext, mimelen);
2863 BIO_write(in, bodytext, bodylen);
2864 so_give(&msg_so);
2866 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
2867 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, 0);
2868 if(result < 0)
2869 return modified_the_body;
2875 BIO_free(in);
2876 if(b->subtype)
2877 fs_give((void**) &b->subtype);
2879 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
2880 b->encoding = ENC8BIT;
2882 if(b->description)
2883 fs_give ((void**) &b->description);
2885 what_we_did = result ? _("This message was cryptographically signed.") :
2886 _("This message was cryptographically signed but the signature could not be verified.");
2888 b->description = cpystr(what_we_did);
2890 b->sparep = create_smime_sparep(P7Type, p7);
2892 p = b->nested.part;
2894 /* p is signed plaintext */
2895 if(p && p->next)
2896 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
2898 modified_the_body = 1;
2900 return modified_the_body;
2904 PERSONAL_CERT *
2905 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
2907 PERSONAL_CERT *x = NULL;
2909 if(ps_global->smime){
2910 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
2911 X509 *mine;
2913 mine = x->cert;
2915 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,mine->cert_info->issuer) &&
2916 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,mine->cert_info->serialNumber)){
2917 break;
2922 return x;
2926 static PERSONAL_CERT *
2927 find_certificate_matching_pkcs7(PKCS7 *p7)
2929 int i;
2930 STACK_OF(PKCS7_RECIP_INFO) *recips;
2931 PERSONAL_CERT *x = NULL;
2933 recips = p7->d.enveloped->recipientinfo;
2935 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
2936 PKCS7_RECIP_INFO *ri;
2938 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
2940 if((x=find_certificate_matching_recip_info(ri))!=0){
2941 break;
2945 return x;
2948 /* decrypt an encrypted file.
2949 Args: fp - the path to the encrypted file.
2950 rv - a code that tells the caller what happened inside the function
2951 pcert - a personal certificate that was used to encrypt this file
2952 Returns the decoded text allocated in a char *, whose memory must be
2953 freed by caller
2956 char *
2957 decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc)
2959 PKCS7 *p7 = NULL;
2960 char *text, *tmp;
2961 BIO *in = NULL, *out = NULL;
2962 int i, j;
2963 long unsigned int len;
2964 void *ret;
2966 if(pc == NULL || (text = read_file(fp, 0)) == NULL || *text == '\0')
2967 return NULL;
2969 tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1);
2970 for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0'
2971 && text[i] != '-'; j++, i++)
2972 tmp[j] = text[i];
2973 tmp[j] = '\0';
2975 ret = rfc822_base64((unsigned char *)tmp, strlen(tmp), &len);
2977 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
2978 p7 = d2i_PKCS7_bio(in, NULL);
2979 BIO_free(in);
2982 if (text) fs_give((void **)&text);
2983 if (ret) fs_give((void **)&ret);
2985 if (rv) *rv = pc->key == NULL ? -1 : 1;
2987 out = BIO_new(BIO_s_mem());
2988 (void) BIO_reset(out);
2990 i = PKCS7_decrypt(p7, pc->key, pc->cert, out, 0);
2992 if(i == 0){
2993 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
2994 (char*) openssl_error_string());
2995 goto end;
2998 BIO_get_mem_data(out, &tmp);
3000 text = cpystr(tmp);
3001 BIO_free(out);
3003 end:
3004 PKCS7_free(p7);
3006 return text;
3010 * Try to decode (decrypt or verify a signature) a PKCS7 body
3011 * Returns non-zero if something was changed.
3013 static int
3014 do_decoding(BODY *b, long msgno, const char *section)
3016 int modified_the_body = 0;
3017 BIO *out = NULL;
3018 PKCS7 *p7 = NULL;
3019 X509 *recip = NULL;
3020 EVP_PKEY *key = NULL;
3021 PERSONAL_CERT *pcert = NULL;
3022 char *what_we_did = "";
3023 char null[1];
3025 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"));
3026 null[0] = '\0';
3027 smime_init();
3030 * Extract binary data from part to an in-memory store
3033 if(b->sparep){
3034 if(get_smime_sparep_type(b->sparep) == P7Type)
3035 p7 = (PKCS7*) get_smime_sparep_data(b->sparep);
3037 else{
3038 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
3039 if(!p7){
3040 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
3041 (char*) openssl_error_string());
3042 goto end;
3046 * Save the PKCS7 object for later dealings by the user interface.
3047 * It will be cleaned up when the body is garbage collected.
3049 b->sparep = create_smime_sparep(P7Type, p7);
3052 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
3054 if(PKCS7_type_is_signed(p7)){
3055 int sigok;
3057 out = BIO_new(BIO_s_mem());
3058 (void) BIO_reset(out);
3059 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3061 sigok = do_signature_verify(p7, NULL, out, 0);
3063 what_we_did = sigok ? _("This message was cryptographically signed.") :
3064 _("This message was cryptographically signed but the signature could not be verified.");
3066 /* make sure it's null terminated */
3067 BIO_write(out, null, 1);
3069 else if(!PKCS7_type_is_enveloped(p7)){
3070 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
3071 goto end;
3073 else{ /* It *is* enveloped */
3074 int decrypt_result;
3076 what_we_did = _("This message was encrypted.");
3078 /* now need to find a cert that can decrypt this */
3079 pcert = find_certificate_matching_pkcs7(p7);
3081 if(!pcert){
3082 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3083 goto end;
3086 recip = pcert->cert;
3088 if(!load_private_key(pcert)
3089 && ps_global->smime
3090 && ps_global->smime->need_passphrase
3091 && !ps_global->smime->already_auto_asked){
3092 /* Couldn't load key with blank password, ask user */
3093 ps_global->smime->already_auto_asked = 1;
3094 if(pith_opt_smime_get_passphrase){
3095 (*pith_opt_smime_get_passphrase)();
3096 load_private_key(pcert);
3100 key = pcert->key;
3101 if(!key)
3102 goto end;
3104 out = BIO_new(BIO_s_mem());
3105 (void) BIO_reset(out);
3106 BIO_puts(out, "MIME-Version: 1.0\r\n");
3108 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
3110 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3111 forget_private_keys();
3113 if(!decrypt_result){
3114 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3115 (char*) openssl_error_string());
3116 goto end; }
3118 BIO_write(out, null, 1);
3122 * We've now produced a flattened MIME object in BIO out.
3123 * It needs to be turned back into a BODY.
3126 if(out){
3127 BODY *body;
3128 ENVELOPE *env;
3129 char *h = NULL;
3130 char *bstart;
3131 STRING s;
3132 BUF_MEM *bptr = NULL;
3134 BIO_get_mem_ptr(out, &bptr);
3135 if(bptr)
3136 h = bptr->data;
3138 /* look for start of body */
3139 bstart = strstr(h, "\r\n\r\n");
3141 if(!bstart){
3142 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
3144 else{
3145 SIZEDTEXT *st;
3146 bstart += 4; /* skip over CRLF*2 */
3148 INIT(&s, mail_string, bstart, strlen(bstart));
3149 rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
3150 mail_free_envelope(&env); /* Don't care about this */
3152 if(body->type == TYPEMULTIPART
3153 && !strucmp(body->subtype, "SIGNED")){
3154 char *cookie = NULL;
3155 PARAMETER *param;
3156 for (param = body->parameter; param && !cookie; param = param->next)
3157 if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value;
3158 if(cookie != NULL){
3159 st = fs_get(sizeof(SIZEDTEXT));
3160 st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */
3161 st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4);
3162 body->sparep = create_smime_sparep(SizedText, (void *)st);
3164 else
3165 q_status_message(SM_ORDER, 3, 3, _("Couldn't find cookie in attachment list."));
3167 body->mime.offset = 0;
3168 body->mime.text.size = 0;
3171 * Now convert original body (application/pkcs7-mime)
3172 * to a multipart body with one sub-part (the decrypted body).
3173 * Note that the sub-part may also be multipart!
3176 b->type = TYPEMULTIPART;
3177 if(b->subtype)
3178 fs_give((void**) &b->subtype);
3181 * This subtype is used in mailview.c to annotate the display of
3182 * encrypted or signed messages. We know for sure then that it's a PKCS7
3183 * part because the sparep field is set to the PKCS7 object (see above).
3185 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3186 b->encoding = ENC8BIT;
3188 if(b->description)
3189 fs_give((void**) &b->description);
3191 b->description = cpystr(what_we_did);
3193 if(b->disposition.type)
3194 fs_give((void **) &b->disposition.type);
3196 if(b->contents.text.data)
3197 fs_give((void **) &b->contents.text.data);
3199 if(b->parameter)
3200 mail_free_body_parameter(&b->parameter);
3202 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3203 b->nested.part = fs_get(sizeof(PART));
3204 b->nested.part->body = *body;
3205 b->nested.part->next = NULL;
3207 fs_give((void**) &body);
3210 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3211 * the decrypted data. Otherwise, it'll try to load it from the original
3212 * data. Eek.
3214 create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body, 0);
3216 modified_the_body = 1;
3220 end:
3221 if(out)
3222 BIO_free(out);
3224 return modified_the_body;
3229 * Recursively handle PKCS7 bodies in our message.
3231 * Returns non-zero if some fiddling was done.
3233 static int
3234 do_fiddle_smime_message(BODY *b, long msgno, char *section)
3236 int modified_the_body = 0;
3238 if(!b)
3239 return 0;
3241 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"));
3243 if(is_pkcs7_body(b)){
3245 if(do_decoding(b, msgno, section)){
3247 * b should now be a multipart message:
3248 * fiddle it too in case it's been multiply-encrypted!
3251 /* fallthru */
3252 modified_the_body = 1;
3256 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
3258 PART *p;
3259 int partNum;
3260 char newSec[100];
3262 if(MIME_MULT_SIGNED(b->type, b->subtype)){
3266 * Ahah. We have a multipart signed entity.
3268 * Multipart/signed
3269 * part 1 (signed thing)
3270 * part 2 (the pkcs7 signature)
3272 * We're going to convert that to
3274 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3275 * part 1 (signed thing)
3276 * part 2 has been freed
3278 * We also extract the signature from part 2 and save it
3279 * in the multipart body->sparep, and we add a description
3280 * in the multipart body->description.
3283 * The results of a decrypted message will be similar. It
3284 * will be
3286 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3287 * part 1 (decrypted thing)
3290 modified_the_body += do_detached_signature_verify(b, msgno, section);
3292 else if(MIME_MSG(b->type, b->subtype)){
3293 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
3295 else{
3297 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
3298 /* Append part number to the section string */
3300 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
3302 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
3307 return modified_the_body;
3312 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3313 * Returns non-zero if something was changed.
3316 fiddle_smime_message(BODY *b, long msgno)
3318 return do_fiddle_smime_message(b, msgno, "");
3322 /********************************************************************************/
3326 * Output a string in a distinctive style
3328 void
3329 gf_puts_uline(char *txt, gf_io_t pc)
3331 pc(TAG_EMBED); pc(TAG_BOLDON);
3332 gf_puts(txt, pc);
3333 pc(TAG_EMBED); pc(TAG_BOLDOFF);
3336 /* get_chain_for_cert: error and level are mandatory arguments */
3337 STACK_OF(X509) *
3338 get_chain_for_cert(X509 *cert, int *error, int *level)
3340 STACK_OF(X509) *chain = NULL;
3341 X509_STORE_CTX *ctx;
3342 X509 *x, *xtmp;
3343 int rc; /* return code */
3345 *level = -1;
3346 *error = 0;
3347 ERR_clear_error();
3348 if((ctx = X509_STORE_CTX_new()) != NULL){
3349 X509_STORE_set_flags(s_cert_store, 0);
3350 if(!X509_STORE_CTX_init(ctx, s_cert_store, cert, NULL))
3351 *error = X509_STORE_CTX_get_error(ctx);
3352 else if((chain = sk_X509_new_null()) != NULL){
3353 for(x = cert; ; x = xtmp){
3354 if(++*level > 0)
3355 sk_X509_push(chain, X509_dup(x));
3356 rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x);
3357 if(rc < 0)
3358 *error = 1;
3359 if(rc <= 0)
3360 break;
3361 if(!X509_check_issued(xtmp, xtmp))
3362 break;
3365 X509_STORE_CTX_free(ctx);
3367 return chain;
3372 * Sign a message. Called from call_mailer in send.c.
3374 * This takes the header for the outgoing message as well as a pointer
3375 * to the current body (which may be reallocated).
3376 * The last argument (BODY **bp) is an argument that tells Alpine
3377 * if the body has 8 bit. if *bp is not null we compute two signatures
3378 * one for the quoted-printable encoded message, and another for the
3379 * 8bit encoded message. We return the signature for the 8bit encoded
3380 * part in p2->body.mime.text.data.
3381 * The reason why we compute two signatures is so that we can decide
3382 * which one to use later, and we only do it in the case that *bp is
3383 * not null. If we did not do this, then we might not be able to sign
3384 * a message until we log in to the smtp server, so instead of doing
3385 * that, we get ready for any possible situation we might find.
3388 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp)
3390 STORE_S *outs = NULL;
3391 STORE_S *outs_2 = NULL;
3392 BODY *body = *bodyP;
3393 BODY *newBody = NULL;
3394 PART *p1 = NULL;
3395 PART *p2 = NULL;
3396 PERSONAL_CERT *pcert;
3397 BIO *in = NULL;
3398 BIO *in_2 = NULL;
3399 BIO *out = NULL;
3400 BIO *out_2 = NULL;
3401 PKCS7 *p7 = NULL;
3402 PKCS7 *p7_2 = NULL;
3403 STACK_OF(X509) *chain;
3404 int result = 0, error;
3405 int flags = dont_detach ? 0 : PKCS7_DETACHED;
3406 int level;
3408 dprint((9, "sign_outgoing_message()"));
3410 smime_init();
3412 /* Look for a private key matching the sender address... */
3414 pcert = match_personal_cert(header->env);
3416 if(!pcert){
3417 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
3418 goto end;
3421 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
3422 /* Couldn't load key with blank password, try again */
3423 if(pith_opt_smime_get_passphrase){
3424 (*pith_opt_smime_get_passphrase)();
3425 load_private_key(pcert);
3429 if(!pcert->key)
3430 goto end;
3432 if(((chain = get_chain_for_cert(pcert->cert, &error, &level)) != NULL && error)
3433 || level == 0){
3434 sk_X509_pop_free(chain, X509_free);
3435 chain = NULL;
3438 if(error)
3439 q_status_message(SM_ORDER, 1, 1,
3440 _("Not all certificates needed to verify signature included in signed message"));
3442 in = body_to_bio(body);
3444 p7 = PKCS7_sign(pcert->cert, pcert->key, chain, in, flags);
3446 if(bp && *bp){
3447 int i, save_encoding;
3449 for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++);
3451 if(i > ENCMAX){ /* no empty encoding slots! */
3452 *bp = NULL;
3454 else {
3455 save_encoding = (*bp)->encoding;
3456 body_encodings[(*bp)->encoding = i] = body_encodings[ENC8BIT];
3458 in_2 = body_to_bio(body);
3460 body_encodings[i] = NULL;
3461 (*bp)->encoding = save_encoding;
3465 if(bp && *bp)
3466 p7_2 = PKCS7_sign(pcert->cert, pcert->key, chain, in_2, flags);
3468 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3469 forget_private_keys();
3471 if(chain)
3472 sk_X509_pop_free(chain, X509_free);
3474 if(!p7){
3475 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
3476 goto end;
3479 outs = so_get(BioType, NULL, EDIT_ACCESS);
3480 out = bio_from_store(outs);
3482 i2d_PKCS7_bio(out, p7);
3483 (void) BIO_flush(out);
3485 so_seek(outs, 0, SEEK_SET);
3487 if(bp && *bp && p7_2){
3488 outs_2 = so_get(BioType, NULL, EDIT_ACCESS);
3489 out_2 = bio_from_store(outs_2);
3491 i2d_PKCS7_bio(out_2, p7_2);
3492 (void) BIO_flush(out_2);
3494 so_seek(outs_2, 0, SEEK_SET);
3497 if((flags&PKCS7_DETACHED)==0){
3499 /* the simple case: the signed data is in the pkcs7 object */
3501 newBody = mail_newbody();
3503 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3505 newBody->contents.text.data = (unsigned char *) outs;
3506 *bodyP = newBody;
3508 result = 1;
3510 else{
3513 * OK.
3514 * We have to create a new body as follows:
3516 * multipart/signed; blah blah blah
3517 * reference to existing body
3519 * pkcs7 object
3522 newBody = mail_newbody();
3524 newBody->type = TYPEMULTIPART;
3525 newBody->subtype = cpystr("signed");
3526 newBody->encoding = ENC7BIT;
3528 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
3529 set_parameter(&newBody->parameter, "micalg", "sha1");
3531 p1 = mail_newbody_part();
3532 p2 = mail_newbody_part();
3535 * This is nasty. We're just copying the body in here,
3536 * but since our newBody is freed at the end of call_mailer,
3537 * we mustn't let this body (the original one) be freed twice.
3539 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
3541 p1->next = p2;
3543 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL);
3544 p2->body.mime.text.data = (unsigned char *) outs_2;
3545 p2->body.contents.text.data = (unsigned char *) outs;
3547 newBody->nested.part = p1;
3549 *bodyP = newBody;
3551 result = 1;
3554 end:
3556 PKCS7_free(p7);
3557 BIO_free(in);
3559 if(bp && *bp){
3560 if(p7_2) PKCS7_free(p7_2);
3561 BIO_free(in_2);
3564 dprint((9, "sign_outgoing_message returns %d", result));
3565 return result;
3569 SMIME_STUFF_S *
3570 new_smime_struct(void)
3572 SMIME_STUFF_S *ret = NULL;
3574 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
3575 memset((void *) ret, 0, sizeof(*ret));
3576 ret->publictype = Nada;
3578 return ret;
3582 static void
3583 free_smime_struct(SMIME_STUFF_S **smime)
3585 if(smime && *smime){
3586 if((*smime)->passphrase_emailaddr){
3587 int i;
3588 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
3589 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
3590 fs_give((void **) (*smime)->passphrase_emailaddr);
3593 if((*smime)->publicpath)
3594 fs_give((void **) &(*smime)->publicpath);
3596 if((*smime)->publiccertlist)
3597 free_certlist(&(*smime)->publiccertlist);
3599 if((*smime)->backuppubliccertlist)
3600 free_certlist(&(*smime)->backuppubliccertlist);
3602 if((*smime)->cacertlist)
3603 free_certlist(&(*smime)->cacertlist);
3605 if((*smime)->backupcacertlist)
3606 free_certlist(&(*smime)->backupcacertlist);
3608 if((*smime)->privatecertlist)
3609 free_certlist(&(*smime)->privatecertlist);
3611 if((*smime)->backupprivatecertlist)
3612 free_certlist(&(*smime)->backupprivatecertlist);
3614 if((*smime)->publiccontent)
3615 fs_give((void **) &(*smime)->publiccontent);
3617 if((*smime)->privatepath)
3618 fs_give((void **) &(*smime)->privatepath);
3620 if((*smime)->personal_certs){
3621 PERSONAL_CERT *pc;
3623 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
3624 free_personal_certs(&pc);
3625 (*smime)->personal_certs = NULL;
3628 if((*smime)->backuppersonal_certs){
3629 PERSONAL_CERT *pc;
3631 pc = (PERSONAL_CERT *) (*smime)->backuppersonal_certs;
3632 free_personal_certs(&pc);
3633 (*smime)->backuppersonal_certs = NULL;
3636 if((*smime)->privatecontent)
3637 fs_give((void **) &(*smime)->privatecontent);
3639 if((*smime)->capath)
3640 fs_give((void **) &(*smime)->capath);
3642 if((*smime)->cacontent)
3643 fs_give((void **) &(*smime)->cacontent);
3645 fs_give((void **) smime);
3649 #endif /* SMIME */