* Bug (introduced in version 2.20.9): Saving a password in the password
[alpine.git] / pith / smime.c
blob6f9efc7f01c6d6a20e4d9c447ca9545ff2a9fc1d
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-2016 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, int check_cert);
75 int same_cert(X509 *, X509 *);
76 CertList * certlist_from_personal_certs(PERSONAL_CERT *pc);
77 #ifdef PASSFILE
78 int load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile, char **certfile, EVP_PKEY **pkey, X509 **pcert);
79 #endif /* PASSFILE */
80 EVP_PKEY *load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *);
81 void smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen);
82 void smime_remove_folding_space(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen);
83 int smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag);
85 int (*pith_opt_smime_get_passphrase)(void);
86 int (*pith_smime_import_certificate)(char *, char *, size_t);
87 int (*pith_smime_enter_password)(char *prompt, char *, size_t);
88 int (*pith_smime_confirm_save)(char *email);
90 static X509_STORE *s_cert_store;
92 /* State management for randomness functions below */
93 static int seeded = 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.
125 * Return values:
126 * -1 : user cancelled load
127 * 0 : load was successful
128 * 1 : there was an error in the loading.
131 load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile,
132 char **certfile, EVP_PKEY **pkey, X509 **pcert)
134 char buf[MAXPATH+1], pathkey[MAXPATH+1], prompt[MAILTMPLEN];
135 DIR *dirp;
136 struct dirent *d;
137 int b = 0, ret = 1; /* assume error */
139 if(pathkeydir == NULL || pathcertdir == NULL || keyfile == NULL
140 || pkey == NULL || certfile == NULL || pcert == NULL)
141 return 1;
143 *keyfile = NULL;
144 *certfile = NULL;
145 *pkey = NULL;
146 *pcert = NULL;
148 if((dirp = opendir(pathkeydir)) != NULL){
149 while(b == 0 && (d=readdir(dirp)) != NULL){
150 size_t ll;
152 if((ll=strlen(d->d_name)) && ll > 4){
153 if(!strcmp(d->d_name+ll-4, ".key")){
154 strncpy(buf, d->d_name, sizeof(buf));
155 buf[sizeof(buf)-1] = '\0';
156 build_path(pathkey, pathkeydir, buf, sizeof(pathkey));
157 buf[strlen(buf)-4] = '\0';
158 snprintf(prompt, sizeof(prompt),
159 _("Enter password of key <%s> to unlock password file: "), buf);
160 if((*pkey = load_pkey_with_prompt(pathkey, NULL, prompt, &ret)) != NULL){
161 if(load_cert_for_key(pathcertdir, *pkey, certfile, pcert)){
162 b = 1; /* break */
163 *keyfile = cpystr(buf);
164 } else {
165 EVP_PKEY_free(*pkey);
166 *pkey = NULL;
167 q_status_message1(SM_ORDER, 0, 2,
168 _("Cannot find certificate that matches key <%s>. Continuing..."), buf);
174 closedir(dirp);
176 return ret;
180 /* setup a key and certificate to encrypt and decrypt a password file.
181 * These files will be saved in the .alpine-smime/.pwd directory, but its
182 * location can be setup in the command line with the -pwdcertdir option.
183 * Here are the rules:
185 * Check if the .alpine-smime/.pwd (or -pwdcertdir directory) exists,
186 * if not create it. If we are successful, move to the next step
188 * - If the user has a key/cert pair, in the .alpine-smime/.pwd dir
189 * setup is successful;
190 * - if the user does not have a key/cert pair, look to see if
191 * ps_global->smime->personal_certs is already setup, if so, use it.
192 * - if ps_global->smime->personal_certs is not set up, see if we can
193 * find a certificate/cert pair in the default locations at compilation
194 * time. (~/.alpine-smime/private and ~/.alpine-smime/public
195 * - if none of this is successful, create a key/certificate pair
196 * (TODO: implement this)
197 * - in any other case, setup is not successful.
199 * If setup is successful, setup ps_global->pwdcert.
200 * If any of this fails, ps_global->pwdcert will be null.
201 * Ok, that should do it.
203 void
204 setup_pwdcert(void **pwdcert)
206 int we_inited = 0;
207 int setup_dir = 0; /* make it non zero if we know which dir to use */
208 struct stat sbuf;
209 char pathdir[MAXPATH+1], pathkey[MAXPATH+1], fpath[MAXPATH+1], pathcert[MAXPATH+1];
210 char fpath2[MAXPATH+1], prompt[MAILTMPLEN];
211 char *keyfile, *certfile, *text;
212 EVP_PKEY *pkey = NULL;
213 X509 *pcert = NULL;
214 PERSONAL_CERT *pc, *pc2 = NULL;
215 static int was_here = 0;
217 if(pwdcert == NULL || was_here == 1)
218 return;
220 was_here++;
221 if(ps_global->pwdcertdir){
222 if(our_stat(ps_global->pwdcertdir, &sbuf) == 0
223 && ((sbuf.st_mode & S_IFMT) == S_IFDIR)){
224 setup_dir++;
225 strncpy(pathdir, ps_global->pwdcertdir, sizeof(pathdir));
226 pathdir[sizeof(pathdir)-1] = '\0';
228 } else {
229 smime_path(DF_PASSWORD_DIR, pathdir, sizeof(pathdir));
230 if(our_stat(pathdir, &sbuf) == 0){
231 if((sbuf.st_mode & S_IFMT) == S_IFDIR)
232 setup_dir++;
233 } else if(can_access(pathdir, ACCESS_EXISTS) != 0
234 && our_mkpath(pathdir, 0700) == 0)
235 setup_dir++;
238 if(setup_dir == 0){
239 was_here = 0;
240 return;
243 if(load_key_and_cert(pathdir, pathdir, &keyfile, &certfile, &pkey, &pcert) < 0){
244 was_here = 0;
245 return;
248 if(certfile && keyfile){
249 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
250 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
251 pc->name = keyfile;
252 pc->key = pkey;
253 pc->cert = pcert;
254 *pwdcert = (void *) pc;
255 fs_give((void **)&certfile);
256 was_here = 0;
257 return;
260 /* if the user gave a pwdcertdir and there is nothing there, do not
261 * continue. Let the user initialize on their own this directory.
263 if(ps_global->pwdcertdir != NULL){
264 was_here = 0;
265 return;
268 /* look to see if there are any certificates lying around, first
269 * we try to load ps_global->smime to see if that has information
270 * we can use. If we are the process filling the smime structure
271 * we deinit at the end, since this might not do a full init.
273 if(ps_global && ps_global->smime && !ps_global->smime->inited){
274 we_inited++;
275 smime_init();
278 /* at this point ps_global->smime->inited == 1 */
279 if(ps_global->smime && ps_global->smime->personal_certs != NULL){
280 pc = (PERSONAL_CERT *) ps_global->smime->personal_certs;
281 if(ps_global->smime->privatetype == Directory){
282 build_path(pathkey, ps_global->smime->privatepath, pc->name, sizeof(pathkey));
283 strncat(pathkey, ".key", 4);
284 pathkey[sizeof(pathkey)-1] = '\0';
285 text = NULL;
286 } else if (ps_global->smime->privatetype == Container){
287 if(pc->keytext == NULL){ /* we should *never* be here, but just in case */
288 if(ps_global->smime->privatecontent != NULL){
289 char tmp[MAILTMPLEN], *s, *t, c;
290 snprintf(tmp, sizeof(tmp), "%s%s", EMAILADDRLEADER, pc->name);
291 tmp[sizeof(tmp)-1] = '\0';
292 if((s = strstr(ps_global->smime->privatecontent, tmp)) != NULL){
293 if((t = strstr(s+strlen(tmp), EMAILADDRLEADER)) != NULL){
294 c = *t;
295 *t = '\0';
296 pc->keytext = cpystr(s + strlen(tmp) + 1); /* 1 = strlen("\n") */
297 *t = c;
299 else
300 pc->keytext = cpystr(s + strlen(tmp) + 1); /* 1 = strlen("\n") */
304 if(pc->keytext != NULL) /* we should go straigth here */
305 text = pc->keytext;
306 } else if (ps_global->smime->privatetype == Keychain){
307 pathkey[0] = '\0'; /* no apple key chain support yet */
308 text = NULL;
310 if((pathkey && *pathkey) || text){
311 snprintf(prompt, sizeof(prompt),
312 _("Enter password of key <%s> to unlock password file: "), pc->name);
314 if((pkey = load_pkey_with_prompt(pathkey, text, prompt, NULL)) != NULL){
315 pc2 = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
316 memset((void *)pc2, 0, sizeof(PERSONAL_CERT));
317 pc2->name = cpystr(pc->name);
318 pc2->key = pkey;
319 pc2->cert = X509_dup(pc->cert);
321 /* now copy the keys and certs, starting by the key... */
322 build_path(fpath, pathdir, pc->name, sizeof(fpath));
323 strncat(fpath, ".key", 4);
324 fpath[sizeof(fpath)-1] = '\0';
325 if(our_stat(fpath, &sbuf) == 0){ /* if fpath exists */
326 if((sbuf.st_mode & S_IFMT) == S_IFREG) /* and is a regular file */
327 setup_dir++; /* we are done */
328 } else if(ps_global->smime->privatetype == Directory){
329 if(our_copy(fpath, pathkey) == 0)
330 setup_dir++;
331 } else if(ps_global->smime->privatetype == Container){
332 BIO *out;
333 if((out = BIO_new_file(fpath, "w")) != NULL){
334 if(BIO_puts(out, pc->keytext) > 0)
335 setup_dir++;
336 BIO_free(out);
338 } else if(ps_global->smime->privatetype == Keychain){
339 /* add support for Apple Mac OS X */
343 /* successful copy of key, now continue with certificate */
344 if(setup_dir){
345 setup_dir = 0;
347 build_path(pathkey, ps_global->smime->publicpath, pc->name, sizeof(pathkey));
348 strncat(pathkey, ".crt", 4);
349 pathkey[sizeof(pathkey)-1] = '\0';
351 build_path(fpath, pathdir, pc->name, sizeof(fpath));
352 strncat(fpath, ".crt", 4);
353 fpath[sizeof(fpath)-1] = '\0';
355 if(our_stat(fpath, &sbuf) == 0){
356 if((sbuf.st_mode & S_IFMT) == S_IFREG)
357 setup_dir++;
359 else if(ps_global->smime->privatetype == Directory){
360 if(our_copy(fpath, pathkey) == 0)
361 setup_dir++;
362 } else if(ps_global->smime->privatetype == Container) {
363 BIO *out;
364 if((out = BIO_new_file(fpath, "w")) != NULL){
365 if(PEM_write_bio_X509(out, pc->cert))
366 setup_dir++;
367 BIO_free(out);
369 } else if (ps_global->smime->privatetype == Keychain) {
370 /* add support for Mac OS X */
374 if(setup_dir){
375 *pwdcert = (void *) pc2;
376 was_here = 0;
377 return;
379 else if(pc2 != NULL)
380 free_personal_certs(&pc2);
381 } /* if (pathkey...) */
382 } /* if(ps_global->smime->personal_certs) */
385 if(setup_dir == 0){
386 /* PATHCERTDIR(Private) must be null, so create a path */
387 set_current_val(&ps_global->vars[V_PRIVATEKEY_DIR], TRUE, TRUE);
388 smime_path(ps_global->VAR_PRIVATEKEY_DIR, pathkey, sizeof(pathkey));
390 /* PATHCERTDIR(Public) must be null, so create a path */
391 set_current_val(&ps_global->vars[V_PUBLICCERT_DIR], TRUE, TRUE);
392 smime_path(ps_global->VAR_PUBLICCERT_DIR, pathcert, sizeof(pathcert));
394 /* BUG: this does not support local containers */
395 load_key_and_cert(pathkey, pathcert, &keyfile, &certfile, &pkey, &pcert);
397 if(certfile && keyfile){
398 build_path(fpath, pathdir, keyfile, sizeof(fpath));
399 strncat(fpath, ".key", 4);
400 fpath[sizeof(fpath)-1] = '\0';
402 build_path(fpath2, pathkey, keyfile, sizeof(fpath));
403 strncat(fpath2, ".key", 4);
404 fpath2[sizeof(fpath2)-1] = '\0';
406 if(our_copy(fpath, fpath2) == 0)
407 setup_dir++;
409 if(setup_dir){
410 setup_dir = 0;
412 build_path(fpath, pathdir, certfile, sizeof(fpath));
413 build_path(fpath2, pathcert, certfile, sizeof(fpath2));
415 if(our_copy(fpath, fpath2) == 0)
416 setup_dir++;
421 if(keyfile && certfile){
422 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
423 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
424 pc->name = keyfile;
425 pc->key = pkey;
426 pc->cert = pcert;
427 *pwdcert = (void *) pc;
428 fs_give((void **)&certfile);
429 was_here = 0;
430 return;
433 /* TODO: create self signed certificate
434 q_status_message(SM_ORDER, 2, 2,
435 _("No key/certificate pair found for password file encryption support"));
437 was_here = 0;
438 if(we_inited)
439 smime_deinit();
441 #endif /* PASSFILE */
443 /* smime_expunge_cert.
444 * Return values: < 0 there was an error.
445 * >=0 the number of messages expunged
448 smime_expunge_cert(WhichCerts ctype)
450 int count, removed;
451 CertList *cl, *dummy, *data;
452 char *path, buf[MAXPATH+1];
453 char *contents;
455 if(DATACERT(ctype)== NULL)
456 return -1;
458 /* data cert is the way we unify certificate management across
459 * functions, but it is not where we really save the information in the
460 * case ctype is equal to Private. What we will do is to update the
461 * datacert, and in the case of ctype equal to Private use the updated
462 * certdata to update the personal_certs data.
465 path = PATHCERTDIR(ctype);
467 if(path){
468 /* add a fake certificate at the beginning of the list */
469 dummy = fs_get(sizeof(CertList));
470 memset((void *)dummy, 0, sizeof(CertList));
471 dummy->next = DATACERT(ctype);
473 for(cl = dummy, count = 0; cl && cl->next;){
474 if(cl->next->data.deleted == 0){
475 cl = cl->next;
476 continue;
479 removed = 1; /* assume success */
480 if(SMHOLDERTYPE(ctype) == Directory){
481 build_path(buf, path, cl->next->name, sizeof(buf));
482 if(ctype == Private && strlen(buf) + strlen(EXTCERT(Private)) < sizeof(buf)){
483 strncat(buf, EXTCERT(Private), 4);
484 buf[sizeof(buf)-1] = '\0';
487 if(our_unlink(buf) < 0){
488 q_status_message1(SM_ORDER, 3, 3, _("Error removing certificate %s"), cl->next->name);
489 cl = cl->next;
490 removed = 0;
493 else if(SMHOLDERTYPE(ctype) == Container){
494 char *prefix= ctype == CACert ? CACERTSTORELEADER : EMAILADDRLEADER;
495 char tmp[MAILTMPLEN], *s, *t;
497 contents = CONTENTCERTLIST(ctype);
498 snprintf(tmp, sizeof(tmp), "%s%s", prefix, cl->next->name);
499 tmp[sizeof(tmp) - 1] = '\0';
500 if((s = strstr(contents, tmp)) != NULL){
501 if((t = strstr(s+strlen(tmp), prefix)) == NULL)
502 *s = '\0';
503 else
504 memmove(s, t, strlen(t)+1);
505 fs_resize((void **)&contents, strlen(contents)+1);
506 switch(ctype){
507 case Private: ps_global->smime->privatecontent = contents; break;
508 case Public : ps_global->smime->publiccontent = contents; break;
509 case CACert : ps_global->smime->cacontent = contents; break;
510 default : break;
513 else
514 removed = 0;
515 } else { /* unhandled case */
518 if(removed > 0){
519 count++; /* count it! */
520 data = cl->next;
521 cl->next = data->next;
522 if(data->name) fs_give((void **)&data->name);
523 fs_give((void **)&data);
526 } else
527 q_status_message(SM_ORDER, 3, 3, _("Error expunging certificate"));
529 switch(ctype){
530 case Private: ps_global->smime->privatecertlist = dummy->next; break;
531 case Public : ps_global->smime->publiccertlist = dummy->next; break;
532 case CACert : ps_global->smime->cacertlist = dummy->next; break;
533 default : break;
535 fs_give((void **)&dummy);
536 if(SMHOLDERTYPE(ctype) == Container){
537 if(copy_dir_to_container(ctype, contents) < 0)
538 count = 0;
540 if(count > 0){
541 q_status_message2(SM_ORDER, 3, 3, _("Removed %s certificate%s"), comatose(count), plural(count));
543 else
544 q_status_message(SM_ORDER, 3, 3, _("Error: No certificates were removed"));
545 return count;
548 void
549 mark_cert_deleted(WhichCerts ctype, int num, unsigned state)
551 CertList *cl;
552 int i;
554 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
555 cl->data.deleted = state;
558 unsigned
559 get_cert_deleted(WhichCerts ctype, int num)
561 CertList *cl;
562 int i;
564 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
565 return (cl && cl->data.deleted) ? 1 : 0;
568 EVP_PKEY *
569 load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *ret)
571 EVP_PKEY *pkey;
572 int rc = 0; /* rc == 1, cancel, rc == 0 success */
573 char pass[MAILTMPLEN+1];
574 BIO *in;
576 /* attempt to load with empty password */
577 in = text ? BIO_new_mem_buf(text, strlen(text)) : BIO_new_file(fpath, "r");
578 if(in != NULL){
579 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, "");
580 if(pkey != NULL) return pkey;
581 } else return NULL;
583 if(pith_smime_enter_password)
584 while(pkey == NULL && rc != 1){
585 do {
586 rc = (*pith_smime_enter_password)(prompt, (char *)pass, sizeof(pass));
587 } while (rc!=0 && rc!=1 && rc>0);
589 (void) BIO_reset(in);
590 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, (char *)pass);
593 BIO_free(in);
595 if(ret) *ret = rc == 1 ? -1 : pkey != NULL ? 0 : 1;
596 return pkey;
602 import_certificate(WhichCerts ctype)
604 int r = 1, rc;
605 char filename[MAXPATH+1], full_filename[MAXPATH+1], buf[MAXPATH+1];
607 if(pith_smime_import_certificate == NULL){
608 q_status_message(SM_ORDER, 0, 2,
609 _("import of certificates not implemented yet!"));
610 return -1;
613 smime_init();
615 r = (*pith_smime_import_certificate)(filename, full_filename, sizeof(filename) - 20);
617 ps_global->mangled_screen = 1;
619 if(r < 0)
620 return r;
621 else if (ctype == Private){
622 char prompt[500], *s, *t;
623 EVP_PKEY *key = NULL;
625 if(!ps_global->smime->privatecertlist){
626 ps_global->smime->privatecertlist = fs_get(sizeof(CertList));
627 memset((void *)DATACERT(ctype), 0, sizeof(CertList));
630 for(s = t = filename; (t = strstr(s, ".key")) != NULL; s = t + 1);
631 if(s) *(s-1) = 0;
633 snprintf(prompt, sizeof(prompt), _("Enter passphrase for <%s>: "), filename);
634 prompt[sizeof(prompt)-1] = '\0';
635 if((key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){
636 if(SMHOLDERTYPE(ctype) == Directory){
637 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
638 if(strcmp(buf + strlen(buf) - 4, EXTCERT(ctype)) != 0 && strlen(buf) + 4 < sizeof(buf)){
639 strncat(buf, EXTCERT(ctype), 4);
640 buf[sizeof(buf)-1] = '\0';
642 rc = our_copy(buf, full_filename);
644 else /* if(SMHOLDERTYPE(ctype) == Container){ */
645 rc = add_file_to_container(ctype, full_filename, NULL);
646 if(rc == 0)
647 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
648 else
649 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
650 if(ps_global->smime->publiccertlist)
651 ps_global->smime->publiccertlist->data.renew = 1;
653 else
654 q_status_message(SM_ORDER, 1, 3, _("Problem unlocking key (not a certificate and/or wrong password)"));
655 } else if (ctype == CACert){
656 BIO *ins;
657 X509 *cert;
659 if((ins = BIO_new_file(full_filename, "r")) != NULL){
660 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
661 if(SMHOLDERTYPE(ctype) == Directory){
662 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
663 if(strcmp(buf + strlen(buf) - 4, ".crt") != 0 && strlen(buf) + 4 < sizeof(buf)){
664 strncat(buf, EXTCERT(ctype), 4);
665 buf[sizeof(buf)-1] = '\0';
668 rc = our_copy(buf, full_filename);
670 else /* if(SMHOLDERTYPE(ctype) == Container){ */
671 rc = add_file_to_container(ctype, full_filename, NULL);
672 if(rc == 0)
673 q_status_message(SM_ORDER, 1, 3, _("Certificate saved"));
674 else
675 q_status_message(SM_ORDER, 1, 3, _("Error saving certificate"));
676 X509_free(cert); /* not needed anymore */
678 else
679 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
680 BIO_free(ins);
682 renew_store();
683 } else { /* ctype == Public. save certificate, but first validate that it is one */
684 BIO *ins;
685 X509 *cert;
687 if((ins = BIO_new_file(full_filename, "r")) != NULL){
688 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
689 if(SMHOLDERTYPE(ctype) == Directory){
690 char **email;
692 if((email = get_x509_subject_email(cert)) != NULL){
693 int i;
694 for(i = 0; email[i] != NULL; i++){
695 save_cert_for(email[i], cert, Public);
696 fs_give((void **)&email[i]);
698 fs_give((void **)email);
700 if(strcmp(filename + strlen(filename) - 4, ".crt") == 0)
701 filename[strlen(filename) - 4] = '\0';
702 save_cert_for(filename, cert, Public);
704 else /* if(SMHOLDERTYPE(ctype) == Container){ */
705 add_file_to_container(ctype, full_filename, NULL);
706 X509_free(cert);
707 if(ps_global->smime->publiccertlist)
708 ps_global->smime->publiccertlist->data.renew = 1;
710 else
711 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
712 BIO_free(ins);
715 if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1;
716 return 0;
719 /* itype: information type to add: 0 - public, 1 - private.
720 * Memory freed by caller
722 BIO *
723 print_private_key_information(char *email, int itype)
725 BIO *out;
726 PERSONAL_CERT *pc;
728 if(ps_global->smime == NULL
729 || ps_global->smime->personal_certs == NULL
730 || (itype != 0 && itype != 1))
731 return NULL;
733 for(pc = ps_global->smime->personal_certs;
734 pc != NULL && strcmp(pc->name, email) != 0; pc = pc->next);
735 if(pc->key == NULL
736 && !load_private_key(pc)
737 && ps_global->smime
738 && ps_global->smime->need_passphrase){
739 if (*pith_opt_smime_get_passphrase)
740 (*pith_opt_smime_get_passphrase)();
741 load_private_key(pc);
744 if(pc->key == NULL)
745 return NULL;
747 out = BIO_new(BIO_s_mem());
748 if(itype == 0) /* 0 means public */
749 EVP_PKEY_print_public(out, pc->key, 0, NULL);
750 else if (itype == 1) /* 1 means private */
751 EVP_PKEY_print_private(out, pc->key, 0, NULL);
753 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
754 forget_private_keys();
756 return out;
760 * Forget any cached private keys
762 static void
763 forget_private_keys(void)
765 PERSONAL_CERT *pcert;
766 size_t len;
767 volatile char *p;
769 dprint((9, "forget_private_keys()"));
770 if(ps_global->smime){
771 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
772 pcert;
773 pcert=pcert->next){
775 if(pcert->key){
776 EVP_PKEY_free(pcert->key);
777 pcert->key = NULL;
781 ps_global->smime->entered_passphrase = 0;
782 len = sizeof(ps_global->smime->passphrase);
783 p = ps_global->smime->passphrase;
785 while(len-- > 0)
786 *p++ = '\0';
790 /* modelled after signature_path in reply.c, but uses home dir instead of the
791 * directory where the .pinerc is located, since according to documentation,
792 * the .alpine-smime directories are subdirectories of the home directory
794 int smime_path(char *rpath, char *fpath, size_t len)
796 *fpath = '\0';
797 if(rpath && *rpath){
798 size_t spl = strlen(rpath);
800 if(IS_REMOTE(rpath)){
801 if(spl < len - 1)
802 strncpy(fpath, rpath, len-1);
803 fpath[len-1] = '\0';
805 else if(is_absolute_path(rpath)){
806 strncpy(fpath, rpath, len-1);
807 fpath[len-1] = '\0';
808 fnexpand(fpath, len);
810 else if(ps_global->VAR_OPER_DIR){
811 if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
812 build_path(fpath, ps_global->VAR_OPER_DIR, rpath, len);
814 else if(ps_global->home_dir){
815 if(strlen(ps_global->home_dir) + spl < len - 1)
816 build_path(fpath, ps_global->home_dir, rpath, len);
819 return fpath && *fpath ? 1 : 0;
825 * taken from openssl/apps/app_rand.c
827 static int
828 app_RAND_load_file(const char *file)
830 #define RANDBUFLEN 200
831 char buffer[RANDBUFLEN];
833 if(file == NULL)
834 file = RAND_file_name(buffer, RANDBUFLEN);
836 if(file == NULL || !RAND_load_file(file, -1)){
837 if(RAND_status() == 0){
838 dprint((1, "unable to load 'random state'\n"));
839 dprint((1, "This means that the random number generator has not been seeded\n"));
840 dprint((1, "with much random data.\n"));
843 return 0;
846 seeded = 1;
847 return 1;
852 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
854 static void
855 openssl_extra_randomness(void)
857 #if !defined(WIN32)
858 int fd;
859 unsigned long i;
860 char *tf = NULL;
861 char tmp[MAXPATH];
862 struct stat sbuf;
863 /* if system doesn't have /dev/urandom */
864 if(stat ("/dev/urandom", &sbuf)){
865 tmp[0] = '0';
866 tf = temp_nam(NULL, NULL);
867 if(tf){
868 strncpy(tmp, tf, sizeof(tmp));
869 tmp[sizeof(tmp)-1] = '\0';
870 fs_give((void **) &tf);
873 if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
874 i = (unsigned long) tmp;
875 else{
876 unlink(tmp); /* don't need the file */
877 fstat(fd, &sbuf); /* get information about the file */
878 i = sbuf.st_ino; /* remember its inode */
879 close(fd); /* or its descriptor */
881 /* not great but it'll have to do */
882 snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
883 tcp_serverhost (),i,
884 (unsigned long) (time (0) ^ gethostid ()),
885 (unsigned long) getpid ());
886 RAND_seed(tmp, strlen(tmp));
888 #endif
892 /* taken from openssl/apps/app_rand.c */
893 static int
894 app_RAND_write_file(const char *file)
896 char buffer[200];
898 if(!seeded)
900 * If we did not manage to read the seed file,
901 * we should not write a low-entropy seed file back --
902 * it would suppress a crucial warning the next time
903 * we want to use it.
905 return 0;
907 if(file == NULL)
908 file = RAND_file_name(buffer, sizeof buffer);
910 if(file == NULL || !RAND_write_file(file)){
911 dprint((1, "unable to write 'random state'\n"));
912 return 0;
915 return 1;
918 CertList *
919 certlist_from_personal_certs(PERSONAL_CERT *pc)
921 CertList *cl;
922 X509 *x;
923 char buf[MAXPATH];
925 if(pc == NULL)
926 return NULL;
928 cl = fs_get(sizeof(CertList));
929 memset((void *)cl, 0, sizeof(CertList));
930 cl->name = cpystr(pc->name);
931 x = get_cert_for(pc->name, Public, 1);
932 if(x){
933 if(x->cert_info){
934 cl->data.date_from = smime_get_date(x->cert_info->validity->notBefore);
935 cl->data.date_to = smime_get_date(x->cert_info->validity->notAfter);
936 get_fingerprint(x, EVP_md5(), buf, sizeof(buf), NULL);
937 cl->data.md5 = cpystr(buf);
938 cl->cn = smime_get_cn(x->cert_info->subject);
940 X509_free(x);
942 cl->next = certlist_from_personal_certs(pc->next);
944 return cl;
947 void
948 renew_cert_data(CertList **data, WhichCerts ctype)
950 smime_init();
951 if(ctype == Private){
952 if(data){
953 PERSONAL_CERT *pc = (PERSONAL_CERT *)ps_global->smime->personal_certs;
954 if(*data)
955 free_certlist(data);
956 free_personal_certs(&pc);
957 setup_privatekey_storage();
958 *data = certlist_from_personal_certs((PERSONAL_CERT *)ps_global->smime->personal_certs);
959 if(data && *data){
960 resort_certificates(data, ctype);
961 RENEWCERT(*data) = 0;
963 ps_global->smime->privatecertlist = *data;
965 if(ps_global->smime->privatecertlist)
966 RENEWCERT(ps_global->smime->privatecertlist) = 0;
967 } else {
968 X509_LOOKUP *lookup = NULL;
969 X509_STORE *store = NULL;
971 if((store = X509_STORE_new()) != NULL){
972 if((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL){
973 X509_STORE_free(store);
974 store = NULL;
975 } else{
976 free_certlist(data);
977 if(SMHOLDERTYPE(ctype) == Directory)
978 add_certs_in_dir(lookup, PATHCERTDIR(ctype), EXTCERT(ctype), data);
979 else /* if(SMHOLDERTYPE(ctype) == Container) */
980 *data = mem_to_certlist(CONTENTCERTLIST(ctype), ctype);
981 if(data && *data){
982 resort_certificates(data, ctype);
983 RENEWCERT(*data) = 0;
985 if(ctype == Public)
986 ps_global->smime->publiccertlist = *data;
987 else
988 ps_global->smime->cacertlist = *data;
992 setup_certs_backup_by_type(ctype);
995 void
996 smime_reinit(void)
998 smime_deinit();
999 smime_init();
1002 /* Installed as an atexit() handler to save the random data */
1003 void
1004 smime_deinit(void)
1006 dprint((9, "smime_deinit()"));
1007 app_RAND_write_file(NULL);
1008 free_smime_struct(&ps_global->smime);
1011 /* we renew the store when it has changed */
1012 void renew_store(void)
1014 if(ps_global->smime->inited){
1015 if(s_cert_store != NULL)
1016 X509_STORE_free(s_cert_store);
1017 s_cert_store = get_ca_store();
1021 /* Initialise openssl stuff if needed */
1022 void
1023 smime_init(void)
1025 if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
1027 dprint((9, "smime_init()"));
1028 if(!ps_global->smime)
1029 ps_global->smime = new_smime_struct();
1031 setup_storage_locations();
1033 s_cert_store = get_ca_store();
1034 setup_certs_backup_by_type(CACert);
1036 OpenSSL_add_all_algorithms();
1037 ERR_load_crypto_strings();
1039 app_RAND_load_file(NULL);
1040 openssl_extra_randomness();
1041 ps_global->smime->inited = 1;
1044 ERR_clear_error();
1048 /* validate a certificate. Return value : 0 for no error, -1 for error.
1049 * In the latter case, set the openssl smime error in *error.
1051 int smime_validate_cert(X509 *cert, long *error)
1053 X509_STORE_CTX *csc;
1055 ERR_clear_error();
1056 *error = 0;
1057 if((csc = X509_STORE_CTX_new()) != NULL){
1058 X509_STORE_set_flags(s_cert_store, 0);
1059 if(X509_STORE_CTX_init(csc,s_cert_store,cert,NULL)
1060 && X509_verify_cert(csc) <= 0)
1061 *error = X509_STORE_CTX_get_error(csc);
1062 X509_STORE_CTX_free(csc);
1064 return *error ? -1 : 0;
1067 PERSONAL_CERT *
1068 get_personal_certs(char *path)
1070 PERSONAL_CERT *result = NULL;
1071 char buf2[MAXPATH];
1072 struct dirent *d;
1073 DIR *dirp;
1075 ps_global->smime->privatepath = cpystr(path);
1076 dirp = opendir(path);
1077 if(dirp){
1078 while((d=readdir(dirp)) != NULL){
1079 X509 *cert;
1080 size_t ll;
1082 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, ".key")){
1084 /* copy file name to temp buffer */
1085 strncpy(buf2, d->d_name, sizeof(buf2)-1);
1086 buf2[sizeof(buf2)-1] = '\0';
1087 /* chop off ".key" trailier */
1088 buf2[strlen(buf2)-4] = 0;
1089 /* Look for certificate */
1090 cert = get_cert_for(buf2, Public, 1);
1092 if(cert){
1093 PERSONAL_CERT *pc;
1095 /* create a new PERSONAL_CERT, fill it in */
1097 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1098 pc->cert = cert;
1099 pc->name = cpystr(buf2);
1101 /* Try to load the key with an empty password */
1102 pc->key = load_key(pc, "", SM_NORMALCERT);
1104 pc->next = result;
1105 result = pc;
1109 closedir(dirp);
1111 return result;
1115 void
1116 setup_privatekey_storage(void)
1118 char path[MAXPATH+1], *contents;
1119 int privatekeycontainer = 0;
1121 /* private keys in a container */
1122 if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
1124 privatekeycontainer = 1;
1125 contents = NULL;
1126 path[0] = '\0';
1127 if(!smime_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
1128 privatekeycontainer = 0;
1130 if(privatekeycontainer && !IS_REMOTE(path)
1131 && ps_global->VAR_OPER_DIR
1132 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1133 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1134 /* TRANSLATORS: First arg is the directory name, second is
1135 the file user wants to read but can't. */
1136 _("Can't read file outside %s: %s"),
1137 ps_global->VAR_OPER_DIR, path);
1138 privatekeycontainer = 0;
1141 if(privatekeycontainer
1142 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1143 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1145 !(contents = read_file(path, READ_FROM_LOCALE)))
1146 privatekeycontainer = 0;
1149 if(privatekeycontainer && path[0]){
1150 ps_global->smime->privatetype = Container;
1151 ps_global->smime->privatepath = cpystr(path);
1153 if(contents){
1154 ps_global->smime->privatecontent = contents;
1155 ps_global->smime->personal_certs = mem_to_personal_certs(contents);
1160 /* private keys in a directory of files */
1161 if(!privatekeycontainer){
1162 ps_global->smime->privatetype = Directory;
1164 path[0] = '\0';
1165 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1166 && !IS_REMOTE(path)))
1167 ps_global->smime->privatetype = Nada;
1168 else if(can_access(path, ACCESS_EXISTS)){
1169 if(our_mkpath(path, 0700)){
1170 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1171 ps_global->smime->privatetype = Nada;
1175 if(ps_global->smime->privatetype == Directory)
1176 ps_global->smime->personal_certs = get_personal_certs(path);
1178 setup_certs_backup_by_type(Private);
1183 static void
1184 setup_storage_locations(void)
1186 int publiccertcontainer = 0, cacertcontainer = 0;
1187 char path[MAXPATH+1], *contents;
1189 if(!ps_global->smime)
1190 return;
1192 #ifdef APPLEKEYCHAIN
1193 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1194 ps_global->smime->publictype = Keychain;
1196 else{
1197 #endif /* APPLEKEYCHAIN */
1198 /* Public certificates in a container */
1199 if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
1201 publiccertcontainer = 1;
1202 contents = NULL;
1203 path[0] = '\0';
1204 if(!smime_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
1205 publiccertcontainer = 0;
1207 if(publiccertcontainer && !IS_REMOTE(path)
1208 && ps_global->VAR_OPER_DIR
1209 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1210 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1211 /* TRANSLATORS: First arg is the directory name, second is
1212 the file user wants to read but can't. */
1213 _("Can't read file outside %s: %s"),
1214 ps_global->VAR_OPER_DIR, path);
1215 publiccertcontainer = 0;
1218 if(publiccertcontainer
1219 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1220 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1222 !(contents = read_file(path, READ_FROM_LOCALE)))
1223 publiccertcontainer = 0;
1226 if(publiccertcontainer && path[0]){
1227 ps_global->smime->publictype = Container;
1228 ps_global->smime->publicpath = cpystr(path);
1230 if(contents){
1231 ps_global->smime->publiccontent = contents;
1232 ps_global->smime->publiccertlist = mem_to_certlist(contents, Public);
1237 /* Public certificates in a directory of files */
1238 if(!publiccertcontainer){
1239 ps_global->smime->publictype = Directory;
1241 path[0] = '\0';
1242 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1243 && !IS_REMOTE(path)))
1244 ps_global->smime->publictype = Nada;
1245 else if(can_access(path, ACCESS_EXISTS)){
1246 if(our_mkpath(path, 0700)){
1247 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1248 ps_global->smime->publictype = Nada;
1252 if(ps_global->smime->publictype == Directory)
1253 ps_global->smime->publicpath = cpystr(path);
1256 #ifdef APPLEKEYCHAIN
1258 #endif /* APPLEKEYCHAIN */
1260 setup_privatekey_storage();
1262 /* extra cacerts in a container */
1263 if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
1265 cacertcontainer = 1;
1266 contents = NULL;
1267 path[0] = '\0';
1268 if(!smime_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
1269 cacertcontainer = 0;
1271 if(cacertcontainer && !IS_REMOTE(path)
1272 && ps_global->VAR_OPER_DIR
1273 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1274 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1275 /* TRANSLATORS: First arg is the directory name, second is
1276 the file user wants to read but can't. */
1277 _("Can't read file outside %s: %s"),
1278 ps_global->VAR_OPER_DIR, path);
1279 cacertcontainer = 0;
1282 if(cacertcontainer
1283 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1284 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1286 !(contents = read_file(path, READ_FROM_LOCALE)))
1287 cacertcontainer = 0;
1290 if(cacertcontainer && path[0]){
1291 ps_global->smime->catype = Container;
1292 ps_global->smime->capath = cpystr(path);
1293 ps_global->smime->cacontent = contents;
1294 if(contents)
1295 ps_global->smime->cacertlist = mem_to_certlist(contents, CACert);
1299 if(!cacertcontainer){
1300 ps_global->smime->catype = Directory;
1302 path[0] = '\0';
1303 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1304 && !IS_REMOTE(path)))
1305 ps_global->smime->catype = Nada;
1306 else if(can_access(path, ACCESS_EXISTS)){
1307 if(our_mkpath(path, 0700)){
1308 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1309 ps_global->smime->catype = Nada;
1313 if(ps_global->smime->catype == Directory)
1314 ps_global->smime->capath = cpystr(path);
1320 copy_publiccert_dir_to_container(void)
1322 return(copy_dir_to_container(Public, NULL));
1327 copy_publiccert_container_to_dir(void)
1329 return(copy_container_to_dir(Public));
1334 copy_privatecert_dir_to_container(void)
1336 return(copy_dir_to_container(Private, NULL));
1341 copy_privatecert_container_to_dir(void)
1343 return(copy_container_to_dir(Private));
1348 copy_cacert_dir_to_container(void)
1350 return(copy_dir_to_container(CACert, NULL));
1355 copy_cacert_container_to_dir(void)
1357 return(copy_container_to_dir(CACert));
1360 /* Add the contents of a file to a container. Do not check the content
1361 * of the file, just add it using the format for that container. The
1362 * caller must check the format, so that there is no data corruption
1363 * in the future.
1364 * return value: 0 - success,
1365 * != 0 - failure.
1368 add_file_to_container(WhichCerts ctype, char *fpath, char *altname)
1370 char *sep = (ctype == Public || ctype == Private)
1371 ? EMAILADDRLEADER : CACERTSTORELEADER;
1372 char *content = ctype == Public ? ps_global->smime->publiccontent
1373 : (ctype == Private ? ps_global->smime->privatecontent
1374 : ps_global->smime->cacontent);
1375 char *name;
1376 char *s;
1377 unsigned char c;
1378 struct stat sbuf;
1379 STORE_S *in = NULL;
1380 int rv = -1; /* assume error */
1382 if(our_stat(fpath, &sbuf) < 0
1383 || (in = so_get(FileStar, fpath, READ_ACCESS | READ_FROM_LOCALE)) == NULL)
1384 goto endadd;
1386 if(altname != NULL)
1387 name = altname;
1388 else if((name = strrchr(fpath, '/')) != NULL){
1389 size_t ll;
1390 if((ll = strlen(++name)) > 4 && strucmp(name + ll - 4, EXTCERT(ctype)) == 0)
1391 name[ll-strlen(EXTCERT(ctype))] = '\0';
1393 else
1394 goto endadd;
1396 if(content){
1397 fs_resize((void **)&content, strlen(content) + strlen(sep) + strlen(name) + sbuf.st_size + 3); /* 2 = \n + \n + \0*/
1398 s = content;
1399 content += strlen(content);
1401 else{
1402 s = content = fs_get(strlen(sep) + strlen(name) + sbuf.st_size + 1); /* 2 = \n + \0 */
1403 *content = '\0';
1405 strncat(content, sep, strlen(sep));
1406 strncat(content, name, strlen(name));
1407 content += strlen(content);
1408 *content++ = '\n';
1410 while(so_readc(&c, in))
1411 *content++ = (char) c;
1412 *content = '\0';
1414 switch(ctype){
1415 case Private: ps_global->smime->privatecontent = s; break;
1416 case Public : ps_global->smime->publiccontent = s; break;
1417 case CACert : ps_global->smime->cacontent = s; break;
1418 default : break;
1421 rv = copy_dir_to_container(ctype, s);
1423 endadd:
1424 if(in) so_give(&in);
1426 return rv;
1431 * returns 0 on success, -1 on failure
1432 * contents is an argument which tells this function to write the value
1433 * of this variable instead of reading the contents of the directory.
1434 * If the var contents is not null use its value as the value of the
1435 * container.
1438 copy_dir_to_container(WhichCerts which, char *contents)
1440 int ret = 0, container = 0;
1441 BIO *bio_out = NULL, *bio_in = NULL;
1442 char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
1443 char *tempfile = NULL, fpath[MAXPATH+1];
1444 DIR *dirp;
1445 struct dirent *d;
1446 REMDATA_S *rd = NULL;
1447 char *configdir = NULL;
1448 char *configpath = NULL;
1449 char *configcontainer = NULL;
1450 char *filesuffix = NULL;
1451 char *ret_dir = NULL;
1453 dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1454 smime_init();
1456 srcpath[0] = '\0';
1457 dstpath[0] = '\0';
1458 file[0] = '\0';
1459 emailaddr[0] = '\0';
1461 if(which == Public){
1462 configdir = ps_global->VAR_PUBLICCERT_DIR;
1463 configpath = ps_global->smime->publicpath;
1464 configcontainer = cpystr(DF_PUBLIC_CONTAINER);
1465 filesuffix = ".crt";
1467 else if(which == Private){
1468 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1469 configpath = ps_global->smime->privatepath;
1470 configcontainer = cpystr(DF_PRIVATE_CONTAINER);
1471 filesuffix = ".key";
1473 else if(which == CACert){
1474 configdir = ps_global->VAR_CACERT_DIR;
1475 configpath = ps_global->smime->capath;
1476 configcontainer = cpystr(DF_CA_CONTAINER);
1477 filesuffix = ".crt";
1479 container = SMHOLDERTYPE(which) == Container;
1481 if(!(configdir && configdir[0])){
1482 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1483 return -1;
1486 if(!(configpath && configpath[0])){
1487 #ifdef APPLEKEYCHAIN
1488 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1489 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1490 return -1;
1492 #endif /* APPLEKEYCHAIN */
1493 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1494 return -1;
1497 if(!(filesuffix && strlen(filesuffix) == 4)){
1498 return -1;
1503 * If there is a legit directory to read from set up the
1504 * container file to write to.
1506 if(smime_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
1508 if(IS_REMOTE(configpath)){
1509 rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
1510 NULL, "Error: ",
1511 _("Can't access remote smime configuration."));
1512 if(!rd)
1513 return -1;
1515 (void) rd_read_metadata(rd);
1517 if(rd->access == MaybeRorW){
1518 if(rd->read_status == 'R')
1519 rd->access = ReadOnly;
1520 else
1521 rd->access = ReadWrite;
1524 if(rd->access != NoExists){
1526 rd_check_remvalid(rd, 1L);
1529 * If the cached info says it is readonly but
1530 * it looks like it's been fixed now, change it to readwrite.
1532 if(rd->read_status == 'R'){
1533 rd_check_readonly_access(rd);
1534 if(rd->read_status == 'W'){
1535 rd->access = ReadWrite;
1536 rd->flags |= REM_OUTOFDATE;
1538 else
1539 rd->access = ReadOnly;
1543 if(rd->flags & REM_OUTOFDATE){
1544 if(rd_update_local(rd) != 0){
1546 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1547 rd_close_remdata(&rd);
1548 return -1;
1551 else
1552 rd_open_remote(rd);
1554 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1555 rd_close_remdata(&rd);
1556 return -1;
1559 rd->flags |= DO_REMTRIM;
1561 strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
1562 dstpath[sizeof(dstpath)-1] = '\0';
1564 else{
1565 strncpy(dstpath, configpath, sizeof(dstpath)-1);
1566 dstpath[sizeof(dstpath)-1] = '\0';
1570 * dstpath is either the local Container file or the local cache file
1571 * for the remote Container file.
1573 tempfile = tempfile_in_same_dir(dstpath, "az", &ret_dir);
1577 * If there is a legit directory to read from and a tempfile
1578 * to write to we continue.
1580 if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
1582 if(contents != NULL){
1583 if(BIO_puts(bio_out, contents) < 0)
1584 ret = -1;
1586 else {
1587 if((dirp = opendir(srcpath)) != NULL){
1589 while((d=readdir(dirp)) && !ret){
1590 size_t ll;
1592 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, filesuffix)){
1594 /* copy file name to temp buffer */
1595 strncpy(emailaddr, d->d_name, sizeof(emailaddr)-1);
1596 emailaddr[sizeof(emailaddr)-1] = '\0';
1597 /* chop off suffix trailier */
1598 emailaddr[strlen(emailaddr)-4] = 0;
1601 * This is the separator between the contents of
1602 * different files.
1604 if(which == CACert){
1605 if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
1606 && (BIO_puts(bio_out, emailaddr) > 0)
1607 && (BIO_puts(bio_out, "\n") > 0)))
1608 ret = -1;
1610 else{
1611 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1612 && (BIO_puts(bio_out, emailaddr) > 0)
1613 && (BIO_puts(bio_out, "\n") > 0)))
1614 ret = -1;
1617 /* read then write contents of file */
1618 build_path(file, srcpath, d->d_name, sizeof(file));
1619 if(!(bio_in = BIO_new_file(file, "r")))
1620 ret = -1;
1622 if(!ret){
1623 int good_stuff = 0;
1625 while(BIO_gets(bio_in, line, sizeof(line)) > 0){
1626 if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
1627 good_stuff = 1;
1629 if(good_stuff)
1630 BIO_puts(bio_out, line);
1632 if(strncmp("-----END", line, strlen("-----END")) == 0)
1633 good_stuff = 0;
1637 BIO_free(bio_in);
1641 closedir(dirp);
1645 BIO_free(bio_out);
1647 if(!ret){
1648 if(container && configpath && *configpath){
1649 strncpy(fpath, configpath, sizeof(fpath));
1650 fpath[sizeof(fpath) - 1] = '\0';
1652 else if(ret_dir){
1653 if(strlen(dstpath) + strlen(configcontainer) - strlen(ret_dir) + 1 < sizeof(dstpath))
1654 snprintf(fpath, sizeof(fpath), "%s%c%s",
1655 dstpath, tempfile[strlen(ret_dir)], configcontainer);
1656 else
1657 ret = -1;
1659 else ret = -1;
1661 if(!ret){
1662 if(!IS_REMOTE(configpath)){
1663 if(rename_file(tempfile, fpath) < 0){
1664 q_status_message2(SM_ORDER, 3, 3,
1665 _("Can't rename %s to %s"), tempfile, fpath);
1666 ret = -1;
1667 } else q_status_message1(SM_ORDER, 3, 3,
1668 _("saved container to %s"), fpath);
1670 else { /* if the container is remote, copy it */
1671 int e;
1672 char datebuf[200];
1674 if(rd != NULL && rename_file(tempfile, rd->lf) < 0){
1675 q_status_message2(SM_ORDER, 3, 3,
1676 _("Can't rename %s to %s"), tempfile, rd->lf);
1677 ret = -1;
1680 datebuf[0] = '\0';
1682 if((e = rd_update_remote(rd, datebuf)) != 0){
1683 if(e == -1){
1684 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1685 _("Error opening temporary smime file %s: %s"),
1686 rd->lf, error_description(errno));
1687 dprint((1,
1688 "write_remote_smime: error opening temp file %s\n",
1689 rd->lf ? rd->lf : "?"));
1691 else{
1692 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1693 _("Error copying to %s: %s"),
1694 rd->rn, error_description(errno));
1695 dprint((1,
1696 "write_remote_smime: error copying from %s to %s\n",
1697 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1700 q_status_message(SM_ORDER | SM_DING, 5, 5,
1701 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1703 else{
1704 rd_update_metadata(rd, datebuf);
1705 rd->read_status = 'W';
1708 rd_close_remdata(&rd);
1714 if(tempfile)
1715 fs_give((void **) &tempfile);
1717 if(ret_dir)
1718 fs_give((void **) &ret_dir);
1720 if(configcontainer)
1721 fs_give((void **) &configcontainer);
1723 return ret;
1728 * returns 0 on success, -1 on failure
1731 copy_container_to_dir(WhichCerts which)
1733 char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
1734 char iobuf[4096];
1735 char *contents = NULL;
1736 char *leader = NULL;
1737 char *filesuffix = NULL;
1738 char *configdir = NULL;
1739 char *configpath = NULL;
1740 char *tempfile = NULL;
1741 char *p, *q, *line, *name, *certtext, *save_p;
1742 int len;
1743 BIO *in, *out;
1745 dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1746 smime_init();
1748 path[0] = '\0';
1750 if(which == Public){
1751 leader = EMAILADDRLEADER;
1752 contents = ps_global->smime->publiccontent;
1753 configdir = ps_global->VAR_PUBLICCERT_DIR;
1754 configpath = ps_global->smime->publicpath;
1755 filesuffix = ".crt";
1756 if(!(configpath && configpath[0])){
1757 #ifdef APPLEKEYCHAIN
1758 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1759 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1760 return -1;
1762 #endif /* APPLEKEYCHAIN */
1763 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1764 return -1;
1767 fs_give((void **) &ps_global->smime->publicpath);
1769 path[0] = '\0';
1770 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1771 && !IS_REMOTE(path))){
1772 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1773 return -1;
1776 if(can_access(path, ACCESS_EXISTS)){
1777 if(our_mkpath(path, 0700)){
1778 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1779 return -1;
1783 ps_global->smime->publicpath = cpystr(path);
1784 configpath = ps_global->smime->publicpath;
1786 else if(which == Private){
1787 leader = EMAILADDRLEADER;
1788 contents = ps_global->smime->privatecontent;
1789 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1790 configpath = ps_global->smime->privatepath;
1791 filesuffix = ".key";
1792 if(!(configpath && configpath[0])){
1793 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1794 return -1;
1797 fs_give((void **) &ps_global->smime->privatepath);
1799 path[0] = '\0';
1800 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1801 && !IS_REMOTE(path))){
1802 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1803 return -1;
1806 if(can_access(path, ACCESS_EXISTS)){
1807 if(our_mkpath(path, 0700)){
1808 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1809 return -1;
1813 ps_global->smime->privatepath = cpystr(path);
1814 configpath = ps_global->smime->privatepath;
1816 else if(which == CACert){
1817 leader = CACERTSTORELEADER;
1818 contents = ps_global->smime->cacontent;
1819 configdir = ps_global->VAR_CACERT_DIR;
1820 configpath = ps_global->smime->capath;
1821 filesuffix = ".crt";
1822 if(!(configpath && configpath[0])){
1823 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1824 return -1;
1827 fs_give((void **) &ps_global->smime->capath);
1829 path[0] = '\0';
1830 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1831 && !IS_REMOTE(path))){
1832 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1833 return -1;
1836 if(can_access(path, ACCESS_EXISTS)){
1837 if(our_mkpath(path, 0700)){
1838 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1839 return -1;
1843 ps_global->smime->capath = cpystr(path);
1844 configpath = ps_global->smime->capath;
1847 if(!(configdir && configdir[0])){
1848 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1849 return -1;
1852 if(!(configpath && configpath[0])){
1853 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1854 return -1;
1857 if(!(filesuffix && strlen(filesuffix) == 4)){
1858 return -1;
1862 if(contents && *contents){
1863 for(p = contents; *p != '\0';){
1864 line = p;
1866 while(*p && *p != '\n')
1867 p++;
1869 save_p = NULL;
1870 if(*p == '\n'){
1871 save_p = p;
1872 *p++ = '\0';
1875 if(strncmp(leader, line, strlen(leader)) == 0){
1876 name = line + strlen(leader);
1877 certtext = p;
1878 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
1879 if((q = strstr(certtext, leader)) != NULL){
1880 p = q;
1882 else{ /* end of file */
1883 q = certtext + strlen(certtext);
1884 p = q;
1887 strncpy(buf, name, sizeof(buf)-5);
1888 buf[sizeof(buf)-5] = '\0';
1889 strncat(buf, filesuffix, 5);
1890 build_path(file, configpath, buf, sizeof(file));
1892 in = BIO_new_mem_buf(certtext, q-certtext);
1893 if(in){
1894 tempfile = tempfile_in_same_dir(file, "az", NULL);
1895 out = NULL;
1896 if(tempfile)
1897 out = BIO_new_file(tempfile, "w");
1899 if(out){
1900 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1901 BIO_write(out, iobuf, len);
1903 BIO_free(out);
1905 if(rename_file(tempfile, file) < 0){
1906 q_status_message2(SM_ORDER, 3, 3,
1907 _("Can't rename %s to %s"),
1908 tempfile, file);
1909 return -1;
1912 fs_give((void **) &tempfile);
1915 BIO_free(in);
1920 if(save_p)
1921 *save_p = '\n';
1925 return 0;
1929 #ifdef APPLEKEYCHAIN
1932 copy_publiccert_container_to_keychain(void)
1934 /* NOT IMPLEMNTED */
1935 return -1;
1939 copy_publiccert_keychain_to_container(void)
1941 /* NOT IMPLEMNTED */
1942 return -1;
1945 #endif /* APPLEKEYCHAIN */
1949 * Get a pointer to a string describing the most recent OpenSSL error.
1950 * It's statically allocated, so don't change or attempt to free it.
1952 static const char *
1953 openssl_error_string(void)
1955 char *errs;
1956 const char *data = NULL;
1957 long errn;
1959 errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
1960 errs = (char*) ERR_reason_error_string(errn);
1962 if(errs)
1963 return errs;
1964 else if(data)
1965 return data;
1967 return "unknown error";
1971 /* Return true if the body looks like a PKCS7 object */
1973 is_pkcs7_body(BODY *body)
1975 int result;
1977 result = body->type==TYPEAPPLICATION &&
1978 body->subtype &&
1979 (strucmp(body->subtype,"pkcs7-mime")==0 ||
1980 strucmp(body->subtype,"x-pkcs7-mime")==0 ||
1981 strucmp(body->subtype,"pkcs7-signature")==0 ||
1982 strucmp(body->subtype,"x-pkcs7-signature")==0);
1984 return result;
1989 * Recursively stash a pointer to the decrypted data in our
1990 * manufactured body.
1991 * parameters: type: call of type 1, save the base and header for multipart messages
1992 call of type 0, do not save the base and header for multipart messages
1994 static void
1995 create_local_cache(char *h, char *base, BODY *b, int type)
1997 if(b->type==TYPEMULTIPART){
1998 PART *p;
2000 if(type == 1){
2001 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2002 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2003 } else if(type == 0){
2005 * We don't really want to copy the real body contents. It shouldn't be
2006 * used, and in the case of a message with attachments, we'll be
2007 * duplicating the files multiple times.
2009 cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
2011 for(p=b->nested.part; p; p=p->next)
2012 create_local_cache(h, base, (BODY *) p, type);
2015 else{
2016 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2017 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2022 static long
2023 rfc822_output_func(void *b, char *string)
2025 BIO *bio = (BIO *) b;
2027 return(string ? *string ? (BIO_puts(bio, string) > 0 ? 1L : 0L)
2028 : (BIO_puts(bio, string) >= 0 ? 1L : 0L)
2029 : 0L);
2034 * Attempt to load the private key for the given PERSONAL_CERT.
2035 * This sets the appropriate passphrase globals in order to
2036 * interact with the user correctly.
2038 static int
2039 load_private_key(PERSONAL_CERT *pcert)
2041 if(!pcert->key){
2043 /* Try empty password by default */
2044 char *password = "";
2046 if(ps_global->smime
2047 && (ps_global->smime->need_passphrase
2048 || ps_global->smime->entered_passphrase)){
2049 /* We've already been in here and discovered we need a different password */
2051 if(ps_global->smime->entered_passphrase)
2052 password = (char *) ps_global->smime->passphrase; /* already entered */
2053 else
2054 return 0;
2057 ERR_clear_error();
2059 if(!(pcert->key = load_key(pcert, password, SM_NORMALCERT))){
2060 long err = ERR_get_error();
2062 /* Couldn't load key... */
2064 if(ps_global->smime && ps_global->smime->entered_passphrase){
2066 /* The user got the password wrong maybe? */
2068 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
2069 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
2070 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
2071 else
2072 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2074 /* This passphrase is no good; forget it */
2075 ps_global->smime->entered_passphrase = 0;
2078 if(ps_global->smime){
2079 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2080 ps_global->smime->need_passphrase = 1;
2081 if(ps_global->smime->passphrase_emailaddr){
2082 int i;
2083 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
2084 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
2085 fs_give((void **) ps_global->smime->passphrase_emailaddr);
2088 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
2091 return 0;
2093 else{
2094 /* This key will be cached, so we won't be called again */
2095 if(ps_global->smime){
2096 ps_global->smime->entered_passphrase = 0;
2097 ps_global->smime->need_passphrase = 0;
2101 return 1;
2104 return 0;
2108 static void
2109 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename, char *smime_type)
2111 b->type = TYPEAPPLICATION;
2112 b->subtype = cpystr(type);
2113 b->encoding = ENCBINARY;
2114 b->description = cpystr(description);
2116 b->disposition.type = cpystr("attachment");
2117 set_parameter(&b->disposition.parameter, "filename", filename);
2119 set_parameter(&b->parameter, "name", filename);
2120 if(smime_type && *smime_type)
2121 set_parameter(&b->parameter, "smime-type", smime_type);
2126 * Look for a personal certificate matching the
2127 * given address
2129 PERSONAL_CERT *
2130 match_personal_cert_to_email(ADDRESS *a)
2132 PERSONAL_CERT *pcert = NULL;
2133 char buf[MAXPATH];
2134 char **email;
2135 int i, done;
2137 if(!a || !a->mailbox || !a->host)
2138 return NULL;
2140 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2142 if(ps_global->smime){
2143 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
2144 pcert;
2145 pcert=pcert->next){
2147 if(!pcert->cert)
2148 continue;
2150 email = get_x509_subject_email(pcert->cert);
2152 done = 0;
2153 if(email != NULL){
2154 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
2155 if(email[i] != NULL) done++;
2156 for(i = 0; email[i] != NULL; i++)
2157 fs_give((void **)&email[i]);
2158 fs_give((void **)email);
2161 if(done > 0)
2162 break;
2166 return pcert;
2171 * Look for a personal certificate matching the from
2172 * (or reply_to? in the given envelope)
2174 PERSONAL_CERT *
2175 match_personal_cert(ENVELOPE *env)
2177 PERSONAL_CERT *pcert;
2179 pcert = match_personal_cert_to_email(env->reply_to);
2180 if(!pcert)
2181 pcert = match_personal_cert_to_email(env->from);
2183 return pcert;
2188 * Flatten the given body into its MIME representation.
2189 * Return the result in a BIO.
2191 static BIO *
2192 body_to_bio(BODY *body)
2194 BIO *bio = NULL;
2195 int len;
2197 bio = BIO_new(BIO_s_mem());
2198 if(!bio)
2199 return NULL;
2201 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
2202 pine_write_body_header(body, rfc822_output_func, bio);
2203 pine_rfc822_output_body(body, rfc822_output_func, bio);
2206 * Now need to truncate by two characters since the above
2207 * appends CRLF.
2209 if((len=BIO_ctrl_pending(bio)) > 1){
2210 BUF_MEM *biobuf = NULL;
2212 BIO_get_mem_ptr(bio, &biobuf);
2213 if(biobuf){
2214 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
2218 return bio;
2222 static BIO *
2223 bio_from_store(STORE_S *store)
2225 BIO *ret = NULL;
2227 if(store && store->src == BioType && store->txt){
2228 ret = (BIO *) store->txt;
2231 return(ret);
2235 * Encrypt file; given a path (char *) fp, replace the file
2236 * by an encrypted version of it. If (char *) text is not null, then
2237 * replace the text of (char *) fp by the encrypted version of (char *) text.
2238 * certpath is the FULL path to the file containing the certificate used for
2239 * encryption.
2242 encrypt_file(char *fp, char *text, PERSONAL_CERT *pc)
2244 const EVP_CIPHER *cipher = NULL;
2245 STACK_OF(X509) *encerts = NULL;
2246 BIO *out = NULL;
2247 PKCS7 *p7 = NULL;
2248 int rv = 0;
2250 if(pc == NULL)
2251 return 0;
2253 cipher = EVP_aes_256_cbc();
2254 encerts = sk_X509_new_null();
2256 sk_X509_push(encerts, X509_dup(pc->cert));
2258 if(text){
2259 if((out = BIO_new(BIO_s_mem())) != NULL){
2260 (void) BIO_reset(out);
2261 BIO_puts(out, text);
2264 else if((out = BIO_new_file(fp, "rb")) != NULL)
2265 BIO_read_filename(out, fp);
2267 if((p7 = PKCS7_encrypt(encerts, out, cipher, 0)) != NULL){
2268 BIO_set_close(out, BIO_CLOSE);
2269 BIO_free(out);
2270 if((out = BIO_new_file(fp, "w")) != NULL){
2271 BIO_reset(out);
2272 rv = PEM_write_bio_PKCS7(out, p7);
2273 BIO_flush(out);
2277 if(out != NULL)
2278 BIO_free(out);
2279 PKCS7_free(p7);
2280 sk_X509_pop_free(encerts, X509_free);
2282 return rv;
2286 * Encrypt a message on the way out. Called from call_mailer in send.c
2287 * The body may be reallocated.
2290 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
2292 PKCS7 *p7 = NULL;
2293 BIO *in = NULL;
2294 BIO *out = NULL;
2295 const EVP_CIPHER *cipher = NULL;
2296 STACK_OF(X509) *encerts = NULL;
2297 STORE_S *outs = NULL;
2298 PINEFIELD *pf;
2299 ADDRESS *a;
2300 BODY *body = *bodyP;
2301 BODY *newBody = NULL;
2302 int result = 0;
2303 X509 *cert;
2304 char buf[MAXPATH];
2306 dprint((9, "encrypt_outgoing_message()"));
2307 smime_init();
2309 cipher = EVP_aes_256_cbc();
2311 encerts = sk_X509_new_null();
2313 /* Look for a certificate for each of the recipients */
2314 for(pf = header->local; pf && pf->name; pf = pf->next)
2315 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
2316 for(a=*pf->addr; a; a=a->next){
2317 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2319 if((cert = get_cert_for(buf, Public, 1)) != NULL){
2320 sk_X509_push(encerts,cert);
2321 }else{
2322 q_status_message2(SM_ORDER, 1, 1,
2323 _("Unable to find certificate for <%s@%s>"),
2324 a->mailbox, a->host);
2325 goto end;
2330 /* add the sender's certificate so that they can decrypt the message too */
2331 for(a=header->env->from; a ; a = a->next){
2332 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2334 if((cert = get_cert_for(buf, Public, 1)) != NULL
2335 && sk_X509_find(encerts, cert) == -1)
2336 sk_X509_push(encerts,cert);
2339 in = body_to_bio(body);
2341 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
2343 outs = so_get(BioType, NULL, EDIT_ACCESS);
2344 out = bio_from_store(outs);
2346 i2d_PKCS7_bio(out, p7);
2347 (void) BIO_flush(out);
2349 so_seek(outs, 0, SEEK_SET);
2351 newBody = mail_newbody();
2353 newBody->type = TYPEAPPLICATION;
2354 newBody->subtype = cpystr("pkcs7-mime");
2355 newBody->encoding = ENCBINARY;
2357 newBody->disposition.type = cpystr("attachment");
2358 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
2360 newBody->description = cpystr("S/MIME Encrypted Message");
2361 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
2362 set_parameter(&newBody->parameter, "name", "smime.p7m");
2364 newBody->contents.text.data = (unsigned char *) outs;
2366 *bodyP = newBody;
2368 result = 1;
2370 end:
2372 BIO_free(in);
2373 PKCS7_free(p7);
2374 sk_X509_pop_free(encerts, X509_free);
2376 dprint((9, "encrypt_outgoing_message returns %d", result));
2377 return result;
2382 Get (and decode) the body of the given section of msg
2384 static STORE_S*
2385 get_part_contents(long msgno, const char *section)
2387 long len;
2388 gf_io_t pc;
2389 STORE_S *store = NULL;
2390 char *err;
2392 store = so_get(CharStar, NULL, EDIT_ACCESS);
2393 if(store){
2394 gf_set_so_writec(&pc,store);
2396 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
2398 gf_clear_so_writec(store);
2400 so_seek(store, 0, SEEK_SET);
2402 if(err)
2403 so_give(&store);
2406 return store;
2410 static PKCS7 *
2411 get_pkcs7_from_part(long msgno,const char *section)
2413 STORE_S *store = NULL;
2414 PKCS7 *p7 = NULL;
2415 BIO *in = NULL;
2417 store = get_part_contents(msgno, section);
2419 if(store){
2420 if(store->src == CharStar){
2421 int len;
2424 * We're reaching inside the STORE_S structure. We should
2425 * probably have a way to get the length, instead.
2427 len = (int) (store->eod - store->dp);
2428 in = BIO_new_mem_buf(store->txt, len);
2430 else{ /* just copy it */
2431 unsigned char c;
2433 in = BIO_new(BIO_s_mem());
2434 (void) BIO_reset(in);
2436 so_seek(store, 0L, 0);
2437 while(so_readc(&c, store)){
2438 BIO_write(in, &c, 1);
2442 if(in){
2443 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2444 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
2445 /* error */
2448 BIO_free(in);
2451 so_give(&store);
2454 return p7;
2457 int same_cert(X509 *x, X509 *cert)
2459 char bufcert[256], bufx[256];
2460 int rv = 0;
2462 get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert), ":");
2463 get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx), ":");
2464 if(strcmp(bufx, bufcert) == 0)
2465 rv = 1;
2467 return rv;
2471 /* extract and save certificates from a PKCS7 package. The ctype variable
2472 * tells us if we want to extract it to a public/ or a ca/ directory. The
2473 * later makes sense only for recoverable errors (errors that can be fixed
2474 * by saving to the ca/ directory before we verify the signature).
2475 * Return value:
2476 * 0 - no errors (in public/) no need to try again,
2477 * or validated self signed certificate (in ca/)
2478 * < 0 - certificate error is not recoverable, don't even think about it.
2481 int smime_extract_and_save_cert(PKCS7 *p7, int check_cert)
2483 STACK_OF(X509) *signers;
2484 X509 *x, *cert;
2485 char **email;
2486 int i, j;
2487 long error;
2489 if((signers = PKCS7_get0_signers(p7, NULL, 0)) == NULL)
2490 return -1;
2492 for(i = 0; i < sk_X509_num(signers); i++){
2493 if((x = sk_X509_value(signers,i)) == NULL)
2494 continue;
2496 if((email = get_x509_subject_email(x)) != NULL){
2497 for(j = 0; email[j] != NULL; j++){
2498 if((cert = get_cert_for(email[j], Public, 1)) == NULL
2499 || same_cert(x, cert) == 0){
2500 if(check_cert == 0
2501 || smime_validate_cert(x, &error) == 0
2502 || (*pith_smime_confirm_save)(email[j]) == 1)
2503 save_cert_for(email[j], x, Public);
2505 if(cert != NULL)
2506 X509_free(cert);
2507 fs_give((void **) &email[j]);
2509 fs_give((void **) email);
2512 sk_X509_free(signers);
2514 return 0;
2518 * Try to verify a signature.
2520 * p7 - the pkcs7 object to verify
2521 * in - the plain data to verify (NULL if not detached)
2522 * out - BIO to which to write the opaque data
2523 * silent - if non zero, do not print errors, only print success.
2525 static int
2526 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
2528 STACK_OF(X509) *otherCerts = NULL;
2529 CertList *cl;
2530 int result, flags;
2531 const char *data;
2532 long err;
2534 if(!s_cert_store){
2535 if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
2536 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2538 return -1;
2541 flags = F_ON(F_USE_CERT_STORE_ONLY, ps_global) ? PKCS7_NOINTERN : 0;
2543 if(ps_global->smime->publiccertlist == NULL){
2544 renew_cert_data(&ps_global->smime->publiccertlist, Public);
2545 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next){
2546 if(cl->x509_cert == NULL){
2547 char *s = strrchr(cl->name, '.');
2548 *s = '\0';
2549 cl->x509_cert = get_cert_for(cl->name, Public, 1);
2550 *s = '.';
2555 if(ps_global->smime->publiccertlist){
2556 otherCerts = sk_X509_new_null();
2557 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next)
2558 if(cl->x509_cert != NULL)
2559 sk_X509_push(otherCerts, X509_dup(cl->x509_cert));
2562 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, flags);
2564 sk_X509_pop_free(otherCerts, X509_free);
2566 if(result){
2567 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
2569 else{
2570 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2572 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
2574 /* Retry verification so we can get the plain text */
2575 /* Might be better to reimplement PKCS7_verify here? */
2577 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY);
2579 if (!silent) q_status_message1(SM_ORDER | SM_DING, 3, 3,
2580 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
2583 return result;
2587 void
2588 free_smime_body_sparep(void **sparep)
2590 char *s;
2591 SIZEDTEXT *st;
2592 if(sparep && *sparep){
2593 switch(get_smime_sparep_type(*sparep)){
2594 case P7Type: PKCS7_free((PKCS7 *) get_smime_sparep_data(*sparep));
2595 break;
2596 case CharType: s = (char *)get_smime_sparep_data(*sparep);
2597 fs_give((void **) &s);
2598 break;
2599 case SizedText : st = (SIZEDTEXT *)get_smime_sparep_data(*sparep);
2600 fs_give((void **) &st->data);
2601 fs_give((void **) &st);
2602 break;
2603 default : break;
2605 ((SMIME_SPARE_S *)(*sparep))->data = NULL;
2606 fs_give(sparep);
2610 /* Big comment, explaining the mess that exists out there, and how we deal
2611 with it, and also how we solve the problems that are created this way.
2613 When Alpine sends a message, it constructs that message, computes the
2614 signature, but then it forgets the message it signed and reconstructs it
2615 again. Since it signs a message containing a notice about "mime aware
2616 tools", but it does not send that we do not include that in the part
2617 that is signed, and that takes care of much of the problems.
2619 Another problem is what is received from the servers. All servers tested
2620 seem to transmit the message that was signed intact and Alpine can check
2621 the signature correctly. That is not a problem. The problem arises when
2622 the message includes attachments. In this case different servers send
2623 different things, so it will be up to us to figure out what is the text
2624 that was actually signed. Confused? here is the story:
2626 When a message containing and attachment is sent by Alpine, UW-IMAP,
2627 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2628 that was sent by Alpine, but GMX.com, Exchange, and probably other
2629 servers add a trailing \r\n in the message, so when validating the
2630 signature, these messages will not validate. There are several things
2631 that can be done.
2633 1. Add a trailing \r\n to any message that contains attachments, sign that
2634 and send that. In this way, all messages will validate with all
2635 servers.
2637 2. Compatibility mode: If a message has an attachment, contains a trailing
2638 \r\n and does not validate (sent by an earlier version of Alpine),
2639 remove the trailing \r\n and try to revalidate again.
2641 3. We do not add \r\n to validate a message that we sent, because that
2642 would only work in Alpine, and not in any other client. That would
2643 not be a good thing to do.
2645 PART II
2647 Now we have to deal with encrypted and signed messages. The problem is
2648 that c-client makes all its pointers point to "on disk" content, but
2649 since we decrypted the data earlier, we have to make sure of two things.
2650 One is that we saved that data (so we do not have to decrypt it again)
2651 and second that we can use it.
2653 In order to save the data we use create_local_cache, so that we do not
2654 have to redecrypt the message. Once this is saved, c-client functions will
2655 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2657 PART III
2659 When we are trying to verify messages with detached signatures, some
2660 imap servers send incorrect information in the mail_fetch_mime call. By
2661 incorrect I mean that this is not fetched directly from the message, but
2662 it is read from the message, processed, and then the processed part is
2663 sent to us, so this text might not agree with what is in the message,
2664 and so the validation of the signature might fail. However, the good
2665 news is that the message validates if saved to a local folder. This
2666 means that if normal validation does not work we can make it work by
2667 saving the message locally and validating that. This is implemented
2668 below, and causes delay in the display of the message. I am considering
2669 at this time not to do this automatically, but wait for the user to tell
2670 us to do it for them by means of a command available in the
2671 mail_view_screen. This might help in other situations, where a message
2672 is supposed to have an attachment, but it can not be seen in the
2673 processed text. Nevertheless, at this time, this is automatic, and is
2674 causing a delay in the processing of the message, but it is validating
2675 correctly all messages.
2677 PART IV
2679 When the user sends a message as encrypted and signed, this code used to
2680 encrypt first, and then sign the pkcs7 body, but it turns out that some
2681 other clients can not handle these messages. While we could argue that the
2682 other clients need to improve, we will support reading messages in both
2683 ways, and will send messages using this technique; that is, signed first,
2684 encrypted second. It seems that all tested clients support this way, so it
2685 should be safe to do so.
2688 typedef struct smime_filter_s {
2689 void (*filter)();
2690 } SMIME_FILTER_S;
2692 SMIME_FILTER_S sig_filter[] = {
2693 {smime_remove_trailing_crlf},
2694 {smime_remove_folding_space}
2697 #define TOTAL_FILTERS (sizeof(sig_filter)/sizeof(sig_filter[0]))
2698 #define TOTAL_SIGFLTR (1 << TOTAL_FILTERS) /* not good, keep filters to a low number */
2700 void
2701 smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen,
2702 char **bodytext, unsigned long *bodylen)
2704 if(*bodylen > 2 && !strncmp(*bodytext+*bodylen-2, "\r\n", 2))
2705 *bodylen -= 2;
2708 void
2709 smime_remove_folding_space(char **mimetext, unsigned long *mimelen,
2710 char **bodytext, unsigned long *bodylen)
2712 char *s = NULL, *t;
2713 unsigned long mlen = *mimelen;
2715 if(*mimetext){
2716 for (s = t = *mimetext; t - *mimetext < *mimelen; ){
2717 if(*t == '\r' && *(t+1) == '\n' && (*(t+2) == '\t' || *(t+2) == ' ')){
2718 *s++ = ' ';
2719 t += 3;
2720 mlen -= 2;
2722 else
2723 *s++ = *t++;
2725 *mimelen = mlen;
2730 smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag)
2732 int result, i, j, flag;
2733 char *mtext, *btext;
2734 unsigned long mlen, blen;
2735 BIO *in;
2737 mtext = mimelen ? fs_get(mimelen+1) : NULL;
2738 btext = fs_get(bodylen+1);
2740 flag = 1; /* silence all failures */
2741 for(i = 1; result == 0 && i < TOTAL_SIGFLTR; i++){
2742 if((in = BIO_new(BIO_s_mem())) == NULL)
2743 return -1;
2745 (void) BIO_reset(in);
2747 if(i+1 == TOTAL_SIGFLTR)
2748 flag = nflag;
2750 if(mimelen)
2751 strncpy(mtext, mimetext, mlen = mimelen);
2752 strncpy(btext, bodytext, blen = bodylen);
2753 for(j = 0; j < TOTAL_FILTERS; j++)
2754 if((i >> j) & 1)
2755 (sig_filter[j].filter)(&mtext, &mlen, &btext, &blen);
2756 if(mtext != NULL)
2757 BIO_write(in, mtext, mlen);
2758 BIO_write(in, btext, blen);
2759 result = do_signature_verify(p7, in, NULL, flag);
2760 BIO_free(in);
2762 if(mtext) fs_give((void **)&mtext);
2763 if(btext) fs_give((void **)&btext);
2764 return result;
2768 * Given a multipart body of type multipart/signed, attempt to verify it.
2769 * Returns non-zero if the body was changed.
2771 static int
2772 do_detached_signature_verify(BODY *b, long msgno, char *section)
2774 PKCS7 *p7 = NULL;
2775 BIO *in = NULL;
2776 PART *p;
2777 int result, modified_the_body = 0;
2778 int flag; /* 1 silent, 0 not silent */
2779 int saved = 0;
2780 unsigned long mimelen, bodylen;
2781 char newSec[100], *mimetext, *bodytext;
2782 char *what_we_did;
2783 SIZEDTEXT *st;
2785 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"));
2787 smime_init();
2789 /* if it was signed and then encrypted, use the decrypted text
2790 * to check the validity of the signature
2792 if(b->sparep){
2793 if(get_smime_sparep_type(b->sparep) == SizedText){
2794 /* bodytext includes mimetext */
2795 st = (SIZEDTEXT *) get_smime_sparep_data(b->sparep);
2796 bodytext = (char *) st->data;
2797 bodylen = st->size;
2798 mimetext = NULL;
2799 mimelen = 0L;
2802 else{
2803 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
2804 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
2805 if(mimetext)
2806 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
2808 if(mimetext == NULL || bodytext == NULL)
2809 return modified_the_body;
2812 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
2814 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
2815 || (in = BIO_new(BIO_s_mem())) == NULL)
2816 return modified_the_body;
2818 (void) BIO_reset(in);
2819 if(mimetext != NULL)
2820 BIO_write(in, mimetext, mimelen);
2821 BIO_write(in, bodytext, bodylen);
2823 saved = smime_extract_and_save_cert(p7, F_ON(F_USE_CERT_STORE_ONLY, ps_global));
2824 if(saved < 0 && F_ON(F_USE_CERT_STORE_ONLY, ps_global))
2825 return modified_the_body;
2827 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
2828 flag = (mimelen == 0 || !IS_REMOTE(ps_global->mail_stream->mailbox))
2829 ? 0 : 1;
2830 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, flag);
2831 if(result < 0)
2832 return modified_the_body;
2833 if(result == 0
2834 && mimelen > 0 /* do not do this for encrypted messages */
2835 && IS_REMOTE(ps_global->mail_stream->mailbox)){
2836 char *fetch;
2837 unsigned long hlen, tlen;
2838 STORE_S *msg_so;
2840 BIO_free(in);
2841 if((in = BIO_new(BIO_s_mem())) != NULL
2842 && (fetch = mail_fetch_header(ps_global->mail_stream, msgno, NULL,
2843 NULL, &hlen, FT_PEEK)) != NULL
2844 && (msg_so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL
2845 && so_nputs(msg_so, fetch, (long) hlen)
2846 && (fetch = pine_mail_fetch_text(ps_global->mail_stream, msgno, NULL,
2847 &tlen, FT_PEEK)) != NULL
2848 && so_nputs(msg_so, fetch, tlen)){
2849 STRING bs;
2850 char *h = (char *) so_text(msg_so);
2851 char *bstart = strstr(h, "\r\n\r\n");
2852 ENVELOPE *env;
2853 BODY *body, *tmpB;
2855 bstart += 4;
2856 INIT(&bs, mail_string, bstart, tlen);
2857 rfc822_parse_msg_full(&env, &body, h, bstart-h-4, &bs, BADHOST, 0, 0);
2858 mail_free_envelope(&env);
2860 mail_free_body_part(&b->nested.part);
2861 tmpB = mail_body_section(body, (unsigned char *) section);
2862 if(MIME_MSG(tmpB->type, tmpB->subtype))
2863 b->nested.part = tmpB->nested.msg->body->nested.part;
2864 else
2865 b->nested.part = tmpB->nested.part;
2866 create_local_cache(bstart, bstart, &b->nested.part->body, 1);
2867 modified_the_body = 1;
2869 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
2871 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
2873 if(mimetext)
2874 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
2876 if (mimetext == NULL || bodytext == NULL)
2877 return modified_the_body;
2879 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
2881 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL)
2882 return modified_the_body;
2884 (void) BIO_reset(in);
2885 BIO_write(in, mimetext, mimelen);
2886 BIO_write(in, bodytext, bodylen);
2887 so_give(&msg_so);
2889 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
2890 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, 0);
2891 if(result < 0)
2892 return modified_the_body;
2898 BIO_free(in);
2899 if(b->subtype)
2900 fs_give((void**) &b->subtype);
2902 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
2903 b->encoding = ENC8BIT;
2905 if(b->description)
2906 fs_give ((void**) &b->description);
2908 what_we_did = result ? _("This message was cryptographically signed.") :
2909 _("This message was cryptographically signed but the signature could not be verified.");
2911 b->description = cpystr(what_we_did);
2913 b->sparep = create_smime_sparep(P7Type, p7);
2915 p = b->nested.part;
2917 /* p is signed plaintext */
2918 if(p && p->next)
2919 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
2921 modified_the_body = 1;
2923 return modified_the_body;
2927 PERSONAL_CERT *
2928 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
2930 PERSONAL_CERT *x = NULL;
2932 if(ps_global->smime){
2933 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
2934 X509 *mine;
2936 mine = x->cert;
2938 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,mine->cert_info->issuer) &&
2939 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,mine->cert_info->serialNumber)){
2940 break;
2945 return x;
2949 static PERSONAL_CERT *
2950 find_certificate_matching_pkcs7(PKCS7 *p7)
2952 int i;
2953 STACK_OF(PKCS7_RECIP_INFO) *recips;
2954 PERSONAL_CERT *x = NULL;
2956 recips = p7->d.enveloped->recipientinfo;
2958 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
2959 PKCS7_RECIP_INFO *ri;
2961 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
2963 if((x=find_certificate_matching_recip_info(ri))!=0){
2964 break;
2968 return x;
2971 /* decrypt an encrypted file.
2972 Args: fp - the path to the encrypted file.
2973 rv - a code that tells the caller what happened inside the function
2974 pcert - a personal certificate that was used to encrypt this file
2975 Returns the decoded text allocated in a char *, whose memory must be
2976 freed by caller
2979 char *
2980 decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc)
2982 PKCS7 *p7 = NULL;
2983 char *text, *tmp;
2984 BIO *in = NULL, *out = NULL;
2985 int i, j;
2986 long unsigned int len;
2987 void *ret;
2989 if(pc == NULL || (text = read_file(fp, 0)) == NULL || *text == '\0')
2990 return NULL;
2992 tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1);
2993 for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0'
2994 && text[i] != '-'; j++, i++)
2995 tmp[j] = text[i];
2996 tmp[j] = '\0';
2998 ret = rfc822_base64((unsigned char *)tmp, strlen(tmp), &len);
3000 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
3001 p7 = d2i_PKCS7_bio(in, NULL);
3002 BIO_free(in);
3005 if (text) fs_give((void **)&text);
3006 if (ret) fs_give((void **)&ret);
3008 if (rv) *rv = pc->key == NULL ? -1 : 1;
3010 out = BIO_new(BIO_s_mem());
3011 (void) BIO_reset(out);
3013 if(PKCS7_decrypt(p7, pc->key, pc->cert, out, 0) != 0){
3014 BIO_get_mem_data(out, &tmp);
3015 text = cpystr(tmp);
3016 BIO_free(out);
3017 } else
3018 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3019 (char *) openssl_error_string());
3020 PKCS7_free(p7);
3022 return text;
3026 * Try to decode (decrypt or verify a signature) a PKCS7 body
3027 * Returns non-zero if something was changed.
3029 static int
3030 do_decoding(BODY *b, long msgno, const char *section)
3032 int modified_the_body = 0;
3033 BIO *out = NULL;
3034 PKCS7 *p7 = NULL;
3035 X509 *recip = NULL;
3036 EVP_PKEY *key = NULL;
3037 PERSONAL_CERT *pcert = NULL;
3038 char *what_we_did = "";
3039 char null[1];
3041 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"));
3042 null[0] = '\0';
3043 smime_init();
3046 * Extract binary data from part to an in-memory store
3049 if(b->sparep){
3050 if(get_smime_sparep_type(b->sparep) == P7Type)
3051 p7 = (PKCS7*) get_smime_sparep_data(b->sparep);
3053 else{
3054 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
3055 if(!p7){
3056 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
3057 (char*) openssl_error_string());
3058 goto end;
3062 * Save the PKCS7 object for later dealings by the user interface.
3063 * It will be cleaned up when the body is garbage collected.
3065 b->sparep = create_smime_sparep(P7Type, p7);
3068 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
3070 if(PKCS7_type_is_signed(p7)){
3071 int sigok;
3073 out = BIO_new(BIO_s_mem());
3074 (void) BIO_reset(out);
3075 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3077 sigok = do_signature_verify(p7, NULL, out, 0);
3079 what_we_did = sigok ? _("This message was cryptographically signed.") :
3080 _("This message was cryptographically signed but the signature could not be verified.");
3082 /* make sure it's null terminated */
3083 BIO_write(out, null, 1);
3085 else if(!PKCS7_type_is_enveloped(p7)){
3086 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
3087 goto end;
3089 else{ /* It *is* enveloped */
3090 int decrypt_result;
3092 what_we_did = _("This message was encrypted.");
3094 /* now need to find a cert that can decrypt this */
3095 pcert = find_certificate_matching_pkcs7(p7);
3097 if(!pcert){
3098 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3099 goto end;
3102 recip = pcert->cert;
3104 if(!load_private_key(pcert)
3105 && ps_global->smime
3106 && ps_global->smime->need_passphrase
3107 && !ps_global->smime->already_auto_asked){
3108 /* Couldn't load key with blank password, ask user */
3109 ps_global->smime->already_auto_asked = 1;
3110 if(pith_opt_smime_get_passphrase){
3111 (*pith_opt_smime_get_passphrase)();
3112 load_private_key(pcert);
3116 key = pcert->key;
3117 if(!key)
3118 goto end;
3120 out = BIO_new(BIO_s_mem());
3121 (void) BIO_reset(out);
3122 BIO_puts(out, "MIME-Version: 1.0\r\n");
3124 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
3126 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3127 forget_private_keys();
3129 if(!decrypt_result){
3130 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3131 (char*) openssl_error_string());
3132 goto end; }
3134 BIO_write(out, null, 1);
3138 * We've now produced a flattened MIME object in BIO out.
3139 * It needs to be turned back into a BODY.
3142 if(out){
3143 BODY *body;
3144 ENVELOPE *env;
3145 char *h = NULL;
3146 char *bstart;
3147 STRING s;
3148 BUF_MEM *bptr = NULL;
3150 BIO_get_mem_ptr(out, &bptr);
3151 if(bptr)
3152 h = bptr->data;
3154 /* look for start of body */
3155 bstart = strstr(h, "\r\n\r\n");
3157 if(!bstart){
3158 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
3160 else{
3161 SIZEDTEXT *st;
3162 bstart += 4; /* skip over CRLF*2 */
3164 INIT(&s, mail_string, bstart, strlen(bstart));
3165 rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
3166 mail_free_envelope(&env); /* Don't care about this */
3168 if(body->type == TYPEMULTIPART
3169 && !strucmp(body->subtype, "SIGNED")){
3170 char *cookie = NULL;
3171 PARAMETER *param;
3172 for (param = body->parameter; param && !cookie; param = param->next)
3173 if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value;
3174 if(cookie != NULL){
3175 st = fs_get(sizeof(SIZEDTEXT));
3176 st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */
3177 st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4);
3178 body->sparep = create_smime_sparep(SizedText, (void *)st);
3180 else
3181 q_status_message(SM_ORDER, 3, 3, _("Couldn't find cookie in attachment list."));
3183 body->mime.offset = 0;
3184 body->mime.text.size = 0;
3187 * Now convert original body (application/pkcs7-mime)
3188 * to a multipart body with one sub-part (the decrypted body).
3189 * Note that the sub-part may also be multipart!
3192 b->type = TYPEMULTIPART;
3193 if(b->subtype)
3194 fs_give((void**) &b->subtype);
3197 * This subtype is used in mailview.c to annotate the display of
3198 * encrypted or signed messages. We know for sure then that it's a PKCS7
3199 * part because the sparep field is set to the PKCS7 object (see above).
3201 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3202 b->encoding = ENC8BIT;
3204 if(b->description)
3205 fs_give((void**) &b->description);
3207 b->description = cpystr(what_we_did);
3209 if(b->disposition.type)
3210 fs_give((void **) &b->disposition.type);
3212 if(b->contents.text.data)
3213 fs_give((void **) &b->contents.text.data);
3215 if(b->parameter)
3216 mail_free_body_parameter(&b->parameter);
3218 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3219 b->nested.part = fs_get(sizeof(PART));
3220 b->nested.part->body = *body;
3221 b->nested.part->next = NULL;
3223 fs_give((void**) &body);
3226 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3227 * the decrypted data. Otherwise, it'll try to load it from the original
3228 * data. Eek.
3230 create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body, 0);
3232 modified_the_body = 1;
3236 end:
3237 if(out)
3238 BIO_free(out);
3240 return modified_the_body;
3245 * Recursively handle PKCS7 bodies in our message.
3247 * Returns non-zero if some fiddling was done.
3249 static int
3250 do_fiddle_smime_message(BODY *b, long msgno, char *section)
3252 int modified_the_body = 0;
3254 if(!b)
3255 return 0;
3257 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"));
3259 if(is_pkcs7_body(b)){
3261 if(do_decoding(b, msgno, section)){
3263 * b should now be a multipart message:
3264 * fiddle it too in case it's been multiply-encrypted!
3267 /* fallthru */
3268 modified_the_body = 1;
3272 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
3274 PART *p;
3275 int partNum;
3276 char newSec[100];
3278 if(MIME_MULT_SIGNED(b->type, b->subtype)){
3282 * Ahah. We have a multipart signed entity.
3284 * Multipart/signed
3285 * part 1 (signed thing)
3286 * part 2 (the pkcs7 signature)
3288 * We're going to convert that to
3290 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3291 * part 1 (signed thing)
3292 * part 2 has been freed
3294 * We also extract the signature from part 2 and save it
3295 * in the multipart body->sparep, and we add a description
3296 * in the multipart body->description.
3299 * The results of a decrypted message will be similar. It
3300 * will be
3302 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3303 * part 1 (decrypted thing)
3306 modified_the_body += do_detached_signature_verify(b, msgno, section);
3308 else if(MIME_MSG(b->type, b->subtype)){
3309 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
3311 else{
3313 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
3314 /* Append part number to the section string */
3316 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
3318 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
3323 return modified_the_body;
3328 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3329 * Returns non-zero if something was changed.
3332 fiddle_smime_message(BODY *b, long msgno)
3334 return do_fiddle_smime_message(b, msgno, "");
3338 /********************************************************************************/
3342 * Output a string in a distinctive style
3344 void
3345 gf_puts_uline(char *txt, gf_io_t pc)
3347 pc(TAG_EMBED); pc(TAG_BOLDON);
3348 gf_puts(txt, pc);
3349 pc(TAG_EMBED); pc(TAG_BOLDOFF);
3352 /* get_chain_for_cert: error and level are mandatory arguments */
3353 STACK_OF(X509) *
3354 get_chain_for_cert(X509 *cert, int *error, int *level)
3356 STACK_OF(X509) *chain = NULL;
3357 X509_STORE_CTX *ctx;
3358 X509 *x, *xtmp;
3359 int rc; /* return code */
3361 *level = -1;
3362 *error = 0;
3363 ERR_clear_error();
3364 if((ctx = X509_STORE_CTX_new()) != NULL){
3365 X509_STORE_set_flags(s_cert_store, 0);
3366 if(!X509_STORE_CTX_init(ctx, s_cert_store, cert, NULL))
3367 *error = X509_STORE_CTX_get_error(ctx);
3368 else if((chain = sk_X509_new_null()) != NULL){
3369 for(x = cert; ; x = xtmp){
3370 if(++*level > 0)
3371 sk_X509_push(chain, X509_dup(x));
3372 rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x);
3373 if(rc < 0)
3374 *error = 1;
3375 if(rc <= 0)
3376 break;
3377 if(!X509_check_issued(xtmp, xtmp))
3378 break;
3381 X509_STORE_CTX_free(ctx);
3383 return chain;
3388 * Sign a message. Called from call_mailer in send.c.
3390 * This takes the header for the outgoing message as well as a pointer
3391 * to the current body (which may be reallocated).
3392 * The last argument (BODY **bp) is an argument that tells Alpine
3393 * if the body has 8 bit. if *bp is not null we compute two signatures
3394 * one for the quoted-printable encoded message, and another for the
3395 * 8bit encoded message. We return the signature for the 8bit encoded
3396 * part in p2->body.mime.text.data.
3397 * The reason why we compute two signatures is so that we can decide
3398 * which one to use later, and we only do it in the case that *bp is
3399 * not null. If we did not do this, then we might not be able to sign
3400 * a message until we log in to the smtp server, so instead of doing
3401 * that, we get ready for any possible situation we might find.
3404 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp)
3406 STORE_S *outs = NULL;
3407 STORE_S *outs_2 = NULL;
3408 BODY *body = *bodyP;
3409 BODY *newBody = NULL;
3410 PART *p1 = NULL;
3411 PART *p2 = NULL;
3412 PERSONAL_CERT *pcert;
3413 BIO *in = NULL;
3414 BIO *in_2 = NULL;
3415 BIO *out = NULL;
3416 BIO *out_2 = NULL;
3417 PKCS7 *p7 = NULL;
3418 PKCS7 *p7_2 = NULL;
3419 STACK_OF(X509) *chain;
3420 int result = 0, error;
3421 int flags = dont_detach ? 0 : PKCS7_DETACHED;
3422 int level;
3424 dprint((9, "sign_outgoing_message()"));
3426 smime_init();
3428 /* Look for a private key matching the sender address... */
3430 pcert = match_personal_cert(header->env);
3432 if(!pcert){
3433 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
3434 goto end;
3437 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
3438 /* Couldn't load key with blank password, try again */
3439 if(pith_opt_smime_get_passphrase){
3440 (*pith_opt_smime_get_passphrase)();
3441 load_private_key(pcert);
3445 if(!pcert->key)
3446 goto end;
3448 if(((chain = get_chain_for_cert(pcert->cert, &error, &level)) != NULL && error)
3449 || level == 0){
3450 sk_X509_pop_free(chain, X509_free);
3451 chain = NULL;
3454 if(error)
3455 q_status_message(SM_ORDER, 1, 1,
3456 _("Not all certificates needed to verify signature included in signed message"));
3458 in = body_to_bio(body);
3460 p7 = PKCS7_sign(pcert->cert, pcert->key, chain, in, flags);
3462 if(bp && *bp){
3463 int i, save_encoding;
3465 for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++);
3467 if(i > ENCMAX){ /* no empty encoding slots! */
3468 *bp = NULL;
3470 else {
3471 save_encoding = (*bp)->encoding;
3472 body_encodings[(*bp)->encoding = i] = body_encodings[ENC8BIT];
3474 in_2 = body_to_bio(body);
3476 body_encodings[i] = NULL;
3477 (*bp)->encoding = save_encoding;
3481 if(bp && *bp)
3482 p7_2 = PKCS7_sign(pcert->cert, pcert->key, chain, in_2, flags);
3484 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3485 forget_private_keys();
3487 if(chain)
3488 sk_X509_pop_free(chain, X509_free);
3490 if(!p7){
3491 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
3492 goto end;
3495 outs = so_get(BioType, NULL, EDIT_ACCESS);
3496 out = bio_from_store(outs);
3498 i2d_PKCS7_bio(out, p7);
3499 (void) BIO_flush(out);
3501 so_seek(outs, 0, SEEK_SET);
3503 if(bp && *bp && p7_2){
3504 outs_2 = so_get(BioType, NULL, EDIT_ACCESS);
3505 out_2 = bio_from_store(outs_2);
3507 i2d_PKCS7_bio(out_2, p7_2);
3508 (void) BIO_flush(out_2);
3510 so_seek(outs_2, 0, SEEK_SET);
3513 if((flags&PKCS7_DETACHED)==0){
3515 /* the simple case: the signed data is in the pkcs7 object */
3517 newBody = mail_newbody();
3519 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3521 newBody->contents.text.data = (unsigned char *) outs;
3522 *bodyP = newBody;
3524 result = 1;
3526 else{
3529 * OK.
3530 * We have to create a new body as follows:
3532 * multipart/signed; blah blah blah
3533 * reference to existing body
3535 * pkcs7 object
3538 newBody = mail_newbody();
3540 newBody->type = TYPEMULTIPART;
3541 newBody->subtype = cpystr("signed");
3542 newBody->encoding = ENC7BIT;
3544 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
3545 set_parameter(&newBody->parameter, "micalg", "sha1");
3547 p1 = mail_newbody_part();
3548 p2 = mail_newbody_part();
3551 * This is nasty. We're just copying the body in here,
3552 * but since our newBody is freed at the end of call_mailer,
3553 * we mustn't let this body (the original one) be freed twice.
3555 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
3557 p1->next = p2;
3559 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL);
3560 p2->body.mime.text.data = (unsigned char *) outs_2;
3561 p2->body.contents.text.data = (unsigned char *) outs;
3563 newBody->nested.part = p1;
3565 *bodyP = newBody;
3567 result = 1;
3570 end:
3572 PKCS7_free(p7);
3573 BIO_free(in);
3575 if(bp && *bp){
3576 if(p7_2) PKCS7_free(p7_2);
3577 BIO_free(in_2);
3580 dprint((9, "sign_outgoing_message returns %d", result));
3581 return result;
3585 SMIME_STUFF_S *
3586 new_smime_struct(void)
3588 SMIME_STUFF_S *ret = NULL;
3590 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
3591 memset((void *) ret, 0, sizeof(*ret));
3592 ret->publictype = Nada;
3594 return ret;
3598 static void
3599 free_smime_struct(SMIME_STUFF_S **smime)
3601 if(smime && *smime){
3602 if((*smime)->passphrase_emailaddr){
3603 int i;
3604 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
3605 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
3606 fs_give((void **) (*smime)->passphrase_emailaddr);
3609 if((*smime)->publicpath)
3610 fs_give((void **) &(*smime)->publicpath);
3612 if((*smime)->publiccertlist)
3613 free_certlist(&(*smime)->publiccertlist);
3615 if((*smime)->backuppubliccertlist)
3616 free_certlist(&(*smime)->backuppubliccertlist);
3618 if((*smime)->cacertlist)
3619 free_certlist(&(*smime)->cacertlist);
3621 if((*smime)->backupcacertlist)
3622 free_certlist(&(*smime)->backupcacertlist);
3624 if((*smime)->privatecertlist)
3625 free_certlist(&(*smime)->privatecertlist);
3627 if((*smime)->backupprivatecertlist)
3628 free_certlist(&(*smime)->backupprivatecertlist);
3630 if((*smime)->publiccontent)
3631 fs_give((void **) &(*smime)->publiccontent);
3633 if((*smime)->privatepath)
3634 fs_give((void **) &(*smime)->privatepath);
3636 if((*smime)->personal_certs){
3637 PERSONAL_CERT *pc;
3639 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
3640 free_personal_certs(&pc);
3641 (*smime)->personal_certs = NULL;
3644 if((*smime)->backuppersonal_certs){
3645 PERSONAL_CERT *pc;
3647 pc = (PERSONAL_CERT *) (*smime)->backuppersonal_certs;
3648 free_personal_certs(&pc);
3649 (*smime)->backuppersonal_certs = NULL;
3652 if((*smime)->privatecontent)
3653 fs_give((void **) &(*smime)->privatecontent);
3655 if((*smime)->capath)
3656 fs_give((void **) &(*smime)->capath);
3658 if((*smime)->cacontent)
3659 fs_give((void **) &(*smime)->cacontent);
3661 fs_give((void **) smime);
3665 #endif /* SMIME */