* new version 2.19.9993
[alpine.git] / pith / smime.c
blob46e4278b52c09e404488d0ac7f8bd64709f65407
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-2014 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 = get_x509_subject_email(cert);
676 int i;
678 for(i = 0; email[i] != NULL; i++){
679 save_cert_for(email[i], cert, Public);
680 fs_give((void **)&email[i]);
682 fs_give((void **)email);
684 else /* if(SMHOLDERTYPE(ctype) == Container){ */
685 add_file_to_container(ctype, full_filename, NULL);
686 X509_free(cert);
687 if(ps_global->smime->publiccertlist)
688 ps_global->smime->publiccertlist->data.renew = 1;
690 else
691 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
692 BIO_free(ins);
695 if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1;
696 return 0;
699 /* itype: information type to add: 0 - public, 1 - private.
700 * Memory freed by caller
702 BIO *
703 print_private_key_information(char *email, int itype)
705 BIO *out;
706 PERSONAL_CERT *pc;
708 if(ps_global->smime == NULL
709 || ps_global->smime->personal_certs == NULL
710 || (itype != 0 && itype != 1))
711 return NULL;
713 for(pc = ps_global->smime->personal_certs;
714 pc != NULL && strcmp(pc->name, email) != 0; pc = pc->next);
715 if(pc->key == NULL
716 && !load_private_key(pc)
717 && ps_global->smime
718 && ps_global->smime->need_passphrase){
719 if (*pith_opt_smime_get_passphrase)
720 (*pith_opt_smime_get_passphrase)();
721 load_private_key(pc);
724 if(pc->key == NULL)
725 return NULL;
727 out = BIO_new(BIO_s_mem());
728 if(itype == 0) /* 0 means public */
729 EVP_PKEY_print_public(out, pc->key, 0, NULL);
730 else if (itype == 1) /* 1 means private */
731 EVP_PKEY_print_private(out, pc->key, 0, NULL);
733 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
734 forget_private_keys();
736 return out;
740 * Forget any cached private keys
742 static void
743 forget_private_keys(void)
745 PERSONAL_CERT *pcert;
746 size_t len;
747 volatile char *p;
749 dprint((9, "forget_private_keys()"));
750 if(ps_global->smime){
751 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
752 pcert;
753 pcert=pcert->next){
755 if(pcert->key){
756 EVP_PKEY_free(pcert->key);
757 pcert->key = NULL;
761 ps_global->smime->entered_passphrase = 0;
762 len = sizeof(ps_global->smime->passphrase);
763 p = ps_global->smime->passphrase;
765 while(len-- > 0)
766 *p++ = '\0';
770 /* modelled after signature_path in reply.c, but uses home dir instead of the
771 * directory where the .pinerc is located, since according to documentation,
772 * the .alpine-smime directories are subdirectories of the home directory
774 int smime_path(char *rpath, char *fpath, size_t len)
776 *fpath = '\0';
777 if(rpath && *rpath){
778 size_t spl = strlen(rpath);
780 if(IS_REMOTE(rpath)){
781 if(spl < len - 1)
782 strncpy(fpath, rpath, len-1);
783 fpath[len-1] = '\0';
785 else if(is_absolute_path(rpath)){
786 strncpy(fpath, rpath, len-1);
787 fpath[len-1] = '\0';
788 fnexpand(fpath, len);
790 else if(ps_global->VAR_OPER_DIR){
791 if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
792 build_path(fpath, ps_global->VAR_OPER_DIR, rpath, len);
794 else if(ps_global->home_dir){
795 if(strlen(ps_global->home_dir) + spl < len - 1)
796 build_path(fpath, ps_global->home_dir, rpath, len);
799 return fpath && *fpath ? 1 : 0;
805 * taken from openssl/apps/app_rand.c
807 static int
808 app_RAND_load_file(const char *file)
810 char buffer[200];
812 if(file == NULL)
813 file = RAND_file_name(buffer, sizeof buffer);
814 else if(RAND_egd(file) > 0){
815 /* we try if the given filename is an EGD socket.
816 if it is, we don't write anything back to the file. */
817 egdsocket = 1;
818 return 1;
821 if(file == NULL || !RAND_load_file(file, -1)){
822 if(RAND_status() == 0){
823 dprint((1, "unable to load 'random state'\n"));
824 dprint((1, "This means that the random number generator has not been seeded\n"));
825 dprint((1, "with much random data.\n"));
828 return 0;
831 seeded = 1;
832 return 1;
837 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
839 static void
840 openssl_extra_randomness(void)
842 #if !defined(WIN32)
843 int fd;
844 unsigned long i;
845 char *tf = NULL;
846 char tmp[MAXPATH];
847 struct stat sbuf;
848 /* if system doesn't have /dev/urandom */
849 if(stat ("/dev/urandom", &sbuf)){
850 tmp[0] = '0';
851 tf = temp_nam(NULL, NULL);
852 if(tf){
853 strncpy(tmp, tf, sizeof(tmp));
854 tmp[sizeof(tmp)-1] = '\0';
855 fs_give((void **) &tf);
858 if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
859 i = (unsigned long) tmp;
860 else{
861 unlink(tmp); /* don't need the file */
862 fstat(fd, &sbuf); /* get information about the file */
863 i = sbuf.st_ino; /* remember its inode */
864 close(fd); /* or its descriptor */
866 /* not great but it'll have to do */
867 snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
868 tcp_serverhost (),i,
869 (unsigned long) (time (0) ^ gethostid ()),
870 (unsigned long) getpid ());
871 RAND_seed(tmp, strlen(tmp));
873 #endif
877 /* taken from openssl/apps/app_rand.c */
878 static int
879 app_RAND_write_file(const char *file)
881 char buffer[200];
883 if(egdsocket || !seeded)
885 * If we did not manage to read the seed file,
886 * we should not write a low-entropy seed file back --
887 * it would suppress a crucial warning the next time
888 * we want to use it.
890 return 0;
892 if(file == NULL)
893 file = RAND_file_name(buffer, sizeof buffer);
895 if(file == NULL || !RAND_write_file(file)){
896 dprint((1, "unable to write 'random state'\n"));
897 return 0;
900 return 1;
903 CertList *
904 certlist_from_personal_certs(PERSONAL_CERT *pc)
906 CertList *cl;
907 X509 *x;
908 char buf[MAXPATH];
910 if(pc == NULL)
911 return NULL;
913 cl = fs_get(sizeof(CertList));
914 memset((void *)cl, 0, sizeof(CertList));
915 cl->name = cpystr(pc->name);
916 x = get_cert_for(pc->name, Public);
917 if(x){
918 if(x->cert_info){
919 cl->data.date_from = smime_get_date(x->cert_info->validity->notBefore);
920 cl->data.date_to = smime_get_date(x->cert_info->validity->notAfter);
921 get_fingerprint(x, EVP_md5(), buf, sizeof(buf), NULL);
922 cl->data.md5 = cpystr(buf);
924 X509_free(x);
926 cl->next = certlist_from_personal_certs(pc->next);
928 return cl;
931 void
932 renew_cert_data(CertList **data, WhichCerts ctype)
934 smime_init();
935 if(ctype == Private){
936 if(data){
937 PERSONAL_CERT *pc = (PERSONAL_CERT *)ps_global->smime->personal_certs;
938 if(*data)
939 free_certlist(data);
940 free_personal_certs(&pc);
941 setup_privatekey_storage();
942 *data = certlist_from_personal_certs((PERSONAL_CERT *)ps_global->smime->personal_certs);
943 if(data && *data){
944 resort_certificates(data, ctype);
945 RENEWCERT(*data) = 0;
947 ps_global->smime->privatecertlist = *data;
949 if(ps_global->smime->privatecertlist)
950 RENEWCERT(ps_global->smime->privatecertlist) = 0;
951 } else {
952 X509_LOOKUP *lookup = NULL;
953 X509_STORE *store = NULL;
955 if((store = X509_STORE_new()) != NULL){
956 if((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL){
957 X509_STORE_free(store);
958 store = NULL;
959 } else{
960 free_certlist(data);
961 if(SMHOLDERTYPE(ctype) == Directory)
962 add_certs_in_dir(lookup, PATHCERTDIR(ctype), EXTCERT(ctype), data);
963 else /* if(SMHOLDERTYPE(ctype) == Container) */
964 *data = mem_to_certlist(CONTENTCERTLIST(ctype), ctype);
965 if(data && *data){
966 resort_certificates(data, ctype);
967 RENEWCERT(*data) = 0;
969 if(ctype == Public)
970 ps_global->smime->publiccertlist = *data;
971 else
972 ps_global->smime->cacertlist = *data;
976 setup_certs_backup_by_type(ctype);
979 void
980 smime_reinit(void)
982 smime_deinit();
983 smime_init();
986 /* Installed as an atexit() handler to save the random data */
987 void
988 smime_deinit(void)
990 dprint((9, "smime_deinit()"));
991 app_RAND_write_file(NULL);
992 free_smime_struct(&ps_global->smime);
995 /* we renew the store when it has changed */
996 void renew_store(void)
998 if(ps_global->smime->inited){
999 if(s_cert_store != NULL)
1000 X509_STORE_free(s_cert_store);
1001 s_cert_store = get_ca_store();
1005 /* Initialise openssl stuff if needed */
1006 void
1007 smime_init(void)
1009 if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
1011 dprint((9, "smime_init()"));
1012 if(!ps_global->smime)
1013 ps_global->smime = new_smime_struct();
1015 setup_storage_locations();
1017 s_cert_store = get_ca_store();
1018 setup_certs_backup_by_type(CACert);
1020 OpenSSL_add_all_algorithms();
1021 ERR_load_crypto_strings();
1023 app_RAND_load_file(NULL);
1024 openssl_extra_randomness();
1025 ps_global->smime->inited = 1;
1028 ERR_clear_error();
1032 /* validate a certificate. Return value : 0 for no error, -1 for error.
1033 * In the latter case, set the openssl smime error in *error.
1035 int smime_validate_cert(X509 *cert, long *error)
1037 X509_STORE_CTX *csc;
1039 ERR_clear_error();
1040 *error = 0;
1041 if((csc = X509_STORE_CTX_new()) != NULL){
1042 X509_STORE_set_flags(s_cert_store, 0);
1043 if(X509_STORE_CTX_init(csc,s_cert_store,cert,NULL)
1044 && X509_verify_cert(csc) <= 0)
1045 *error = X509_STORE_CTX_get_error(csc);
1046 X509_STORE_CTX_free(csc);
1048 return *error ? -1 : 0;
1051 PERSONAL_CERT *
1052 get_personal_certs(char *path)
1054 PERSONAL_CERT *result = NULL;
1055 char buf2[MAXPATH];
1056 struct dirent *d;
1057 DIR *dirp;
1059 ps_global->smime->privatepath = cpystr(path);
1060 dirp = opendir(path);
1061 if(dirp){
1062 while((d=readdir(dirp)) != NULL){
1063 X509 *cert;
1064 size_t ll;
1066 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, ".key")){
1068 /* copy file name to temp buffer */
1069 strncpy(buf2, d->d_name, sizeof(buf2)-1);
1070 buf2[sizeof(buf2)-1] = '\0';
1071 /* chop off ".key" trailier */
1072 buf2[strlen(buf2)-4] = 0;
1073 /* Look for certificate */
1074 cert = get_cert_for(buf2, Public);
1076 if(cert){
1077 PERSONAL_CERT *pc;
1079 /* create a new PERSONAL_CERT, fill it in */
1081 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1082 pc->cert = cert;
1083 pc->name = cpystr(buf2);
1085 /* Try to load the key with an empty password */
1086 pc->key = load_key(pc, "", SM_NORMALCERT);
1088 pc->next = result;
1089 result = pc;
1093 closedir(dirp);
1095 return result;
1099 void
1100 setup_privatekey_storage(void)
1102 char path[MAXPATH+1], *contents;
1103 int privatekeycontainer = 0;
1105 /* private keys in a container */
1106 if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
1108 privatekeycontainer = 1;
1109 contents = NULL;
1110 path[0] = '\0';
1111 if(!smime_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
1112 privatekeycontainer = 0;
1114 if(privatekeycontainer && !IS_REMOTE(path)
1115 && ps_global->VAR_OPER_DIR
1116 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1117 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1118 /* TRANSLATORS: First arg is the directory name, second is
1119 the file user wants to read but can't. */
1120 _("Can't read file outside %s: %s"),
1121 ps_global->VAR_OPER_DIR, path);
1122 privatekeycontainer = 0;
1125 if(privatekeycontainer
1126 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1127 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1129 !(contents = read_file(path, READ_FROM_LOCALE)))
1130 privatekeycontainer = 0;
1133 if(privatekeycontainer && path[0]){
1134 ps_global->smime->privatetype = Container;
1135 ps_global->smime->privatepath = cpystr(path);
1137 if(contents){
1138 ps_global->smime->privatecontent = contents;
1139 ps_global->smime->personal_certs = mem_to_personal_certs(contents);
1144 /* private keys in a directory of files */
1145 if(!privatekeycontainer){
1146 ps_global->smime->privatetype = Directory;
1148 path[0] = '\0';
1149 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1150 && !IS_REMOTE(path)))
1151 ps_global->smime->privatetype = Nada;
1152 else if(can_access(path, ACCESS_EXISTS)){
1153 if(our_mkpath(path, 0700)){
1154 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1155 ps_global->smime->privatetype = Nada;
1159 if(ps_global->smime->privatetype == Directory)
1160 ps_global->smime->personal_certs = get_personal_certs(path);
1162 setup_certs_backup_by_type(Private);
1167 static void
1168 setup_storage_locations(void)
1170 int publiccertcontainer = 0, cacertcontainer = 0;
1171 char path[MAXPATH+1], *contents;
1173 if(!ps_global->smime)
1174 return;
1176 #ifdef APPLEKEYCHAIN
1177 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1178 ps_global->smime->publictype = Keychain;
1180 else{
1181 #endif /* APPLEKEYCHAIN */
1182 /* Public certificates in a container */
1183 if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
1185 publiccertcontainer = 1;
1186 contents = NULL;
1187 path[0] = '\0';
1188 if(!smime_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
1189 publiccertcontainer = 0;
1191 if(publiccertcontainer && !IS_REMOTE(path)
1192 && ps_global->VAR_OPER_DIR
1193 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1194 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1195 /* TRANSLATORS: First arg is the directory name, second is
1196 the file user wants to read but can't. */
1197 _("Can't read file outside %s: %s"),
1198 ps_global->VAR_OPER_DIR, path);
1199 publiccertcontainer = 0;
1202 if(publiccertcontainer
1203 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1204 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1206 !(contents = read_file(path, READ_FROM_LOCALE)))
1207 publiccertcontainer = 0;
1210 if(publiccertcontainer && path[0]){
1211 ps_global->smime->publictype = Container;
1212 ps_global->smime->publicpath = cpystr(path);
1214 if(contents){
1215 ps_global->smime->publiccontent = contents;
1216 ps_global->smime->publiccertlist = mem_to_certlist(contents, Public);
1221 /* Public certificates in a directory of files */
1222 if(!publiccertcontainer){
1223 ps_global->smime->publictype = Directory;
1225 path[0] = '\0';
1226 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1227 && !IS_REMOTE(path)))
1228 ps_global->smime->publictype = Nada;
1229 else if(can_access(path, ACCESS_EXISTS)){
1230 if(our_mkpath(path, 0700)){
1231 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1232 ps_global->smime->publictype = Nada;
1236 if(ps_global->smime->publictype == Directory)
1237 ps_global->smime->publicpath = cpystr(path);
1240 #ifdef APPLEKEYCHAIN
1242 #endif /* APPLEKEYCHAIN */
1244 setup_privatekey_storage();
1246 /* extra cacerts in a container */
1247 if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
1249 cacertcontainer = 1;
1250 contents = NULL;
1251 path[0] = '\0';
1252 if(!smime_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
1253 cacertcontainer = 0;
1255 if(cacertcontainer && !IS_REMOTE(path)
1256 && ps_global->VAR_OPER_DIR
1257 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1258 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1259 /* TRANSLATORS: First arg is the directory name, second is
1260 the file user wants to read but can't. */
1261 _("Can't read file outside %s: %s"),
1262 ps_global->VAR_OPER_DIR, path);
1263 cacertcontainer = 0;
1266 if(cacertcontainer
1267 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1268 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1270 !(contents = read_file(path, READ_FROM_LOCALE)))
1271 cacertcontainer = 0;
1274 if(cacertcontainer && path[0]){
1275 ps_global->smime->catype = Container;
1276 ps_global->smime->capath = cpystr(path);
1277 ps_global->smime->cacontent = contents;
1278 if(contents)
1279 ps_global->smime->cacertlist = mem_to_certlist(contents, CACert);
1283 if(!cacertcontainer){
1284 ps_global->smime->catype = Directory;
1286 path[0] = '\0';
1287 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1288 && !IS_REMOTE(path)))
1289 ps_global->smime->catype = Nada;
1290 else if(can_access(path, ACCESS_EXISTS)){
1291 if(our_mkpath(path, 0700)){
1292 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1293 ps_global->smime->catype = Nada;
1297 if(ps_global->smime->catype == Directory)
1298 ps_global->smime->capath = cpystr(path);
1304 copy_publiccert_dir_to_container(void)
1306 return(copy_dir_to_container(Public, NULL));
1311 copy_publiccert_container_to_dir(void)
1313 return(copy_container_to_dir(Public));
1318 copy_privatecert_dir_to_container(void)
1320 return(copy_dir_to_container(Private, NULL));
1325 copy_privatecert_container_to_dir(void)
1327 return(copy_container_to_dir(Private));
1332 copy_cacert_dir_to_container(void)
1334 return(copy_dir_to_container(CACert, NULL));
1339 copy_cacert_container_to_dir(void)
1341 return(copy_container_to_dir(CACert));
1344 /* Add the contents of a file to a container. Do not check the content
1345 * of the file, just add it using the format for that container. The
1346 * caller must check the format, so that there is no data corruption
1347 * in the future.
1348 * return value: 0 - success,
1349 * != 0 - failure.
1352 add_file_to_container(WhichCerts ctype, char *fpath, char *altname)
1354 char *sep = (ctype == Public || ctype == Private)
1355 ? EMAILADDRLEADER : CACERTSTORELEADER;
1356 char *content = ctype == Public ? ps_global->smime->publiccontent
1357 : (ctype == Private ? ps_global->smime->privatecontent
1358 : ps_global->smime->cacontent);
1359 char *name;
1360 char *s;
1361 unsigned char c;
1362 struct stat sbuf;
1363 STORE_S *in = NULL;
1364 int rv = -1; /* assume error */
1366 if(our_stat(fpath, &sbuf) < 0
1367 || (in = so_get(FileStar, fpath, READ_ACCESS | READ_FROM_LOCALE)) == NULL)
1368 goto endadd;
1370 if(altname != NULL)
1371 name = altname;
1372 else if((name = strrchr(fpath, '/')) != NULL){
1373 size_t ll;
1374 if((ll = strlen(++name)) > 4 && strucmp(name + ll - 4, EXTCERT(ctype)) == 0)
1375 name[ll-strlen(EXTCERT(ctype))] = '\0';
1377 else
1378 goto endadd;
1380 if(content){
1381 fs_resize((void **)&content, strlen(content) + strlen(sep) + strlen(name) + sbuf.st_size + 3); /* 2 = \n + \n + \0*/
1382 s = content;
1383 content += strlen(content);
1385 else{
1386 s = content = fs_get(strlen(sep) + strlen(name) + sbuf.st_size + 1); /* 2 = \n + \0 */
1387 *content = '\0';
1389 strncat(content, sep, strlen(sep));
1390 strncat(content, name, strlen(name));
1391 content += strlen(content);
1392 *content++ = '\n';
1394 while(so_readc(&c, in))
1395 *content++ = (char) c;
1396 *content = '\0';
1398 switch(ctype){
1399 case Private: ps_global->smime->privatecontent = s; break;
1400 case Public : ps_global->smime->publiccontent = s; break;
1401 case CACert : ps_global->smime->cacontent = s; break;
1402 default : break;
1405 rv = copy_dir_to_container(ctype, s);
1407 endadd:
1408 if(in) so_give(&in);
1410 return rv;
1415 * returns 0 on success, -1 on failure
1416 * contents is an argument which tells this function to write the value
1417 * of this variable instead of reading the contents of the directory.
1418 * If the var contents is not null use its value as the value of the
1419 * container.
1422 copy_dir_to_container(WhichCerts which, char *contents)
1424 int ret = 0, container = 0;
1425 BIO *bio_out = NULL, *bio_in = NULL;
1426 char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
1427 char *tempfile = NULL, fpath[MAXPATH+1];
1428 DIR *dirp;
1429 struct dirent *d;
1430 REMDATA_S *rd = NULL;
1431 char *configdir = NULL;
1432 char *configpath = NULL;
1433 char *configcontainer = NULL;
1434 char *filesuffix = NULL;
1435 char *ret_dir = NULL;
1437 dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1438 smime_init();
1440 srcpath[0] = '\0';
1441 dstpath[0] = '\0';
1442 file[0] = '\0';
1443 emailaddr[0] = '\0';
1445 if(which == Public){
1446 configdir = ps_global->VAR_PUBLICCERT_DIR;
1447 configpath = ps_global->smime->publicpath;
1448 configcontainer = cpystr(DF_PUBLIC_CONTAINER);
1449 filesuffix = ".crt";
1451 else if(which == Private){
1452 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1453 configpath = ps_global->smime->privatepath;
1454 configcontainer = cpystr(DF_PRIVATE_CONTAINER);
1455 filesuffix = ".key";
1457 else if(which == CACert){
1458 configdir = ps_global->VAR_CACERT_DIR;
1459 configpath = ps_global->smime->capath;
1460 configcontainer = cpystr(DF_CA_CONTAINER);
1461 filesuffix = ".crt";
1463 container = SMHOLDERTYPE(which) == Container;
1465 if(!(configdir && configdir[0])){
1466 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1467 return -1;
1470 if(!(configpath && configpath[0])){
1471 #ifdef APPLEKEYCHAIN
1472 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1473 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1474 return -1;
1476 #endif /* APPLEKEYCHAIN */
1477 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1478 return -1;
1481 if(!(filesuffix && strlen(filesuffix) == 4)){
1482 return -1;
1487 * If there is a legit directory to read from set up the
1488 * container file to write to.
1490 if(smime_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
1492 if(IS_REMOTE(configpath)){
1493 rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
1494 NULL, "Error: ",
1495 _("Can't access remote smime configuration."));
1496 if(!rd)
1497 return -1;
1499 (void) rd_read_metadata(rd);
1501 if(rd->access == MaybeRorW){
1502 if(rd->read_status == 'R')
1503 rd->access = ReadOnly;
1504 else
1505 rd->access = ReadWrite;
1508 if(rd->access != NoExists){
1510 rd_check_remvalid(rd, 1L);
1513 * If the cached info says it is readonly but
1514 * it looks like it's been fixed now, change it to readwrite.
1516 if(rd->read_status == 'R'){
1517 rd_check_readonly_access(rd);
1518 if(rd->read_status == 'W'){
1519 rd->access = ReadWrite;
1520 rd->flags |= REM_OUTOFDATE;
1522 else
1523 rd->access = ReadOnly;
1527 if(rd->flags & REM_OUTOFDATE){
1528 if(rd_update_local(rd) != 0){
1530 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1531 rd_close_remdata(&rd);
1532 return -1;
1535 else
1536 rd_open_remote(rd);
1538 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1539 rd_close_remdata(&rd);
1540 return -1;
1543 rd->flags |= DO_REMTRIM;
1545 strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
1546 dstpath[sizeof(dstpath)-1] = '\0';
1548 else{
1549 strncpy(dstpath, configpath, sizeof(dstpath)-1);
1550 dstpath[sizeof(dstpath)-1] = '\0';
1554 * dstpath is either the local Container file or the local cache file
1555 * for the remote Container file.
1557 tempfile = tempfile_in_same_dir(dstpath, "az", &ret_dir);
1561 * If there is a legit directory to read from and a tempfile
1562 * to write to we continue.
1564 if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
1566 if(contents != NULL){
1567 if(BIO_puts(bio_out, contents) < 0)
1568 ret = -1;
1570 else {
1571 if((dirp = opendir(srcpath)) != NULL){
1573 while((d=readdir(dirp)) && !ret){
1574 size_t ll;
1576 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, filesuffix)){
1578 /* copy file name to temp buffer */
1579 strncpy(emailaddr, d->d_name, sizeof(emailaddr)-1);
1580 emailaddr[sizeof(emailaddr)-1] = '\0';
1581 /* chop off suffix trailier */
1582 emailaddr[strlen(emailaddr)-4] = 0;
1585 * This is the separator between the contents of
1586 * different files.
1588 if(which == CACert){
1589 if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
1590 && (BIO_puts(bio_out, emailaddr) > 0)
1591 && (BIO_puts(bio_out, "\n") > 0)))
1592 ret = -1;
1594 else{
1595 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1596 && (BIO_puts(bio_out, emailaddr) > 0)
1597 && (BIO_puts(bio_out, "\n") > 0)))
1598 ret = -1;
1601 /* read then write contents of file */
1602 build_path(file, srcpath, d->d_name, sizeof(file));
1603 if(!(bio_in = BIO_new_file(file, "r")))
1604 ret = -1;
1606 if(!ret){
1607 int good_stuff = 0;
1609 while(BIO_gets(bio_in, line, sizeof(line)) > 0){
1610 if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
1611 good_stuff = 1;
1613 if(good_stuff)
1614 BIO_puts(bio_out, line);
1616 if(strncmp("-----END", line, strlen("-----END")) == 0)
1617 good_stuff = 0;
1621 BIO_free(bio_in);
1625 closedir(dirp);
1629 BIO_free(bio_out);
1631 if(!ret){
1632 if(container && configpath && *configpath){
1633 strncpy(fpath, configpath, sizeof(fpath));
1634 fpath[sizeof(fpath) - 1] = '\0';
1636 else if(ret_dir){
1637 if(strlen(dstpath) + strlen(configcontainer) - strlen(ret_dir) + 1 < sizeof(dstpath))
1638 snprintf(fpath, sizeof(fpath), "%s%c%s",
1639 dstpath, tempfile[strlen(ret_dir)], configcontainer);
1640 else
1641 ret = -1;
1643 else ret = -1;
1645 if(!ret){
1646 if(rename_file(tempfile, fpath) < 0){
1647 q_status_message2(SM_ORDER, 3, 3,
1648 _("Can't rename %s to %s"), tempfile, fpath);
1649 ret = -1;
1650 } else q_status_message1(SM_ORDER, 3, 3,
1651 _("saved container to %s"), fpath);
1654 /* if the container is remote, copy it */
1655 if(!ret && IS_REMOTE(configpath)){
1656 int e;
1657 char datebuf[200];
1659 datebuf[0] = '\0';
1661 if((e = rd_update_remote(rd, datebuf)) != 0){
1662 if(e == -1){
1663 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1664 _("Error opening temporary smime file %s: %s"),
1665 rd->lf, error_description(errno));
1666 dprint((1,
1667 "write_remote_smime: error opening temp file %s\n",
1668 rd->lf ? rd->lf : "?"));
1670 else{
1671 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1672 _("Error copying to %s: %s"),
1673 rd->rn, error_description(errno));
1674 dprint((1,
1675 "write_remote_smime: error copying from %s to %s\n",
1676 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1679 q_status_message(SM_ORDER | SM_DING, 5, 5,
1680 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1682 else{
1683 rd_update_metadata(rd, datebuf);
1684 rd->read_status = 'W';
1687 rd_close_remdata(&rd);
1692 if(tempfile)
1693 fs_give((void **) &tempfile);
1695 if(ret_dir)
1696 fs_give((void **) &ret_dir);
1698 if(configcontainer)
1699 fs_give((void **) &configcontainer);
1701 return ret;
1706 * returns 0 on success, -1 on failure
1709 copy_container_to_dir(WhichCerts which)
1711 char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
1712 char iobuf[4096];
1713 char *contents = NULL;
1714 char *leader = NULL;
1715 char *filesuffix = NULL;
1716 char *configdir = NULL;
1717 char *configpath = NULL;
1718 char *tempfile = NULL;
1719 char *p, *q, *line, *name, *certtext, *save_p;
1720 int len;
1721 BIO *in, *out;
1723 dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1724 smime_init();
1726 path[0] = '\0';
1728 if(which == Public){
1729 leader = EMAILADDRLEADER;
1730 contents = ps_global->smime->publiccontent;
1731 configdir = ps_global->VAR_PUBLICCERT_DIR;
1732 configpath = ps_global->smime->publicpath;
1733 filesuffix = ".crt";
1734 if(!(configpath && configpath[0])){
1735 #ifdef APPLEKEYCHAIN
1736 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1737 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1738 return -1;
1740 #endif /* APPLEKEYCHAIN */
1741 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1742 return -1;
1745 fs_give((void **) &ps_global->smime->publicpath);
1747 path[0] = '\0';
1748 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1749 && !IS_REMOTE(path))){
1750 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1751 return -1;
1754 if(can_access(path, ACCESS_EXISTS)){
1755 if(our_mkpath(path, 0700)){
1756 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1757 return -1;
1761 ps_global->smime->publicpath = cpystr(path);
1762 configpath = ps_global->smime->publicpath;
1764 else if(which == Private){
1765 leader = EMAILADDRLEADER;
1766 contents = ps_global->smime->privatecontent;
1767 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1768 configpath = ps_global->smime->privatepath;
1769 filesuffix = ".key";
1770 if(!(configpath && configpath[0])){
1771 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1772 return -1;
1775 fs_give((void **) &ps_global->smime->privatepath);
1777 path[0] = '\0';
1778 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1779 && !IS_REMOTE(path))){
1780 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1781 return -1;
1784 if(can_access(path, ACCESS_EXISTS)){
1785 if(our_mkpath(path, 0700)){
1786 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1787 return -1;
1791 ps_global->smime->privatepath = cpystr(path);
1792 configpath = ps_global->smime->privatepath;
1794 else if(which == CACert){
1795 leader = CACERTSTORELEADER;
1796 contents = ps_global->smime->cacontent;
1797 configdir = ps_global->VAR_CACERT_DIR;
1798 configpath = ps_global->smime->capath;
1799 filesuffix = ".crt";
1800 if(!(configpath && configpath[0])){
1801 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1802 return -1;
1805 fs_give((void **) &ps_global->smime->capath);
1807 path[0] = '\0';
1808 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1809 && !IS_REMOTE(path))){
1810 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1811 return -1;
1814 if(can_access(path, ACCESS_EXISTS)){
1815 if(our_mkpath(path, 0700)){
1816 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1817 return -1;
1821 ps_global->smime->capath = cpystr(path);
1822 configpath = ps_global->smime->capath;
1825 if(!(configdir && configdir[0])){
1826 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1827 return -1;
1830 if(!(configpath && configpath[0])){
1831 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1832 return -1;
1835 if(!(filesuffix && strlen(filesuffix) == 4)){
1836 return -1;
1840 if(contents && *contents){
1841 for(p = contents; *p != '\0';){
1842 line = p;
1844 while(*p && *p != '\n')
1845 p++;
1847 save_p = NULL;
1848 if(*p == '\n'){
1849 save_p = p;
1850 *p++ = '\0';
1853 if(strncmp(leader, line, strlen(leader)) == 0){
1854 name = line + strlen(leader);
1855 certtext = p;
1856 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
1857 if((q = strstr(certtext, leader)) != NULL){
1858 p = q;
1860 else{ /* end of file */
1861 q = certtext + strlen(certtext);
1862 p = q;
1865 strncpy(buf, name, sizeof(buf)-5);
1866 buf[sizeof(buf)-5] = '\0';
1867 strncat(buf, filesuffix, 5);
1868 build_path(file, configpath, buf, sizeof(file));
1870 in = BIO_new_mem_buf(certtext, q-certtext);
1871 if(in){
1872 tempfile = tempfile_in_same_dir(file, "az", NULL);
1873 out = NULL;
1874 if(tempfile)
1875 out = BIO_new_file(tempfile, "w");
1877 if(out){
1878 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1879 BIO_write(out, iobuf, len);
1881 BIO_free(out);
1883 if(rename_file(tempfile, file) < 0){
1884 q_status_message2(SM_ORDER, 3, 3,
1885 _("Can't rename %s to %s"),
1886 tempfile, file);
1887 return -1;
1890 fs_give((void **) &tempfile);
1893 BIO_free(in);
1898 if(save_p)
1899 *save_p = '\n';
1903 return 0;
1907 #ifdef APPLEKEYCHAIN
1910 copy_publiccert_container_to_keychain(void)
1912 /* NOT IMPLEMNTED */
1913 return -1;
1917 copy_publiccert_keychain_to_container(void)
1919 /* NOT IMPLEMNTED */
1920 return -1;
1923 #endif /* APPLEKEYCHAIN */
1927 * Get a pointer to a string describing the most recent OpenSSL error.
1928 * It's statically allocated, so don't change or attempt to free it.
1930 static const char *
1931 openssl_error_string(void)
1933 char *errs;
1934 const char *data = NULL;
1935 long errn;
1937 errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
1938 errs = (char*) ERR_reason_error_string(errn);
1940 if(errs)
1941 return errs;
1942 else if(data)
1943 return data;
1945 return "unknown error";
1949 /* Return true if the body looks like a PKCS7 object */
1951 is_pkcs7_body(BODY *body)
1953 int result;
1955 result = body->type==TYPEAPPLICATION &&
1956 body->subtype &&
1957 (strucmp(body->subtype,"pkcs7-mime")==0 ||
1958 strucmp(body->subtype,"x-pkcs7-mime")==0 ||
1959 strucmp(body->subtype,"pkcs7-signature")==0 ||
1960 strucmp(body->subtype,"x-pkcs7-signature")==0);
1962 return result;
1967 * Recursively stash a pointer to the decrypted data in our
1968 * manufactured body.
1969 * parameters: type: call of type 1, save the base and header for multipart messages
1970 call of type 0, do not save the base and header for multipart messages
1972 static void
1973 create_local_cache(char *h, char *base, BODY *b, int type)
1975 if(b->type==TYPEMULTIPART){
1976 PART *p;
1978 if(type == 1){
1979 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
1980 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
1981 } else if(type == 0){
1983 * We don't really want to copy the real body contents. It shouldn't be
1984 * used, and in the case of a message with attachments, we'll be
1985 * duplicating the files multiple times.
1987 cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
1989 for(p=b->nested.part; p; p=p->next)
1990 create_local_cache(h, base, (BODY *) p, type);
1993 else{
1994 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
1995 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2000 static long
2001 rfc822_output_func(void *b, char *string)
2003 BIO *bio = (BIO *) b;
2005 return(string ? *string ? (BIO_puts(bio, string) > 0 ? 1L : 0L)
2006 : (BIO_puts(bio, string) >= 0 ? 1L : 0L)
2007 : 0L);
2012 * Attempt to load the private key for the given PERSONAL_CERT.
2013 * This sets the appropriate passphrase globals in order to
2014 * interact with the user correctly.
2016 static int
2017 load_private_key(PERSONAL_CERT *pcert)
2019 if(!pcert->key){
2021 /* Try empty password by default */
2022 char *password = "";
2024 if(ps_global->smime
2025 && (ps_global->smime->need_passphrase
2026 || ps_global->smime->entered_passphrase)){
2027 /* We've already been in here and discovered we need a different password */
2029 if(ps_global->smime->entered_passphrase)
2030 password = (char *) ps_global->smime->passphrase; /* already entered */
2031 else
2032 return 0;
2035 ERR_clear_error();
2037 if(!(pcert->key = load_key(pcert, password, SM_NORMALCERT))){
2038 long err = ERR_get_error();
2040 /* Couldn't load key... */
2042 if(ps_global->smime && ps_global->smime->entered_passphrase){
2044 /* The user got the password wrong maybe? */
2046 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
2047 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
2048 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
2049 else
2050 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2052 /* This passphrase is no good; forget it */
2053 ps_global->smime->entered_passphrase = 0;
2056 if(ps_global->smime){
2057 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2058 ps_global->smime->need_passphrase = 1;
2059 if(ps_global->smime->passphrase_emailaddr){
2060 int i;
2061 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
2062 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
2063 fs_give((void **) ps_global->smime->passphrase_emailaddr);
2066 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
2069 return 0;
2071 else{
2072 /* This key will be cached, so we won't be called again */
2073 if(ps_global->smime){
2074 ps_global->smime->entered_passphrase = 0;
2075 ps_global->smime->need_passphrase = 0;
2079 return 1;
2082 return 0;
2086 static void
2087 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename, char *smime_type)
2089 b->type = TYPEAPPLICATION;
2090 b->subtype = cpystr(type);
2091 b->encoding = ENCBINARY;
2092 b->description = cpystr(description);
2094 b->disposition.type = cpystr("attachment");
2095 set_parameter(&b->disposition.parameter, "filename", filename);
2097 set_parameter(&b->parameter, "name", filename);
2098 if(smime_type && *smime_type)
2099 set_parameter(&b->parameter, "smime-type", smime_type);
2104 * Look for a personal certificate matching the
2105 * given address
2107 PERSONAL_CERT *
2108 match_personal_cert_to_email(ADDRESS *a)
2110 PERSONAL_CERT *pcert = NULL;
2111 char buf[MAXPATH];
2112 char **email;
2113 int i, done;
2115 if(!a || !a->mailbox || !a->host)
2116 return NULL;
2118 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2120 if(ps_global->smime){
2121 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
2122 pcert;
2123 pcert=pcert->next){
2125 if(!pcert->cert)
2126 continue;
2128 email = get_x509_subject_email(pcert->cert);
2130 done = 0;
2131 if(email != NULL){
2132 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
2133 if(email[i] != NULL) done++;
2134 for(i = 0; email[i] != NULL; i++)
2135 fs_give((void **)&email[i]);
2136 fs_give((void **)email);
2139 if(done > 0)
2140 break;
2144 return pcert;
2149 * Look for a personal certificate matching the from
2150 * (or reply_to? in the given envelope)
2152 PERSONAL_CERT *
2153 match_personal_cert(ENVELOPE *env)
2155 PERSONAL_CERT *pcert;
2157 pcert = match_personal_cert_to_email(env->reply_to);
2158 if(!pcert)
2159 pcert = match_personal_cert_to_email(env->from);
2161 return pcert;
2166 * Flatten the given body into its MIME representation.
2167 * Return the result in a BIO.
2169 static BIO *
2170 body_to_bio(BODY *body)
2172 BIO *bio = NULL;
2173 int len;
2175 bio = BIO_new(BIO_s_mem());
2176 if(!bio)
2177 return NULL;
2179 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
2180 pine_write_body_header(body, rfc822_output_func, bio);
2181 pine_rfc822_output_body(body, rfc822_output_func, bio);
2184 * Now need to truncate by two characters since the above
2185 * appends CRLF.
2187 if((len=BIO_ctrl_pending(bio)) > 1){
2188 BUF_MEM *biobuf = NULL;
2190 BIO_get_mem_ptr(bio, &biobuf);
2191 if(biobuf){
2192 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
2196 return bio;
2200 static BIO *
2201 bio_from_store(STORE_S *store)
2203 BIO *ret = NULL;
2205 if(store && store->src == BioType && store->txt){
2206 ret = (BIO *) store->txt;
2209 return(ret);
2213 * Encrypt file; given a path (char *) fp, replace the file
2214 * by an encrypted version of it. If (char *) text is not null, then
2215 * replace the text of (char *) fp by the encrypted version of (char *) text.
2216 * certpath is the FULL path to the file containing the certificate used for
2217 * encryption.
2220 encrypt_file(char *fp, char *text, PERSONAL_CERT *pc)
2222 const EVP_CIPHER *cipher = NULL;
2223 STACK_OF(X509) *encerts = NULL;
2224 BIO *out = NULL;
2225 PKCS7 *p7 = NULL;
2226 int rv = 0;
2228 if(pc == NULL)
2229 return 0;
2231 cipher = EVP_aes_256_cbc();
2232 encerts = sk_X509_new_null();
2234 sk_X509_push(encerts, X509_dup(pc->cert));
2236 if(text){
2237 if((out = BIO_new(BIO_s_mem())) == NULL)
2238 goto end;
2239 (void) BIO_reset(out);
2240 BIO_puts(out, text);
2242 else{
2243 if(!(out = BIO_new_file(fp, "rb")))
2244 goto end;
2246 BIO_read_filename(out, fp);
2249 if((p7 = PKCS7_encrypt(encerts, out, cipher, 0)) == NULL)
2250 goto end;
2251 BIO_set_close(out, BIO_CLOSE);
2252 BIO_free(out);
2253 if(!(out = BIO_new_file(fp, "w")))
2254 goto end;
2255 BIO_reset(out);
2256 rv = PEM_write_bio_PKCS7(out, p7);
2257 BIO_flush(out);
2259 end:
2260 if(out != NULL)
2261 BIO_free(out);
2262 PKCS7_free(p7);
2263 sk_X509_pop_free(encerts, X509_free);
2265 return rv;
2269 * Encrypt a message on the way out. Called from call_mailer in send.c
2270 * The body may be reallocated.
2273 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
2275 PKCS7 *p7 = NULL;
2276 BIO *in = NULL;
2277 BIO *out = NULL;
2278 const EVP_CIPHER *cipher = NULL;
2279 STACK_OF(X509) *encerts = NULL;
2280 STORE_S *outs = NULL;
2281 PINEFIELD *pf;
2282 ADDRESS *a;
2283 BODY *body = *bodyP;
2284 BODY *newBody = NULL;
2285 int result = 0;
2286 X509 *cert;
2287 char buf[MAXPATH];
2289 dprint((9, "encrypt_outgoing_message()"));
2290 smime_init();
2292 cipher = EVP_aes_256_cbc();
2294 encerts = sk_X509_new_null();
2296 /* Look for a certificate for each of the recipients */
2297 for(pf = header->local; pf && pf->name; pf = pf->next)
2298 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
2299 for(a=*pf->addr; a; a=a->next){
2300 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2302 if((cert = get_cert_for(buf, Public)) != NULL){
2303 sk_X509_push(encerts,cert);
2304 }else{
2305 q_status_message2(SM_ORDER, 1, 1,
2306 _("Unable to find certificate for <%s@%s>"),
2307 a->mailbox, a->host);
2308 goto end;
2313 /* add the sender's certificate so that they can decrypt the message too */
2314 for(a=header->env->from; a ; a = a->next){
2315 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2317 if((cert = get_cert_for(buf, Public)) != NULL
2318 && sk_X509_find(encerts, cert) == -1)
2319 sk_X509_push(encerts,cert);
2322 in = body_to_bio(body);
2324 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
2326 outs = so_get(BioType, NULL, EDIT_ACCESS);
2327 out = bio_from_store(outs);
2329 i2d_PKCS7_bio(out, p7);
2330 (void) BIO_flush(out);
2332 so_seek(outs, 0, SEEK_SET);
2334 newBody = mail_newbody();
2336 newBody->type = TYPEAPPLICATION;
2337 newBody->subtype = cpystr("pkcs7-mime");
2338 newBody->encoding = ENCBINARY;
2340 newBody->disposition.type = cpystr("attachment");
2341 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
2343 newBody->description = cpystr("S/MIME Encrypted Message");
2344 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
2345 set_parameter(&newBody->parameter, "name", "smime.p7m");
2347 newBody->contents.text.data = (unsigned char *) outs;
2349 *bodyP = newBody;
2351 result = 1;
2353 end:
2355 BIO_free(in);
2356 PKCS7_free(p7);
2357 sk_X509_pop_free(encerts, X509_free);
2359 dprint((9, "encrypt_outgoing_message returns %d", result));
2360 return result;
2365 Get (and decode) the body of the given section of msg
2367 static STORE_S*
2368 get_part_contents(long msgno, const char *section)
2370 long len;
2371 gf_io_t pc;
2372 STORE_S *store = NULL;
2373 char *err;
2375 store = so_get(CharStar, NULL, EDIT_ACCESS);
2376 if(store){
2377 gf_set_so_writec(&pc,store);
2379 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
2381 gf_clear_so_writec(store);
2383 so_seek(store, 0, SEEK_SET);
2385 if(err)
2386 so_give(&store);
2389 return store;
2393 static PKCS7 *
2394 get_pkcs7_from_part(long msgno,const char *section)
2396 STORE_S *store = NULL;
2397 PKCS7 *p7 = NULL;
2398 BIO *in = NULL;
2400 store = get_part_contents(msgno, section);
2402 if(store){
2403 if(store->src == CharStar){
2404 int len;
2407 * We're reaching inside the STORE_S structure. We should
2408 * probably have a way to get the length, instead.
2410 len = (int) (store->eod - store->dp);
2411 in = BIO_new_mem_buf(store->txt, len);
2413 else{ /* just copy it */
2414 unsigned char c;
2416 in = BIO_new(BIO_s_mem());
2417 (void) BIO_reset(in);
2419 so_seek(store, 0L, 0);
2420 while(so_readc(&c, store)){
2421 BIO_write(in, &c, 1);
2425 if(in){
2426 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2427 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
2428 /* error */
2431 BIO_free(in);
2434 so_give(&store);
2437 return p7;
2440 int same_cert(X509 *x, X509 *cert)
2442 char bufcert[256], bufx[256];
2443 int rv = 0;
2445 get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert), ":");
2446 get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx), ":");
2447 if(strcmp(bufx, bufcert) == 0)
2448 rv = 1;
2450 return rv;
2454 /* extract and save certificates from a PKCS7 package. The ctype variable
2455 * tells us if we want to extract it to a public/ or a ca/ directory. The
2456 * later makes sense only for recoverable errors (errors that can be fixed
2457 * by saving to the ca/ directory before we verify the signature).
2458 * Return value:
2459 * 0 - no errors (in public/) no need to try again,
2460 * or validated self signed certificate (in ca/)
2461 * < 0 - certificate error is not recoverable, don't even think about it.
2464 int smime_extract_and_save_cert(PKCS7 *p7)
2466 STACK_OF(X509) *signers;
2467 X509 *x, *cert;
2468 char **email;
2469 int i, j;
2471 if((signers = PKCS7_get0_signers(p7, NULL, 0)) == NULL)
2472 return -1;
2474 for(i = 0; i < sk_X509_num(signers); i++){
2475 if((x = sk_X509_value(signers,i)) == NULL)
2476 continue;
2478 if((email = get_x509_subject_email(x)) != NULL){
2479 for(j = 0; email[j] != NULL; j++){
2480 if((cert = get_cert_for(email[j], Public)) == NULL
2481 || same_cert(x, cert) == 0)
2482 save_cert_for(email[j], x, Public);
2483 X509_free(cert);
2484 fs_give((void **) &email[i]);
2486 fs_give((void **) email);
2489 sk_X509_free(signers);
2491 return 0;
2495 * Try to verify a signature.
2497 * p7 - the pkcs7 object to verify
2498 * in - the plain data to verify (NULL if not detached)
2499 * out - BIO to which to write the opaque data
2500 * silent - if non zero, do not print errors, only print success.
2502 static int
2503 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
2505 STACK_OF(X509) *otherCerts = NULL;
2506 CertList *cl;
2507 int result;
2508 int flags;
2509 const char *data;
2510 long err;
2512 if(!s_cert_store){
2513 if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
2514 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2516 return -1;
2519 smime_extract_and_save_cert(p7);
2521 flags = F_ON(F_USE_CERT_STORE_ONLY, ps_global) ? PKCS7_NOINTERN : 0;
2523 if(ps_global->smime->publiccertlist == NULL){
2524 renew_cert_data(&ps_global->smime->publiccertlist, Public);
2525 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next){
2526 if(cl->x509_cert == NULL){
2527 char *s = strrchr(cl->name, '.');
2528 *s = '\0';
2529 cl->x509_cert = get_cert_for(cl->name, Public);
2530 *s = '.';
2535 if(ps_global->smime->publiccertlist){
2536 otherCerts = sk_X509_new_null();
2537 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next)
2538 if(cl->x509_cert != NULL)
2539 sk_X509_push(otherCerts, X509_dup(cl->x509_cert));
2542 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, flags);
2544 sk_X509_pop_free(otherCerts, X509_free);
2546 if(result){
2547 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
2549 else{
2550 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2552 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
2554 /* Retry verification so we can get the plain text */
2555 /* Might be better to reimplement PKCS7_verify here? */
2557 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY);
2559 if (!silent) q_status_message1(SM_ORDER | SM_DING, 3, 3,
2560 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
2563 return result;
2567 void
2568 free_smime_body_sparep(void **sparep)
2570 char *s;
2571 SIZEDTEXT *st;
2572 if(sparep && *sparep){
2573 switch(get_smime_sparep_type(*sparep)){
2574 case P7Type: PKCS7_free((PKCS7 *) get_smime_sparep_data(*sparep));
2575 break;
2576 case CharType: s = (char *)get_smime_sparep_data(*sparep);
2577 fs_give((void **) &s);
2578 break;
2579 case SizedText : st = (SIZEDTEXT *)get_smime_sparep_data(*sparep);
2580 fs_give((void **) &st->data);
2581 fs_give((void **) &st);
2582 break;
2583 default : break;
2585 ((SMIME_SPARE_S *)(*sparep))->data = NULL;
2586 fs_give(sparep);
2590 /* Big comment, explaining the mess that exists out there, and how we deal
2591 with it, and also how we solve the problems that are created this way.
2593 When Alpine sends a message, it constructs that message, computes the
2594 signature, but then it forgets the message it signed and reconstructs it
2595 again. Since it signs a message containing a notice about "mime aware
2596 tools", but it does not send that we do not include that in the part
2597 that is signed, and that takes care of much of the problems.
2599 Another problem is what is received from the servers. All servers tested
2600 seem to transmit the message that was signed intact and Alpine can check
2601 the signature correctly. That is not a problem. The problem arises when
2602 the message includes attachments. In this case different servers send
2603 different things, so it will be up to us to figure out what is the text
2604 that was actually signed. Confused? here is the story:
2606 When a message containing and attachment is sent by Alpine, UW-IMAP,
2607 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2608 that was sent by Alpine, but GMX.com, Exchange, and probably other
2609 servers add a trailing \r\n in the message, so when validating the
2610 signature, these messages will not validate. There are several things
2611 that can be done.
2613 1. Add a trailing \r\n to any message that contains attachments, sign that
2614 and send that. In this way, all messages will validate with all
2615 servers.
2617 2. Compatibility mode: If a message has an attachment, contains a trailing
2618 \r\n and does not validate (sent by an earlier version of Alpine),
2619 remove the trailing \r\n and try to revalidate again.
2621 3. We do not add \r\n to validate a message that we sent, because that
2622 would only work in Alpine, and not in any other client. That would
2623 not be a good thing to do.
2625 PART II
2627 Now we have to deal with encrypted and signed messages. The problem is
2628 that c-client makes all its pointers point to "on disk" content, but
2629 since we decrypted the data earlier, we have to make sure of two things.
2630 One is that we saved that data (so we do not have to decrypt it again)
2631 and second that we can use it.
2633 In order to save the data we use create_local_cache, so that we do not
2634 have to redecrypt the message. Once this is saved, c-client functions will
2635 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2637 PART III
2639 When we are trying to verify messages with detached signatures, some
2640 imap servers send incorrect information in the mail_fetch_mime call. By
2641 incorrect I mean that this is not fetched directly from the message, but
2642 it is read from the message, processed, and then the processed part is
2643 sent to us, so this text might not agree with what is in the message,
2644 and so the validation of the signature might fail. However, the good
2645 news is that the message validates if saved to a local folder. This
2646 means that if normal validation does not work we can make it work by
2647 saving the message locally and validating that. This is implemented
2648 below, and causes delay in the display of the message. I am considering
2649 at this time not to do this automatically, but wait for the user to tell
2650 us to do it for them by means of a command available in the
2651 mail_view_screen. This might help in other situations, where a message
2652 is supposed to have an attachment, but it can not be seen in the
2653 processed text. Nevertheless, at this time, this is automatic, and is
2654 causing a delay in the processing of the message, but it is validating
2655 correctly all messages.
2657 PART IV
2659 When the user sends a message as encrypted and signed, this code used to
2660 encrypt first, and then sign the pkcs7 body, but it turns out that some
2661 other clients can not handle these messages. While we could argue that the
2662 other clients need to improve, we will support reading messages in both
2663 ways, and will send messages using this technique; that is, signed first,
2664 encrypted second. It seems that all tested clients support this way, so it
2665 should be safe to do so.
2668 typedef struct smime_filter_s {
2669 void (*filter)();
2670 } SMIME_FILTER_S;
2672 SMIME_FILTER_S sig_filter[] = {
2673 {smime_remove_trailing_crlf},
2674 {smime_remove_folding_space}
2677 #define TOTAL_FILTERS (sizeof(sig_filter)/sizeof(sig_filter[0]))
2678 #define TOTAL_SIGFLTR (1 << TOTAL_FILTERS) /* not good, keep filters to a low number */
2680 void
2681 smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen,
2682 char **bodytext, unsigned long *bodylen)
2684 if(*bodylen > 2 && !strncmp(*bodytext+*bodylen-2, "\r\n", 2))
2685 *bodylen -= 2;
2688 void
2689 smime_remove_folding_space(char **mimetext, unsigned long *mimelen,
2690 char **bodytext, unsigned long *bodylen)
2692 char *s = NULL, *t;
2693 unsigned long mlen = *mimelen;
2695 if(*mimetext){
2696 for (s = t = *mimetext; t - *mimetext < *mimelen; ){
2697 if(*t == '\r' && *(t+1) == '\n' && (*(t+2) == '\t' || *(t+2) == ' ')){
2698 *s++ = ' ';
2699 t += 3;
2700 mlen -= 2;
2702 else
2703 *s++ = *t++;
2705 *mimelen = mlen;
2710 smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag)
2712 int result, i, j, flag;
2713 char *mtext, *btext;
2714 unsigned long mlen, blen;
2715 BIO *in;
2717 mtext = mimelen ? fs_get(mimelen+1) : NULL;
2718 btext = fs_get(bodylen+1);
2720 flag = 1; /* silence all failures */
2721 for(i = 1; result == 0 && i < TOTAL_SIGFLTR; i++){
2722 if((in = BIO_new(BIO_s_mem())) == NULL)
2723 return -1;
2725 (void) BIO_reset(in);
2727 if(i+1 == TOTAL_SIGFLTR)
2728 flag = nflag;
2730 if(mimelen)
2731 strncpy(mtext, mimetext, mlen = mimelen);
2732 strncpy(btext, bodytext, blen = bodylen);
2733 for(j = 0; j < TOTAL_FILTERS; j++)
2734 if((i >> j) & 1)
2735 (sig_filter[j].filter)(&mtext, &mlen, &btext, &blen);
2736 if(mtext != NULL)
2737 BIO_write(in, mtext, mlen);
2738 BIO_write(in, btext, blen);
2739 result = do_signature_verify(p7, in, NULL, flag);
2740 BIO_free(in);
2742 if(mtext) fs_give((void **)&mtext);
2743 if(btext) fs_give((void **)&btext);
2744 return result;
2748 * Given a multipart body of type multipart/signed, attempt to verify it.
2749 * Returns non-zero if the body was changed.
2751 static int
2752 do_detached_signature_verify(BODY *b, long msgno, char *section)
2754 PKCS7 *p7 = NULL;
2755 BIO *in = NULL;
2756 PART *p;
2757 int result, modified_the_body = 0;
2758 int flag; /* 1 silent, 0 not silent */
2759 unsigned long mimelen, bodylen;
2760 char newSec[100], *mimetext, *bodytext;
2761 char *what_we_did;
2762 SIZEDTEXT *st;
2764 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"));
2766 smime_init();
2768 /* if it was signed and then encrypted, use the decrypted text
2769 * to check the validity of the signature
2771 if(b->sparep){
2772 if(get_smime_sparep_type(b->sparep) == SizedText){
2773 /* bodytext includes mimetext */
2774 st = (SIZEDTEXT *) get_smime_sparep_data(b->sparep);
2775 bodytext = (char *) st->data;
2776 bodylen = st->size;
2777 mimetext = NULL;
2778 mimelen = 0L;
2781 else{
2782 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
2783 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
2784 if(mimetext)
2785 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
2787 if(mimetext == NULL || bodytext == NULL)
2788 return modified_the_body;
2791 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
2793 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
2794 || (in = BIO_new(BIO_s_mem())) == NULL)
2795 return modified_the_body;
2797 (void) BIO_reset(in);
2798 if(mimetext != NULL)
2799 BIO_write(in, mimetext, mimelen);
2800 BIO_write(in, bodytext, bodylen);
2802 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
2803 flag = (mimelen == 0 || !IS_REMOTE(ps_global->mail_stream->mailbox))
2804 ? 0 : 1;
2805 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, flag);
2806 if(result < 0)
2807 return modified_the_body;
2808 if(result == 0
2809 && mimelen > 0 /* do not do this for encrypted messages */
2810 && IS_REMOTE(ps_global->mail_stream->mailbox)){
2811 char *fetch;
2812 unsigned long hlen, tlen;
2813 STORE_S *msg_so;
2815 BIO_free(in);
2816 if((in = BIO_new(BIO_s_mem())) != NULL
2817 && (fetch = mail_fetch_header(ps_global->mail_stream, msgno, NULL,
2818 NULL, &hlen, FT_PEEK)) != NULL
2819 && (msg_so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL
2820 && so_nputs(msg_so, fetch, (long) hlen)
2821 && (fetch = pine_mail_fetch_text(ps_global->mail_stream, msgno, NULL,
2822 &tlen, FT_PEEK)) != NULL
2823 && so_nputs(msg_so, fetch, tlen)){
2824 STRING bs;
2825 char *h = (char *) so_text(msg_so);
2826 char *bstart = strstr(h, "\r\n\r\n");
2827 ENVELOPE *env;
2828 BODY *body;
2830 bstart += 4;
2831 INIT(&bs, mail_string, bstart, tlen);
2832 rfc822_parse_msg_full(&env, &body, h, bstart-h, &bs, BADHOST, 0, 0);
2833 mail_free_envelope(&env);
2835 mail_free_body_part(&b->nested.part);
2836 b->nested.part = mail_body_section(body, section)->nested.part;
2837 create_local_cache(bstart, bstart, &b->nested.part->body, 1);
2838 modified_the_body = 1;
2840 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
2842 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
2844 if(mimetext)
2845 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
2847 if (mimetext == NULL || bodytext == NULL)
2848 return modified_the_body;
2850 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
2852 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL)
2853 return modified_the_body;
2855 (void) BIO_reset(in);
2856 BIO_write(in, mimetext, mimelen);
2857 BIO_write(in, bodytext, bodylen);
2858 so_give(&msg_so);
2860 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
2861 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, 0);
2862 if(result < 0)
2863 return modified_the_body;
2869 BIO_free(in);
2870 if(b->subtype)
2871 fs_give((void**) &b->subtype);
2873 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
2874 b->encoding = ENC8BIT;
2876 if(b->description)
2877 fs_give ((void**) &b->description);
2879 what_we_did = result ? _("This message was cryptographically signed.") :
2880 _("This message was cryptographically signed but the signature could not be verified.");
2882 b->description = cpystr(what_we_did);
2884 b->sparep = create_smime_sparep(P7Type, p7);
2886 p = b->nested.part;
2888 /* p is signed plaintext */
2889 if(p && p->next)
2890 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
2892 modified_the_body = 1;
2894 return modified_the_body;
2898 PERSONAL_CERT *
2899 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
2901 PERSONAL_CERT *x = NULL;
2903 if(ps_global->smime){
2904 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
2905 X509 *mine;
2907 mine = x->cert;
2909 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,mine->cert_info->issuer) &&
2910 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,mine->cert_info->serialNumber)){
2911 break;
2916 return x;
2920 static PERSONAL_CERT *
2921 find_certificate_matching_pkcs7(PKCS7 *p7)
2923 int i;
2924 STACK_OF(PKCS7_RECIP_INFO) *recips;
2925 PERSONAL_CERT *x = NULL;
2927 recips = p7->d.enveloped->recipientinfo;
2929 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
2930 PKCS7_RECIP_INFO *ri;
2932 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
2934 if((x=find_certificate_matching_recip_info(ri))!=0){
2935 break;
2939 return x;
2942 /* decrypt an encrypted file.
2943 Args: fp - the path to the encrypted file.
2944 rv - a code that tells the caller what happened inside the function
2945 pcert - a personal certificate that was used to encrypt this file
2946 Returns the decoded text allocated in a char *, whose memory must be
2947 freed by caller
2950 char *
2951 decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc)
2953 PKCS7 *p7 = NULL;
2954 char *text, *tmp;
2955 BIO *in = NULL, *out = NULL;
2956 int i, j;
2957 long unsigned int len;
2958 void *ret;
2960 if(pc == NULL || (text = read_file(fp, 0)) == NULL || *text == '\0')
2961 return NULL;
2963 tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1);
2964 for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0'
2965 && text[i] != '-'; j++, i++)
2966 tmp[j] = text[i];
2967 tmp[j] = '\0';
2969 ret = rfc822_base64((unsigned char *)tmp, strlen(tmp), &len);
2971 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
2972 p7 = d2i_PKCS7_bio(in, NULL);
2973 BIO_free(in);
2976 if (text) fs_give((void **)&text);
2977 if (ret) fs_give((void **)&ret);
2979 if (rv) *rv = pc->key == NULL ? -1 : 1;
2981 out = BIO_new(BIO_s_mem());
2982 (void) BIO_reset(out);
2984 i = PKCS7_decrypt(p7, pc->key, pc->cert, out, 0);
2986 if(i == 0){
2987 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
2988 (char*) openssl_error_string());
2989 goto end;
2992 BIO_get_mem_data(out, &tmp);
2994 text = cpystr(tmp);
2995 BIO_free(out);
2997 end:
2998 PKCS7_free(p7);
3000 return text;
3004 * Try to decode (decrypt or verify a signature) a PKCS7 body
3005 * Returns non-zero if something was changed.
3007 static int
3008 do_decoding(BODY *b, long msgno, const char *section)
3010 int modified_the_body = 0;
3011 BIO *out = NULL;
3012 PKCS7 *p7 = NULL;
3013 X509 *recip = NULL;
3014 EVP_PKEY *key = NULL;
3015 PERSONAL_CERT *pcert = NULL;
3016 char *what_we_did = "";
3017 char null[1];
3019 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"));
3020 null[0] = '\0';
3021 smime_init();
3024 * Extract binary data from part to an in-memory store
3027 if(b->sparep){
3028 if(get_smime_sparep_type(b->sparep) == P7Type)
3029 p7 = (PKCS7*) get_smime_sparep_data(b->sparep);
3031 else{
3032 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
3033 if(!p7){
3034 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
3035 (char*) openssl_error_string());
3036 goto end;
3040 * Save the PKCS7 object for later dealings by the user interface.
3041 * It will be cleaned up when the body is garbage collected.
3043 b->sparep = create_smime_sparep(P7Type, p7);
3046 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
3048 if(PKCS7_type_is_signed(p7)){
3049 int sigok;
3051 out = BIO_new(BIO_s_mem());
3052 (void) BIO_reset(out);
3053 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3055 sigok = do_signature_verify(p7, NULL, out, 0);
3057 what_we_did = sigok ? _("This message was cryptographically signed.") :
3058 _("This message was cryptographically signed but the signature could not be verified.");
3060 /* make sure it's null terminated */
3061 BIO_write(out, null, 1);
3063 else if(!PKCS7_type_is_enveloped(p7)){
3064 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
3065 goto end;
3067 else{ /* It *is* enveloped */
3068 int decrypt_result;
3070 what_we_did = _("This message was encrypted.");
3072 /* now need to find a cert that can decrypt this */
3073 pcert = find_certificate_matching_pkcs7(p7);
3075 if(!pcert){
3076 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3077 goto end;
3080 recip = pcert->cert;
3082 if(!load_private_key(pcert)
3083 && ps_global->smime
3084 && ps_global->smime->need_passphrase
3085 && !ps_global->smime->already_auto_asked){
3086 /* Couldn't load key with blank password, ask user */
3087 ps_global->smime->already_auto_asked = 1;
3088 if(pith_opt_smime_get_passphrase){
3089 (*pith_opt_smime_get_passphrase)();
3090 load_private_key(pcert);
3094 key = pcert->key;
3095 if(!key)
3096 goto end;
3098 out = BIO_new(BIO_s_mem());
3099 (void) BIO_reset(out);
3100 BIO_puts(out, "MIME-Version: 1.0\r\n");
3102 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
3104 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3105 forget_private_keys();
3107 if(!decrypt_result){
3108 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3109 (char*) openssl_error_string());
3110 goto end; }
3112 BIO_write(out, null, 1);
3116 * We've now produced a flattened MIME object in BIO out.
3117 * It needs to be turned back into a BODY.
3120 if(out){
3121 BODY *body;
3122 ENVELOPE *env;
3123 char *h = NULL;
3124 char *bstart;
3125 STRING s;
3126 BUF_MEM *bptr = NULL;
3128 BIO_get_mem_ptr(out, &bptr);
3129 if(bptr)
3130 h = bptr->data;
3132 /* look for start of body */
3133 bstart = strstr(h, "\r\n\r\n");
3135 if(!bstart){
3136 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
3138 else{
3139 SIZEDTEXT *st;
3140 bstart += 4; /* skip over CRLF*2 */
3142 INIT(&s, mail_string, bstart, strlen(bstart));
3143 rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
3144 mail_free_envelope(&env); /* Don't care about this */
3146 if(body->type == TYPEMULTIPART
3147 && !strucmp(body->subtype, "SIGNED")){
3148 char *cookie = NULL;
3149 PARAMETER *param;
3150 for (param = body->parameter; param && !cookie; param = param->next)
3151 if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value;
3152 if(cookie != NULL){
3153 st = fs_get(sizeof(SIZEDTEXT));
3154 st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */
3155 st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4);
3156 body->sparep = create_smime_sparep(SizedText, (void *)st);
3158 else
3159 q_status_message(SM_ORDER, 3, 3, _("Couldn't find cookie in attachment list."));
3161 body->mime.offset = 0;
3162 body->mime.text.size = 0;
3165 * Now convert original body (application/pkcs7-mime)
3166 * to a multipart body with one sub-part (the decrypted body).
3167 * Note that the sub-part may also be multipart!
3170 b->type = TYPEMULTIPART;
3171 if(b->subtype)
3172 fs_give((void**) &b->subtype);
3175 * This subtype is used in mailview.c to annotate the display of
3176 * encrypted or signed messages. We know for sure then that it's a PKCS7
3177 * part because the sparep field is set to the PKCS7 object (see above).
3179 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3180 b->encoding = ENC8BIT;
3182 if(b->description)
3183 fs_give((void**) &b->description);
3185 b->description = cpystr(what_we_did);
3187 if(b->disposition.type)
3188 fs_give((void **) &b->disposition.type);
3190 if(b->contents.text.data)
3191 fs_give((void **) &b->contents.text.data);
3193 if(b->parameter)
3194 mail_free_body_parameter(&b->parameter);
3196 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3197 b->nested.part = fs_get(sizeof(PART));
3198 b->nested.part->body = *body;
3199 b->nested.part->next = NULL;
3201 fs_give((void**) &body);
3204 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3205 * the decrypted data. Otherwise, it'll try to load it from the original
3206 * data. Eek.
3208 create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body, 0);
3210 modified_the_body = 1;
3214 end:
3215 if(out)
3216 BIO_free(out);
3218 return modified_the_body;
3223 * Recursively handle PKCS7 bodies in our message.
3225 * Returns non-zero if some fiddling was done.
3227 static int
3228 do_fiddle_smime_message(BODY *b, long msgno, char *section)
3230 int modified_the_body = 0;
3232 if(!b)
3233 return 0;
3235 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"));
3237 if(is_pkcs7_body(b)){
3239 if(do_decoding(b, msgno, section)){
3241 * b should now be a multipart message:
3242 * fiddle it too in case it's been multiply-encrypted!
3245 /* fallthru */
3246 modified_the_body = 1;
3250 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
3252 PART *p;
3253 int partNum;
3254 char newSec[100];
3256 if(MIME_MULT_SIGNED(b->type, b->subtype)){
3260 * Ahah. We have a multipart signed entity.
3262 * Multipart/signed
3263 * part 1 (signed thing)
3264 * part 2 (the pkcs7 signature)
3266 * We're going to convert that to
3268 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3269 * part 1 (signed thing)
3270 * part 2 has been freed
3272 * We also extract the signature from part 2 and save it
3273 * in the multipart body->sparep, and we add a description
3274 * in the multipart body->description.
3277 * The results of a decrypted message will be similar. It
3278 * will be
3280 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3281 * part 1 (decrypted thing)
3284 modified_the_body += do_detached_signature_verify(b, msgno, section);
3286 else if(MIME_MSG(b->type, b->subtype)){
3287 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
3289 else{
3291 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
3292 /* Append part number to the section string */
3294 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
3296 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
3301 return modified_the_body;
3306 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3307 * Returns non-zero if something was changed.
3310 fiddle_smime_message(BODY *b, long msgno)
3312 return do_fiddle_smime_message(b, msgno, "");
3316 /********************************************************************************/
3320 * Output a string in a distinctive style
3322 void
3323 gf_puts_uline(char *txt, gf_io_t pc)
3325 pc(TAG_EMBED); pc(TAG_BOLDON);
3326 gf_puts(txt, pc);
3327 pc(TAG_EMBED); pc(TAG_BOLDOFF);
3330 /* get_chain_for_cert: error and level are mandatory arguments */
3331 STACK_OF(X509) *
3332 get_chain_for_cert(X509 *cert, int *error, int *level)
3334 STACK_OF(X509) *chain = NULL;
3335 X509_STORE_CTX *ctx;
3336 X509 *x, *xtmp;
3337 int rc; /* return code */
3339 *level = -1;
3340 *error = 0;
3341 ERR_clear_error();
3342 if((ctx = X509_STORE_CTX_new()) != NULL){
3343 X509_STORE_set_flags(s_cert_store, 0);
3344 if(!X509_STORE_CTX_init(ctx, s_cert_store, cert, NULL))
3345 *error = X509_STORE_CTX_get_error(ctx);
3346 else if((chain = sk_X509_new_null()) != NULL){
3347 for(x = cert; ; x = xtmp){
3348 if(++*level > 0)
3349 sk_X509_push(chain, X509_dup(x));
3350 rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x);
3351 if(rc < 0)
3352 *error = 1;
3353 if(rc <= 0)
3354 break;
3355 if(!X509_check_issued(xtmp, xtmp))
3356 break;
3359 X509_STORE_CTX_free(ctx);
3361 return chain;
3366 * Sign a message. Called from call_mailer in send.c.
3368 * This takes the header for the outgoing message as well as a pointer
3369 * to the current body (which may be reallocated).
3370 * The last argument (BODY **bp) is an argument that tells Alpine
3371 * if the body has 8 bit. if *bp is not null we compute two signatures
3372 * one for the quoted-printable encoded message, and another for the
3373 * 8bit encoded message. We return the signature for the 8bit encoded
3374 * part in p2->body.mime.text.data.
3375 * The reason why we compute two signatures is so that we can decide
3376 * which one to use later, and we only do it in the case that *bp is
3377 * not null. If we did not do this, then we might not be able to sign
3378 * a message until we log in to the smtp server, so instead of doing
3379 * that, we get ready for any possible situation we might find.
3382 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp)
3384 STORE_S *outs = NULL;
3385 STORE_S *outs_2 = NULL;
3386 BODY *body = *bodyP;
3387 BODY *newBody = NULL;
3388 PART *p1 = NULL;
3389 PART *p2 = NULL;
3390 PERSONAL_CERT *pcert;
3391 BIO *in = NULL;
3392 BIO *in_2 = NULL;
3393 BIO *out = NULL;
3394 BIO *out_2 = NULL;
3395 PKCS7 *p7 = NULL;
3396 PKCS7 *p7_2 = NULL;
3397 STACK_OF(X509) *chain;
3398 int result = 0, error;
3399 int flags = dont_detach ? 0 : PKCS7_DETACHED;
3400 int level;
3402 dprint((9, "sign_outgoing_message()"));
3404 smime_init();
3406 /* Look for a private key matching the sender address... */
3408 pcert = match_personal_cert(header->env);
3410 if(!pcert){
3411 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
3412 goto end;
3415 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
3416 /* Couldn't load key with blank password, try again */
3417 if(pith_opt_smime_get_passphrase){
3418 (*pith_opt_smime_get_passphrase)();
3419 load_private_key(pcert);
3423 if(!pcert->key)
3424 goto end;
3426 if(((chain = get_chain_for_cert(pcert->cert, &error, &level)) != NULL && error)
3427 || level == 0){
3428 sk_X509_pop_free(chain, X509_free);
3429 chain = NULL;
3432 if(error)
3433 q_status_message(SM_ORDER, 1, 1,
3434 _("Not all certificates needed to verify signature included in signed message"));
3436 in = body_to_bio(body);
3438 p7 = PKCS7_sign(pcert->cert, pcert->key, chain, in, flags);
3440 if(bp && *bp){
3441 int i, save_encoding;
3443 for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++);
3445 if(i > ENCMAX){ /* no empty encoding slots! */
3446 *bp = NULL;
3448 else {
3449 save_encoding = (*bp)->encoding;
3450 body_encodings[(*bp)->encoding = i] = body_encodings[ENC8BIT];
3452 in_2 = body_to_bio(body);
3454 body_encodings[i] = NULL;
3455 (*bp)->encoding = save_encoding;
3459 if(bp && *bp)
3460 p7_2 = PKCS7_sign(pcert->cert, pcert->key, chain, in_2, flags);
3462 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3463 forget_private_keys();
3465 if(chain)
3466 sk_X509_pop_free(chain, X509_free);
3468 if(!p7){
3469 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
3470 goto end;
3473 outs = so_get(BioType, NULL, EDIT_ACCESS);
3474 out = bio_from_store(outs);
3476 i2d_PKCS7_bio(out, p7);
3477 (void) BIO_flush(out);
3479 so_seek(outs, 0, SEEK_SET);
3481 if(bp && *bp && p7_2){
3482 outs_2 = so_get(BioType, NULL, EDIT_ACCESS);
3483 out_2 = bio_from_store(outs_2);
3485 i2d_PKCS7_bio(out_2, p7_2);
3486 (void) BIO_flush(out_2);
3488 so_seek(outs_2, 0, SEEK_SET);
3491 if((flags&PKCS7_DETACHED)==0){
3493 /* the simple case: the signed data is in the pkcs7 object */
3495 newBody = mail_newbody();
3497 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3499 newBody->contents.text.data = (unsigned char *) outs;
3500 *bodyP = newBody;
3502 result = 1;
3504 else{
3507 * OK.
3508 * We have to create a new body as follows:
3510 * multipart/signed; blah blah blah
3511 * reference to existing body
3513 * pkcs7 object
3516 newBody = mail_newbody();
3518 newBody->type = TYPEMULTIPART;
3519 newBody->subtype = cpystr("signed");
3520 newBody->encoding = ENC7BIT;
3522 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
3523 set_parameter(&newBody->parameter, "micalg", "sha1");
3525 p1 = mail_newbody_part();
3526 p2 = mail_newbody_part();
3529 * This is nasty. We're just copying the body in here,
3530 * but since our newBody is freed at the end of call_mailer,
3531 * we mustn't let this body (the original one) be freed twice.
3533 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
3535 p1->next = p2;
3537 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL);
3538 p2->body.mime.text.data = (unsigned char *) outs_2;
3539 p2->body.contents.text.data = (unsigned char *) outs;
3541 newBody->nested.part = p1;
3543 *bodyP = newBody;
3545 result = 1;
3548 end:
3550 PKCS7_free(p7);
3551 BIO_free(in);
3553 if(bp && *bp){
3554 if(p7_2) PKCS7_free(p7_2);
3555 BIO_free(in_2);
3558 dprint((9, "sign_outgoing_message returns %d", result));
3559 return result;
3563 SMIME_STUFF_S *
3564 new_smime_struct(void)
3566 SMIME_STUFF_S *ret = NULL;
3568 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
3569 memset((void *) ret, 0, sizeof(*ret));
3570 ret->publictype = Nada;
3572 return ret;
3576 static void
3577 free_smime_struct(SMIME_STUFF_S **smime)
3579 if(smime && *smime){
3580 if((*smime)->passphrase_emailaddr){
3581 int i;
3582 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
3583 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
3584 fs_give((void **) (*smime)->passphrase_emailaddr);
3587 if((*smime)->publicpath)
3588 fs_give((void **) &(*smime)->publicpath);
3590 if((*smime)->publiccertlist)
3591 free_certlist(&(*smime)->publiccertlist);
3593 if((*smime)->backuppubliccertlist)
3594 free_certlist(&(*smime)->backuppubliccertlist);
3596 if((*smime)->cacertlist)
3597 free_certlist(&(*smime)->cacertlist);
3599 if((*smime)->backupcacertlist)
3600 free_certlist(&(*smime)->backupcacertlist);
3602 if((*smime)->privatecertlist)
3603 free_certlist(&(*smime)->privatecertlist);
3605 if((*smime)->backupprivatecertlist)
3606 free_certlist(&(*smime)->backupprivatecertlist);
3608 if((*smime)->publiccontent)
3609 fs_give((void **) &(*smime)->publiccontent);
3611 if((*smime)->privatepath)
3612 fs_give((void **) &(*smime)->privatepath);
3614 if((*smime)->personal_certs){
3615 PERSONAL_CERT *pc;
3617 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
3618 free_personal_certs(&pc);
3619 (*smime)->personal_certs = NULL;
3622 if((*smime)->backuppersonal_certs){
3623 PERSONAL_CERT *pc;
3625 pc = (PERSONAL_CERT *) (*smime)->backuppersonal_certs;
3626 free_personal_certs(&pc);
3627 (*smime)->backuppersonal_certs = NULL;
3630 if((*smime)->privatecontent)
3631 fs_give((void **) &(*smime)->privatecontent);
3633 if((*smime)->capath)
3634 fs_give((void **) &(*smime)->capath);
3636 if((*smime)->cacontent)
3637 fs_give((void **) &(*smime)->cacontent);
3639 fs_give((void **) smime);
3643 #endif /* SMIME */