* Create help for explaining how encrypted password file support
[alpine.git] / pith / smime.c
blobce7b6a709ce3591d3d839b3becad35b89387bbe4
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: smime.c 1176 2008-09-29 21:16:42Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2014 Eduardo Chappa
8 * Copyright 2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
20 * This is based on a contribution from Jonathan Paisley
22 * File: smime.c
23 * Author: paisleyj@dcs.gla.ac.uk
24 * Date: 01/2001
28 #include "../pith/headers.h"
30 #ifdef SMIME
32 #include "../pith/osdep/canaccess.h"
33 #include "../pith/helptext.h"
34 #include "../pith/store.h"
35 #include "../pith/status.h"
36 #include "../pith/detach.h"
37 #include "../pith/conf.h"
38 #include "../pith/smkeys.h"
39 #include "../pith/smime.h"
40 #include "../pith/mailpart.h"
41 #include "../pith/reply.h"
42 #include "../pith/tempfile.h"
43 #include "../pith/readfile.h"
44 #include "../pith/remote.h"
46 #include <openssl/buffer.h>
47 #include <openssl/x509v3.h>
49 /* internal prototypes */
50 static void forget_private_keys(void);
51 static int app_RAND_load_file(const char *file);
52 static void openssl_extra_randomness(void);
53 static int app_RAND_write_file(const char *file);
54 static const char *openssl_error_string(void);
55 static int load_private_key(PERSONAL_CERT *pcert);
56 static void create_local_cache(char *h, char *base, BODY *b, int type);
57 static long rfc822_output_func(void *b, char *string);
58 static void setup_pkcs7_body_for_signature(BODY *b, char *description,
59 char *type, char *filename, char *smime_type);
60 static BIO *body_to_bio(BODY *body);
61 static BIO *bio_from_store(STORE_S *store);
62 static STORE_S *get_part_contents(long msgno, const char *section);
63 static PKCS7 *get_pkcs7_from_part(long msgno, const char *section);
64 static int do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent);
65 int do_detached_signature_verify(BODY *b, long msgno, char *section);
66 static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7);
67 static int do_decoding(BODY *b, long msgno, const char *section);
68 static void free_smime_struct(SMIME_STUFF_S **smime);
69 static void setup_storage_locations(void);
70 static int copy_container_to_dir(WhichCerts which);
71 static int do_fiddle_smime_message(BODY *b, long msgno, char *section);
72 void setup_privatekey_storage(void);
73 int smime_path(char *rpath, char *fpath, size_t len);
74 int smime_extract_and_save_cert(PKCS7 *p7);
75 int same_cert(X509 *, X509 *);
76 CertList * certlist_from_personal_certs(PERSONAL_CERT *pc);
77 #ifdef PASSFILE
78 void load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile, char **certfile, EVP_PKEY **pkey, X509 **pcert);
79 #endif /* PASSFILE */
80 STACK_OF(X509) *get_chain_for_cert(X509 *cert, int *error);
81 EVP_PKEY *load_pkey_with_prompt(char *fpath, char *text, char *prompt);
83 int (*pith_opt_smime_get_passphrase)(void);
84 int (*pith_smime_import_certificate)(char *, char *, size_t);
85 int (*pith_smime_enter_password)(char *prompt, char *, size_t);
87 static X509_STORE *s_cert_store;
89 /* State management for randomness functions below */
90 static int seeded = 0;
91 static int egdsocket = 0;
93 typedef enum {P7Type, CharType, SizedText} SpareType;
95 typedef struct smime_sparep_t {
96 SpareType sptype;
97 void *data;
98 } SMIME_SPARE_S;
100 void *
101 create_smime_sparep(SpareType stype, void *s)
103 SMIME_SPARE_S *rv;
105 rv = fs_get(sizeof(SMIME_SPARE_S));
106 rv->sptype = stype;
107 rv->data = s;
108 return (void *) rv;
111 SpareType
112 get_smime_sparep_type(void *s)
114 return ((SMIME_SPARE_S *)s)->sptype;
117 void *
118 get_smime_sparep_data(void *s)
120 return ((SMIME_SPARE_S *)s)->data;
124 #ifdef PASSFILE
126 * load key from pathkeydir and cert from pathcertdir. It chooses the first
127 * key/certificate pair that matches. Delete pairs that you do not want used,
128 * if you do not want them selected. All parameters must be non-null.
129 * Memory freed by caller.
131 void
132 load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile,
133 char **certfile, EVP_PKEY **pkey, X509 **pcert)
135 char buf[MAXPATH+1], pathkey[MAXPATH+1], prompt[MAILTMPLEN];
136 DIR *dirp;
137 struct dirent *d;
138 int b = 0;
140 if(pathkeydir == NULL || pathcertdir == NULL || keyfile == NULL
141 || pkey == NULL || certfile == NULL || pcert == NULL)
142 return;
144 *keyfile = NULL;
145 *certfile = NULL;
146 *pkey = NULL;
147 *pcert = NULL;
149 if((dirp = opendir(pathkeydir)) != NULL){
150 while(b == 0 && (d=readdir(dirp)) != NULL){
151 size_t ll;
153 if((ll=strlen(d->d_name)) && ll > 4){
154 if(!strcmp(d->d_name+ll-4, ".key")){
155 strncpy(buf, d->d_name, sizeof(buf));
156 buf[sizeof(buf)-1] = '\0';
157 build_path(pathkey, pathkeydir, buf, sizeof(pathkey));
158 buf[strlen(buf)-4] = '\0';
159 snprintf(prompt, sizeof(prompt),
160 _("Enter password of key <%s> to unlock password file: "), buf);
161 if((*pkey = load_pkey_with_prompt(pathkey, NULL, prompt)) != NULL){
162 if(load_cert_for_key(pathcertdir, *pkey, certfile, pcert)){
163 b = 1; /* break */
164 *keyfile = cpystr(buf);
165 } else {
166 EVP_PKEY_free(*pkey);
167 *pkey = NULL;
168 q_status_message1(SM_ORDER, 0, 2,
169 _("Cannot find certificate that matches key <%s>. Continuing..."), buf);
175 closedir(dirp);
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;
216 if(pwdcert == NULL)
217 return;
219 if(ps_global->pwdcertdir){
220 if(our_stat(ps_global->pwdcertdir, &sbuf) == 0
221 && ((sbuf.st_mode & S_IFMT) == S_IFDIR)){
222 setup_dir++;
223 strncpy(pathdir, ps_global->pwdcertdir, sizeof(pathdir));
224 pathdir[sizeof(pathdir)-1] = '\0';
226 } else {
227 smime_path(DF_PASSWORD_DIR, pathdir, sizeof(pathdir));
228 if(our_stat(pathdir, &sbuf) == 0){
229 if((sbuf.st_mode & S_IFMT) == S_IFDIR)
230 setup_dir++;
231 } else if(can_access(pathdir, ACCESS_EXISTS) != 0
232 && our_mkpath(pathdir, 0700) == 0)
233 setup_dir++;
236 if(setup_dir == 0)
237 return;
239 load_key_and_cert(pathdir, pathdir, &keyfile, &certfile, &pkey, &pcert);
241 if(certfile && keyfile){
242 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
243 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
244 pc->name = keyfile;
245 pc->key = pkey;
246 pc->cert = pcert;
247 *pwdcert = (void *) pc;
248 fs_give((void **)&certfile);
249 return;
252 /* if the user gave a pwdcertdir and there is nothing there, do not
253 * continue. Let the user initialize on their own this directory.
255 if(ps_global->pwdcertdir != NULL)
256 return;
258 /* look to see if there are any certificates lying around, first
259 * we try to load ps_global->smime to see if that has information
260 * we can use. If we are the process filling the smime structure
261 * we deinit at the end, since this might not do a full init.
263 if(ps_global && ps_global->smime && !ps_global->smime->inited){
264 we_inited++;
265 smime_init();
268 /* at this point ps_global->smime->inited == 1 */
269 if(ps_global->smime && ps_global->smime->personal_certs != NULL){
270 pc = (PERSONAL_CERT *) ps_global->smime->personal_certs;
271 if(ps_global->smime->privatetype == Directory){
272 build_path(pathkey, ps_global->smime->privatepath, pc->name, sizeof(pathkey));
273 strncat(pathkey, ".key", 4);
274 pathkey[sizeof(pathkey)-1] = '\0';
275 text = NULL;
276 } else if (ps_global->smime->privatetype == Container){
277 if(pc->keytext == NULL){ /* we should *never* be here, but just in case */
278 if(ps_global->smime->privatecontent != NULL){
279 char tmp[MAILTMPLEN], *s, *t, c;
280 snprintf(tmp, sizeof(tmp), "%s%s", EMAILADDRLEADER, pc->name);
281 tmp[sizeof(tmp)-1] = '\0';
282 if((s = strstr(ps_global->smime->privatecontent, tmp)) != NULL){
283 if((t = strstr(s+strlen(tmp), EMAILADDRLEADER)) != NULL){
284 c = *t;
285 *t = '\0';
286 pc->keytext = cpystr(s + strlen(tmp) + 1); /* 1 = strlen("\n") */
287 *t = c;
289 else
290 pc->keytext = cpystr(s + strlen(tmp) + 1); /* 1 = strlen("\n") */
294 if(pc->keytext != NULL) /* we should go straigth here */
295 text = pc->keytext;
296 } else if (ps_global->smime->privatetype == Keychain){
297 pathkey[0] = '\0'; /* no apple key chain support yet */
298 text = NULL;
300 if((pathkey && *pathkey) || text){
301 snprintf(prompt, sizeof(prompt),
302 _("Enter password of key <%s> to unlock password file: "), pc->name);
304 if((pkey = load_pkey_with_prompt(pathkey, text, prompt)) != NULL){
305 pc2 = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
306 memset((void *)pc2, 0, sizeof(PERSONAL_CERT));
307 pc2->name = cpystr(pc->name);
308 pc2->key = pkey;
309 pc2->cert = X509_dup(pc->cert);
311 /* now copy the keys and certs, starting by the key... */
312 build_path(fpath, pathdir, pc->name, sizeof(fpath));
313 strncat(fpath, ".key", 4);
314 fpath[sizeof(fpath)-1] = '\0';
315 if(our_stat(fpath, &sbuf) == 0){ /* if fpath exists */
316 if((sbuf.st_mode & S_IFMT) == S_IFREG) /* and is a regular file */
317 setup_dir++; /* we are done */
318 } else if(ps_global->smime->privatetype == Directory){
319 if(our_copy(fpath, pathkey) == 0)
320 setup_dir++;
321 } else if(ps_global->smime->privatetype == Container){
322 BIO *out;
323 if((out = BIO_new_file(fpath, "w")) != NULL){
324 if(BIO_puts(out, pc->keytext) > 0)
325 setup_dir++;
326 BIO_free(out);
328 } else if(ps_global->smime->privatetype == Keychain){
329 /* add support for Apple Mac OS X */
333 /* successful copy of key, now continue with certificate */
334 if(setup_dir){
335 setup_dir = 0;
337 build_path(pathkey, ps_global->smime->publicpath, pc->name, sizeof(pathkey));
338 strncat(pathkey, ".crt", 4);
339 pathkey[sizeof(pathkey)-1] = '\0';
341 build_path(fpath, pathdir, pc->name, sizeof(fpath));
342 strncat(fpath, ".crt", 4);
343 fpath[sizeof(fpath)-1] = '\0';
345 if(our_stat(fpath, &sbuf) == 0){
346 if((sbuf.st_mode & S_IFMT) == S_IFREG)
347 setup_dir++;
349 else if(ps_global->smime->privatetype == Directory){
350 if(our_copy(fpath, pathkey) == 0)
351 setup_dir++;
352 } else if(ps_global->smime->privatetype == Container) {
353 BIO *out;
354 if((out = BIO_new_file(fpath, "w")) != NULL){
355 if(PEM_write_bio_X509(out, pc->cert))
356 setup_dir++;
357 BIO_free(out);
359 } else if (ps_global->smime->privatetype == Keychain) {
360 /* add support for Mac OS X */
364 if(setup_dir){
365 *pwdcert = (void *) pc2;
366 return;
368 else if(pc2 != NULL)
369 free_personal_certs(&pc2);
370 } /* if (pathkey...) */
371 } /* if(ps_global->smime->personal_certs) */
374 if(setup_dir == 0){
375 /* PATHCERTDIR(Private) must be null, so create a path */
376 set_current_val(&ps_global->vars[V_PRIVATEKEY_DIR], TRUE, TRUE);
377 smime_path(ps_global->VAR_PRIVATEKEY_DIR, pathkey, sizeof(pathkey));
379 /* PATHCERTDIR(Public) must be null, so create a path */
380 set_current_val(&ps_global->vars[V_PUBLICCERT_DIR], TRUE, TRUE);
381 smime_path(ps_global->VAR_PUBLICCERT_DIR, pathcert, sizeof(pathcert));
383 /* BUG: this does not support local containers */
384 load_key_and_cert(pathkey, pathcert, &keyfile, &certfile, &pkey, &pcert);
386 if(certfile && keyfile){
387 build_path(fpath, pathdir, keyfile, sizeof(fpath));
388 strncat(fpath, ".key", 4);
389 fpath[sizeof(fpath)-1] = '\0';
391 build_path(fpath2, pathkey, keyfile, sizeof(fpath));
392 strncat(fpath2, ".key", 4);
393 fpath2[sizeof(fpath2)-1] = '\0';
395 if(our_copy(fpath, fpath2) == 0)
396 setup_dir++;
398 if(setup_dir){
399 setup_dir = 0;
401 build_path(fpath, pathdir, certfile, sizeof(fpath));
402 build_path(fpath2, pathcert, certfile, sizeof(fpath2));
404 if(our_copy(fpath, fpath2) == 0)
405 setup_dir++;
410 if(keyfile && certfile){
411 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
412 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
413 pc->name = keyfile;
414 pc->key = pkey;
415 pc->cert = pcert;
416 *pwdcert = (void *) pc;
417 fs_give((void **)&certfile);
418 return;
421 /* TODO: create self signed certificate
422 q_status_message(SM_ORDER, 2, 2,
423 _("No key/certificate pair found for password file encryption support"));
426 if(we_inited)
427 smime_deinit();
429 #endif /* PASSFILE */
431 /* smime_expunge_cert.
432 * Return values: < 0 there was an error.
433 * >=0 the number of messages expunged
436 smime_expunge_cert(WhichCerts ctype)
438 int count, removed;
439 CertList *cl, *dummy, *data;
440 char *path, buf[MAXPATH+1];
441 char *contents;
443 if(DATACERT(ctype)== NULL)
444 return -1;
446 /* data cert is the way we unify certificate management across
447 * functions, but it is not where we really save the information in the
448 * case ctype is equal to Private. What we will do is to update the
449 * datacert, and in the case of ctype equal to Private use the updated
450 * certdata to update the personal_certs data.
453 path = PATHCERTDIR(ctype);
455 if(path){
456 /* add a fake certificate at the beginning of the list */
457 dummy = fs_get(sizeof(CertList));
458 memset((void *)dummy, 0, sizeof(CertList));
459 dummy->next = DATACERT(ctype);
461 for(cl = dummy, count = 0; cl && cl->next;){
462 if(cl->next->data.deleted == 0){
463 cl = cl->next;
464 continue;
467 removed = 1; /* assume success */
468 if(SMHOLDERTYPE(ctype) == Directory){
469 build_path(buf, path, cl->next->name, sizeof(buf));
470 if(ctype == Private && strlen(buf) + strlen(EXTCERT(Private)) < sizeof(buf)){
471 strncat(buf, EXTCERT(Private), 4);
472 buf[sizeof(buf)-1] = '\0';
475 if(our_unlink(buf) < 0){
476 q_status_message1(SM_ORDER, 3, 3, _("Error removing certificate %s"), cl->next->name);
477 cl = cl->next;
478 removed = 0;
481 else { /* SMHOLDERTYPE(ctype) == Container */
482 char *prefix= ctype == CACert ? CACERTSTORELEADER : EMAILADDRLEADER;
483 char tmp[MAILTMPLEN], *s, *t;
485 contents = CONTENTCERTLIST(ctype);
486 snprintf(tmp, sizeof(tmp), "%s%s", prefix, cl->next->name);
487 tmp[sizeof(tmp) - 1] = '\0';
488 if((s = strstr(contents, tmp)) != NULL){
489 if((t = strstr(s+strlen(tmp), prefix)) == NULL)
490 *s = '\0';
491 else
492 memmove(s, t, strlen(t)+1);
493 fs_resize((void **)&contents, strlen(contents)+1);
494 switch(ctype){
495 case Private: ps_global->smime->privatecontent = contents; break;
496 case Public : ps_global->smime->publiccontent = contents; break;
497 case CACert : ps_global->smime->cacontent = contents; break;
498 default : break;
501 else
502 removed = 0;
505 if(removed > 0){
506 count++; /* count it! */
507 data = cl->next;
508 cl->next = data->next;
509 if(data->name) fs_give((void **)&data->name);
510 fs_give((void **)&data);
513 } else
514 q_status_message(SM_ORDER, 3, 3, _("Error expunging certificate"));
516 switch(ctype){
517 case Private: ps_global->smime->privatecertlist = dummy->next; break;
518 case Public : ps_global->smime->publiccertlist = dummy->next; break;
519 case CACert : ps_global->smime->cacertlist = dummy->next; break;
520 default : break;
522 fs_give((void **)&dummy);
523 if(SMHOLDERTYPE(ctype) == Container){
524 if(copy_dir_to_container(ctype, contents) < 0)
525 count = 0;
527 if(count > 0){
528 q_status_message2(SM_ORDER, 3, 3, _("Removed %s certificate%s"), comatose(count), plural(count));
530 else
531 q_status_message(SM_ORDER, 3, 3, _("Error: No certificates were removed"));
532 return count;
535 void
536 mark_cert_deleted(WhichCerts ctype, int num, unsigned state)
538 CertList *cl;
539 int i;
541 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
542 cl->data.deleted = state;
545 unsigned
546 get_cert_deleted(WhichCerts ctype, int num)
548 CertList *cl;
549 int i;
551 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
552 return (cl && cl->data.deleted) ? 1 : 0;
555 EVP_PKEY *
556 load_pkey_with_prompt(char *fpath, char *text, char *prompt)
558 EVP_PKEY *pkey;
559 int rc = 0; /* rc == 1, cancel, rc == 0 success */
560 char pass[MAILTMPLEN+1];
561 BIO *in;
563 /* attempt to load with empty password */
564 in = text ? BIO_new_mem_buf(text, strlen(text)) : BIO_new_file(fpath, "r");
565 if(in != NULL){
566 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, "");
567 BIO_free(in);
568 if(pkey != NULL) return pkey;
571 if(pith_smime_enter_password)
572 while(pkey == NULL && rc != 1){
573 in = text ? BIO_new_mem_buf(text, strlen(text)) : BIO_new_file(fpath, "r");
574 if(in != NULL){
575 do {
576 rc = (*pith_smime_enter_password)(prompt, (char *)pass, sizeof(pass));
577 } while (rc!=0 && rc!=1 && rc>0);
579 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, (char *)pass);
580 BIO_free(in);
582 else rc = 1;
585 return pkey;
591 import_certificate(WhichCerts ctype)
593 int r = 1, rc;
594 char filename[MAXPATH+1], full_filename[MAXPATH+1], buf[MAXPATH+1];
596 if(pith_smime_import_certificate == NULL){
597 q_status_message(SM_ORDER, 0, 2,
598 _("import of certificates not implemented yet!"));
599 return -1;
602 smime_init();
604 r = (*pith_smime_import_certificate)(filename, full_filename, sizeof(filename) - 20);
606 ps_global->mangled_screen = 1;
608 if(r < 0)
609 return r;
610 else if (ctype == Private){
611 char prompt[500], *s, *t;
612 EVP_PKEY *key = NULL;
614 if(!ps_global->smime->privatecertlist){
615 ps_global->smime->privatecertlist = fs_get(sizeof(CertList));
616 memset((void *)DATACERT(ctype), 0, sizeof(CertList));
619 for(s = t = filename; (t = strstr(s, ".key")) != NULL; s = t + 1);
620 if(s) *(s-1) = 0;
622 snprintf(prompt, sizeof(prompt), _("Enter passphrase for <%s>: "), filename);
623 prompt[sizeof(prompt)-1] = '\0';
624 if((key = load_pkey_with_prompt(full_filename, NULL, prompt)) != NULL){
625 if(SMHOLDERTYPE(ctype) == Directory){
626 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
627 if(strcmp(buf + strlen(buf) - 4, EXTCERT(ctype)) != 0 && strlen(buf) + 4 < sizeof(buf)){
628 strncat(buf, EXTCERT(ctype), 4);
629 buf[sizeof(buf)-1] = '\0';
631 rc = our_copy(buf, full_filename);
633 else /* if(SMHOLDERTYPE(ctype) == Container){ */
634 rc = add_file_to_container(ctype, full_filename, NULL);
635 if(rc == 0)
636 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
637 else
638 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
639 if(ps_global->smime->publiccertlist)
640 ps_global->smime->publiccertlist->data.renew = 1;
642 else
643 q_status_message(SM_ORDER, 1, 3, _("Problem unlocking key (not a certificate and/or wrong password)"));
644 } else if (ctype == CACert){
645 BIO *ins;
646 X509 *cert;
648 if((ins = BIO_new_file(full_filename, "r")) != NULL){
649 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
650 if(SMHOLDERTYPE(ctype) == Directory){
651 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
652 if(strcmp(buf + strlen(buf) - 4, ".crt") != 0 && strlen(buf) + 4 < sizeof(buf)){
653 strncat(buf, EXTCERT(ctype), 4);
654 buf[sizeof(buf)-1] = '\0';
657 rc = our_copy(buf, full_filename);
659 else /* if(SMHOLDERTYPE(ctype) == Container){ */
660 rc = add_file_to_container(ctype, full_filename, NULL);
661 if(rc == 0)
662 q_status_message(SM_ORDER, 1, 3, _("Certificate saved"));
663 else
664 q_status_message(SM_ORDER, 1, 3, _("Error saving certificate"));
665 X509_free(cert); /* not needed anymore */
667 else
668 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
669 BIO_free(ins);
671 renew_store();
672 } else { /* ctype == Public. save certificate, but first validate that it is one */
673 BIO *ins;
674 X509 *cert;
676 if((ins = BIO_new_file(full_filename, "r")) != NULL){
677 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
678 if(SMHOLDERTYPE(ctype) == Directory){
679 char **email = get_x509_subject_email(cert);
680 int i;
682 for(i = 0; email[i] != NULL; i++){
683 save_cert_for(email[i], cert, Public);
684 fs_give((void **)&email[i]);
686 fs_give((void **)email);
688 else /* if(SMHOLDERTYPE(ctype) == Container){ */
689 add_file_to_container(ctype, full_filename, NULL);
690 X509_free(cert);
691 if(ps_global->smime->publiccertlist)
692 ps_global->smime->publiccertlist->data.renew = 1;
694 else
695 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
696 BIO_free(ins);
699 if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1;
700 return 0;
703 /* itype: information type to add: 0 - public, 1 - private.
704 * Memory freed by caller
706 BIO *
707 print_private_key_information(char *email, int itype)
709 BIO *out;
710 PERSONAL_CERT *pc;
712 if(ps_global->smime == NULL
713 || ps_global->smime->personal_certs == NULL
714 || (itype != 0 && itype != 1))
715 return NULL;
717 for(pc = ps_global->smime->personal_certs;
718 pc != NULL && strcmp(pc->name, email) != 0; pc = pc->next);
719 if(pc->key == NULL
720 && !load_private_key(pc)
721 && ps_global->smime
722 && ps_global->smime->need_passphrase){
723 if (*pith_opt_smime_get_passphrase)
724 (*pith_opt_smime_get_passphrase)();
725 load_private_key(pc);
728 if(pc->key == NULL)
729 return NULL;
731 out = BIO_new(BIO_s_mem());
732 if(itype == 0) /* 0 means public */
733 EVP_PKEY_print_public(out, pc->key, 0, NULL);
734 else if (itype == 1) /* 1 means private */
735 EVP_PKEY_print_private(out, pc->key, 0, NULL);
737 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
738 forget_private_keys();
740 return out;
744 * Forget any cached private keys
746 static void
747 forget_private_keys(void)
749 PERSONAL_CERT *pcert;
750 size_t len;
751 volatile char *p;
753 dprint((9, "forget_private_keys()"));
754 if(ps_global->smime){
755 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
756 pcert;
757 pcert=pcert->next){
759 if(pcert->key){
760 EVP_PKEY_free(pcert->key);
761 pcert->key = NULL;
765 ps_global->smime->entered_passphrase = 0;
766 len = sizeof(ps_global->smime->passphrase);
767 p = ps_global->smime->passphrase;
769 while(len-- > 0)
770 *p++ = '\0';
774 /* modelled after signature_path in reply.c, but uses home dir instead of the
775 * directory where the .pinerc is located, since according to documentation,
776 * the .alpine-smime directories are subdirectories of the home directory
778 int smime_path(char *rpath, char *fpath, size_t len)
780 *fpath = '\0';
781 if(rpath && *rpath){
782 size_t spl = strlen(rpath);
784 if(IS_REMOTE(rpath)){
785 if(spl < len - 1)
786 strncpy(fpath, rpath, len-1);
787 fpath[len-1] = '\0';
789 else if(is_absolute_path(rpath)){
790 strncpy(fpath, rpath, len-1);
791 fpath[len-1] = '\0';
792 fnexpand(fpath, len);
794 else if(ps_global->VAR_OPER_DIR){
795 if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
796 build_path(fpath, ps_global->VAR_OPER_DIR, rpath, len);
798 else if(ps_global->home_dir){
799 if(strlen(ps_global->home_dir) + spl < len - 1)
800 build_path(fpath, ps_global->home_dir, rpath, len);
803 return fpath && *fpath ? 1 : 0;
809 * taken from openssl/apps/app_rand.c
811 static int
812 app_RAND_load_file(const char *file)
814 char buffer[200];
816 if(file == NULL)
817 file = RAND_file_name(buffer, sizeof buffer);
818 else if(RAND_egd(file) > 0){
819 /* we try if the given filename is an EGD socket.
820 if it is, we don't write anything back to the file. */
821 egdsocket = 1;
822 return 1;
825 if(file == NULL || !RAND_load_file(file, -1)){
826 if(RAND_status() == 0){
827 dprint((1, "unable to load 'random state'\n"));
828 dprint((1, "This means that the random number generator has not been seeded\n"));
829 dprint((1, "with much random data.\n"));
832 return 0;
835 seeded = 1;
836 return 1;
841 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
843 static void
844 openssl_extra_randomness(void)
846 #if !defined(WIN32)
847 int fd;
848 unsigned long i;
849 char *tf = NULL;
850 char tmp[MAXPATH];
851 struct stat sbuf;
852 /* if system doesn't have /dev/urandom */
853 if(stat ("/dev/urandom", &sbuf)){
854 tmp[0] = '0';
855 tf = temp_nam(NULL, NULL);
856 if(tf){
857 strncpy(tmp, tf, sizeof(tmp));
858 tmp[sizeof(tmp)-1] = '\0';
859 fs_give((void **) &tf);
862 if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
863 i = (unsigned long) tmp;
864 else{
865 unlink(tmp); /* don't need the file */
866 fstat(fd, &sbuf); /* get information about the file */
867 i = sbuf.st_ino; /* remember its inode */
868 close(fd); /* or its descriptor */
870 /* not great but it'll have to do */
871 snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
872 tcp_serverhost (),i,
873 (unsigned long) (time (0) ^ gethostid ()),
874 (unsigned long) getpid ());
875 RAND_seed(tmp, strlen(tmp));
877 #endif
881 /* taken from openssl/apps/app_rand.c */
882 static int
883 app_RAND_write_file(const char *file)
885 char buffer[200];
887 if(egdsocket || !seeded)
889 * If we did not manage to read the seed file,
890 * we should not write a low-entropy seed file back --
891 * it would suppress a crucial warning the next time
892 * we want to use it.
894 return 0;
896 if(file == NULL)
897 file = RAND_file_name(buffer, sizeof buffer);
899 if(file == NULL || !RAND_write_file(file)){
900 dprint((1, "unable to write 'random state'\n"));
901 return 0;
904 return 1;
907 CertList *
908 certlist_from_personal_certs(PERSONAL_CERT *pc)
910 CertList *cl;
912 if(pc == NULL)
913 return NULL;
915 cl = fs_get(sizeof(CertList));
916 memset((void *)cl, 0, sizeof(CertList));
917 cl->name = cpystr(pc->name);
918 cl->next = certlist_from_personal_certs(pc->next);
920 return cl;
924 void
925 renew_cert_data(CertList **data, WhichCerts ctype)
927 smime_init();
928 if(ctype == Private){
929 if(data){
930 PERSONAL_CERT *pc = (PERSONAL_CERT *)ps_global->smime->personal_certs;
931 if(*data)
932 free_certlist(data);
933 free_personal_certs(&pc);
934 setup_privatekey_storage();
935 *data = certlist_from_personal_certs((PERSONAL_CERT *)ps_global->smime->personal_certs);
936 if(data && *data)
937 RENEWCERT(*data) = 0;
938 ps_global->smime->privatecertlist = *data;
940 if(ps_global->smime->privatecertlist)
941 RENEWCERT(ps_global->smime->privatecertlist) = 0;
942 } else {
943 X509_LOOKUP *lookup = NULL;
944 X509_STORE *store = NULL;
946 if((store = X509_STORE_new()) != NULL){
947 if((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL){
948 X509_STORE_free(store);
949 store = NULL;
950 } else{
951 free_certlist(data);
952 if(SMHOLDERTYPE(ctype) == Directory)
953 add_certs_in_dir(lookup, PATHCERTDIR(ctype), EXTCERT(ctype), data);
954 else /* if(SMHOLDERTYPE(ctype) == Container) */
955 *data = mem_to_certlist(CONTENTCERTLIST(ctype), ctype);
956 if(data && *data)
957 RENEWCERT(*data) = 0;
958 if(ctype == Public)
959 ps_global->smime->publiccertlist = *data;
960 else
961 ps_global->smime->cacertlist = *data;
967 void
968 smime_reinit(void)
970 smime_deinit();
971 smime_init();
974 /* Installed as an atexit() handler to save the random data */
975 void
976 smime_deinit(void)
978 dprint((9, "smime_deinit()"));
979 app_RAND_write_file(NULL);
980 free_smime_struct(&ps_global->smime);
983 /* we renew the store when it has changed */
984 void renew_store(void)
986 if(ps_global->smime->inited){
987 if(s_cert_store != NULL)
988 X509_STORE_free(s_cert_store);
989 s_cert_store = get_ca_store();
993 /* Initialise openssl stuff if needed */
994 void
995 smime_init(void)
997 if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
999 dprint((9, "smime_init()"));
1000 if(!ps_global->smime)
1001 ps_global->smime = new_smime_struct();
1003 setup_storage_locations();
1005 s_cert_store = get_ca_store();
1007 OpenSSL_add_all_algorithms();
1008 ERR_load_crypto_strings();
1010 app_RAND_load_file(NULL);
1011 openssl_extra_randomness();
1012 ps_global->smime->inited = 1;
1015 ERR_clear_error();
1019 /* validate a certificate. Return value : 0 for no error, -1 for error.
1020 * In the latter case, set the openssl smime error in *error.
1022 int smime_validate_cert(X509 *cert, long *error)
1024 X509_STORE_CTX *csc;
1026 ERR_clear_error();
1027 *error = 0;
1028 if((csc = X509_STORE_CTX_new()) != NULL){
1029 X509_STORE_set_flags(s_cert_store, 0);
1030 if(X509_STORE_CTX_init(csc,s_cert_store,cert,NULL)
1031 && X509_verify_cert(csc) <= 0)
1032 *error = X509_STORE_CTX_get_error(csc);
1033 X509_STORE_CTX_free(csc);
1035 return *error ? -1 : 0;
1038 PERSONAL_CERT *
1039 get_personal_certs(char *path)
1041 PERSONAL_CERT *result = NULL;
1042 char buf2[MAXPATH];
1043 struct dirent *d;
1044 DIR *dirp;
1046 ps_global->smime->privatepath = cpystr(path);
1047 dirp = opendir(path);
1048 if(dirp){
1049 while((d=readdir(dirp)) != NULL){
1050 X509 *cert;
1051 size_t ll;
1053 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, ".key")){
1055 /* copy file name to temp buffer */
1056 strncpy(buf2, d->d_name, sizeof(buf2)-1);
1057 buf2[sizeof(buf2)-1] = '\0';
1058 /* chop off ".key" trailier */
1059 buf2[strlen(buf2)-4] = 0;
1060 /* Look for certificate */
1061 cert = get_cert_for(buf2, Public);
1063 if(cert){
1064 PERSONAL_CERT *pc;
1066 /* create a new PERSONAL_CERT, fill it in */
1068 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1069 pc->cert = cert;
1070 pc->name = cpystr(buf2);
1072 /* Try to load the key with an empty password */
1073 pc->key = load_key(pc, "");
1075 pc->next = result;
1076 result = pc;
1080 closedir(dirp);
1082 return result;
1086 void
1087 setup_privatekey_storage(void)
1089 char path[MAXPATH+1], *contents;
1090 int privatekeycontainer = 0;
1092 /* private keys in a container */
1093 if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
1095 privatekeycontainer = 1;
1096 contents = NULL;
1097 path[0] = '\0';
1098 if(!smime_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
1099 privatekeycontainer = 0;
1101 if(privatekeycontainer && !IS_REMOTE(path)
1102 && ps_global->VAR_OPER_DIR
1103 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1104 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1105 /* TRANSLATORS: First arg is the directory name, second is
1106 the file user wants to read but can't. */
1107 _("Can't read file outside %s: %s"),
1108 ps_global->VAR_OPER_DIR, path);
1109 privatekeycontainer = 0;
1112 if(privatekeycontainer
1113 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1114 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1116 !(contents = read_file(path, READ_FROM_LOCALE)))
1117 privatekeycontainer = 0;
1120 if(privatekeycontainer && path[0]){
1121 ps_global->smime->privatetype = Container;
1122 ps_global->smime->privatepath = cpystr(path);
1124 if(contents){
1125 ps_global->smime->privatecontent = contents;
1126 ps_global->smime->personal_certs = mem_to_personal_certs(contents);
1131 /* private keys in a directory of files */
1132 if(!privatekeycontainer){
1133 ps_global->smime->privatetype = Directory;
1135 path[0] = '\0';
1136 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1137 && !IS_REMOTE(path)))
1138 ps_global->smime->privatetype = Nada;
1139 else if(can_access(path, ACCESS_EXISTS)){
1140 if(our_mkpath(path, 0700)){
1141 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1142 ps_global->smime->privatetype = Nada;
1146 if(ps_global->smime->privatetype == Directory)
1147 ps_global->smime->personal_certs = get_personal_certs(path);
1153 static void
1154 setup_storage_locations(void)
1156 int publiccertcontainer = 0, cacertcontainer = 0;
1157 char path[MAXPATH+1], *contents;
1159 if(!ps_global->smime)
1160 return;
1162 #ifdef APPLEKEYCHAIN
1163 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1164 ps_global->smime->publictype = Keychain;
1166 else{
1167 #endif /* APPLEKEYCHAIN */
1168 /* Public certificates in a container */
1169 if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
1171 publiccertcontainer = 1;
1172 contents = NULL;
1173 path[0] = '\0';
1174 if(!smime_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
1175 publiccertcontainer = 0;
1177 if(publiccertcontainer && !IS_REMOTE(path)
1178 && ps_global->VAR_OPER_DIR
1179 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1180 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1181 /* TRANSLATORS: First arg is the directory name, second is
1182 the file user wants to read but can't. */
1183 _("Can't read file outside %s: %s"),
1184 ps_global->VAR_OPER_DIR, path);
1185 publiccertcontainer = 0;
1188 if(publiccertcontainer
1189 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1190 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1192 !(contents = read_file(path, READ_FROM_LOCALE)))
1193 publiccertcontainer = 0;
1196 if(publiccertcontainer && path[0]){
1197 ps_global->smime->publictype = Container;
1198 ps_global->smime->publicpath = cpystr(path);
1200 if(contents){
1201 ps_global->smime->publiccontent = contents;
1202 ps_global->smime->publiccertlist = mem_to_certlist(contents, Public);
1207 /* Public certificates in a directory of files */
1208 if(!publiccertcontainer){
1209 ps_global->smime->publictype = Directory;
1211 path[0] = '\0';
1212 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1213 && !IS_REMOTE(path)))
1214 ps_global->smime->publictype = Nada;
1215 else if(can_access(path, ACCESS_EXISTS)){
1216 if(our_mkpath(path, 0700)){
1217 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1218 ps_global->smime->publictype = Nada;
1222 if(ps_global->smime->publictype == Directory)
1223 ps_global->smime->publicpath = cpystr(path);
1226 #ifdef APPLEKEYCHAIN
1228 #endif /* APPLEKEYCHAIN */
1230 setup_privatekey_storage();
1232 /* extra cacerts in a container */
1233 if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
1235 cacertcontainer = 1;
1236 contents = NULL;
1237 path[0] = '\0';
1238 if(!smime_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
1239 cacertcontainer = 0;
1241 if(cacertcontainer && !IS_REMOTE(path)
1242 && ps_global->VAR_OPER_DIR
1243 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1244 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1245 /* TRANSLATORS: First arg is the directory name, second is
1246 the file user wants to read but can't. */
1247 _("Can't read file outside %s: %s"),
1248 ps_global->VAR_OPER_DIR, path);
1249 cacertcontainer = 0;
1252 if(cacertcontainer
1253 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1254 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1256 !(contents = read_file(path, READ_FROM_LOCALE)))
1257 cacertcontainer = 0;
1260 if(cacertcontainer && path[0]){
1261 ps_global->smime->catype = Container;
1262 ps_global->smime->capath = cpystr(path);
1263 ps_global->smime->cacontent = contents;
1264 if(contents)
1265 ps_global->smime->cacertlist = mem_to_certlist(contents, CACert);
1269 if(!cacertcontainer){
1270 ps_global->smime->catype = Directory;
1272 path[0] = '\0';
1273 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1274 && !IS_REMOTE(path)))
1275 ps_global->smime->catype = Nada;
1276 else if(can_access(path, ACCESS_EXISTS)){
1277 if(our_mkpath(path, 0700)){
1278 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1279 ps_global->smime->catype = Nada;
1283 if(ps_global->smime->catype == Directory)
1284 ps_global->smime->capath = cpystr(path);
1290 copy_publiccert_dir_to_container(void)
1292 return(copy_dir_to_container(Public, NULL));
1297 copy_publiccert_container_to_dir(void)
1299 return(copy_container_to_dir(Public));
1304 copy_privatecert_dir_to_container(void)
1306 return(copy_dir_to_container(Private, NULL));
1311 copy_privatecert_container_to_dir(void)
1313 return(copy_container_to_dir(Private));
1318 copy_cacert_dir_to_container(void)
1320 return(copy_dir_to_container(CACert, NULL));
1325 copy_cacert_container_to_dir(void)
1327 return(copy_container_to_dir(CACert));
1330 /* Add the contents of a file to a container. Do not check the content
1331 * of the file, just add it using the format for that container. The
1332 * caller must check the format, so that there is no data corruption
1333 * in the future.
1334 * return value: 0 - success,
1335 * != 0 - failure.
1338 add_file_to_container(WhichCerts ctype, char *fpath, char *altname)
1340 char *sep = (ctype == Public || ctype == Private)
1341 ? EMAILADDRLEADER : CACERTSTORELEADER;
1342 char *content = ctype == Public ? ps_global->smime->publiccontent
1343 : (ctype == Private ? ps_global->smime->privatecontent
1344 : ps_global->smime->cacontent);
1345 char *name;
1346 char *s;
1347 unsigned char c;
1348 struct stat sbuf;
1349 STORE_S *in = NULL;
1350 int rv = -1; /* assume error */
1352 if(our_stat(fpath, &sbuf) < 0
1353 || (in = so_get(FileStar, fpath, READ_ACCESS | READ_FROM_LOCALE)) == NULL)
1354 goto endadd;
1356 if(altname != NULL)
1357 name = altname;
1358 else if((name = strrchr(fpath, '/')) != NULL){
1359 size_t ll;
1360 if((ll = strlen(++name)) > 4 && strucmp(name + ll - 4, EXTCERT(ctype)) == 0)
1361 name[ll-strlen(EXTCERT(ctype))] = '\0';
1363 else
1364 goto endadd;
1366 if(content){
1367 fs_resize((void **)&content, strlen(content) + strlen(sep) + strlen(name) + sbuf.st_size + 3); /* 2 = \n + \n + \0*/
1368 s = content;
1369 content += strlen(content);
1371 else{
1372 s = content = fs_get(strlen(sep) + strlen(name) + sbuf.st_size + 1); /* 2 = \n + \0 */
1373 *content = '\0';
1375 strncat(content, sep, strlen(sep));
1376 strncat(content, name, strlen(name));
1377 content += strlen(content);
1378 *content++ = '\n';
1380 while(so_readc(&c, in))
1381 *content++ = (char) c;
1382 *content = '\0';
1384 switch(ctype){
1385 case Private: ps_global->smime->privatecontent = s; break;
1386 case Public : ps_global->smime->publiccontent = s; break;
1387 case CACert : ps_global->smime->cacontent = s; break;
1388 default : break;
1391 rv = copy_dir_to_container(ctype, s);
1393 endadd:
1394 if(in) so_give(&in);
1396 return rv;
1401 * returns 0 on success, -1 on failure
1402 * contents is an argument which tells this function to write the value
1403 * of this variable instead of reading the contents of the directory.
1404 * If the var contents is not null use its value as the value of the
1405 * container.
1408 copy_dir_to_container(WhichCerts which, char *contents)
1410 int ret = 0;
1411 BIO *bio_out = NULL, *bio_in = NULL;
1412 char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
1413 char *tempfile = NULL;
1414 DIR *dirp;
1415 struct dirent *d;
1416 REMDATA_S *rd = NULL;
1417 char *configdir = NULL;
1418 char *configpath = NULL;
1419 char *filesuffix = NULL;
1421 dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1422 smime_init();
1424 srcpath[0] = '\0';
1425 dstpath[0] = '\0';
1426 file[0] = '\0';
1427 emailaddr[0] = '\0';
1429 if(which == Public){
1430 configdir = ps_global->VAR_PUBLICCERT_DIR;
1431 configpath = ps_global->smime->publicpath;
1432 filesuffix = ".crt";
1434 else if(which == Private){
1435 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1436 configpath = ps_global->smime->privatepath;
1437 filesuffix = ".key";
1439 else if(which == CACert){
1440 configdir = ps_global->VAR_CACERT_DIR;
1441 configpath = ps_global->smime->capath;
1442 filesuffix = ".crt";
1445 if(!(configdir && configdir[0])){
1446 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1447 return -1;
1450 if(!(configpath && configpath[0])){
1451 #ifdef APPLEKEYCHAIN
1452 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1453 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1454 return -1;
1456 #endif /* APPLEKEYCHAIN */
1457 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1458 return -1;
1461 if(!(filesuffix && strlen(filesuffix) == 4)){
1462 return -1;
1467 * If there is a legit directory to read from set up the
1468 * container file to write to.
1470 if(smime_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
1472 if(IS_REMOTE(configpath)){
1473 rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
1474 NULL, "Error: ",
1475 _("Can't access remote smime configuration."));
1476 if(!rd)
1477 return -1;
1479 (void) rd_read_metadata(rd);
1481 if(rd->access == MaybeRorW){
1482 if(rd->read_status == 'R')
1483 rd->access = ReadOnly;
1484 else
1485 rd->access = ReadWrite;
1488 if(rd->access != NoExists){
1490 rd_check_remvalid(rd, 1L);
1493 * If the cached info says it is readonly but
1494 * it looks like it's been fixed now, change it to readwrite.
1496 if(rd->read_status == 'R'){
1497 rd_check_readonly_access(rd);
1498 if(rd->read_status == 'W'){
1499 rd->access = ReadWrite;
1500 rd->flags |= REM_OUTOFDATE;
1502 else
1503 rd->access = ReadOnly;
1507 if(rd->flags & REM_OUTOFDATE){
1508 if(rd_update_local(rd) != 0){
1510 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1511 rd_close_remdata(&rd);
1512 return -1;
1515 else
1516 rd_open_remote(rd);
1518 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1519 rd_close_remdata(&rd);
1520 return -1;
1523 rd->flags |= DO_REMTRIM;
1525 strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
1526 dstpath[sizeof(dstpath)-1] = '\0';
1528 else{
1529 strncpy(dstpath, configpath, sizeof(dstpath)-1);
1530 dstpath[sizeof(dstpath)-1] = '\0';
1534 * dstpath is either the local Container file or the local cache file
1535 * for the remote Container file.
1537 tempfile = tempfile_in_same_dir(dstpath, "az", NULL);
1541 * If there is a legit directory to read from and a tempfile
1542 * to write to we continue.
1544 if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
1546 if(contents != NULL){
1547 if(BIO_puts(bio_out, contents) < 0)
1548 ret = -1;
1550 else {
1551 if((dirp = opendir(srcpath)) != NULL){
1553 while((d=readdir(dirp)) && !ret){
1554 size_t ll;
1556 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, filesuffix)){
1558 /* copy file name to temp buffer */
1559 strncpy(emailaddr, d->d_name, sizeof(emailaddr)-1);
1560 emailaddr[sizeof(emailaddr)-1] = '\0';
1561 /* chop off suffix trailier */
1562 emailaddr[strlen(emailaddr)-4] = 0;
1565 * This is the separator between the contents of
1566 * different files.
1568 if(which == CACert){
1569 if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
1570 && (BIO_puts(bio_out, emailaddr) > 0)
1571 && (BIO_puts(bio_out, "\n") > 0)))
1572 ret = -1;
1574 else{
1575 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1576 && (BIO_puts(bio_out, emailaddr) > 0)
1577 && (BIO_puts(bio_out, "\n") > 0)))
1578 ret = -1;
1581 /* read then write contents of file */
1582 build_path(file, srcpath, d->d_name, sizeof(file));
1583 if(!(bio_in = BIO_new_file(file, "r")))
1584 ret = -1;
1586 if(!ret){
1587 int good_stuff = 0;
1589 while(BIO_gets(bio_in, line, sizeof(line)) > 0){
1590 if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
1591 good_stuff = 1;
1593 if(good_stuff)
1594 BIO_puts(bio_out, line);
1596 if(strncmp("-----END", line, strlen("-----END")) == 0)
1597 good_stuff = 0;
1601 BIO_free(bio_in);
1605 closedir(dirp);
1609 BIO_free(bio_out);
1611 if(!ret){
1612 if(rename_file(tempfile, dstpath) < 0){
1613 q_status_message2(SM_ORDER, 3, 3,
1614 _("Can't rename %s to %s"), tempfile, dstpath);
1615 ret = -1;
1618 /* if the container is remote, copy it */
1619 if(!ret && IS_REMOTE(configpath)){
1620 int e;
1621 char datebuf[200];
1623 datebuf[0] = '\0';
1625 if((e = rd_update_remote(rd, datebuf)) != 0){
1626 if(e == -1){
1627 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1628 _("Error opening temporary smime file %s: %s"),
1629 rd->lf, error_description(errno));
1630 dprint((1,
1631 "write_remote_smime: error opening temp file %s\n",
1632 rd->lf ? rd->lf : "?"));
1634 else{
1635 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1636 _("Error copying to %s: %s"),
1637 rd->rn, error_description(errno));
1638 dprint((1,
1639 "write_remote_smime: error copying from %s to %s\n",
1640 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1643 q_status_message(SM_ORDER | SM_DING, 5, 5,
1644 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1646 else{
1647 rd_update_metadata(rd, datebuf);
1648 rd->read_status = 'W';
1651 rd_close_remdata(&rd);
1656 if(tempfile)
1657 fs_give((void **) &tempfile);
1659 return ret;
1664 * returns 0 on success, -1 on failure
1667 copy_container_to_dir(WhichCerts which)
1669 char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
1670 char iobuf[4096];
1671 char *contents = NULL;
1672 char *leader = NULL;
1673 char *filesuffix = NULL;
1674 char *configdir = NULL;
1675 char *configpath = NULL;
1676 char *tempfile = NULL;
1677 char *p, *q, *line, *name, *certtext, *save_p;
1678 int len;
1679 BIO *in, *out;
1681 dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1682 smime_init();
1684 path[0] = '\0';
1686 if(which == Public){
1687 leader = EMAILADDRLEADER;
1688 contents = ps_global->smime->publiccontent;
1689 configdir = ps_global->VAR_PUBLICCERT_DIR;
1690 configpath = ps_global->smime->publicpath;
1691 filesuffix = ".crt";
1692 if(!(configpath && configpath[0])){
1693 #ifdef APPLEKEYCHAIN
1694 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1695 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1696 return -1;
1698 #endif /* APPLEKEYCHAIN */
1699 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1700 return -1;
1703 fs_give((void **) &ps_global->smime->publicpath);
1705 path[0] = '\0';
1706 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1707 && !IS_REMOTE(path))){
1708 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1709 return -1;
1712 if(can_access(path, ACCESS_EXISTS)){
1713 if(our_mkpath(path, 0700)){
1714 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1715 return -1;
1719 ps_global->smime->publicpath = cpystr(path);
1720 configpath = ps_global->smime->publicpath;
1722 else if(which == Private){
1723 leader = EMAILADDRLEADER;
1724 contents = ps_global->smime->privatecontent;
1725 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1726 configpath = ps_global->smime->privatepath;
1727 filesuffix = ".key";
1728 if(!(configpath && configpath[0])){
1729 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1730 return -1;
1733 fs_give((void **) &ps_global->smime->privatepath);
1735 path[0] = '\0';
1736 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1737 && !IS_REMOTE(path))){
1738 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1739 return -1;
1742 if(can_access(path, ACCESS_EXISTS)){
1743 if(our_mkpath(path, 0700)){
1744 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1745 return -1;
1749 ps_global->smime->privatepath = cpystr(path);
1750 configpath = ps_global->smime->privatepath;
1752 else if(which == CACert){
1753 leader = CACERTSTORELEADER;
1754 contents = ps_global->smime->cacontent;
1755 configdir = ps_global->VAR_CACERT_DIR;
1756 configpath = ps_global->smime->capath;
1757 filesuffix = ".crt";
1758 if(!(configpath && configpath[0])){
1759 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1760 return -1;
1763 fs_give((void **) &ps_global->smime->capath);
1765 path[0] = '\0';
1766 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1767 && !IS_REMOTE(path))){
1768 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1769 return -1;
1772 if(can_access(path, ACCESS_EXISTS)){
1773 if(our_mkpath(path, 0700)){
1774 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1775 return -1;
1779 ps_global->smime->capath = cpystr(path);
1780 configpath = ps_global->smime->capath;
1783 if(!(configdir && configdir[0])){
1784 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1785 return -1;
1788 if(!(configpath && configpath[0])){
1789 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1790 return -1;
1793 if(!(filesuffix && strlen(filesuffix) == 4)){
1794 return -1;
1798 if(contents && *contents){
1799 for(p = contents; *p != '\0';){
1800 line = p;
1802 while(*p && *p != '\n')
1803 p++;
1805 save_p = NULL;
1806 if(*p == '\n'){
1807 save_p = p;
1808 *p++ = '\0';
1811 if(strncmp(leader, line, strlen(leader)) == 0){
1812 name = line + strlen(leader);
1813 certtext = p;
1814 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
1815 if((q = strstr(certtext, leader)) != NULL){
1816 p = q;
1818 else{ /* end of file */
1819 q = certtext + strlen(certtext);
1820 p = q;
1823 strncpy(buf, name, sizeof(buf)-5);
1824 buf[sizeof(buf)-5] = '\0';
1825 strncat(buf, filesuffix, 5);
1826 build_path(file, configpath, buf, sizeof(file));
1828 in = BIO_new_mem_buf(certtext, q-certtext);
1829 if(in){
1830 tempfile = tempfile_in_same_dir(file, "az", NULL);
1831 out = NULL;
1832 if(tempfile)
1833 out = BIO_new_file(tempfile, "w");
1835 if(out){
1836 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1837 BIO_write(out, iobuf, len);
1839 BIO_free(out);
1841 if(rename_file(tempfile, file) < 0){
1842 q_status_message2(SM_ORDER, 3, 3,
1843 _("Can't rename %s to %s"),
1844 tempfile, file);
1845 return -1;
1848 fs_give((void **) &tempfile);
1851 BIO_free(in);
1856 if(save_p)
1857 *save_p = '\n';
1861 return 0;
1865 #ifdef APPLEKEYCHAIN
1868 copy_publiccert_container_to_keychain(void)
1870 /* NOT IMPLEMNTED */
1871 return -1;
1875 copy_publiccert_keychain_to_container(void)
1877 /* NOT IMPLEMNTED */
1878 return -1;
1881 #endif /* APPLEKEYCHAIN */
1885 * Get a pointer to a string describing the most recent OpenSSL error.
1886 * It's statically allocated, so don't change or attempt to free it.
1888 static const char *
1889 openssl_error_string(void)
1891 char *errs;
1892 const char *data = NULL;
1893 long errn;
1895 errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
1896 errs = (char*) ERR_reason_error_string(errn);
1898 if(errs)
1899 return errs;
1900 else if(data)
1901 return data;
1903 return "unknown error";
1907 /* Return true if the body looks like a PKCS7 object */
1909 is_pkcs7_body(BODY *body)
1911 int result;
1913 result = body->type==TYPEAPPLICATION &&
1914 body->subtype &&
1915 (strucmp(body->subtype,"pkcs7-mime")==0 ||
1916 strucmp(body->subtype,"x-pkcs7-mime")==0 ||
1917 strucmp(body->subtype,"pkcs7-signature")==0 ||
1918 strucmp(body->subtype,"x-pkcs7-signature")==0);
1920 return result;
1924 #ifdef notdef
1926 * Somewhat useful debug utility to dump the contents of a BIO to a file.
1927 * Note that a memory BIO will have its contents eliminated after they
1928 * are read so this will break the next step.
1930 static void
1931 dump_bio_to_file(BIO *in, char *filename)
1933 char iobuf[4096];
1934 int len;
1935 BIO *out;
1937 out = BIO_new_file(filename, "w");
1939 if(out){
1940 if(BIO_method_type(in) != BIO_TYPE_MEM)
1941 BIO_reset(in);
1943 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1944 BIO_write(out, iobuf, len);
1946 BIO_free(out);
1949 BIO_reset(in);
1951 #endif
1955 * Recursively stash a pointer to the decrypted data in our
1956 * manufactured body.
1957 * parameters: type: call of type 1, save the base and header for multipart messages
1958 call of type 0, do not save the base and header for multipart messages
1960 static void
1961 create_local_cache(char *h, char *base, BODY *b, int type)
1963 if(b->type==TYPEMULTIPART){
1964 PART *p;
1966 if(type == 1){
1967 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
1968 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
1969 } else if(type == 0){
1971 * We don't really want to copy the real body contents. It shouldn't be
1972 * used, and in the case of a message with attachments, we'll be
1973 * duplicating the files multiple times.
1975 cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
1977 for(p=b->nested.part; p; p=p->next)
1978 create_local_cache(h, base, (BODY *) p, type);
1981 else{
1982 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
1983 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
1988 static long
1989 rfc822_output_func(void *b, char *string)
1991 BIO *bio = (BIO *) b;
1993 return(string ? *string ? (BIO_puts(bio, string) > 0 ? 1L : 0L)
1994 : (BIO_puts(bio, string) >= 0 ? 1L : 0L)
1995 : 0L);
2000 * Attempt to load the private key for the given PERSONAL_CERT.
2001 * This sets the appropriate passphrase globals in order to
2002 * interact with the user correctly.
2004 static int
2005 load_private_key(PERSONAL_CERT *pcert)
2007 if(!pcert->key){
2009 /* Try empty password by default */
2010 char *password = "";
2012 if(ps_global->smime
2013 && (ps_global->smime->need_passphrase
2014 || ps_global->smime->entered_passphrase)){
2015 /* We've already been in here and discovered we need a different password */
2017 if(ps_global->smime->entered_passphrase)
2018 password = (char *) ps_global->smime->passphrase; /* already entered */
2019 else
2020 return 0;
2023 ERR_clear_error();
2025 if(!(pcert->key = load_key(pcert, password))){
2026 long err = ERR_get_error();
2028 /* Couldn't load key... */
2030 if(ps_global->smime && ps_global->smime->entered_passphrase){
2032 /* The user got the password wrong maybe? */
2034 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
2035 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
2036 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
2037 else
2038 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2040 /* This passphrase is no good; forget it */
2041 ps_global->smime->entered_passphrase = 0;
2044 if(ps_global->smime){
2045 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2046 ps_global->smime->need_passphrase = 1;
2047 if(ps_global->smime->passphrase_emailaddr){
2048 int i;
2049 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
2050 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
2051 fs_give((void **) ps_global->smime->passphrase_emailaddr);
2054 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
2057 return 0;
2059 else{
2060 /* This key will be cached, so we won't be called again */
2061 if(ps_global->smime){
2062 ps_global->smime->entered_passphrase = 0;
2063 ps_global->smime->need_passphrase = 0;
2067 return 1;
2070 return 0;
2074 static void
2075 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename, char *smime_type)
2077 b->type = TYPEAPPLICATION;
2078 b->subtype = cpystr(type);
2079 b->encoding = ENCBINARY;
2080 b->description = cpystr(description);
2082 b->disposition.type = cpystr("attachment");
2083 set_parameter(&b->disposition.parameter, "filename", filename);
2085 set_parameter(&b->parameter, "name", filename);
2086 if(smime_type && *smime_type)
2087 set_parameter(&b->parameter, "smime-type", smime_type);
2092 * Look for a personal certificate matching the
2093 * given address
2095 PERSONAL_CERT *
2096 match_personal_cert_to_email(ADDRESS *a)
2098 PERSONAL_CERT *pcert = NULL;
2099 char buf[MAXPATH];
2100 char **email;
2101 int i, done;
2103 if(!a || !a->mailbox || !a->host)
2104 return NULL;
2106 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2108 if(ps_global->smime){
2109 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
2110 pcert;
2111 pcert=pcert->next){
2113 if(!pcert->cert)
2114 continue;
2116 email = get_x509_subject_email(pcert->cert);
2118 done = 0;
2119 if(email != NULL){
2120 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
2121 if(email[i] != NULL) done++;
2122 for(i = 0; email[i] != NULL; i++)
2123 fs_give((void **)&email[i]);
2124 fs_give((void **)email);
2127 if(done > 0)
2128 break;
2132 return pcert;
2137 * Look for a personal certificate matching the from
2138 * (or reply_to? in the given envelope)
2140 PERSONAL_CERT *
2141 match_personal_cert(ENVELOPE *env)
2143 PERSONAL_CERT *pcert;
2145 pcert = match_personal_cert_to_email(env->reply_to);
2146 if(!pcert)
2147 pcert = match_personal_cert_to_email(env->from);
2149 return pcert;
2154 * Flatten the given body into its MIME representation.
2155 * Return the result in a BIO.
2157 static BIO *
2158 body_to_bio(BODY *body)
2160 BIO *bio = NULL;
2161 int len;
2163 bio = BIO_new(BIO_s_mem());
2164 if(!bio)
2165 return NULL;
2167 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
2168 pine_write_body_header(body, rfc822_output_func, bio);
2169 pine_rfc822_output_body(body, rfc822_output_func, bio);
2172 * Now need to truncate by two characters since the above
2173 * appends CRLF.
2175 if((len=BIO_ctrl_pending(bio)) > 1){
2176 BUF_MEM *biobuf = NULL;
2178 BIO_get_mem_ptr(bio, &biobuf);
2179 if(biobuf){
2180 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
2184 return bio;
2188 static BIO *
2189 bio_from_store(STORE_S *store)
2191 BIO *ret = NULL;
2193 if(store && store->src == BioType && store->txt){
2194 ret = (BIO *) store->txt;
2197 return(ret);
2201 * Encrypt file; given a path (char *) fp, replace the file
2202 * by an encrypted version of it. If (char *) text is not null, then
2203 * replace the text of (char *) fp by the encrypted version of (char *) text.
2204 * certpath is the FULL path to the file containing the certificate used for
2205 * encryption.
2208 encrypt_file(char *fp, char *text, PERSONAL_CERT *pc)
2210 const EVP_CIPHER *cipher = NULL;
2211 STACK_OF(X509) *encerts = NULL;
2212 BIO *out = NULL;
2213 PKCS7 *p7 = NULL;
2214 int rv = 0;
2216 if(pc == NULL)
2217 return 0;
2219 cipher = EVP_aes_256_cbc();
2220 encerts = sk_X509_new_null();
2222 sk_X509_push(encerts, X509_dup(pc->cert));
2224 if(text){
2225 if((out = BIO_new(BIO_s_mem())) == NULL)
2226 goto end;
2227 (void) BIO_reset(out);
2228 BIO_puts(out, text);
2230 else{
2231 if(!(out = BIO_new_file(fp, "rb")))
2232 goto end;
2234 BIO_read_filename(out, fp);
2237 if((p7 = PKCS7_encrypt(encerts, out, cipher, 0)) == NULL)
2238 goto end;
2239 BIO_set_close(out, BIO_CLOSE);
2240 BIO_free(out);
2241 if(!(out = BIO_new_file(fp, "w")))
2242 goto end;
2243 BIO_reset(out);
2244 rv = PEM_write_bio_PKCS7(out, p7);
2245 BIO_flush(out);
2247 end:
2248 if(out != NULL)
2249 BIO_free(out);
2250 PKCS7_free(p7);
2251 sk_X509_pop_free(encerts, X509_free);
2253 return rv;
2257 * Encrypt a message on the way out. Called from call_mailer in send.c
2258 * The body may be reallocated.
2261 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
2263 PKCS7 *p7 = NULL;
2264 BIO *in = NULL;
2265 BIO *out = NULL;
2266 const EVP_CIPHER *cipher = NULL;
2267 STACK_OF(X509) *encerts = NULL;
2268 STORE_S *outs = NULL;
2269 PINEFIELD *pf;
2270 ADDRESS *a;
2271 BODY *body = *bodyP;
2272 BODY *newBody = NULL;
2273 int result = 0;
2274 X509 *cert;
2275 char buf[MAXPATH];
2277 dprint((9, "encrypt_outgoing_message()"));
2278 smime_init();
2280 cipher = EVP_aes_256_cbc();
2282 encerts = sk_X509_new_null();
2284 /* Look for a certificate for each of the recipients */
2285 for(pf = header->local; pf && pf->name; pf = pf->next)
2286 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
2287 for(a=*pf->addr; a; a=a->next){
2288 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2290 if((cert = get_cert_for(buf, Public)) != NULL)
2291 sk_X509_push(encerts,cert);
2292 else{
2293 q_status_message2(SM_ORDER, 1, 1,
2294 _("Unable to find certificate for <%s@%s>"),
2295 a->mailbox, a->host);
2296 goto end;
2301 /* add the sender's certificate so that they can decrypt the message too */
2302 for(a=header->env->from; a ; a = a->next){
2303 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2305 if((cert = get_cert_for(buf, Public)) != NULL
2306 && sk_X509_find(encerts, cert) == -1)
2307 sk_X509_push(encerts,cert);
2310 in = body_to_bio(body);
2312 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
2314 outs = so_get(BioType, NULL, EDIT_ACCESS);
2315 out = bio_from_store(outs);
2317 i2d_PKCS7_bio(out, p7);
2318 (void) BIO_flush(out);
2320 so_seek(outs, 0, SEEK_SET);
2322 newBody = mail_newbody();
2324 newBody->type = TYPEAPPLICATION;
2325 newBody->subtype = cpystr("pkcs7-mime");
2326 newBody->encoding = ENCBINARY;
2328 newBody->disposition.type = cpystr("attachment");
2329 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
2331 newBody->description = cpystr("S/MIME Encrypted Message");
2332 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
2333 set_parameter(&newBody->parameter, "name", "smime.p7m");
2335 newBody->contents.text.data = (unsigned char *) outs;
2337 *bodyP = newBody;
2339 result = 1;
2341 end:
2343 BIO_free(in);
2344 PKCS7_free(p7);
2345 sk_X509_pop_free(encerts, X509_free);
2347 dprint((9, "encrypt_outgoing_message returns %d", result));
2348 return result;
2353 Get (and decode) the body of the given section of msg
2355 static STORE_S*
2356 get_part_contents(long msgno, const char *section)
2358 long len;
2359 gf_io_t pc;
2360 STORE_S *store = NULL;
2361 char *err;
2363 store = so_get(CharStar, NULL, EDIT_ACCESS);
2364 if(store){
2365 gf_set_so_writec(&pc,store);
2367 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
2369 gf_clear_so_writec(store);
2371 so_seek(store, 0, SEEK_SET);
2373 if(err)
2374 so_give(&store);
2377 return store;
2381 static PKCS7 *
2382 get_pkcs7_from_part(long msgno,const char *section)
2384 STORE_S *store = NULL;
2385 PKCS7 *p7 = NULL;
2386 BIO *in = NULL;
2388 store = get_part_contents(msgno, section);
2390 if(store){
2391 if(store->src == CharStar){
2392 int len;
2395 * We're reaching inside the STORE_S structure. We should
2396 * probably have a way to get the length, instead.
2398 len = (int) (store->eod - store->dp);
2399 in = BIO_new_mem_buf(store->txt, len);
2401 else{ /* just copy it */
2402 unsigned char c;
2404 in = BIO_new(BIO_s_mem());
2405 (void) BIO_reset(in);
2407 so_seek(store, 0L, 0);
2408 while(so_readc(&c, store)){
2409 BIO_write(in, &c, 1);
2413 if(in){
2414 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2415 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
2416 /* error */
2419 BIO_free(in);
2422 so_give(&store);
2425 return p7;
2428 int same_cert(X509 *x, X509 *cert)
2430 char bufcert[256], bufx[256];
2431 int rv = 0;
2433 get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert));
2434 get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx));
2435 if(strcmp(bufx, bufcert) == 0)
2436 rv = 1;
2438 return rv;
2442 /* extract and save certificates from a PKCS7 package. The ctype variable
2443 * tells us if we want to extract it to a public/ or a ca/ directory. The
2444 * later makes sense only for recoverable errors (errors that can be fixed
2445 * by saving to the ca/ directory before we verify the signature).
2446 * Return value:
2447 * 0 - no errors (in public/) no need to try again,
2448 * or validated self signed certificate (in ca/)
2449 * < 0 - certificate error is not recoverable, don't even think about it.
2452 int smime_extract_and_save_cert(PKCS7 *p7)
2454 STACK_OF(X509) *signers;
2455 X509 *x, *cert;
2456 char **email;
2457 int i, j;
2459 if((signers = PKCS7_get0_signers(p7, NULL, 0)) == NULL)
2460 return -1;
2462 for(i = 0; i < sk_X509_num(signers); i++){
2463 if((x = sk_X509_value(signers,i)) == NULL)
2464 continue;
2466 if((email = get_x509_subject_email(x)) != NULL){
2467 for(j = 0; email[j] != NULL; j++){
2468 if((cert = get_cert_for(email[j], Public)) == NULL
2469 || same_cert(x, cert) == 0)
2470 save_cert_for(email[j], x, Public);
2471 X509_free(cert);
2472 fs_give((void **) &email[i]);
2474 fs_give((void **) email);
2477 sk_X509_free(signers);
2479 return 0;
2483 * Try to verify a signature.
2485 * p7 - the pkcs7 object to verify
2486 * in - the plain data to verify (NULL if not detached)
2487 * out - BIO to which to write the opaque data
2488 * silent - if non zero, do not print errors, only print success.
2490 static int
2491 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
2493 STACK_OF(X509) *otherCerts = NULL;
2494 int result;
2495 const char *data;
2496 long err;
2498 if(!s_cert_store){
2499 if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
2500 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2502 return -1;
2505 smime_extract_and_save_cert(p7);
2507 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, 0);
2509 if(result){
2510 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
2512 else{
2513 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2515 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
2517 /* Retry verification so we can get the plain text */
2518 /* Might be better to reimplement PKCS7_verify here? */
2520 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY);
2522 if (!silent) q_status_message1(SM_ORDER | SM_DING, 3, 3,
2523 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
2526 return result;
2530 void
2531 free_smime_body_sparep(void **sparep)
2533 char *s;
2534 SIZEDTEXT *st;
2535 if(sparep && *sparep){
2536 switch(get_smime_sparep_type(*sparep)){
2537 case P7Type: PKCS7_free((PKCS7 *) get_smime_sparep_data(*sparep));
2538 break;
2539 case CharType: s = (char *)get_smime_sparep_data(*sparep);
2540 fs_give((void **) &s);
2541 break;
2542 case SizedText : st = (SIZEDTEXT *)get_smime_sparep_data(*sparep);
2543 fs_give((void **) &st->data);
2544 fs_give((void **) &st);
2545 break;
2546 default : break;
2548 ((SMIME_SPARE_S *)(*sparep))->data = NULL;
2549 fs_give(sparep);
2553 /* return the mime header for this body part. Memory freed by caller */
2554 char *
2555 smime_fetch_mime(BODY *b, MAILSTREAM *stream, long msgno, char * section,
2556 unsigned long *mimelen)
2558 char *rv;
2559 char newSec[100];
2561 if(b->type == TYPEMULTIPART)
2562 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
2563 else
2564 strncpy(newSec, section, sizeof(newSec));
2565 newSec[sizeof(newSec)-1] = '\0';
2566 rv = mail_fetch_mime(stream, msgno, newSec, mimelen, 0);
2567 return rv ? cpystr(rv) : NULL;
2571 void
2572 smime_write_body_header(BODY *b, MAILSTREAM *stream, long msgno, char *section, soutr_t f, void *s)
2574 char *rv;
2575 char newSec[100];
2577 if(b->nested.part == NULL)
2578 return;
2580 if(b->nested.part->body.type == TYPEMULTIPART)
2581 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
2582 else
2583 strncpy(newSec, section, sizeof(newSec));
2584 newSec[sizeof(newSec)-1] = '\0';
2585 rv = mail_fetch_mime(stream, msgno, newSec, NULL, 0);
2586 if(f && rv != NULL)
2587 (*f)(s, rv);
2591 write_signed_body(BODY *b, MAILSTREAM *stream, long msgno, char *section, BIO *in)
2594 smime_write_body_header(b, stream, msgno, section, rfc822_output_func, in);
2601 /* Big comment, explaining the mess that exists out there, and how we deal
2602 with it, and also how we solve the problems that are created this way.
2604 When Alpine sends a message, it constructs that message, computes the
2605 signature, but then it forgets the message it signed and reconstructs it
2606 again. Since it signs a message containing a notice about "mime aware
2607 tools", but it does not send that we do not include that in the part
2608 that is signed, and that takes care of much of the problems.
2610 Another problem is what is received from the servers. All servers tested
2611 seem to transmit the message that was signed intact and Alpine can check
2612 the signature correctly. That is not a problem. The problem arises when
2613 the message includes attachments. In this case different servers send
2614 different things, so it will be up to us to figure out what is the text
2615 that was actually signed. Confused? here is the story:
2617 When a message containing and attachment is sent by Alpine, UW-IMAP,
2618 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2619 that was sent by Alpine, but GMX.com, Exchange, and probably other
2620 servers add a trailing \r\n in the message, so when validating the
2621 signature, these messages will not validate. There are several things
2622 that can be done.
2624 1. Add a trailing \r\n to any message that contains attachments, sign that
2625 and send that. In this way, all messages will validate with all
2626 servers.
2628 2. Compatibility mode: If a message has an attachment, contains a trailing
2629 \r\n and does not validate (sent by an earlier version of Alpine),
2630 remove the trailing \r\n and try to revalidate again.
2632 3. We do not add \r\n to validate a message that we sent, because that
2633 would only work in Alpine, and not in any other client. That would
2634 not be a good thing to do.
2636 PART II
2638 Now we have to deal with encrypted and signed messages. The problem is
2639 that c-client makes all its pointers point to "on disk" content, but
2640 since we decrypted the data earlier, we have to make sure of two things.
2641 One is that we saved that data (so we do not have to decrypt it again)
2642 and second that we can use it.
2644 In order to save the data we use create_local_cache, so that we do not
2645 have to redecrypt the message. Once this is saved, c-client functions will
2646 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2648 PART III
2650 When we are trying to verify messages with detached signatures, some
2651 imap servers send incorrect information in the mail_fetch_mime call. By
2652 incorrect I mean that this is not fetched directly from the message, but
2653 it is read from the message, processed, and then the processed part is
2654 sent to us, so this text might not agree with what is in the message,
2655 and so the validation of the signature might fail. However, the good
2656 news is that the message validates if saved to a local folder. This
2657 means that if normal validation does not work we can make it work by
2658 saving the message locally and validating that. This is implemented
2659 below, and causes delay in the display of the message. I am considering
2660 at this time not to do this automatically, but wait for the user to tell
2661 us to do it for them by means of a command available in the
2662 mail_view_screen. This might help in other situations, where a message
2663 is supposed to have an attachment, but it can not be seen in the
2664 processed text. Nevertheless, at this time, this is automatic, and is
2665 causing a delay in the processing of the message, but it is validating
2666 correctly all messages.
2670 * Given a multipart body of type multipart/signed, attempt to verify it.
2671 * Returns non-zero if the body was changed.
2674 do_detached_signature_verify(BODY *b, long msgno, char *section)
2676 PKCS7 *p7 = NULL;
2677 BIO *in = NULL;
2678 PART *p;
2679 int result, modified_the_body = 0;
2680 unsigned long mimelen, bodylen;
2681 char newSec[100], *mimetext, *bodytext;
2682 char *what_we_did;
2683 SIZEDTEXT *st;
2685 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"));
2687 smime_init();
2689 /* if it was signed and then encrypted, use the decrypted text
2690 * to check the validity of the signature
2692 if(b->sparep){
2693 if(get_smime_sparep_type(b->sparep) == SizedText){
2694 /* bodytext includes mimetext */
2695 st = (SIZEDTEXT *) get_smime_sparep_data(b->sparep);
2696 bodytext = st->data;
2697 bodylen = st->size;
2698 mimetext = NULL;
2699 mimelen = 0L;
2702 else{
2703 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
2704 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
2705 if(mimetext)
2706 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
2708 if(mimetext == NULL || bodytext == NULL)
2709 return modified_the_body;
2712 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
2714 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
2715 || (in = BIO_new(BIO_s_mem())) == NULL)
2716 return modified_the_body;
2718 (void) BIO_reset(in);
2719 if(mimetext != NULL)
2720 BIO_write(in, mimetext, mimelen);
2721 BIO_write(in, bodytext, bodylen);
2723 /* Try compatibility with the past and check if this message
2724 * validates when we remove the last two characters. Silence
2725 * any failures first.
2727 if(((result = do_signature_verify(p7, in, NULL, 1)) == 0)
2728 && bodylen > 2
2729 && (strncmp(bodytext+bodylen-2,"\r\n", 2) == 0)){
2730 BUF_MEM *biobuf = NULL;
2732 BIO_get_mem_ptr(in, &biobuf);
2733 if(biobuf)
2734 BUF_MEM_grow(biobuf, mimelen + bodylen - 2);
2736 /* test one more time in case this is a remote connection and the
2737 * server massages the message to the point that it sends bogus
2738 * information. In this case, we fetch the message and we process
2739 * it by hand.
2741 if((result = do_signature_verify(p7, in, NULL, 1)) == 0
2742 && mimelen > 0 /* do not do this for encrypted messages */
2743 && IS_REMOTE(ps_global->mail_stream->mailbox)){
2744 char *fetch;
2745 unsigned long hlen, tlen;
2746 STORE_S *msg_so;
2748 BIO_free(in);
2749 if((in = BIO_new(BIO_s_mem())) != NULL
2750 && (fetch = mail_fetch_header(ps_global->mail_stream, msgno, NULL,
2751 NULL, &hlen, FT_PEEK)) != NULL
2752 && (msg_so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL
2753 && so_nputs(msg_so, fetch, (long) hlen)
2754 && (fetch = pine_mail_fetch_text(ps_global->mail_stream, msgno, NULL,
2755 &tlen, FT_PEEK)) != NULL
2756 && so_nputs(msg_so, fetch, tlen)){
2757 STRING bs;
2758 char *h = (char *) so_text(msg_so);
2759 char *bstart = strstr(h, "\r\n\r\n");
2760 ENVELOPE *env;
2761 BODY *body;
2763 bstart += 4;
2764 INIT(&bs, mail_string, bstart, tlen);
2765 rfc822_parse_msg_full(&env, &body, h, bstart-h, &bs, BADHOST, 0, 0);
2766 mail_free_envelope(&env);
2768 mail_free_body_part(&b->nested.part);
2769 b->nested.part = body->nested.part;
2770 create_local_cache(bstart, bstart, &b->nested.part->body, 1);
2771 modified_the_body = 1;
2773 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
2775 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
2777 if(mimetext)
2778 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
2780 if (mimetext == NULL || bodytext == NULL)
2781 return modified_the_body;
2783 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
2785 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL)
2786 return modified_the_body;
2788 (void) BIO_reset(in);
2789 BIO_write(in, mimetext, mimelen);
2790 BIO_write(in, bodytext, bodylen);
2791 so_give(&msg_so);
2793 if(((result = do_signature_verify(p7, in, NULL, 1)) == 0)
2794 && bodylen > 2
2795 && (strncmp(bodytext+bodylen-2,"\r\n", 2) == 0)){
2796 BUF_MEM *biobuf = NULL;
2798 BIO_get_mem_ptr(in, &biobuf);
2799 if(biobuf)
2800 BUF_MEM_grow(biobuf, mimelen + bodylen - 2);
2801 result = do_signature_verify(p7, in, NULL, 0);
2807 BIO_free(in);
2808 if(b->subtype)
2809 fs_give((void**) &b->subtype);
2811 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
2812 b->encoding = ENC8BIT;
2814 if(b->description)
2815 fs_give ((void**) &b->description);
2817 what_we_did = result ? _("This message was cryptographically signed.") :
2818 _("This message was cryptographically signed but the signature could not be verified.");
2820 b->description = cpystr(what_we_did);
2822 b->sparep = create_smime_sparep(P7Type, p7);
2824 p = b->nested.part;
2826 /* p is signed plaintext */
2827 if(p && p->next)
2828 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
2830 modified_the_body = 1;
2832 return modified_the_body;
2836 PERSONAL_CERT *
2837 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
2839 PERSONAL_CERT *x = NULL;
2841 if(ps_global->smime){
2842 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
2843 X509 *mine;
2845 mine = x->cert;
2847 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,mine->cert_info->issuer) &&
2848 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,mine->cert_info->serialNumber)){
2849 break;
2854 return x;
2858 static PERSONAL_CERT *
2859 find_certificate_matching_pkcs7(PKCS7 *p7)
2861 int i;
2862 STACK_OF(PKCS7_RECIP_INFO) *recips;
2863 PERSONAL_CERT *x = NULL;
2865 recips = p7->d.enveloped->recipientinfo;
2867 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
2868 PKCS7_RECIP_INFO *ri;
2870 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
2872 if((x=find_certificate_matching_recip_info(ri))!=0){
2873 break;
2877 return x;
2880 /* decrypt an encrypted file.
2881 Args: fp - the path to the encrypted file.
2882 rv - a code that tells the caller what happened inside the function
2883 pcert - a personal certificate that was used to encrypt this file
2884 Returns the decoded text allocated in a char *, whose memory must be
2885 freed by caller
2888 char *
2889 decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc)
2891 PKCS7 *p7 = NULL;
2892 char *text, *tmp;
2893 BIO *in = NULL, *out = NULL;
2894 int i, j;
2895 long unsigned int len;
2896 void *ret;
2898 if(pc == NULL || (text = read_file(fp, 0)) == NULL || *text == '\0')
2899 return NULL;
2901 tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1);
2902 for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0'
2903 && text[i] != '-'; j++, i++)
2904 tmp[j] = text[i];
2905 tmp[j] = '\0';
2907 ret = rfc822_base64((unsigned char *)tmp, strlen(tmp), &len);
2909 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
2910 p7 = d2i_PKCS7_bio(in, NULL);
2911 BIO_free(in);
2914 if (text) fs_give((void **)&text);
2915 if (ret) fs_give((void **)&ret);
2917 if (rv) *rv = pc->key == NULL ? -1 : 1;
2919 out = BIO_new(BIO_s_mem());
2920 (void) BIO_reset(out);
2922 i = PKCS7_decrypt(p7, pc->key, pc->cert, out, 0);
2924 if(i == 0){
2925 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
2926 (char*) openssl_error_string());
2927 goto end;
2930 BIO_get_mem_data(out, &tmp);
2932 text = cpystr(tmp);
2933 BIO_free(out);
2935 end:
2936 PKCS7_free(p7);
2938 return text;
2942 * Try to decode (decrypt or verify a signature) a PKCS7 body
2943 * Returns non-zero if something was changed.
2945 static int
2946 do_decoding(BODY *b, long msgno, const char *section)
2948 int modified_the_body = 0;
2949 BIO *out = NULL;
2950 PKCS7 *p7 = NULL;
2951 X509 *recip = NULL;
2952 EVP_PKEY *key = NULL;
2953 PERSONAL_CERT *pcert = NULL;
2954 char *what_we_did = "";
2955 char null[1];
2957 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"));
2958 null[0] = '\0';
2959 smime_init();
2962 * Extract binary data from part to an in-memory store
2965 if(b->sparep){
2966 if(get_smime_sparep_type(b->sparep) == P7Type)
2967 p7 = (PKCS7*) get_smime_sparep_data(b->sparep);
2969 else{
2970 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
2971 if(!p7){
2972 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
2973 (char*) openssl_error_string());
2974 goto end;
2978 * Save the PKCS7 object for later dealings by the user interface.
2979 * It will be cleaned up when the body is garbage collected.
2981 b->sparep = create_smime_sparep(P7Type, p7);
2984 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
2986 if(PKCS7_type_is_signed(p7)){
2987 int sigok;
2989 out = BIO_new(BIO_s_mem());
2990 (void) BIO_reset(out);
2991 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
2993 sigok = do_signature_verify(p7, NULL, out, 0);
2995 what_we_did = sigok ? _("This message was cryptographically signed.") :
2996 _("This message was cryptographically signed but the signature could not be verified.");
2998 /* make sure it's null terminated */
2999 BIO_write(out, null, 1);
3001 else if(!PKCS7_type_is_enveloped(p7)){
3002 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
3003 goto end;
3005 else{ /* It *is* enveloped */
3006 int decrypt_result;
3008 what_we_did = _("This message was encrypted.");
3010 /* now need to find a cert that can decrypt this */
3011 pcert = find_certificate_matching_pkcs7(p7);
3013 if(!pcert){
3014 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3015 goto end;
3018 recip = pcert->cert;
3020 if(!load_private_key(pcert)
3021 && ps_global->smime
3022 && ps_global->smime->need_passphrase
3023 && !ps_global->smime->already_auto_asked){
3024 /* Couldn't load key with blank password, ask user */
3025 ps_global->smime->already_auto_asked = 1;
3026 if(pith_opt_smime_get_passphrase){
3027 (*pith_opt_smime_get_passphrase)();
3028 load_private_key(pcert);
3032 key = pcert->key;
3033 if(!key)
3034 goto end;
3036 out = BIO_new(BIO_s_mem());
3037 (void) BIO_reset(out);
3038 BIO_puts(out, "MIME-Version: 1.0\r\n");
3040 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
3042 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3043 forget_private_keys();
3045 if(!decrypt_result){
3046 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3047 (char*) openssl_error_string());
3048 goto end; }
3050 BIO_write(out, null, 1);
3054 * We've now produced a flattened MIME object in BIO out.
3055 * It needs to be turned back into a BODY.
3058 if(out){
3059 BODY *body;
3060 ENVELOPE *env;
3061 char *h = NULL;
3062 char *bstart;
3063 STRING s;
3064 BUF_MEM *bptr = NULL;
3066 BIO_get_mem_ptr(out, &bptr);
3067 if(bptr)
3068 h = bptr->data;
3070 /* look for start of body */
3071 bstart = strstr(h, "\r\n\r\n");
3073 if(!bstart){
3074 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
3076 else{
3077 SIZEDTEXT *st;
3078 bstart += 4; /* skip over CRLF*2 */
3080 INIT(&s, mail_string, bstart, strlen(bstart));
3081 rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
3082 mail_free_envelope(&env); /* Don't care about this */
3084 if(body->type == TYPEMULTIPART
3085 && !strucmp(body->subtype, "SIGNED")){
3086 char *cookie = NULL;
3087 PARAMETER *param;
3088 for (param = body->parameter; param && !cookie; param = param->next)
3089 if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value;
3090 st = fs_get(sizeof(SIZEDTEXT));
3091 st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */
3092 st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4);
3093 body->sparep = create_smime_sparep(SizedText, (void *)st);
3095 body->mime.offset = 0;
3096 body->mime.text.size = 0;
3099 * Now convert original body (application/pkcs7-mime)
3100 * to a multipart body with one sub-part (the decrypted body).
3101 * Note that the sub-part may also be multipart!
3104 b->type = TYPEMULTIPART;
3105 if(b->subtype)
3106 fs_give((void**) &b->subtype);
3109 * This subtype is used in mailview.c to annotate the display of
3110 * encrypted or signed messages. We know for sure then that it's a PKCS7
3111 * part because the sparep field is set to the PKCS7 object (see above).
3113 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3114 b->encoding = ENC8BIT;
3116 if(b->description)
3117 fs_give((void**) &b->description);
3119 b->description = cpystr(what_we_did);
3121 if(b->disposition.type)
3122 fs_give((void **) &b->disposition.type);
3124 if(b->contents.text.data)
3125 fs_give((void **) &b->contents.text.data);
3127 if(b->parameter)
3128 mail_free_body_parameter(&b->parameter);
3130 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3131 b->nested.part = fs_get(sizeof(PART));
3132 b->nested.part->body = *body;
3133 b->nested.part->next = NULL;
3135 fs_give((void**) &body);
3138 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3139 * the decrypted data. Otherwise, it'll try to load it from the original
3140 * data. Eek.
3142 create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body, 0);
3144 modified_the_body = 1;
3148 end:
3149 if(out)
3150 BIO_free(out);
3152 return modified_the_body;
3157 * Recursively handle PKCS7 bodies in our message.
3159 * Returns non-zero if some fiddling was done.
3161 static int
3162 do_fiddle_smime_message(BODY *b, long msgno, char *section)
3164 int modified_the_body = 0;
3166 if(!b)
3167 return 0;
3169 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"));
3171 if(is_pkcs7_body(b)){
3173 if(do_decoding(b, msgno, section)){
3175 * b should now be a multipart message:
3176 * fiddle it too in case it's been multiply-encrypted!
3179 /* fallthru */
3180 modified_the_body = 1;
3184 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
3186 PART *p;
3187 int partNum;
3188 char newSec[100];
3190 if(MIME_MULT_SIGNED(b->type, b->subtype)){
3194 * Ahah. We have a multipart signed entity.
3196 * Multipart/signed
3197 * part 1 (signed thing)
3198 * part 2 (the pkcs7 signature)
3200 * We're going to convert that to
3202 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3203 * part 1 (signed thing)
3204 * part 2 has been freed
3206 * We also extract the signature from part 2 and save it
3207 * in the multipart body->sparep, and we add a description
3208 * in the multipart body->description.
3211 * The results of a decrypted message will be similar. It
3212 * will be
3214 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3215 * part 1 (decrypted thing)
3218 modified_the_body += do_detached_signature_verify(b, msgno, section);
3220 else if(MIME_MSG(b->type, b->subtype)){
3221 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
3223 else{
3225 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
3226 /* Append part number to the section string */
3228 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
3230 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
3235 return modified_the_body;
3240 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3241 * Returns non-zero if something was changed.
3244 fiddle_smime_message(BODY *b, long msgno)
3246 return do_fiddle_smime_message(b, msgno, "");
3250 /********************************************************************************/
3254 * Output a string in a distinctive style
3256 void
3257 gf_puts_uline(char *txt, gf_io_t pc)
3259 pc(TAG_EMBED); pc(TAG_BOLDON);
3260 gf_puts(txt, pc);
3261 pc(TAG_EMBED); pc(TAG_BOLDOFF);
3265 STACK_OF(X509) *
3266 get_chain_for_cert(X509 *cert, int *error)
3268 STACK_OF(X509) *chain = NULL;
3269 X509_STORE_CTX *ctx;
3270 X509 *x, *xtmp;
3271 int rc; /* return code */
3272 int level = -1;
3274 *error = 0;
3275 ERR_clear_error();
3276 if((ctx = X509_STORE_CTX_new()) != NULL){
3277 X509_STORE_set_flags(s_cert_store, 0);
3278 if(!X509_STORE_CTX_init(ctx, s_cert_store, cert, NULL))
3279 *error = X509_STORE_CTX_get_error(ctx);
3280 else if((chain = sk_X509_new_null()) != NULL){
3281 for(x = cert; ; x = xtmp){
3282 if(++level > 0)
3283 sk_X509_push(chain, X509_dup(x));
3284 rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x);
3285 if(rc < 0)
3286 *error = 1;
3287 if(rc <= 0)
3288 break;
3289 if(!X509_check_issued(xtmp, xtmp))
3290 break;
3293 if((*error && chain != NULL) || level == 0){
3294 sk_X509_pop_free(chain, X509_free);
3295 chain = NULL;
3297 X509_STORE_CTX_free(ctx);
3299 return chain;
3304 * Sign a message. Called from call_mailer in send.c.
3306 * This takes the header for the outgoing message as well as a pointer
3307 * to the current body (which may be reallocated).
3310 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach)
3312 STORE_S *outs = NULL;
3313 BODY *body = *bodyP;
3314 BODY *newBody = NULL;
3315 PART *p1 = NULL;
3316 PART *p2 = NULL;
3317 PERSONAL_CERT *pcert;
3318 BIO *in = NULL;
3319 BIO *out = NULL;
3320 PKCS7 *p7 = NULL;
3321 STACK_OF(X509) *chain;
3322 int result = 0, error;
3323 int flags = dont_detach ? 0 : PKCS7_DETACHED;
3325 dprint((9, "sign_outgoing_message()"));
3327 smime_init();
3329 /* Look for a private key matching the sender address... */
3331 pcert = match_personal_cert(header->env);
3333 if(!pcert){
3334 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
3335 goto end;
3338 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
3339 /* Couldn't load key with blank password, try again */
3340 if(pith_opt_smime_get_passphrase){
3341 (*pith_opt_smime_get_passphrase)();
3342 load_private_key(pcert);
3346 if(!pcert->key)
3347 goto end;
3349 in = body_to_bio(body);
3351 chain = get_chain_for_cert(pcert->cert, &error);
3353 p7 = PKCS7_sign(pcert->cert, pcert->key, chain, in, flags);
3355 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3356 forget_private_keys();
3358 if(chain)
3359 sk_X509_pop_free(chain, X509_free);
3361 if(!p7){
3362 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
3363 goto end;
3366 if(error)
3367 q_status_message(SM_ORDER, 1, 1, _("Not all certificates needed to verify signature included in signed message"));
3369 outs = so_get(BioType, NULL, EDIT_ACCESS);
3370 out = bio_from_store(outs);
3372 i2d_PKCS7_bio(out, p7);
3373 (void) BIO_flush(out);
3375 so_seek(outs, 0, SEEK_SET);
3377 if((flags&PKCS7_DETACHED)==0){
3379 /* the simple case: the signed data is in the pkcs7 object */
3381 newBody = mail_newbody();
3383 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3385 newBody->contents.text.data = (unsigned char *) outs;
3386 *bodyP = newBody;
3388 result = 1;
3390 else{
3393 * OK.
3394 * We have to create a new body as follows:
3396 * multipart/signed; blah blah blah
3397 * reference to existing body
3399 * pkcs7 object
3402 newBody = mail_newbody();
3404 newBody->type = TYPEMULTIPART;
3405 newBody->subtype = cpystr("signed");
3406 newBody->encoding = ENC7BIT;
3408 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
3409 set_parameter(&newBody->parameter, "micalg", "sha1");
3411 p1 = mail_newbody_part();
3412 p2 = mail_newbody_part();
3415 * This is nasty. We're just copying the body in here,
3416 * but since our newBody is freed at the end of call_mailer,
3417 * we mustn't let this body (the original one) be freed twice.
3419 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
3421 p1->next = p2;
3423 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL);
3424 p2->body.contents.text.data = (unsigned char *) outs;
3426 newBody->nested.part = p1;
3428 *bodyP = newBody;
3430 result = 1;
3433 end:
3435 PKCS7_free(p7);
3436 BIO_free(in);
3438 dprint((9, "sign_outgoing_message returns %d", result));
3439 return result;
3443 SMIME_STUFF_S *
3444 new_smime_struct(void)
3446 SMIME_STUFF_S *ret = NULL;
3448 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
3449 memset((void *) ret, 0, sizeof(*ret));
3450 ret->publictype = Nada;
3452 return ret;
3456 static void
3457 free_smime_struct(SMIME_STUFF_S **smime)
3459 if(smime && *smime){
3460 if((*smime)->passphrase_emailaddr){
3461 int i;
3462 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
3463 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
3464 fs_give((void **) (*smime)->passphrase_emailaddr);
3467 if((*smime)->publicpath)
3468 fs_give((void **) &(*smime)->publicpath);
3470 if((*smime)->publiccertlist)
3471 free_certlist(&(*smime)->publiccertlist);
3473 if((*smime)->cacertlist)
3474 free_certlist(&(*smime)->cacertlist);
3476 if((*smime)->privatecertlist)
3477 free_certlist(&(*smime)->privatecertlist);
3479 if((*smime)->publiccontent)
3480 fs_give((void **) &(*smime)->publiccontent);
3482 if((*smime)->privatepath)
3483 fs_give((void **) &(*smime)->privatepath);
3485 if((*smime)->personal_certs){
3486 PERSONAL_CERT *pc;
3488 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
3489 free_personal_certs(&pc);
3490 (*smime)->personal_certs = NULL;
3493 if((*smime)->privatecontent)
3494 fs_give((void **) &(*smime)->privatecontent);
3496 if((*smime)->capath)
3497 fs_give((void **) &(*smime)->capath);
3499 if((*smime)->cacontent)
3500 fs_give((void **) &(*smime)->cacontent);
3502 fs_give((void **) smime);
3506 #endif /* SMIME */