* new version 2.20.2
[alpine.git] / pith / smime.c
blob3482e045309966abb5d3d42fda9af42a6f911cee
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 void 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);
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;
93 static int egdsocket = 0;
95 void *
96 create_smime_sparep(SpareType stype, void *s)
98 SMIME_SPARE_S *rv;
100 rv = fs_get(sizeof(SMIME_SPARE_S));
101 rv->sptype = stype;
102 rv->data = s;
103 return (void *) rv;
106 SpareType
107 get_smime_sparep_type(void *s)
109 return ((SMIME_SPARE_S *)s)->sptype;
112 void *
113 get_smime_sparep_data(void *s)
115 return ((SMIME_SPARE_S *)s)->data;
119 #ifdef PASSFILE
121 * load key from pathkeydir and cert from pathcertdir. It chooses the first
122 * key/certificate pair that matches. Delete pairs that you do not want used,
123 * if you do not want them selected. All parameters must be non-null.
124 * Memory freed by caller.
126 void
127 load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile,
128 char **certfile, EVP_PKEY **pkey, X509 **pcert)
130 char buf[MAXPATH+1], pathkey[MAXPATH+1], prompt[MAILTMPLEN];
131 DIR *dirp;
132 struct dirent *d;
133 int b = 0;
135 if(pathkeydir == NULL || pathcertdir == NULL || keyfile == NULL
136 || pkey == NULL || certfile == NULL || pcert == NULL)
137 return;
139 *keyfile = NULL;
140 *certfile = NULL;
141 *pkey = NULL;
142 *pcert = NULL;
144 if((dirp = opendir(pathkeydir)) != NULL){
145 while(b == 0 && (d=readdir(dirp)) != NULL){
146 size_t ll;
148 if((ll=strlen(d->d_name)) && ll > 4){
149 if(!strcmp(d->d_name+ll-4, ".key")){
150 strncpy(buf, d->d_name, sizeof(buf));
151 buf[sizeof(buf)-1] = '\0';
152 build_path(pathkey, pathkeydir, buf, sizeof(pathkey));
153 buf[strlen(buf)-4] = '\0';
154 snprintf(prompt, sizeof(prompt),
155 _("Enter password of key <%s> to unlock password file: "), buf);
156 if((*pkey = load_pkey_with_prompt(pathkey, NULL, prompt)) != NULL){
157 if(load_cert_for_key(pathcertdir, *pkey, certfile, pcert)){
158 b = 1; /* break */
159 *keyfile = cpystr(buf);
160 } else {
161 EVP_PKEY_free(*pkey);
162 *pkey = NULL;
163 q_status_message1(SM_ORDER, 0, 2,
164 _("Cannot find certificate that matches key <%s>. Continuing..."), buf);
170 closedir(dirp);
175 /* setup a key and certificate to encrypt and decrypt a password file.
176 * These files will be saved in the .alpine-smime/.pwd directory, but its
177 * location can be setup in the command line with the -pwdcertdir option.
178 * Here are the rules:
180 * Check if the .alpine-smime/.pwd (or -pwdcertdir directory) exists,
181 * if not create it. If we are successful, move to the next step
183 * - If the user has a key/cert pair, in the .alpine-smime/.pwd dir
184 * setup is successful;
185 * - if the user does not have a key/cert pair, look to see if
186 * ps_global->smime->personal_certs is already setup, if so, use it.
187 * - if ps_global->smime->personal_certs is not set up, see if we can
188 * find a certificate/cert pair in the default locations at compilation
189 * time. (~/.alpine-smime/private and ~/.alpine-smime/public
190 * - if none of this is successful, create a key/certificate pair
191 * (TODO: implement this)
192 * - in any other case, setup is not successful.
194 * If setup is successful, setup ps_global->pwdcert.
195 * If any of this fails, ps_global->pwdcert will be null.
196 * Ok, that should do it.
198 void
199 setup_pwdcert(void **pwdcert)
201 int we_inited = 0;
202 int setup_dir = 0; /* make it non zero if we know which dir to use */
203 struct stat sbuf;
204 char pathdir[MAXPATH+1], pathkey[MAXPATH+1], fpath[MAXPATH+1], pathcert[MAXPATH+1];
205 char fpath2[MAXPATH+1], prompt[MAILTMPLEN];
206 char *keyfile, *certfile, *text;
207 EVP_PKEY *pkey = NULL;
208 X509 *pcert = NULL;
209 PERSONAL_CERT *pc, *pc2 = NULL;
211 if(pwdcert == NULL)
212 return;
214 if(ps_global->pwdcertdir){
215 if(our_stat(ps_global->pwdcertdir, &sbuf) == 0
216 && ((sbuf.st_mode & S_IFMT) == S_IFDIR)){
217 setup_dir++;
218 strncpy(pathdir, ps_global->pwdcertdir, sizeof(pathdir));
219 pathdir[sizeof(pathdir)-1] = '\0';
221 } else {
222 smime_path(DF_PASSWORD_DIR, pathdir, sizeof(pathdir));
223 if(our_stat(pathdir, &sbuf) == 0){
224 if((sbuf.st_mode & S_IFMT) == S_IFDIR)
225 setup_dir++;
226 } else if(can_access(pathdir, ACCESS_EXISTS) != 0
227 && our_mkpath(pathdir, 0700) == 0)
228 setup_dir++;
231 if(setup_dir == 0)
232 return;
234 load_key_and_cert(pathdir, pathdir, &keyfile, &certfile, &pkey, &pcert);
236 if(certfile && keyfile){
237 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
238 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
239 pc->name = keyfile;
240 pc->key = pkey;
241 pc->cert = pcert;
242 *pwdcert = (void *) pc;
243 fs_give((void **)&certfile);
244 return;
247 /* if the user gave a pwdcertdir and there is nothing there, do not
248 * continue. Let the user initialize on their own this directory.
250 if(ps_global->pwdcertdir != NULL)
251 return;
253 /* look to see if there are any certificates lying around, first
254 * we try to load ps_global->smime to see if that has information
255 * we can use. If we are the process filling the smime structure
256 * we deinit at the end, since this might not do a full init.
258 if(ps_global && ps_global->smime && !ps_global->smime->inited){
259 we_inited++;
260 smime_init();
263 /* at this point ps_global->smime->inited == 1 */
264 if(ps_global->smime && ps_global->smime->personal_certs != NULL){
265 pc = (PERSONAL_CERT *) ps_global->smime->personal_certs;
266 if(ps_global->smime->privatetype == Directory){
267 build_path(pathkey, ps_global->smime->privatepath, pc->name, sizeof(pathkey));
268 strncat(pathkey, ".key", 4);
269 pathkey[sizeof(pathkey)-1] = '\0';
270 text = NULL;
271 } else if (ps_global->smime->privatetype == Container){
272 if(pc->keytext == NULL){ /* we should *never* be here, but just in case */
273 if(ps_global->smime->privatecontent != NULL){
274 char tmp[MAILTMPLEN], *s, *t, c;
275 snprintf(tmp, sizeof(tmp), "%s%s", EMAILADDRLEADER, pc->name);
276 tmp[sizeof(tmp)-1] = '\0';
277 if((s = strstr(ps_global->smime->privatecontent, tmp)) != NULL){
278 if((t = strstr(s+strlen(tmp), EMAILADDRLEADER)) != NULL){
279 c = *t;
280 *t = '\0';
281 pc->keytext = cpystr(s + strlen(tmp) + 1); /* 1 = strlen("\n") */
282 *t = c;
284 else
285 pc->keytext = cpystr(s + strlen(tmp) + 1); /* 1 = strlen("\n") */
289 if(pc->keytext != NULL) /* we should go straigth here */
290 text = pc->keytext;
291 } else if (ps_global->smime->privatetype == Keychain){
292 pathkey[0] = '\0'; /* no apple key chain support yet */
293 text = NULL;
295 if((pathkey && *pathkey) || text){
296 snprintf(prompt, sizeof(prompt),
297 _("Enter password of key <%s> to unlock password file: "), pc->name);
299 if((pkey = load_pkey_with_prompt(pathkey, text, prompt)) != NULL){
300 pc2 = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
301 memset((void *)pc2, 0, sizeof(PERSONAL_CERT));
302 pc2->name = cpystr(pc->name);
303 pc2->key = pkey;
304 pc2->cert = X509_dup(pc->cert);
306 /* now copy the keys and certs, starting by the key... */
307 build_path(fpath, pathdir, pc->name, sizeof(fpath));
308 strncat(fpath, ".key", 4);
309 fpath[sizeof(fpath)-1] = '\0';
310 if(our_stat(fpath, &sbuf) == 0){ /* if fpath exists */
311 if((sbuf.st_mode & S_IFMT) == S_IFREG) /* and is a regular file */
312 setup_dir++; /* we are done */
313 } else if(ps_global->smime->privatetype == Directory){
314 if(our_copy(fpath, pathkey) == 0)
315 setup_dir++;
316 } else if(ps_global->smime->privatetype == Container){
317 BIO *out;
318 if((out = BIO_new_file(fpath, "w")) != NULL){
319 if(BIO_puts(out, pc->keytext) > 0)
320 setup_dir++;
321 BIO_free(out);
323 } else if(ps_global->smime->privatetype == Keychain){
324 /* add support for Apple Mac OS X */
328 /* successful copy of key, now continue with certificate */
329 if(setup_dir){
330 setup_dir = 0;
332 build_path(pathkey, ps_global->smime->publicpath, pc->name, sizeof(pathkey));
333 strncat(pathkey, ".crt", 4);
334 pathkey[sizeof(pathkey)-1] = '\0';
336 build_path(fpath, pathdir, pc->name, sizeof(fpath));
337 strncat(fpath, ".crt", 4);
338 fpath[sizeof(fpath)-1] = '\0';
340 if(our_stat(fpath, &sbuf) == 0){
341 if((sbuf.st_mode & S_IFMT) == S_IFREG)
342 setup_dir++;
344 else if(ps_global->smime->privatetype == Directory){
345 if(our_copy(fpath, pathkey) == 0)
346 setup_dir++;
347 } else if(ps_global->smime->privatetype == Container) {
348 BIO *out;
349 if((out = BIO_new_file(fpath, "w")) != NULL){
350 if(PEM_write_bio_X509(out, pc->cert))
351 setup_dir++;
352 BIO_free(out);
354 } else if (ps_global->smime->privatetype == Keychain) {
355 /* add support for Mac OS X */
359 if(setup_dir){
360 *pwdcert = (void *) pc2;
361 return;
363 else if(pc2 != NULL)
364 free_personal_certs(&pc2);
365 } /* if (pathkey...) */
366 } /* if(ps_global->smime->personal_certs) */
369 if(setup_dir == 0){
370 /* PATHCERTDIR(Private) must be null, so create a path */
371 set_current_val(&ps_global->vars[V_PRIVATEKEY_DIR], TRUE, TRUE);
372 smime_path(ps_global->VAR_PRIVATEKEY_DIR, pathkey, sizeof(pathkey));
374 /* PATHCERTDIR(Public) must be null, so create a path */
375 set_current_val(&ps_global->vars[V_PUBLICCERT_DIR], TRUE, TRUE);
376 smime_path(ps_global->VAR_PUBLICCERT_DIR, pathcert, sizeof(pathcert));
378 /* BUG: this does not support local containers */
379 load_key_and_cert(pathkey, pathcert, &keyfile, &certfile, &pkey, &pcert);
381 if(certfile && keyfile){
382 build_path(fpath, pathdir, keyfile, sizeof(fpath));
383 strncat(fpath, ".key", 4);
384 fpath[sizeof(fpath)-1] = '\0';
386 build_path(fpath2, pathkey, keyfile, sizeof(fpath));
387 strncat(fpath2, ".key", 4);
388 fpath2[sizeof(fpath2)-1] = '\0';
390 if(our_copy(fpath, fpath2) == 0)
391 setup_dir++;
393 if(setup_dir){
394 setup_dir = 0;
396 build_path(fpath, pathdir, certfile, sizeof(fpath));
397 build_path(fpath2, pathcert, certfile, sizeof(fpath2));
399 if(our_copy(fpath, fpath2) == 0)
400 setup_dir++;
405 if(keyfile && certfile){
406 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
407 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
408 pc->name = keyfile;
409 pc->key = pkey;
410 pc->cert = pcert;
411 *pwdcert = (void *) pc;
412 fs_give((void **)&certfile);
413 return;
416 /* TODO: create self signed certificate
417 q_status_message(SM_ORDER, 2, 2,
418 _("No key/certificate pair found for password file encryption support"));
421 if(we_inited)
422 smime_deinit();
424 #endif /* PASSFILE */
426 /* smime_expunge_cert.
427 * Return values: < 0 there was an error.
428 * >=0 the number of messages expunged
431 smime_expunge_cert(WhichCerts ctype)
433 int count, removed;
434 CertList *cl, *dummy, *data;
435 char *path, buf[MAXPATH+1];
436 char *contents;
438 if(DATACERT(ctype)== NULL)
439 return -1;
441 /* data cert is the way we unify certificate management across
442 * functions, but it is not where we really save the information in the
443 * case ctype is equal to Private. What we will do is to update the
444 * datacert, and in the case of ctype equal to Private use the updated
445 * certdata to update the personal_certs data.
448 path = PATHCERTDIR(ctype);
450 if(path){
451 /* add a fake certificate at the beginning of the list */
452 dummy = fs_get(sizeof(CertList));
453 memset((void *)dummy, 0, sizeof(CertList));
454 dummy->next = DATACERT(ctype);
456 for(cl = dummy, count = 0; cl && cl->next;){
457 if(cl->next->data.deleted == 0){
458 cl = cl->next;
459 continue;
462 removed = 1; /* assume success */
463 if(SMHOLDERTYPE(ctype) == Directory){
464 build_path(buf, path, cl->next->name, sizeof(buf));
465 if(ctype == Private && strlen(buf) + strlen(EXTCERT(Private)) < sizeof(buf)){
466 strncat(buf, EXTCERT(Private), 4);
467 buf[sizeof(buf)-1] = '\0';
470 if(our_unlink(buf) < 0){
471 q_status_message1(SM_ORDER, 3, 3, _("Error removing certificate %s"), cl->next->name);
472 cl = cl->next;
473 removed = 0;
476 else if(SMHOLDERTYPE(ctype) == Container){
477 char *prefix= ctype == CACert ? CACERTSTORELEADER : EMAILADDRLEADER;
478 char tmp[MAILTMPLEN], *s, *t;
480 contents = CONTENTCERTLIST(ctype);
481 snprintf(tmp, sizeof(tmp), "%s%s", prefix, cl->next->name);
482 tmp[sizeof(tmp) - 1] = '\0';
483 if((s = strstr(contents, tmp)) != NULL){
484 if((t = strstr(s+strlen(tmp), prefix)) == NULL)
485 *s = '\0';
486 else
487 memmove(s, t, strlen(t)+1);
488 fs_resize((void **)&contents, strlen(contents)+1);
489 switch(ctype){
490 case Private: ps_global->smime->privatecontent = contents; break;
491 case Public : ps_global->smime->publiccontent = contents; break;
492 case CACert : ps_global->smime->cacontent = contents; break;
493 default : break;
496 else
497 removed = 0;
498 } else { /* unhandled case */
501 if(removed > 0){
502 count++; /* count it! */
503 data = cl->next;
504 cl->next = data->next;
505 if(data->name) fs_give((void **)&data->name);
506 fs_give((void **)&data);
509 } else
510 q_status_message(SM_ORDER, 3, 3, _("Error expunging certificate"));
512 switch(ctype){
513 case Private: ps_global->smime->privatecertlist = dummy->next; break;
514 case Public : ps_global->smime->publiccertlist = dummy->next; break;
515 case CACert : ps_global->smime->cacertlist = dummy->next; break;
516 default : break;
518 fs_give((void **)&dummy);
519 if(SMHOLDERTYPE(ctype) == Container){
520 if(copy_dir_to_container(ctype, contents) < 0)
521 count = 0;
523 if(count > 0){
524 q_status_message2(SM_ORDER, 3, 3, _("Removed %s certificate%s"), comatose(count), plural(count));
526 else
527 q_status_message(SM_ORDER, 3, 3, _("Error: No certificates were removed"));
528 return count;
531 void
532 mark_cert_deleted(WhichCerts ctype, int num, unsigned state)
534 CertList *cl;
535 int i;
537 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
538 cl->data.deleted = state;
541 unsigned
542 get_cert_deleted(WhichCerts ctype, int num)
544 CertList *cl;
545 int i;
547 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
548 return (cl && cl->data.deleted) ? 1 : 0;
551 EVP_PKEY *
552 load_pkey_with_prompt(char *fpath, char *text, char *prompt)
554 EVP_PKEY *pkey;
555 int rc = 0; /* rc == 1, cancel, rc == 0 success */
556 char pass[MAILTMPLEN+1];
557 BIO *in;
559 /* attempt to load with empty password */
560 in = text ? BIO_new_mem_buf(text, strlen(text)) : BIO_new_file(fpath, "r");
561 if(in != NULL){
562 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, "");
563 BIO_free(in);
564 if(pkey != NULL) return pkey;
567 if(pith_smime_enter_password)
568 while(pkey == NULL && rc != 1){
569 in = text ? BIO_new_mem_buf(text, strlen(text)) : BIO_new_file(fpath, "r");
570 if(in != NULL){
571 do {
572 rc = (*pith_smime_enter_password)(prompt, (char *)pass, sizeof(pass));
573 } while (rc!=0 && rc!=1 && rc>0);
575 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, (char *)pass);
576 BIO_free(in);
578 else rc = 1;
581 return pkey;
587 import_certificate(WhichCerts ctype)
589 int r = 1, rc;
590 char filename[MAXPATH+1], full_filename[MAXPATH+1], buf[MAXPATH+1];
592 if(pith_smime_import_certificate == NULL){
593 q_status_message(SM_ORDER, 0, 2,
594 _("import of certificates not implemented yet!"));
595 return -1;
598 smime_init();
600 r = (*pith_smime_import_certificate)(filename, full_filename, sizeof(filename) - 20);
602 ps_global->mangled_screen = 1;
604 if(r < 0)
605 return r;
606 else if (ctype == Private){
607 char prompt[500], *s, *t;
608 EVP_PKEY *key = NULL;
610 if(!ps_global->smime->privatecertlist){
611 ps_global->smime->privatecertlist = fs_get(sizeof(CertList));
612 memset((void *)DATACERT(ctype), 0, sizeof(CertList));
615 for(s = t = filename; (t = strstr(s, ".key")) != NULL; s = t + 1);
616 if(s) *(s-1) = 0;
618 snprintf(prompt, sizeof(prompt), _("Enter passphrase for <%s>: "), filename);
619 prompt[sizeof(prompt)-1] = '\0';
620 if((key = load_pkey_with_prompt(full_filename, NULL, prompt)) != NULL){
621 if(SMHOLDERTYPE(ctype) == Directory){
622 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
623 if(strcmp(buf + strlen(buf) - 4, EXTCERT(ctype)) != 0 && strlen(buf) + 4 < sizeof(buf)){
624 strncat(buf, EXTCERT(ctype), 4);
625 buf[sizeof(buf)-1] = '\0';
627 rc = our_copy(buf, full_filename);
629 else /* if(SMHOLDERTYPE(ctype) == Container){ */
630 rc = add_file_to_container(ctype, full_filename, NULL);
631 if(rc == 0)
632 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
633 else
634 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
635 if(ps_global->smime->publiccertlist)
636 ps_global->smime->publiccertlist->data.renew = 1;
638 else
639 q_status_message(SM_ORDER, 1, 3, _("Problem unlocking key (not a certificate and/or wrong password)"));
640 } else if (ctype == CACert){
641 BIO *ins;
642 X509 *cert;
644 if((ins = BIO_new_file(full_filename, "r")) != NULL){
645 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
646 if(SMHOLDERTYPE(ctype) == Directory){
647 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
648 if(strcmp(buf + strlen(buf) - 4, ".crt") != 0 && strlen(buf) + 4 < sizeof(buf)){
649 strncat(buf, EXTCERT(ctype), 4);
650 buf[sizeof(buf)-1] = '\0';
653 rc = our_copy(buf, full_filename);
655 else /* if(SMHOLDERTYPE(ctype) == Container){ */
656 rc = add_file_to_container(ctype, full_filename, NULL);
657 if(rc == 0)
658 q_status_message(SM_ORDER, 1, 3, _("Certificate saved"));
659 else
660 q_status_message(SM_ORDER, 1, 3, _("Error saving certificate"));
661 X509_free(cert); /* not needed anymore */
663 else
664 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
665 BIO_free(ins);
667 renew_store();
668 } else { /* ctype == Public. save certificate, but first validate that it is one */
669 BIO *ins;
670 X509 *cert;
672 if((ins = BIO_new_file(full_filename, "r")) != NULL){
673 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
674 if(SMHOLDERTYPE(ctype) == Directory){
675 char **email;
677 if((email = get_x509_subject_email(cert)) != NULL){
678 int i;
679 for(i = 0; email[i] != NULL; i++){
680 save_cert_for(email[i], cert, Public);
681 fs_give((void **)&email[i]);
683 fs_give((void **)email);
685 else
686 save_cert_for(filename, cert, Public);
688 else /* if(SMHOLDERTYPE(ctype) == Container){ */
689 add_file_to_container(ctype, full_filename, NULL);
690 X509_free(cert);
691 if(ps_global->smime->publiccertlist)
692 ps_global->smime->publiccertlist->data.renew = 1;
694 else
695 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
696 BIO_free(ins);
699 if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1;
700 return 0;
703 /* itype: information type to add: 0 - public, 1 - private.
704 * Memory freed by caller
706 BIO *
707 print_private_key_information(char *email, int itype)
709 BIO *out;
710 PERSONAL_CERT *pc;
712 if(ps_global->smime == NULL
713 || ps_global->smime->personal_certs == NULL
714 || (itype != 0 && itype != 1))
715 return NULL;
717 for(pc = ps_global->smime->personal_certs;
718 pc != NULL && strcmp(pc->name, email) != 0; pc = pc->next);
719 if(pc->key == NULL
720 && !load_private_key(pc)
721 && ps_global->smime
722 && ps_global->smime->need_passphrase){
723 if (*pith_opt_smime_get_passphrase)
724 (*pith_opt_smime_get_passphrase)();
725 load_private_key(pc);
728 if(pc->key == NULL)
729 return NULL;
731 out = BIO_new(BIO_s_mem());
732 if(itype == 0) /* 0 means public */
733 EVP_PKEY_print_public(out, pc->key, 0, NULL);
734 else if (itype == 1) /* 1 means private */
735 EVP_PKEY_print_private(out, pc->key, 0, NULL);
737 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
738 forget_private_keys();
740 return out;
744 * Forget any cached private keys
746 static void
747 forget_private_keys(void)
749 PERSONAL_CERT *pcert;
750 size_t len;
751 volatile char *p;
753 dprint((9, "forget_private_keys()"));
754 if(ps_global->smime){
755 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
756 pcert;
757 pcert=pcert->next){
759 if(pcert->key){
760 EVP_PKEY_free(pcert->key);
761 pcert->key = NULL;
765 ps_global->smime->entered_passphrase = 0;
766 len = sizeof(ps_global->smime->passphrase);
767 p = ps_global->smime->passphrase;
769 while(len-- > 0)
770 *p++ = '\0';
774 /* modelled after signature_path in reply.c, but uses home dir instead of the
775 * directory where the .pinerc is located, since according to documentation,
776 * the .alpine-smime directories are subdirectories of the home directory
778 int smime_path(char *rpath, char *fpath, size_t len)
780 *fpath = '\0';
781 if(rpath && *rpath){
782 size_t spl = strlen(rpath);
784 if(IS_REMOTE(rpath)){
785 if(spl < len - 1)
786 strncpy(fpath, rpath, len-1);
787 fpath[len-1] = '\0';
789 else if(is_absolute_path(rpath)){
790 strncpy(fpath, rpath, len-1);
791 fpath[len-1] = '\0';
792 fnexpand(fpath, len);
794 else if(ps_global->VAR_OPER_DIR){
795 if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
796 build_path(fpath, ps_global->VAR_OPER_DIR, rpath, len);
798 else if(ps_global->home_dir){
799 if(strlen(ps_global->home_dir) + spl < len - 1)
800 build_path(fpath, ps_global->home_dir, rpath, len);
803 return fpath && *fpath ? 1 : 0;
809 * taken from openssl/apps/app_rand.c
811 static int
812 app_RAND_load_file(const char *file)
814 char buffer[200];
816 if(file == NULL)
817 file = RAND_file_name(buffer, sizeof buffer);
818 else if(RAND_egd(file) > 0){
819 /* we try if the given filename is an EGD socket.
820 if it is, we don't write anything back to the file. */
821 egdsocket = 1;
822 return 1;
825 if(file == NULL || !RAND_load_file(file, -1)){
826 if(RAND_status() == 0){
827 dprint((1, "unable to load 'random state'\n"));
828 dprint((1, "This means that the random number generator has not been seeded\n"));
829 dprint((1, "with much random data.\n"));
832 return 0;
835 seeded = 1;
836 return 1;
841 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
843 static void
844 openssl_extra_randomness(void)
846 #if !defined(WIN32)
847 int fd;
848 unsigned long i;
849 char *tf = NULL;
850 char tmp[MAXPATH];
851 struct stat sbuf;
852 /* if system doesn't have /dev/urandom */
853 if(stat ("/dev/urandom", &sbuf)){
854 tmp[0] = '0';
855 tf = temp_nam(NULL, NULL);
856 if(tf){
857 strncpy(tmp, tf, sizeof(tmp));
858 tmp[sizeof(tmp)-1] = '\0';
859 fs_give((void **) &tf);
862 if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
863 i = (unsigned long) tmp;
864 else{
865 unlink(tmp); /* don't need the file */
866 fstat(fd, &sbuf); /* get information about the file */
867 i = sbuf.st_ino; /* remember its inode */
868 close(fd); /* or its descriptor */
870 /* not great but it'll have to do */
871 snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
872 tcp_serverhost (),i,
873 (unsigned long) (time (0) ^ gethostid ()),
874 (unsigned long) getpid ());
875 RAND_seed(tmp, strlen(tmp));
877 #endif
881 /* taken from openssl/apps/app_rand.c */
882 static int
883 app_RAND_write_file(const char *file)
885 char buffer[200];
887 if(egdsocket || !seeded)
889 * If we did not manage to read the seed file,
890 * we should not write a low-entropy seed file back --
891 * it would suppress a crucial warning the next time
892 * we want to use it.
894 return 0;
896 if(file == NULL)
897 file = RAND_file_name(buffer, sizeof buffer);
899 if(file == NULL || !RAND_write_file(file)){
900 dprint((1, "unable to write 'random state'\n"));
901 return 0;
904 return 1;
907 CertList *
908 certlist_from_personal_certs(PERSONAL_CERT *pc)
910 CertList *cl;
911 X509 *x;
912 char buf[MAXPATH];
914 if(pc == NULL)
915 return NULL;
917 cl = fs_get(sizeof(CertList));
918 memset((void *)cl, 0, sizeof(CertList));
919 cl->name = cpystr(pc->name);
920 x = get_cert_for(pc->name, Public);
921 if(x){
922 if(x->cert_info){
923 cl->data.date_from = smime_get_date(x->cert_info->validity->notBefore);
924 cl->data.date_to = smime_get_date(x->cert_info->validity->notAfter);
925 get_fingerprint(x, EVP_md5(), buf, sizeof(buf), NULL);
926 cl->data.md5 = cpystr(buf);
928 X509_free(x);
930 cl->next = certlist_from_personal_certs(pc->next);
932 return cl;
935 void
936 renew_cert_data(CertList **data, WhichCerts ctype)
938 smime_init();
939 if(ctype == Private){
940 if(data){
941 PERSONAL_CERT *pc = (PERSONAL_CERT *)ps_global->smime->personal_certs;
942 if(*data)
943 free_certlist(data);
944 free_personal_certs(&pc);
945 setup_privatekey_storage();
946 *data = certlist_from_personal_certs((PERSONAL_CERT *)ps_global->smime->personal_certs);
947 if(data && *data){
948 resort_certificates(data, ctype);
949 RENEWCERT(*data) = 0;
951 ps_global->smime->privatecertlist = *data;
953 if(ps_global->smime->privatecertlist)
954 RENEWCERT(ps_global->smime->privatecertlist) = 0;
955 } else {
956 X509_LOOKUP *lookup = NULL;
957 X509_STORE *store = NULL;
959 if((store = X509_STORE_new()) != NULL){
960 if((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL){
961 X509_STORE_free(store);
962 store = NULL;
963 } else{
964 free_certlist(data);
965 if(SMHOLDERTYPE(ctype) == Directory)
966 add_certs_in_dir(lookup, PATHCERTDIR(ctype), EXTCERT(ctype), data);
967 else /* if(SMHOLDERTYPE(ctype) == Container) */
968 *data = mem_to_certlist(CONTENTCERTLIST(ctype), ctype);
969 if(data && *data){
970 resort_certificates(data, ctype);
971 RENEWCERT(*data) = 0;
973 if(ctype == Public)
974 ps_global->smime->publiccertlist = *data;
975 else
976 ps_global->smime->cacertlist = *data;
980 setup_certs_backup_by_type(ctype);
983 void
984 smime_reinit(void)
986 smime_deinit();
987 smime_init();
990 /* Installed as an atexit() handler to save the random data */
991 void
992 smime_deinit(void)
994 dprint((9, "smime_deinit()"));
995 app_RAND_write_file(NULL);
996 free_smime_struct(&ps_global->smime);
999 /* we renew the store when it has changed */
1000 void renew_store(void)
1002 if(ps_global->smime->inited){
1003 if(s_cert_store != NULL)
1004 X509_STORE_free(s_cert_store);
1005 s_cert_store = get_ca_store();
1009 /* Initialise openssl stuff if needed */
1010 void
1011 smime_init(void)
1013 if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
1015 dprint((9, "smime_init()"));
1016 if(!ps_global->smime)
1017 ps_global->smime = new_smime_struct();
1019 setup_storage_locations();
1021 s_cert_store = get_ca_store();
1022 setup_certs_backup_by_type(CACert);
1024 OpenSSL_add_all_algorithms();
1025 ERR_load_crypto_strings();
1027 app_RAND_load_file(NULL);
1028 openssl_extra_randomness();
1029 ps_global->smime->inited = 1;
1032 ERR_clear_error();
1036 /* validate a certificate. Return value : 0 for no error, -1 for error.
1037 * In the latter case, set the openssl smime error in *error.
1039 int smime_validate_cert(X509 *cert, long *error)
1041 X509_STORE_CTX *csc;
1043 ERR_clear_error();
1044 *error = 0;
1045 if((csc = X509_STORE_CTX_new()) != NULL){
1046 X509_STORE_set_flags(s_cert_store, 0);
1047 if(X509_STORE_CTX_init(csc,s_cert_store,cert,NULL)
1048 && X509_verify_cert(csc) <= 0)
1049 *error = X509_STORE_CTX_get_error(csc);
1050 X509_STORE_CTX_free(csc);
1052 return *error ? -1 : 0;
1055 PERSONAL_CERT *
1056 get_personal_certs(char *path)
1058 PERSONAL_CERT *result = NULL;
1059 char buf2[MAXPATH];
1060 struct dirent *d;
1061 DIR *dirp;
1063 ps_global->smime->privatepath = cpystr(path);
1064 dirp = opendir(path);
1065 if(dirp){
1066 while((d=readdir(dirp)) != NULL){
1067 X509 *cert;
1068 size_t ll;
1070 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, ".key")){
1072 /* copy file name to temp buffer */
1073 strncpy(buf2, d->d_name, sizeof(buf2)-1);
1074 buf2[sizeof(buf2)-1] = '\0';
1075 /* chop off ".key" trailier */
1076 buf2[strlen(buf2)-4] = 0;
1077 /* Look for certificate */
1078 cert = get_cert_for(buf2, Public);
1080 if(cert){
1081 PERSONAL_CERT *pc;
1083 /* create a new PERSONAL_CERT, fill it in */
1085 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1086 pc->cert = cert;
1087 pc->name = cpystr(buf2);
1089 /* Try to load the key with an empty password */
1090 pc->key = load_key(pc, "", SM_NORMALCERT);
1092 pc->next = result;
1093 result = pc;
1097 closedir(dirp);
1099 return result;
1103 void
1104 setup_privatekey_storage(void)
1106 char path[MAXPATH+1], *contents;
1107 int privatekeycontainer = 0;
1109 /* private keys in a container */
1110 if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
1112 privatekeycontainer = 1;
1113 contents = NULL;
1114 path[0] = '\0';
1115 if(!smime_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
1116 privatekeycontainer = 0;
1118 if(privatekeycontainer && !IS_REMOTE(path)
1119 && ps_global->VAR_OPER_DIR
1120 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1121 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1122 /* TRANSLATORS: First arg is the directory name, second is
1123 the file user wants to read but can't. */
1124 _("Can't read file outside %s: %s"),
1125 ps_global->VAR_OPER_DIR, path);
1126 privatekeycontainer = 0;
1129 if(privatekeycontainer
1130 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1131 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1133 !(contents = read_file(path, READ_FROM_LOCALE)))
1134 privatekeycontainer = 0;
1137 if(privatekeycontainer && path[0]){
1138 ps_global->smime->privatetype = Container;
1139 ps_global->smime->privatepath = cpystr(path);
1141 if(contents){
1142 ps_global->smime->privatecontent = contents;
1143 ps_global->smime->personal_certs = mem_to_personal_certs(contents);
1148 /* private keys in a directory of files */
1149 if(!privatekeycontainer){
1150 ps_global->smime->privatetype = Directory;
1152 path[0] = '\0';
1153 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1154 && !IS_REMOTE(path)))
1155 ps_global->smime->privatetype = Nada;
1156 else if(can_access(path, ACCESS_EXISTS)){
1157 if(our_mkpath(path, 0700)){
1158 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1159 ps_global->smime->privatetype = Nada;
1163 if(ps_global->smime->privatetype == Directory)
1164 ps_global->smime->personal_certs = get_personal_certs(path);
1166 setup_certs_backup_by_type(Private);
1171 static void
1172 setup_storage_locations(void)
1174 int publiccertcontainer = 0, cacertcontainer = 0;
1175 char path[MAXPATH+1], *contents;
1177 if(!ps_global->smime)
1178 return;
1180 #ifdef APPLEKEYCHAIN
1181 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1182 ps_global->smime->publictype = Keychain;
1184 else{
1185 #endif /* APPLEKEYCHAIN */
1186 /* Public certificates in a container */
1187 if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
1189 publiccertcontainer = 1;
1190 contents = NULL;
1191 path[0] = '\0';
1192 if(!smime_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
1193 publiccertcontainer = 0;
1195 if(publiccertcontainer && !IS_REMOTE(path)
1196 && ps_global->VAR_OPER_DIR
1197 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1198 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1199 /* TRANSLATORS: First arg is the directory name, second is
1200 the file user wants to read but can't. */
1201 _("Can't read file outside %s: %s"),
1202 ps_global->VAR_OPER_DIR, path);
1203 publiccertcontainer = 0;
1206 if(publiccertcontainer
1207 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1208 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1210 !(contents = read_file(path, READ_FROM_LOCALE)))
1211 publiccertcontainer = 0;
1214 if(publiccertcontainer && path[0]){
1215 ps_global->smime->publictype = Container;
1216 ps_global->smime->publicpath = cpystr(path);
1218 if(contents){
1219 ps_global->smime->publiccontent = contents;
1220 ps_global->smime->publiccertlist = mem_to_certlist(contents, Public);
1225 /* Public certificates in a directory of files */
1226 if(!publiccertcontainer){
1227 ps_global->smime->publictype = Directory;
1229 path[0] = '\0';
1230 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1231 && !IS_REMOTE(path)))
1232 ps_global->smime->publictype = Nada;
1233 else if(can_access(path, ACCESS_EXISTS)){
1234 if(our_mkpath(path, 0700)){
1235 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1236 ps_global->smime->publictype = Nada;
1240 if(ps_global->smime->publictype == Directory)
1241 ps_global->smime->publicpath = cpystr(path);
1244 #ifdef APPLEKEYCHAIN
1246 #endif /* APPLEKEYCHAIN */
1248 setup_privatekey_storage();
1250 /* extra cacerts in a container */
1251 if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
1253 cacertcontainer = 1;
1254 contents = NULL;
1255 path[0] = '\0';
1256 if(!smime_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
1257 cacertcontainer = 0;
1259 if(cacertcontainer && !IS_REMOTE(path)
1260 && ps_global->VAR_OPER_DIR
1261 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1262 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1263 /* TRANSLATORS: First arg is the directory name, second is
1264 the file user wants to read but can't. */
1265 _("Can't read file outside %s: %s"),
1266 ps_global->VAR_OPER_DIR, path);
1267 cacertcontainer = 0;
1270 if(cacertcontainer
1271 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1272 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1274 !(contents = read_file(path, READ_FROM_LOCALE)))
1275 cacertcontainer = 0;
1278 if(cacertcontainer && path[0]){
1279 ps_global->smime->catype = Container;
1280 ps_global->smime->capath = cpystr(path);
1281 ps_global->smime->cacontent = contents;
1282 if(contents)
1283 ps_global->smime->cacertlist = mem_to_certlist(contents, CACert);
1287 if(!cacertcontainer){
1288 ps_global->smime->catype = Directory;
1290 path[0] = '\0';
1291 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1292 && !IS_REMOTE(path)))
1293 ps_global->smime->catype = Nada;
1294 else if(can_access(path, ACCESS_EXISTS)){
1295 if(our_mkpath(path, 0700)){
1296 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1297 ps_global->smime->catype = Nada;
1301 if(ps_global->smime->catype == Directory)
1302 ps_global->smime->capath = cpystr(path);
1308 copy_publiccert_dir_to_container(void)
1310 return(copy_dir_to_container(Public, NULL));
1315 copy_publiccert_container_to_dir(void)
1317 return(copy_container_to_dir(Public));
1322 copy_privatecert_dir_to_container(void)
1324 return(copy_dir_to_container(Private, NULL));
1329 copy_privatecert_container_to_dir(void)
1331 return(copy_container_to_dir(Private));
1336 copy_cacert_dir_to_container(void)
1338 return(copy_dir_to_container(CACert, NULL));
1343 copy_cacert_container_to_dir(void)
1345 return(copy_container_to_dir(CACert));
1348 /* Add the contents of a file to a container. Do not check the content
1349 * of the file, just add it using the format for that container. The
1350 * caller must check the format, so that there is no data corruption
1351 * in the future.
1352 * return value: 0 - success,
1353 * != 0 - failure.
1356 add_file_to_container(WhichCerts ctype, char *fpath, char *altname)
1358 char *sep = (ctype == Public || ctype == Private)
1359 ? EMAILADDRLEADER : CACERTSTORELEADER;
1360 char *content = ctype == Public ? ps_global->smime->publiccontent
1361 : (ctype == Private ? ps_global->smime->privatecontent
1362 : ps_global->smime->cacontent);
1363 char *name;
1364 char *s;
1365 unsigned char c;
1366 struct stat sbuf;
1367 STORE_S *in = NULL;
1368 int rv = -1; /* assume error */
1370 if(our_stat(fpath, &sbuf) < 0
1371 || (in = so_get(FileStar, fpath, READ_ACCESS | READ_FROM_LOCALE)) == NULL)
1372 goto endadd;
1374 if(altname != NULL)
1375 name = altname;
1376 else if((name = strrchr(fpath, '/')) != NULL){
1377 size_t ll;
1378 if((ll = strlen(++name)) > 4 && strucmp(name + ll - 4, EXTCERT(ctype)) == 0)
1379 name[ll-strlen(EXTCERT(ctype))] = '\0';
1381 else
1382 goto endadd;
1384 if(content){
1385 fs_resize((void **)&content, strlen(content) + strlen(sep) + strlen(name) + sbuf.st_size + 3); /* 2 = \n + \n + \0*/
1386 s = content;
1387 content += strlen(content);
1389 else{
1390 s = content = fs_get(strlen(sep) + strlen(name) + sbuf.st_size + 1); /* 2 = \n + \0 */
1391 *content = '\0';
1393 strncat(content, sep, strlen(sep));
1394 strncat(content, name, strlen(name));
1395 content += strlen(content);
1396 *content++ = '\n';
1398 while(so_readc(&c, in))
1399 *content++ = (char) c;
1400 *content = '\0';
1402 switch(ctype){
1403 case Private: ps_global->smime->privatecontent = s; break;
1404 case Public : ps_global->smime->publiccontent = s; break;
1405 case CACert : ps_global->smime->cacontent = s; break;
1406 default : break;
1409 rv = copy_dir_to_container(ctype, s);
1411 endadd:
1412 if(in) so_give(&in);
1414 return rv;
1419 * returns 0 on success, -1 on failure
1420 * contents is an argument which tells this function to write the value
1421 * of this variable instead of reading the contents of the directory.
1422 * If the var contents is not null use its value as the value of the
1423 * container.
1426 copy_dir_to_container(WhichCerts which, char *contents)
1428 int ret = 0, container = 0;
1429 BIO *bio_out = NULL, *bio_in = NULL;
1430 char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
1431 char *tempfile = NULL, fpath[MAXPATH+1];
1432 DIR *dirp;
1433 struct dirent *d;
1434 REMDATA_S *rd = NULL;
1435 char *configdir = NULL;
1436 char *configpath = NULL;
1437 char *configcontainer = NULL;
1438 char *filesuffix = NULL;
1439 char *ret_dir = NULL;
1441 dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1442 smime_init();
1444 srcpath[0] = '\0';
1445 dstpath[0] = '\0';
1446 file[0] = '\0';
1447 emailaddr[0] = '\0';
1449 if(which == Public){
1450 configdir = ps_global->VAR_PUBLICCERT_DIR;
1451 configpath = ps_global->smime->publicpath;
1452 configcontainer = cpystr(DF_PUBLIC_CONTAINER);
1453 filesuffix = ".crt";
1455 else if(which == Private){
1456 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1457 configpath = ps_global->smime->privatepath;
1458 configcontainer = cpystr(DF_PRIVATE_CONTAINER);
1459 filesuffix = ".key";
1461 else if(which == CACert){
1462 configdir = ps_global->VAR_CACERT_DIR;
1463 configpath = ps_global->smime->capath;
1464 configcontainer = cpystr(DF_CA_CONTAINER);
1465 filesuffix = ".crt";
1467 container = SMHOLDERTYPE(which) == Container;
1469 if(!(configdir && configdir[0])){
1470 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1471 return -1;
1474 if(!(configpath && configpath[0])){
1475 #ifdef APPLEKEYCHAIN
1476 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1477 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1478 return -1;
1480 #endif /* APPLEKEYCHAIN */
1481 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1482 return -1;
1485 if(!(filesuffix && strlen(filesuffix) == 4)){
1486 return -1;
1491 * If there is a legit directory to read from set up the
1492 * container file to write to.
1494 if(smime_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
1496 if(IS_REMOTE(configpath)){
1497 rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
1498 NULL, "Error: ",
1499 _("Can't access remote smime configuration."));
1500 if(!rd)
1501 return -1;
1503 (void) rd_read_metadata(rd);
1505 if(rd->access == MaybeRorW){
1506 if(rd->read_status == 'R')
1507 rd->access = ReadOnly;
1508 else
1509 rd->access = ReadWrite;
1512 if(rd->access != NoExists){
1514 rd_check_remvalid(rd, 1L);
1517 * If the cached info says it is readonly but
1518 * it looks like it's been fixed now, change it to readwrite.
1520 if(rd->read_status == 'R'){
1521 rd_check_readonly_access(rd);
1522 if(rd->read_status == 'W'){
1523 rd->access = ReadWrite;
1524 rd->flags |= REM_OUTOFDATE;
1526 else
1527 rd->access = ReadOnly;
1531 if(rd->flags & REM_OUTOFDATE){
1532 if(rd_update_local(rd) != 0){
1534 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1535 rd_close_remdata(&rd);
1536 return -1;
1539 else
1540 rd_open_remote(rd);
1542 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1543 rd_close_remdata(&rd);
1544 return -1;
1547 rd->flags |= DO_REMTRIM;
1549 strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
1550 dstpath[sizeof(dstpath)-1] = '\0';
1552 else{
1553 strncpy(dstpath, configpath, sizeof(dstpath)-1);
1554 dstpath[sizeof(dstpath)-1] = '\0';
1558 * dstpath is either the local Container file or the local cache file
1559 * for the remote Container file.
1561 tempfile = tempfile_in_same_dir(dstpath, "az", &ret_dir);
1565 * If there is a legit directory to read from and a tempfile
1566 * to write to we continue.
1568 if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
1570 if(contents != NULL){
1571 if(BIO_puts(bio_out, contents) < 0)
1572 ret = -1;
1574 else {
1575 if((dirp = opendir(srcpath)) != NULL){
1577 while((d=readdir(dirp)) && !ret){
1578 size_t ll;
1580 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, filesuffix)){
1582 /* copy file name to temp buffer */
1583 strncpy(emailaddr, d->d_name, sizeof(emailaddr)-1);
1584 emailaddr[sizeof(emailaddr)-1] = '\0';
1585 /* chop off suffix trailier */
1586 emailaddr[strlen(emailaddr)-4] = 0;
1589 * This is the separator between the contents of
1590 * different files.
1592 if(which == CACert){
1593 if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
1594 && (BIO_puts(bio_out, emailaddr) > 0)
1595 && (BIO_puts(bio_out, "\n") > 0)))
1596 ret = -1;
1598 else{
1599 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1600 && (BIO_puts(bio_out, emailaddr) > 0)
1601 && (BIO_puts(bio_out, "\n") > 0)))
1602 ret = -1;
1605 /* read then write contents of file */
1606 build_path(file, srcpath, d->d_name, sizeof(file));
1607 if(!(bio_in = BIO_new_file(file, "r")))
1608 ret = -1;
1610 if(!ret){
1611 int good_stuff = 0;
1613 while(BIO_gets(bio_in, line, sizeof(line)) > 0){
1614 if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
1615 good_stuff = 1;
1617 if(good_stuff)
1618 BIO_puts(bio_out, line);
1620 if(strncmp("-----END", line, strlen("-----END")) == 0)
1621 good_stuff = 0;
1625 BIO_free(bio_in);
1629 closedir(dirp);
1633 BIO_free(bio_out);
1635 if(!ret){
1636 if(container && configpath && *configpath){
1637 strncpy(fpath, configpath, sizeof(fpath));
1638 fpath[sizeof(fpath) - 1] = '\0';
1640 else if(ret_dir){
1641 if(strlen(dstpath) + strlen(configcontainer) - strlen(ret_dir) + 1 < sizeof(dstpath))
1642 snprintf(fpath, sizeof(fpath), "%s%c%s",
1643 dstpath, tempfile[strlen(ret_dir)], configcontainer);
1644 else
1645 ret = -1;
1647 else ret = -1;
1649 if(!ret){
1650 if(rename_file(tempfile, fpath) < 0){
1651 q_status_message2(SM_ORDER, 3, 3,
1652 _("Can't rename %s to %s"), tempfile, fpath);
1653 ret = -1;
1654 } else q_status_message1(SM_ORDER, 3, 3,
1655 _("saved container to %s"), fpath);
1658 /* if the container is remote, copy it */
1659 if(!ret && IS_REMOTE(configpath)){
1660 int e;
1661 char datebuf[200];
1663 datebuf[0] = '\0';
1665 if((e = rd_update_remote(rd, datebuf)) != 0){
1666 if(e == -1){
1667 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1668 _("Error opening temporary smime file %s: %s"),
1669 rd->lf, error_description(errno));
1670 dprint((1,
1671 "write_remote_smime: error opening temp file %s\n",
1672 rd->lf ? rd->lf : "?"));
1674 else{
1675 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1676 _("Error copying to %s: %s"),
1677 rd->rn, error_description(errno));
1678 dprint((1,
1679 "write_remote_smime: error copying from %s to %s\n",
1680 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1683 q_status_message(SM_ORDER | SM_DING, 5, 5,
1684 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1686 else{
1687 rd_update_metadata(rd, datebuf);
1688 rd->read_status = 'W';
1691 rd_close_remdata(&rd);
1696 if(tempfile)
1697 fs_give((void **) &tempfile);
1699 if(ret_dir)
1700 fs_give((void **) &ret_dir);
1702 if(configcontainer)
1703 fs_give((void **) &configcontainer);
1705 return ret;
1710 * returns 0 on success, -1 on failure
1713 copy_container_to_dir(WhichCerts which)
1715 char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
1716 char iobuf[4096];
1717 char *contents = NULL;
1718 char *leader = NULL;
1719 char *filesuffix = NULL;
1720 char *configdir = NULL;
1721 char *configpath = NULL;
1722 char *tempfile = NULL;
1723 char *p, *q, *line, *name, *certtext, *save_p;
1724 int len;
1725 BIO *in, *out;
1727 dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1728 smime_init();
1730 path[0] = '\0';
1732 if(which == Public){
1733 leader = EMAILADDRLEADER;
1734 contents = ps_global->smime->publiccontent;
1735 configdir = ps_global->VAR_PUBLICCERT_DIR;
1736 configpath = ps_global->smime->publicpath;
1737 filesuffix = ".crt";
1738 if(!(configpath && configpath[0])){
1739 #ifdef APPLEKEYCHAIN
1740 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1741 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1742 return -1;
1744 #endif /* APPLEKEYCHAIN */
1745 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1746 return -1;
1749 fs_give((void **) &ps_global->smime->publicpath);
1751 path[0] = '\0';
1752 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1753 && !IS_REMOTE(path))){
1754 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1755 return -1;
1758 if(can_access(path, ACCESS_EXISTS)){
1759 if(our_mkpath(path, 0700)){
1760 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1761 return -1;
1765 ps_global->smime->publicpath = cpystr(path);
1766 configpath = ps_global->smime->publicpath;
1768 else if(which == Private){
1769 leader = EMAILADDRLEADER;
1770 contents = ps_global->smime->privatecontent;
1771 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1772 configpath = ps_global->smime->privatepath;
1773 filesuffix = ".key";
1774 if(!(configpath && configpath[0])){
1775 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1776 return -1;
1779 fs_give((void **) &ps_global->smime->privatepath);
1781 path[0] = '\0';
1782 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1783 && !IS_REMOTE(path))){
1784 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1785 return -1;
1788 if(can_access(path, ACCESS_EXISTS)){
1789 if(our_mkpath(path, 0700)){
1790 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1791 return -1;
1795 ps_global->smime->privatepath = cpystr(path);
1796 configpath = ps_global->smime->privatepath;
1798 else if(which == CACert){
1799 leader = CACERTSTORELEADER;
1800 contents = ps_global->smime->cacontent;
1801 configdir = ps_global->VAR_CACERT_DIR;
1802 configpath = ps_global->smime->capath;
1803 filesuffix = ".crt";
1804 if(!(configpath && configpath[0])){
1805 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1806 return -1;
1809 fs_give((void **) &ps_global->smime->capath);
1811 path[0] = '\0';
1812 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1813 && !IS_REMOTE(path))){
1814 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1815 return -1;
1818 if(can_access(path, ACCESS_EXISTS)){
1819 if(our_mkpath(path, 0700)){
1820 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1821 return -1;
1825 ps_global->smime->capath = cpystr(path);
1826 configpath = ps_global->smime->capath;
1829 if(!(configdir && configdir[0])){
1830 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1831 return -1;
1834 if(!(configpath && configpath[0])){
1835 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1836 return -1;
1839 if(!(filesuffix && strlen(filesuffix) == 4)){
1840 return -1;
1844 if(contents && *contents){
1845 for(p = contents; *p != '\0';){
1846 line = p;
1848 while(*p && *p != '\n')
1849 p++;
1851 save_p = NULL;
1852 if(*p == '\n'){
1853 save_p = p;
1854 *p++ = '\0';
1857 if(strncmp(leader, line, strlen(leader)) == 0){
1858 name = line + strlen(leader);
1859 certtext = p;
1860 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
1861 if((q = strstr(certtext, leader)) != NULL){
1862 p = q;
1864 else{ /* end of file */
1865 q = certtext + strlen(certtext);
1866 p = q;
1869 strncpy(buf, name, sizeof(buf)-5);
1870 buf[sizeof(buf)-5] = '\0';
1871 strncat(buf, filesuffix, 5);
1872 build_path(file, configpath, buf, sizeof(file));
1874 in = BIO_new_mem_buf(certtext, q-certtext);
1875 if(in){
1876 tempfile = tempfile_in_same_dir(file, "az", NULL);
1877 out = NULL;
1878 if(tempfile)
1879 out = BIO_new_file(tempfile, "w");
1881 if(out){
1882 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1883 BIO_write(out, iobuf, len);
1885 BIO_free(out);
1887 if(rename_file(tempfile, file) < 0){
1888 q_status_message2(SM_ORDER, 3, 3,
1889 _("Can't rename %s to %s"),
1890 tempfile, file);
1891 return -1;
1894 fs_give((void **) &tempfile);
1897 BIO_free(in);
1902 if(save_p)
1903 *save_p = '\n';
1907 return 0;
1911 #ifdef APPLEKEYCHAIN
1914 copy_publiccert_container_to_keychain(void)
1916 /* NOT IMPLEMNTED */
1917 return -1;
1921 copy_publiccert_keychain_to_container(void)
1923 /* NOT IMPLEMNTED */
1924 return -1;
1927 #endif /* APPLEKEYCHAIN */
1931 * Get a pointer to a string describing the most recent OpenSSL error.
1932 * It's statically allocated, so don't change or attempt to free it.
1934 static const char *
1935 openssl_error_string(void)
1937 char *errs;
1938 const char *data = NULL;
1939 long errn;
1941 errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
1942 errs = (char*) ERR_reason_error_string(errn);
1944 if(errs)
1945 return errs;
1946 else if(data)
1947 return data;
1949 return "unknown error";
1953 /* Return true if the body looks like a PKCS7 object */
1955 is_pkcs7_body(BODY *body)
1957 int result;
1959 result = body->type==TYPEAPPLICATION &&
1960 body->subtype &&
1961 (strucmp(body->subtype,"pkcs7-mime")==0 ||
1962 strucmp(body->subtype,"x-pkcs7-mime")==0 ||
1963 strucmp(body->subtype,"pkcs7-signature")==0 ||
1964 strucmp(body->subtype,"x-pkcs7-signature")==0);
1966 return result;
1971 * Recursively stash a pointer to the decrypted data in our
1972 * manufactured body.
1973 * parameters: type: call of type 1, save the base and header for multipart messages
1974 call of type 0, do not save the base and header for multipart messages
1976 static void
1977 create_local_cache(char *h, char *base, BODY *b, int type)
1979 if(b->type==TYPEMULTIPART){
1980 PART *p;
1982 if(type == 1){
1983 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
1984 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
1985 } else if(type == 0){
1987 * We don't really want to copy the real body contents. It shouldn't be
1988 * used, and in the case of a message with attachments, we'll be
1989 * duplicating the files multiple times.
1991 cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
1993 for(p=b->nested.part; p; p=p->next)
1994 create_local_cache(h, base, (BODY *) p, type);
1997 else{
1998 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
1999 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2004 static long
2005 rfc822_output_func(void *b, char *string)
2007 BIO *bio = (BIO *) b;
2009 return(string ? *string ? (BIO_puts(bio, string) > 0 ? 1L : 0L)
2010 : (BIO_puts(bio, string) >= 0 ? 1L : 0L)
2011 : 0L);
2016 * Attempt to load the private key for the given PERSONAL_CERT.
2017 * This sets the appropriate passphrase globals in order to
2018 * interact with the user correctly.
2020 static int
2021 load_private_key(PERSONAL_CERT *pcert)
2023 if(!pcert->key){
2025 /* Try empty password by default */
2026 char *password = "";
2028 if(ps_global->smime
2029 && (ps_global->smime->need_passphrase
2030 || ps_global->smime->entered_passphrase)){
2031 /* We've already been in here and discovered we need a different password */
2033 if(ps_global->smime->entered_passphrase)
2034 password = (char *) ps_global->smime->passphrase; /* already entered */
2035 else
2036 return 0;
2039 ERR_clear_error();
2041 if(!(pcert->key = load_key(pcert, password, SM_NORMALCERT))){
2042 long err = ERR_get_error();
2044 /* Couldn't load key... */
2046 if(ps_global->smime && ps_global->smime->entered_passphrase){
2048 /* The user got the password wrong maybe? */
2050 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
2051 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
2052 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
2053 else
2054 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2056 /* This passphrase is no good; forget it */
2057 ps_global->smime->entered_passphrase = 0;
2060 if(ps_global->smime){
2061 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2062 ps_global->smime->need_passphrase = 1;
2063 if(ps_global->smime->passphrase_emailaddr){
2064 int i;
2065 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
2066 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
2067 fs_give((void **) ps_global->smime->passphrase_emailaddr);
2070 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
2073 return 0;
2075 else{
2076 /* This key will be cached, so we won't be called again */
2077 if(ps_global->smime){
2078 ps_global->smime->entered_passphrase = 0;
2079 ps_global->smime->need_passphrase = 0;
2083 return 1;
2086 return 0;
2090 static void
2091 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename, char *smime_type)
2093 b->type = TYPEAPPLICATION;
2094 b->subtype = cpystr(type);
2095 b->encoding = ENCBINARY;
2096 b->description = cpystr(description);
2098 b->disposition.type = cpystr("attachment");
2099 set_parameter(&b->disposition.parameter, "filename", filename);
2101 set_parameter(&b->parameter, "name", filename);
2102 if(smime_type && *smime_type)
2103 set_parameter(&b->parameter, "smime-type", smime_type);
2108 * Look for a personal certificate matching the
2109 * given address
2111 PERSONAL_CERT *
2112 match_personal_cert_to_email(ADDRESS *a)
2114 PERSONAL_CERT *pcert = NULL;
2115 char buf[MAXPATH];
2116 char **email;
2117 int i, done;
2119 if(!a || !a->mailbox || !a->host)
2120 return NULL;
2122 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2124 if(ps_global->smime){
2125 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
2126 pcert;
2127 pcert=pcert->next){
2129 if(!pcert->cert)
2130 continue;
2132 email = get_x509_subject_email(pcert->cert);
2134 done = 0;
2135 if(email != NULL){
2136 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
2137 if(email[i] != NULL) done++;
2138 for(i = 0; email[i] != NULL; i++)
2139 fs_give((void **)&email[i]);
2140 fs_give((void **)email);
2143 if(done > 0)
2144 break;
2148 return pcert;
2153 * Look for a personal certificate matching the from
2154 * (or reply_to? in the given envelope)
2156 PERSONAL_CERT *
2157 match_personal_cert(ENVELOPE *env)
2159 PERSONAL_CERT *pcert;
2161 pcert = match_personal_cert_to_email(env->reply_to);
2162 if(!pcert)
2163 pcert = match_personal_cert_to_email(env->from);
2165 return pcert;
2170 * Flatten the given body into its MIME representation.
2171 * Return the result in a BIO.
2173 static BIO *
2174 body_to_bio(BODY *body)
2176 BIO *bio = NULL;
2177 int len;
2179 bio = BIO_new(BIO_s_mem());
2180 if(!bio)
2181 return NULL;
2183 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
2184 pine_write_body_header(body, rfc822_output_func, bio);
2185 pine_rfc822_output_body(body, rfc822_output_func, bio);
2188 * Now need to truncate by two characters since the above
2189 * appends CRLF.
2191 if((len=BIO_ctrl_pending(bio)) > 1){
2192 BUF_MEM *biobuf = NULL;
2194 BIO_get_mem_ptr(bio, &biobuf);
2195 if(biobuf){
2196 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
2200 return bio;
2204 static BIO *
2205 bio_from_store(STORE_S *store)
2207 BIO *ret = NULL;
2209 if(store && store->src == BioType && store->txt){
2210 ret = (BIO *) store->txt;
2213 return(ret);
2217 * Encrypt file; given a path (char *) fp, replace the file
2218 * by an encrypted version of it. If (char *) text is not null, then
2219 * replace the text of (char *) fp by the encrypted version of (char *) text.
2220 * certpath is the FULL path to the file containing the certificate used for
2221 * encryption.
2224 encrypt_file(char *fp, char *text, PERSONAL_CERT *pc)
2226 const EVP_CIPHER *cipher = NULL;
2227 STACK_OF(X509) *encerts = NULL;
2228 BIO *out = NULL;
2229 PKCS7 *p7 = NULL;
2230 int rv = 0;
2232 if(pc == NULL)
2233 return 0;
2235 cipher = EVP_aes_256_cbc();
2236 encerts = sk_X509_new_null();
2238 sk_X509_push(encerts, X509_dup(pc->cert));
2240 if(text){
2241 if((out = BIO_new(BIO_s_mem())) == NULL)
2242 goto end;
2243 (void) BIO_reset(out);
2244 BIO_puts(out, text);
2246 else{
2247 if(!(out = BIO_new_file(fp, "rb")))
2248 goto end;
2250 BIO_read_filename(out, fp);
2253 if((p7 = PKCS7_encrypt(encerts, out, cipher, 0)) == NULL)
2254 goto end;
2255 BIO_set_close(out, BIO_CLOSE);
2256 BIO_free(out);
2257 if(!(out = BIO_new_file(fp, "w")))
2258 goto end;
2259 BIO_reset(out);
2260 rv = PEM_write_bio_PKCS7(out, p7);
2261 BIO_flush(out);
2263 end:
2264 if(out != NULL)
2265 BIO_free(out);
2266 PKCS7_free(p7);
2267 sk_X509_pop_free(encerts, X509_free);
2269 return rv;
2273 * Encrypt a message on the way out. Called from call_mailer in send.c
2274 * The body may be reallocated.
2277 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
2279 PKCS7 *p7 = NULL;
2280 BIO *in = NULL;
2281 BIO *out = NULL;
2282 const EVP_CIPHER *cipher = NULL;
2283 STACK_OF(X509) *encerts = NULL;
2284 STORE_S *outs = NULL;
2285 PINEFIELD *pf;
2286 ADDRESS *a;
2287 BODY *body = *bodyP;
2288 BODY *newBody = NULL;
2289 int result = 0;
2290 X509 *cert;
2291 char buf[MAXPATH];
2293 dprint((9, "encrypt_outgoing_message()"));
2294 smime_init();
2296 cipher = EVP_aes_256_cbc();
2298 encerts = sk_X509_new_null();
2300 /* Look for a certificate for each of the recipients */
2301 for(pf = header->local; pf && pf->name; pf = pf->next)
2302 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
2303 for(a=*pf->addr; a; a=a->next){
2304 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2306 if((cert = get_cert_for(buf, Public)) != NULL){
2307 sk_X509_push(encerts,cert);
2308 }else{
2309 q_status_message2(SM_ORDER, 1, 1,
2310 _("Unable to find certificate for <%s@%s>"),
2311 a->mailbox, a->host);
2312 goto end;
2317 /* add the sender's certificate so that they can decrypt the message too */
2318 for(a=header->env->from; a ; a = a->next){
2319 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2321 if((cert = get_cert_for(buf, Public)) != NULL
2322 && sk_X509_find(encerts, cert) == -1)
2323 sk_X509_push(encerts,cert);
2326 in = body_to_bio(body);
2328 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
2330 outs = so_get(BioType, NULL, EDIT_ACCESS);
2331 out = bio_from_store(outs);
2333 i2d_PKCS7_bio(out, p7);
2334 (void) BIO_flush(out);
2336 so_seek(outs, 0, SEEK_SET);
2338 newBody = mail_newbody();
2340 newBody->type = TYPEAPPLICATION;
2341 newBody->subtype = cpystr("pkcs7-mime");
2342 newBody->encoding = ENCBINARY;
2344 newBody->disposition.type = cpystr("attachment");
2345 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
2347 newBody->description = cpystr("S/MIME Encrypted Message");
2348 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
2349 set_parameter(&newBody->parameter, "name", "smime.p7m");
2351 newBody->contents.text.data = (unsigned char *) outs;
2353 *bodyP = newBody;
2355 result = 1;
2357 end:
2359 BIO_free(in);
2360 PKCS7_free(p7);
2361 sk_X509_pop_free(encerts, X509_free);
2363 dprint((9, "encrypt_outgoing_message returns %d", result));
2364 return result;
2369 Get (and decode) the body of the given section of msg
2371 static STORE_S*
2372 get_part_contents(long msgno, const char *section)
2374 long len;
2375 gf_io_t pc;
2376 STORE_S *store = NULL;
2377 char *err;
2379 store = so_get(CharStar, NULL, EDIT_ACCESS);
2380 if(store){
2381 gf_set_so_writec(&pc,store);
2383 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
2385 gf_clear_so_writec(store);
2387 so_seek(store, 0, SEEK_SET);
2389 if(err)
2390 so_give(&store);
2393 return store;
2397 static PKCS7 *
2398 get_pkcs7_from_part(long msgno,const char *section)
2400 STORE_S *store = NULL;
2401 PKCS7 *p7 = NULL;
2402 BIO *in = NULL;
2404 store = get_part_contents(msgno, section);
2406 if(store){
2407 if(store->src == CharStar){
2408 int len;
2411 * We're reaching inside the STORE_S structure. We should
2412 * probably have a way to get the length, instead.
2414 len = (int) (store->eod - store->dp);
2415 in = BIO_new_mem_buf(store->txt, len);
2417 else{ /* just copy it */
2418 unsigned char c;
2420 in = BIO_new(BIO_s_mem());
2421 (void) BIO_reset(in);
2423 so_seek(store, 0L, 0);
2424 while(so_readc(&c, store)){
2425 BIO_write(in, &c, 1);
2429 if(in){
2430 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2431 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
2432 /* error */
2435 BIO_free(in);
2438 so_give(&store);
2441 return p7;
2444 int same_cert(X509 *x, X509 *cert)
2446 char bufcert[256], bufx[256];
2447 int rv = 0;
2449 get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert), ":");
2450 get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx), ":");
2451 if(strcmp(bufx, bufcert) == 0)
2452 rv = 1;
2454 return rv;
2458 /* extract and save certificates from a PKCS7 package. The ctype variable
2459 * tells us if we want to extract it to a public/ or a ca/ directory. The
2460 * later makes sense only for recoverable errors (errors that can be fixed
2461 * by saving to the ca/ directory before we verify the signature).
2462 * Return value:
2463 * 0 - no errors (in public/) no need to try again,
2464 * or validated self signed certificate (in ca/)
2465 * < 0 - certificate error is not recoverable, don't even think about it.
2468 int smime_extract_and_save_cert(PKCS7 *p7)
2470 STACK_OF(X509) *signers;
2471 X509 *x, *cert;
2472 char **email;
2473 int i, j;
2475 if((signers = PKCS7_get0_signers(p7, NULL, 0)) == NULL)
2476 return -1;
2478 for(i = 0; i < sk_X509_num(signers); i++){
2479 if((x = sk_X509_value(signers,i)) == NULL)
2480 continue;
2482 if((email = get_x509_subject_email(x)) != NULL){
2483 for(j = 0; email[j] != NULL; j++){
2484 if((cert = get_cert_for(email[j], Public)) == NULL
2485 || same_cert(x, cert) == 0)
2486 save_cert_for(email[j], x, Public);
2487 X509_free(cert);
2488 fs_give((void **) &email[i]);
2490 fs_give((void **) email);
2493 sk_X509_free(signers);
2495 return 0;
2499 * Try to verify a signature.
2501 * p7 - the pkcs7 object to verify
2502 * in - the plain data to verify (NULL if not detached)
2503 * out - BIO to which to write the opaque data
2504 * silent - if non zero, do not print errors, only print success.
2506 static int
2507 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
2509 STACK_OF(X509) *otherCerts = NULL;
2510 CertList *cl;
2511 int result;
2512 int flags;
2513 const char *data;
2514 long err;
2516 if(!s_cert_store){
2517 if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
2518 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2520 return -1;
2523 smime_extract_and_save_cert(p7);
2525 flags = F_ON(F_USE_CERT_STORE_ONLY, ps_global) ? PKCS7_NOINTERN : 0;
2527 if(ps_global->smime->publiccertlist == NULL){
2528 renew_cert_data(&ps_global->smime->publiccertlist, Public);
2529 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next){
2530 if(cl->x509_cert == NULL){
2531 char *s = strrchr(cl->name, '.');
2532 *s = '\0';
2533 cl->x509_cert = get_cert_for(cl->name, Public);
2534 *s = '.';
2539 if(ps_global->smime->publiccertlist){
2540 otherCerts = sk_X509_new_null();
2541 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next)
2542 if(cl->x509_cert != NULL)
2543 sk_X509_push(otherCerts, X509_dup(cl->x509_cert));
2546 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, flags);
2548 sk_X509_pop_free(otherCerts, X509_free);
2550 if(result){
2551 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
2553 else{
2554 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2556 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
2558 /* Retry verification so we can get the plain text */
2559 /* Might be better to reimplement PKCS7_verify here? */
2561 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY);
2563 if (!silent) q_status_message1(SM_ORDER | SM_DING, 3, 3,
2564 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
2567 return result;
2571 void
2572 free_smime_body_sparep(void **sparep)
2574 char *s;
2575 SIZEDTEXT *st;
2576 if(sparep && *sparep){
2577 switch(get_smime_sparep_type(*sparep)){
2578 case P7Type: PKCS7_free((PKCS7 *) get_smime_sparep_data(*sparep));
2579 break;
2580 case CharType: s = (char *)get_smime_sparep_data(*sparep);
2581 fs_give((void **) &s);
2582 break;
2583 case SizedText : st = (SIZEDTEXT *)get_smime_sparep_data(*sparep);
2584 fs_give((void **) &st->data);
2585 fs_give((void **) &st);
2586 break;
2587 default : break;
2589 ((SMIME_SPARE_S *)(*sparep))->data = NULL;
2590 fs_give(sparep);
2594 /* Big comment, explaining the mess that exists out there, and how we deal
2595 with it, and also how we solve the problems that are created this way.
2597 When Alpine sends a message, it constructs that message, computes the
2598 signature, but then it forgets the message it signed and reconstructs it
2599 again. Since it signs a message containing a notice about "mime aware
2600 tools", but it does not send that we do not include that in the part
2601 that is signed, and that takes care of much of the problems.
2603 Another problem is what is received from the servers. All servers tested
2604 seem to transmit the message that was signed intact and Alpine can check
2605 the signature correctly. That is not a problem. The problem arises when
2606 the message includes attachments. In this case different servers send
2607 different things, so it will be up to us to figure out what is the text
2608 that was actually signed. Confused? here is the story:
2610 When a message containing and attachment is sent by Alpine, UW-IMAP,
2611 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2612 that was sent by Alpine, but GMX.com, Exchange, and probably other
2613 servers add a trailing \r\n in the message, so when validating the
2614 signature, these messages will not validate. There are several things
2615 that can be done.
2617 1. Add a trailing \r\n to any message that contains attachments, sign that
2618 and send that. In this way, all messages will validate with all
2619 servers.
2621 2. Compatibility mode: If a message has an attachment, contains a trailing
2622 \r\n and does not validate (sent by an earlier version of Alpine),
2623 remove the trailing \r\n and try to revalidate again.
2625 3. We do not add \r\n to validate a message that we sent, because that
2626 would only work in Alpine, and not in any other client. That would
2627 not be a good thing to do.
2629 PART II
2631 Now we have to deal with encrypted and signed messages. The problem is
2632 that c-client makes all its pointers point to "on disk" content, but
2633 since we decrypted the data earlier, we have to make sure of two things.
2634 One is that we saved that data (so we do not have to decrypt it again)
2635 and second that we can use it.
2637 In order to save the data we use create_local_cache, so that we do not
2638 have to redecrypt the message. Once this is saved, c-client functions will
2639 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2641 PART III
2643 When we are trying to verify messages with detached signatures, some
2644 imap servers send incorrect information in the mail_fetch_mime call. By
2645 incorrect I mean that this is not fetched directly from the message, but
2646 it is read from the message, processed, and then the processed part is
2647 sent to us, so this text might not agree with what is in the message,
2648 and so the validation of the signature might fail. However, the good
2649 news is that the message validates if saved to a local folder. This
2650 means that if normal validation does not work we can make it work by
2651 saving the message locally and validating that. This is implemented
2652 below, and causes delay in the display of the message. I am considering
2653 at this time not to do this automatically, but wait for the user to tell
2654 us to do it for them by means of a command available in the
2655 mail_view_screen. This might help in other situations, where a message
2656 is supposed to have an attachment, but it can not be seen in the
2657 processed text. Nevertheless, at this time, this is automatic, and is
2658 causing a delay in the processing of the message, but it is validating
2659 correctly all messages.
2661 PART IV
2663 When the user sends a message as encrypted and signed, this code used to
2664 encrypt first, and then sign the pkcs7 body, but it turns out that some
2665 other clients can not handle these messages. While we could argue that the
2666 other clients need to improve, we will support reading messages in both
2667 ways, and will send messages using this technique; that is, signed first,
2668 encrypted second. It seems that all tested clients support this way, so it
2669 should be safe to do so.
2672 typedef struct smime_filter_s {
2673 void (*filter)();
2674 } SMIME_FILTER_S;
2676 SMIME_FILTER_S sig_filter[] = {
2677 {smime_remove_trailing_crlf},
2678 {smime_remove_folding_space}
2681 #define TOTAL_FILTERS (sizeof(sig_filter)/sizeof(sig_filter[0]))
2682 #define TOTAL_SIGFLTR (1 << TOTAL_FILTERS) /* not good, keep filters to a low number */
2684 void
2685 smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen,
2686 char **bodytext, unsigned long *bodylen)
2688 if(*bodylen > 2 && !strncmp(*bodytext+*bodylen-2, "\r\n", 2))
2689 *bodylen -= 2;
2692 void
2693 smime_remove_folding_space(char **mimetext, unsigned long *mimelen,
2694 char **bodytext, unsigned long *bodylen)
2696 char *s = NULL, *t;
2697 unsigned long mlen = *mimelen;
2699 if(*mimetext){
2700 for (s = t = *mimetext; t - *mimetext < *mimelen; ){
2701 if(*t == '\r' && *(t+1) == '\n' && (*(t+2) == '\t' || *(t+2) == ' ')){
2702 *s++ = ' ';
2703 t += 3;
2704 mlen -= 2;
2706 else
2707 *s++ = *t++;
2709 *mimelen = mlen;
2714 smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag)
2716 int result, i, j, flag;
2717 char *mtext, *btext;
2718 unsigned long mlen, blen;
2719 BIO *in;
2721 mtext = mimelen ? fs_get(mimelen+1) : NULL;
2722 btext = fs_get(bodylen+1);
2724 flag = 1; /* silence all failures */
2725 for(i = 1; result == 0 && i < TOTAL_SIGFLTR; i++){
2726 if((in = BIO_new(BIO_s_mem())) == NULL)
2727 return -1;
2729 (void) BIO_reset(in);
2731 if(i+1 == TOTAL_SIGFLTR)
2732 flag = nflag;
2734 if(mimelen)
2735 strncpy(mtext, mimetext, mlen = mimelen);
2736 strncpy(btext, bodytext, blen = bodylen);
2737 for(j = 0; j < TOTAL_FILTERS; j++)
2738 if((i >> j) & 1)
2739 (sig_filter[j].filter)(&mtext, &mlen, &btext, &blen);
2740 if(mtext != NULL)
2741 BIO_write(in, mtext, mlen);
2742 BIO_write(in, btext, blen);
2743 result = do_signature_verify(p7, in, NULL, flag);
2744 BIO_free(in);
2746 if(mtext) fs_give((void **)&mtext);
2747 if(btext) fs_give((void **)&btext);
2748 return result;
2752 * Given a multipart body of type multipart/signed, attempt to verify it.
2753 * Returns non-zero if the body was changed.
2755 static int
2756 do_detached_signature_verify(BODY *b, long msgno, char *section)
2758 PKCS7 *p7 = NULL;
2759 BIO *in = NULL;
2760 PART *p;
2761 int result, modified_the_body = 0;
2762 int flag; /* 1 silent, 0 not silent */
2763 unsigned long mimelen, bodylen;
2764 char newSec[100], *mimetext, *bodytext;
2765 char *what_we_did;
2766 SIZEDTEXT *st;
2768 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"));
2770 smime_init();
2772 /* if it was signed and then encrypted, use the decrypted text
2773 * to check the validity of the signature
2775 if(b->sparep){
2776 if(get_smime_sparep_type(b->sparep) == SizedText){
2777 /* bodytext includes mimetext */
2778 st = (SIZEDTEXT *) get_smime_sparep_data(b->sparep);
2779 bodytext = (char *) st->data;
2780 bodylen = st->size;
2781 mimetext = NULL;
2782 mimelen = 0L;
2785 else{
2786 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
2787 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
2788 if(mimetext)
2789 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
2791 if(mimetext == NULL || bodytext == NULL)
2792 return modified_the_body;
2795 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
2797 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
2798 || (in = BIO_new(BIO_s_mem())) == NULL)
2799 return modified_the_body;
2801 (void) BIO_reset(in);
2802 if(mimetext != NULL)
2803 BIO_write(in, mimetext, mimelen);
2804 BIO_write(in, bodytext, bodylen);
2806 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
2807 flag = (mimelen == 0 || !IS_REMOTE(ps_global->mail_stream->mailbox))
2808 ? 0 : 1;
2809 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, flag);
2810 if(result < 0)
2811 return modified_the_body;
2812 if(result == 0
2813 && mimelen > 0 /* do not do this for encrypted messages */
2814 && IS_REMOTE(ps_global->mail_stream->mailbox)){
2815 char *fetch;
2816 unsigned long hlen, tlen;
2817 STORE_S *msg_so;
2819 BIO_free(in);
2820 if((in = BIO_new(BIO_s_mem())) != NULL
2821 && (fetch = mail_fetch_header(ps_global->mail_stream, msgno, NULL,
2822 NULL, &hlen, FT_PEEK)) != NULL
2823 && (msg_so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL
2824 && so_nputs(msg_so, fetch, (long) hlen)
2825 && (fetch = pine_mail_fetch_text(ps_global->mail_stream, msgno, NULL,
2826 &tlen, FT_PEEK)) != NULL
2827 && so_nputs(msg_so, fetch, tlen)){
2828 STRING bs;
2829 char *h = (char *) so_text(msg_so);
2830 char *bstart = strstr(h, "\r\n\r\n");
2831 ENVELOPE *env;
2832 BODY *body, *tmpB;
2834 bstart += 4;
2835 INIT(&bs, mail_string, bstart, tlen);
2836 rfc822_parse_msg_full(&env, &body, h, bstart-h-4, &bs, BADHOST, 0, 0);
2837 mail_free_envelope(&env);
2839 mail_free_body_part(&b->nested.part);
2840 tmpB = mail_body_section(body, section);
2841 if(MIME_MSG(tmpB->type, tmpB->subtype))
2842 b->nested.part = tmpB->nested.msg->body->nested.part;
2843 else
2844 b->nested.part = tmpB->nested.part;
2845 create_local_cache(bstart, bstart, &b->nested.part->body, 1);
2846 modified_the_body = 1;
2848 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
2850 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
2852 if(mimetext)
2853 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
2855 if (mimetext == NULL || bodytext == NULL)
2856 return modified_the_body;
2858 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
2860 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL)
2861 return modified_the_body;
2863 (void) BIO_reset(in);
2864 BIO_write(in, mimetext, mimelen);
2865 BIO_write(in, bodytext, bodylen);
2866 so_give(&msg_so);
2868 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
2869 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, 0);
2870 if(result < 0)
2871 return modified_the_body;
2877 BIO_free(in);
2878 if(b->subtype)
2879 fs_give((void**) &b->subtype);
2881 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
2882 b->encoding = ENC8BIT;
2884 if(b->description)
2885 fs_give ((void**) &b->description);
2887 what_we_did = result ? _("This message was cryptographically signed.") :
2888 _("This message was cryptographically signed but the signature could not be verified.");
2890 b->description = cpystr(what_we_did);
2892 b->sparep = create_smime_sparep(P7Type, p7);
2894 p = b->nested.part;
2896 /* p is signed plaintext */
2897 if(p && p->next)
2898 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
2900 modified_the_body = 1;
2902 return modified_the_body;
2906 PERSONAL_CERT *
2907 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
2909 PERSONAL_CERT *x = NULL;
2911 if(ps_global->smime){
2912 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
2913 X509 *mine;
2915 mine = x->cert;
2917 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,mine->cert_info->issuer) &&
2918 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,mine->cert_info->serialNumber)){
2919 break;
2924 return x;
2928 static PERSONAL_CERT *
2929 find_certificate_matching_pkcs7(PKCS7 *p7)
2931 int i;
2932 STACK_OF(PKCS7_RECIP_INFO) *recips;
2933 PERSONAL_CERT *x = NULL;
2935 recips = p7->d.enveloped->recipientinfo;
2937 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
2938 PKCS7_RECIP_INFO *ri;
2940 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
2942 if((x=find_certificate_matching_recip_info(ri))!=0){
2943 break;
2947 return x;
2950 /* decrypt an encrypted file.
2951 Args: fp - the path to the encrypted file.
2952 rv - a code that tells the caller what happened inside the function
2953 pcert - a personal certificate that was used to encrypt this file
2954 Returns the decoded text allocated in a char *, whose memory must be
2955 freed by caller
2958 char *
2959 decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc)
2961 PKCS7 *p7 = NULL;
2962 char *text, *tmp;
2963 BIO *in = NULL, *out = NULL;
2964 int i, j;
2965 long unsigned int len;
2966 void *ret;
2968 if(pc == NULL || (text = read_file(fp, 0)) == NULL || *text == '\0')
2969 return NULL;
2971 tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1);
2972 for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0'
2973 && text[i] != '-'; j++, i++)
2974 tmp[j] = text[i];
2975 tmp[j] = '\0';
2977 ret = rfc822_base64((unsigned char *)tmp, strlen(tmp), &len);
2979 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
2980 p7 = d2i_PKCS7_bio(in, NULL);
2981 BIO_free(in);
2984 if (text) fs_give((void **)&text);
2985 if (ret) fs_give((void **)&ret);
2987 if (rv) *rv = pc->key == NULL ? -1 : 1;
2989 out = BIO_new(BIO_s_mem());
2990 (void) BIO_reset(out);
2992 i = PKCS7_decrypt(p7, pc->key, pc->cert, out, 0);
2994 if(i == 0){
2995 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
2996 (char*) openssl_error_string());
2997 goto end;
3000 BIO_get_mem_data(out, &tmp);
3002 text = cpystr(tmp);
3003 BIO_free(out);
3005 end:
3006 PKCS7_free(p7);
3008 return text;
3012 * Try to decode (decrypt or verify a signature) a PKCS7 body
3013 * Returns non-zero if something was changed.
3015 static int
3016 do_decoding(BODY *b, long msgno, const char *section)
3018 int modified_the_body = 0;
3019 BIO *out = NULL;
3020 PKCS7 *p7 = NULL;
3021 X509 *recip = NULL;
3022 EVP_PKEY *key = NULL;
3023 PERSONAL_CERT *pcert = NULL;
3024 char *what_we_did = "";
3025 char null[1];
3027 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"));
3028 null[0] = '\0';
3029 smime_init();
3032 * Extract binary data from part to an in-memory store
3035 if(b->sparep){
3036 if(get_smime_sparep_type(b->sparep) == P7Type)
3037 p7 = (PKCS7*) get_smime_sparep_data(b->sparep);
3039 else{
3040 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
3041 if(!p7){
3042 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
3043 (char*) openssl_error_string());
3044 goto end;
3048 * Save the PKCS7 object for later dealings by the user interface.
3049 * It will be cleaned up when the body is garbage collected.
3051 b->sparep = create_smime_sparep(P7Type, p7);
3054 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
3056 if(PKCS7_type_is_signed(p7)){
3057 int sigok;
3059 out = BIO_new(BIO_s_mem());
3060 (void) BIO_reset(out);
3061 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3063 sigok = do_signature_verify(p7, NULL, out, 0);
3065 what_we_did = sigok ? _("This message was cryptographically signed.") :
3066 _("This message was cryptographically signed but the signature could not be verified.");
3068 /* make sure it's null terminated */
3069 BIO_write(out, null, 1);
3071 else if(!PKCS7_type_is_enveloped(p7)){
3072 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
3073 goto end;
3075 else{ /* It *is* enveloped */
3076 int decrypt_result;
3078 what_we_did = _("This message was encrypted.");
3080 /* now need to find a cert that can decrypt this */
3081 pcert = find_certificate_matching_pkcs7(p7);
3083 if(!pcert){
3084 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3085 goto end;
3088 recip = pcert->cert;
3090 if(!load_private_key(pcert)
3091 && ps_global->smime
3092 && ps_global->smime->need_passphrase
3093 && !ps_global->smime->already_auto_asked){
3094 /* Couldn't load key with blank password, ask user */
3095 ps_global->smime->already_auto_asked = 1;
3096 if(pith_opt_smime_get_passphrase){
3097 (*pith_opt_smime_get_passphrase)();
3098 load_private_key(pcert);
3102 key = pcert->key;
3103 if(!key)
3104 goto end;
3106 out = BIO_new(BIO_s_mem());
3107 (void) BIO_reset(out);
3108 BIO_puts(out, "MIME-Version: 1.0\r\n");
3110 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
3112 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3113 forget_private_keys();
3115 if(!decrypt_result){
3116 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3117 (char*) openssl_error_string());
3118 goto end; }
3120 BIO_write(out, null, 1);
3124 * We've now produced a flattened MIME object in BIO out.
3125 * It needs to be turned back into a BODY.
3128 if(out){
3129 BODY *body;
3130 ENVELOPE *env;
3131 char *h = NULL;
3132 char *bstart;
3133 STRING s;
3134 BUF_MEM *bptr = NULL;
3136 BIO_get_mem_ptr(out, &bptr);
3137 if(bptr)
3138 h = bptr->data;
3140 /* look for start of body */
3141 bstart = strstr(h, "\r\n\r\n");
3143 if(!bstart){
3144 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
3146 else{
3147 SIZEDTEXT *st;
3148 bstart += 4; /* skip over CRLF*2 */
3150 INIT(&s, mail_string, bstart, strlen(bstart));
3151 rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
3152 mail_free_envelope(&env); /* Don't care about this */
3154 if(body->type == TYPEMULTIPART
3155 && !strucmp(body->subtype, "SIGNED")){
3156 char *cookie = NULL;
3157 PARAMETER *param;
3158 for (param = body->parameter; param && !cookie; param = param->next)
3159 if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value;
3160 if(cookie != NULL){
3161 st = fs_get(sizeof(SIZEDTEXT));
3162 st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */
3163 st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4);
3164 body->sparep = create_smime_sparep(SizedText, (void *)st);
3166 else
3167 q_status_message(SM_ORDER, 3, 3, _("Couldn't find cookie in attachment list."));
3169 body->mime.offset = 0;
3170 body->mime.text.size = 0;
3173 * Now convert original body (application/pkcs7-mime)
3174 * to a multipart body with one sub-part (the decrypted body).
3175 * Note that the sub-part may also be multipart!
3178 b->type = TYPEMULTIPART;
3179 if(b->subtype)
3180 fs_give((void**) &b->subtype);
3183 * This subtype is used in mailview.c to annotate the display of
3184 * encrypted or signed messages. We know for sure then that it's a PKCS7
3185 * part because the sparep field is set to the PKCS7 object (see above).
3187 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3188 b->encoding = ENC8BIT;
3190 if(b->description)
3191 fs_give((void**) &b->description);
3193 b->description = cpystr(what_we_did);
3195 if(b->disposition.type)
3196 fs_give((void **) &b->disposition.type);
3198 if(b->contents.text.data)
3199 fs_give((void **) &b->contents.text.data);
3201 if(b->parameter)
3202 mail_free_body_parameter(&b->parameter);
3204 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3205 b->nested.part = fs_get(sizeof(PART));
3206 b->nested.part->body = *body;
3207 b->nested.part->next = NULL;
3209 fs_give((void**) &body);
3212 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3213 * the decrypted data. Otherwise, it'll try to load it from the original
3214 * data. Eek.
3216 create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body, 0);
3218 modified_the_body = 1;
3222 end:
3223 if(out)
3224 BIO_free(out);
3226 return modified_the_body;
3231 * Recursively handle PKCS7 bodies in our message.
3233 * Returns non-zero if some fiddling was done.
3235 static int
3236 do_fiddle_smime_message(BODY *b, long msgno, char *section)
3238 int modified_the_body = 0;
3240 if(!b)
3241 return 0;
3243 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"));
3245 if(is_pkcs7_body(b)){
3247 if(do_decoding(b, msgno, section)){
3249 * b should now be a multipart message:
3250 * fiddle it too in case it's been multiply-encrypted!
3253 /* fallthru */
3254 modified_the_body = 1;
3258 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
3260 PART *p;
3261 int partNum;
3262 char newSec[100];
3264 if(MIME_MULT_SIGNED(b->type, b->subtype)){
3268 * Ahah. We have a multipart signed entity.
3270 * Multipart/signed
3271 * part 1 (signed thing)
3272 * part 2 (the pkcs7 signature)
3274 * We're going to convert that to
3276 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3277 * part 1 (signed thing)
3278 * part 2 has been freed
3280 * We also extract the signature from part 2 and save it
3281 * in the multipart body->sparep, and we add a description
3282 * in the multipart body->description.
3285 * The results of a decrypted message will be similar. It
3286 * will be
3288 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3289 * part 1 (decrypted thing)
3292 modified_the_body += do_detached_signature_verify(b, msgno, section);
3294 else if(MIME_MSG(b->type, b->subtype)){
3295 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
3297 else{
3299 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
3300 /* Append part number to the section string */
3302 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
3304 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
3309 return modified_the_body;
3314 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3315 * Returns non-zero if something was changed.
3318 fiddle_smime_message(BODY *b, long msgno)
3320 return do_fiddle_smime_message(b, msgno, "");
3324 /********************************************************************************/
3328 * Output a string in a distinctive style
3330 void
3331 gf_puts_uline(char *txt, gf_io_t pc)
3333 pc(TAG_EMBED); pc(TAG_BOLDON);
3334 gf_puts(txt, pc);
3335 pc(TAG_EMBED); pc(TAG_BOLDOFF);
3338 /* get_chain_for_cert: error and level are mandatory arguments */
3339 STACK_OF(X509) *
3340 get_chain_for_cert(X509 *cert, int *error, int *level)
3342 STACK_OF(X509) *chain = NULL;
3343 X509_STORE_CTX *ctx;
3344 X509 *x, *xtmp;
3345 int rc; /* return code */
3347 *level = -1;
3348 *error = 0;
3349 ERR_clear_error();
3350 if((ctx = X509_STORE_CTX_new()) != NULL){
3351 X509_STORE_set_flags(s_cert_store, 0);
3352 if(!X509_STORE_CTX_init(ctx, s_cert_store, cert, NULL))
3353 *error = X509_STORE_CTX_get_error(ctx);
3354 else if((chain = sk_X509_new_null()) != NULL){
3355 for(x = cert; ; x = xtmp){
3356 if(++*level > 0)
3357 sk_X509_push(chain, X509_dup(x));
3358 rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x);
3359 if(rc < 0)
3360 *error = 1;
3361 if(rc <= 0)
3362 break;
3363 if(!X509_check_issued(xtmp, xtmp))
3364 break;
3367 X509_STORE_CTX_free(ctx);
3369 return chain;
3374 * Sign a message. Called from call_mailer in send.c.
3376 * This takes the header for the outgoing message as well as a pointer
3377 * to the current body (which may be reallocated).
3378 * The last argument (BODY **bp) is an argument that tells Alpine
3379 * if the body has 8 bit. if *bp is not null we compute two signatures
3380 * one for the quoted-printable encoded message, and another for the
3381 * 8bit encoded message. We return the signature for the 8bit encoded
3382 * part in p2->body.mime.text.data.
3383 * The reason why we compute two signatures is so that we can decide
3384 * which one to use later, and we only do it in the case that *bp is
3385 * not null. If we did not do this, then we might not be able to sign
3386 * a message until we log in to the smtp server, so instead of doing
3387 * that, we get ready for any possible situation we might find.
3390 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp)
3392 STORE_S *outs = NULL;
3393 STORE_S *outs_2 = NULL;
3394 BODY *body = *bodyP;
3395 BODY *newBody = NULL;
3396 PART *p1 = NULL;
3397 PART *p2 = NULL;
3398 PERSONAL_CERT *pcert;
3399 BIO *in = NULL;
3400 BIO *in_2 = NULL;
3401 BIO *out = NULL;
3402 BIO *out_2 = NULL;
3403 PKCS7 *p7 = NULL;
3404 PKCS7 *p7_2 = NULL;
3405 STACK_OF(X509) *chain;
3406 int result = 0, error;
3407 int flags = dont_detach ? 0 : PKCS7_DETACHED;
3408 int level;
3410 dprint((9, "sign_outgoing_message()"));
3412 smime_init();
3414 /* Look for a private key matching the sender address... */
3416 pcert = match_personal_cert(header->env);
3418 if(!pcert){
3419 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
3420 goto end;
3423 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
3424 /* Couldn't load key with blank password, try again */
3425 if(pith_opt_smime_get_passphrase){
3426 (*pith_opt_smime_get_passphrase)();
3427 load_private_key(pcert);
3431 if(!pcert->key)
3432 goto end;
3434 if(((chain = get_chain_for_cert(pcert->cert, &error, &level)) != NULL && error)
3435 || level == 0){
3436 sk_X509_pop_free(chain, X509_free);
3437 chain = NULL;
3440 if(error)
3441 q_status_message(SM_ORDER, 1, 1,
3442 _("Not all certificates needed to verify signature included in signed message"));
3444 in = body_to_bio(body);
3446 p7 = PKCS7_sign(pcert->cert, pcert->key, chain, in, flags);
3448 if(bp && *bp){
3449 int i, save_encoding;
3451 for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++);
3453 if(i > ENCMAX){ /* no empty encoding slots! */
3454 *bp = NULL;
3456 else {
3457 save_encoding = (*bp)->encoding;
3458 body_encodings[(*bp)->encoding = i] = body_encodings[ENC8BIT];
3460 in_2 = body_to_bio(body);
3462 body_encodings[i] = NULL;
3463 (*bp)->encoding = save_encoding;
3467 if(bp && *bp)
3468 p7_2 = PKCS7_sign(pcert->cert, pcert->key, chain, in_2, flags);
3470 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3471 forget_private_keys();
3473 if(chain)
3474 sk_X509_pop_free(chain, X509_free);
3476 if(!p7){
3477 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
3478 goto end;
3481 outs = so_get(BioType, NULL, EDIT_ACCESS);
3482 out = bio_from_store(outs);
3484 i2d_PKCS7_bio(out, p7);
3485 (void) BIO_flush(out);
3487 so_seek(outs, 0, SEEK_SET);
3489 if(bp && *bp && p7_2){
3490 outs_2 = so_get(BioType, NULL, EDIT_ACCESS);
3491 out_2 = bio_from_store(outs_2);
3493 i2d_PKCS7_bio(out_2, p7_2);
3494 (void) BIO_flush(out_2);
3496 so_seek(outs_2, 0, SEEK_SET);
3499 if((flags&PKCS7_DETACHED)==0){
3501 /* the simple case: the signed data is in the pkcs7 object */
3503 newBody = mail_newbody();
3505 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3507 newBody->contents.text.data = (unsigned char *) outs;
3508 *bodyP = newBody;
3510 result = 1;
3512 else{
3515 * OK.
3516 * We have to create a new body as follows:
3518 * multipart/signed; blah blah blah
3519 * reference to existing body
3521 * pkcs7 object
3524 newBody = mail_newbody();
3526 newBody->type = TYPEMULTIPART;
3527 newBody->subtype = cpystr("signed");
3528 newBody->encoding = ENC7BIT;
3530 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
3531 set_parameter(&newBody->parameter, "micalg", "sha1");
3533 p1 = mail_newbody_part();
3534 p2 = mail_newbody_part();
3537 * This is nasty. We're just copying the body in here,
3538 * but since our newBody is freed at the end of call_mailer,
3539 * we mustn't let this body (the original one) be freed twice.
3541 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
3543 p1->next = p2;
3545 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL);
3546 p2->body.mime.text.data = (unsigned char *) outs_2;
3547 p2->body.contents.text.data = (unsigned char *) outs;
3549 newBody->nested.part = p1;
3551 *bodyP = newBody;
3553 result = 1;
3556 end:
3558 PKCS7_free(p7);
3559 BIO_free(in);
3561 if(bp && *bp){
3562 if(p7_2) PKCS7_free(p7_2);
3563 BIO_free(in_2);
3566 dprint((9, "sign_outgoing_message returns %d", result));
3567 return result;
3571 SMIME_STUFF_S *
3572 new_smime_struct(void)
3574 SMIME_STUFF_S *ret = NULL;
3576 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
3577 memset((void *) ret, 0, sizeof(*ret));
3578 ret->publictype = Nada;
3580 return ret;
3584 static void
3585 free_smime_struct(SMIME_STUFF_S **smime)
3587 if(smime && *smime){
3588 if((*smime)->passphrase_emailaddr){
3589 int i;
3590 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
3591 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
3592 fs_give((void **) (*smime)->passphrase_emailaddr);
3595 if((*smime)->publicpath)
3596 fs_give((void **) &(*smime)->publicpath);
3598 if((*smime)->publiccertlist)
3599 free_certlist(&(*smime)->publiccertlist);
3601 if((*smime)->backuppubliccertlist)
3602 free_certlist(&(*smime)->backuppubliccertlist);
3604 if((*smime)->cacertlist)
3605 free_certlist(&(*smime)->cacertlist);
3607 if((*smime)->backupcacertlist)
3608 free_certlist(&(*smime)->backupcacertlist);
3610 if((*smime)->privatecertlist)
3611 free_certlist(&(*smime)->privatecertlist);
3613 if((*smime)->backupprivatecertlist)
3614 free_certlist(&(*smime)->backupprivatecertlist);
3616 if((*smime)->publiccontent)
3617 fs_give((void **) &(*smime)->publiccontent);
3619 if((*smime)->privatepath)
3620 fs_give((void **) &(*smime)->privatepath);
3622 if((*smime)->personal_certs){
3623 PERSONAL_CERT *pc;
3625 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
3626 free_personal_certs(&pc);
3627 (*smime)->personal_certs = NULL;
3630 if((*smime)->backuppersonal_certs){
3631 PERSONAL_CERT *pc;
3633 pc = (PERSONAL_CERT *) (*smime)->backuppersonal_certs;
3634 free_personal_certs(&pc);
3635 (*smime)->backuppersonal_certs = NULL;
3638 if((*smime)->privatecontent)
3639 fs_give((void **) &(*smime)->privatecontent);
3641 if((*smime)->capath)
3642 fs_give((void **) &(*smime)->capath);
3644 if((*smime)->cacontent)
3645 fs_give((void **) &(*smime)->cacontent);
3647 fs_give((void **) smime);
3651 #endif /* SMIME */