* Set default ssl configuration for Homebrew in MAC OSX to
[alpine.git] / pith / smime.c
blob8e65cfabefc0239b77abba974d3c4098b87758db
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);
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);
60 static BIO *body_to_bio(BODY *body);
61 static BIO *bio_from_store(STORE_S *store);
62 static STORE_S *get_part_contents(long msgno, const char *section);
63 static PKCS7 *get_pkcs7_from_part(long msgno, const char *section);
64 static int do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent);
65 static int do_detached_signature_verify(BODY *b, long msgno, char *section);
66 static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7);
67 static int do_decoding(BODY *b, long msgno, const char *section);
68 static void free_smime_struct(SMIME_STUFF_S **smime);
69 static void setup_storage_locations(void);
70 static int copy_container_to_dir(WhichCerts which);
71 void setup_privatekey_storage(void);
72 int smime_path(char *rpath, char *fpath, size_t len);
73 int smime_extract_and_save_cert(PKCS7 *p7);
74 int same_cert(X509 *, X509 *);
75 CertList * certlist_from_personal_certs(PERSONAL_CERT *pc);
76 #ifdef PASSFILE
77 void load_key_and_cert(char *pathdir, char **keyfile, char **certfile, EVP_PKEY **pkey, X509 **pcert);
78 #endif /* PASSFILE */
79 STACK_OF(X509) *get_chain_for_cert(X509 *cert, int *error);
80 EVP_PKEY *load_pkey_with_prompt(char *fpath, char *text, char *prompt);
82 int (*pith_opt_smime_get_passphrase)(void);
83 int (*pith_smime_import_certificate)(char *, char *, size_t);
84 int (*pith_smime_enter_password)(char *prompt, char *, size_t);
86 static X509_STORE *s_cert_store;
88 /* State management for randomness functions below */
89 static int seeded = 0;
90 static int egdsocket = 0;
93 #ifdef PASSFILE
94 void
95 load_key_and_cert(char *pathdir, char **keyfile,
96 char **certfile, EVP_PKEY **pkey, X509 **pcert)
98 BIO *in;
99 char buf[MAXPATH+1], pathkey[MAXPATH+1], prompt[MAILTMPLEN];
100 DIR *dirp;
101 struct dirent *d;
103 if(keyfile) *keyfile = NULL;
104 if(certfile) *certfile = NULL;
105 if(pkey) *pkey = NULL;
106 if(pcert) *pcert = NULL;
108 if(pathdir == NULL) return;
110 if((dirp = opendir(pathdir)) != NULL){
111 while((d=readdir(dirp)) != NULL){
112 size_t ll;
114 if((ll=strlen(d->d_name)) && ll > 4){
115 if(pkey && *pkey == NULL && keyfile && !strcmp(d->d_name+ll-4, ".key")){
116 strncpy(buf, d->d_name, sizeof(buf));
117 buf[sizeof(buf)-1] = '\0';
118 build_path(pathkey, pathdir, buf, sizeof(pathkey));
119 buf[strlen(buf)-4] = '\0';
120 snprintf(prompt, sizeof(prompt),
121 _("Enter password of key <%s> to unlock password file: "), buf);
122 if((*pkey = load_pkey_with_prompt(pathkey, NULL, prompt)) != NULL)
123 *keyfile = cpystr(buf);
126 if(pcert && *pcert == NULL && certfile && !strcmp(d->d_name+ll-4, ".crt")){
127 strncpy(buf, d->d_name, sizeof(buf));
128 buf[sizeof(buf)-1] = '\0';
129 build_path(pathkey, pathdir, buf, sizeof(pathkey));
130 if((in = BIO_new_file(pathkey, "r")) != NULL){
131 if((*pcert = PEM_read_bio_X509(in, NULL, NULL, NULL)) != NULL)
132 *certfile = cpystr(buf);
133 else{
134 q_status_message1(SM_ORDER, 0, 2,
135 _("loading public certificate %s failed. Continuing..."), buf);
137 BIO_free(in);
146 /* setup a key and certificate to encrypt and decrypt a password file.
147 * These files will be saved in the .alpine-smime/.pwd directory, but its
148 * location can be setup in the command line with the -pwdcertdir option.
149 * Here are the rules:
151 * Check if the .alpine-smime/.pwd (or -pwdcertdir directory) exists,
152 * if not create it. If we are successful, move to the next step
154 * - If the user has a key/cert pair, setup is successful;
155 * - if the used does not have a key/cert pair, or one cannot be found
156 * create one, setup is successful; (TODO: implement this)
157 * - in any other case, setup is not successful.
159 * If setup is successful, setup ps_global->smime->pwdcert.
160 * If any of this fails, ps_global->smime->pwdcert will be null.
161 * Ok, that should do it.
163 void
164 setup_pwdcert(void **pwdcert)
166 int setup_dir = 0; /* make it non zero if we know which dir to use */
167 int i,j,k;
168 struct stat sbuf;
169 char pathdir[MAXPATH+1], pathkey[MAXPATH+1], fpath[MAXPATH+1];
170 char prompt[MAILTMPLEN], buf[MAXPATH+1];
171 char *keyfile, *certfile, *text;
172 DIR *dirp;
173 struct dirent *d;
174 EVP_PKEY *pkey = NULL;
175 X509 *pcert = NULL;
176 BIO *in = NULL;
177 PERSONAL_CERT *pc, *pc2 = NULL;
179 smime_init();
181 if(ps_global->pwdcertdir){
182 if(our_stat(ps_global->pwdcertdir, &sbuf) == 0
183 && ((sbuf.st_mode & S_IFMT) == S_IFDIR)){
184 setup_dir++;
185 strncpy(pathdir, ps_global->pwdcertdir, sizeof(pathdir));
186 pathdir[sizeof(pathdir)-1] = '\0';
188 } else {
189 smime_path(DF_PASSWORD_DIR, pathdir, sizeof(pathdir));
190 if(our_stat(pathdir, &sbuf) == 0){
191 if((sbuf.st_mode & S_IFMT) == S_IFDIR)
192 setup_dir++;
193 } else {
194 if(can_access(pathdir, ACCESS_EXISTS) != 0
195 && our_mkpath(pathdir, 0700) == 0)
196 setup_dir++;
200 if(setup_dir == 0)
201 return;
203 /* reuse setup dir to mean setup_key */
204 setup_dir = 0;
206 /* BUG: add container support */
207 load_key_and_cert(pathdir, &keyfile, &certfile, &pkey, &pcert);
209 if(certfile && keyfile)
210 setup_dir++;
212 if(setup_dir == 0){
213 if(keyfile == NULL){
214 /* PATHCERTDIR(Private) must be null, so create a path */
215 set_current_val(&ps_global->vars[V_PRIVATEKEY_DIR], TRUE, TRUE);
216 smime_path(ps_global->VAR_PRIVATEKEY_DIR, pathkey, MAXPATH);
217 load_key_and_cert(pathkey, &keyfile, NULL, &pkey, NULL);
219 if(certfile == NULL){
220 /* PATHCERTDIR(Public) must be null, so create a path */
221 set_current_val(&ps_global->vars[V_PUBLICCERT_DIR], TRUE, TRUE);
222 smime_path(ps_global->VAR_PUBLICCERT_DIR, pathkey, MAXPATH);
223 load_key_and_cert(pathkey, NULL, &certfile, NULL, &pcert);
225 smime_reinit();
226 if(certfile && keyfile){
227 build_path(fpath, pathdir, keyfile, sizeof(fpath));
228 strncat(fpath, ".key", 4);
229 fpath[sizeof(fpath)-1] = '\0';
231 smime_path(ps_global->VAR_PRIVATEKEY_DIR, pathkey, MAXPATH);
232 strncat(pathkey, "/", 1);
233 strncat(pathkey, keyfile, strlen(keyfile));
234 strncat(pathkey, ".key", 4);
235 pathkey[sizeof(pathkey)-1] = '\0';
237 if(our_copy(fpath, pathkey) == 0)
238 setup_dir++;
240 if(setup_dir){
241 setup_dir = 0;
243 build_path(fpath, pathdir, certfile, sizeof(fpath));
245 smime_path(ps_global->VAR_PUBLICCERT_DIR, pathkey, MAXPATH);
246 strncat(pathkey, "/", 1);
247 strncat(pathkey, keyfile, strlen(keyfile));
248 strncat(pathkey, ".crt", 4);
249 pathkey[sizeof(pathkey)-1] = '\0';
251 if(our_copy(fpath, pathkey) == 0)
252 setup_dir++;
257 if(setup_dir > 0){
258 pc2 = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
259 memset((void *)pc2, 0, sizeof(PERSONAL_CERT));
260 pc2->name = keyfile;
261 pc2->key = pkey;
262 pc2->cert = pcert;
263 if(pwdcert) *pwdcert = (void *) pc2;
266 if(certfile)
267 fs_give((void **)&certfile);
269 if(setup_dir == 0){ /* try looking for an existent key */
270 if(ps_global->smime->personal_certs){
271 pc = (PERSONAL_CERT *) ps_global->smime->personal_certs;
272 if(ps_global->smime->privatetype == Directory){
273 build_path(pathkey, ps_global->smime->privatepath, pc->name, sizeof(pathkey));
274 strncat(pathkey, ".key", 4);
275 pathkey[sizeof(pathkey)-1] = '\0';
276 text = NULL;
277 } else { /* ps_global->smime->privatetype == Container */
278 if(pc->keytext == NULL){ /* we should *never* be here, but just in case */
279 if(ps_global->smime->privatecontent != NULL){
280 char tmp[MAILTMPLEN], *s, *t, c;
281 snprintf(tmp, sizeof(tmp), "%s%s", EMAILADDRLEADER, pc->name);
282 tmp[sizeof(tmp)-1] = '\0';
283 if((s = strstr(ps_global->smime->privatecontent, tmp)) != NULL){
284 if((t = strstr(s+strlen(tmp), EMAILADDRLEADER)) != NULL){
285 c = *t;
286 *t = '\0';
287 pc->keytext = cpystr(s + strlen(tmp) + 1); /* 1 = strlen("\n") */
288 *t = c;
290 else
291 pc->keytext = cpystr(s + strlen(tmp) + 1); /* 1 = strlen("\n") */
295 if(pc->keytext != NULL) /* we should go straigth here */
296 text = pc->keytext;
298 if((pathkey && *pathkey) || text){
299 snprintf(prompt, sizeof(prompt),
300 _("Enter password of key <%s> to unlock password file: "), pc->name);
302 if((pkey = load_pkey_with_prompt(pathkey, text, prompt)) != NULL){
303 pc2 = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
304 memset((void *)pc2, 0, sizeof(PERSONAL_CERT));
305 pc2->name = cpystr(pc->name);
306 pc2->key = pkey;
307 pc2->cert = X509_dup(pc->cert);
309 /* now copy the keys and certs, starting by the key... */
310 build_path(fpath, pathdir, pc->name, sizeof(fpath));
311 strncat(fpath, ".key", 4);
312 fpath[sizeof(fpath)-1] = '\0';
313 if(our_stat(fpath, &sbuf) == 0){
314 if((sbuf.st_mode & S_IFMT) == S_IFREG)
315 setup_dir++;
317 else { if(ps_global->smime->privatetype == Directory){
318 if(our_copy(fpath, pathkey) == 0)
319 setup_dir++;
320 } else { /* ps_global->smime->privatetype == Container */
321 BIO *out;
322 if((out = BIO_new_file(fpath, "w")) != NULL){
323 if(BIO_puts(out, pc->keytext) > 0)
324 setup_dir++;
325 BIO_free(out);
330 /* successful copy of key, now continue with certificate */
331 if(setup_dir){
332 setup_dir = 0;
334 build_path(pathkey, ps_global->smime->publicpath, pc->name, sizeof(pathkey));
335 strncat(pathkey, ".crt", 4);
336 pathkey[sizeof(pathkey)-1] = '\0';
338 build_path(fpath, pathdir, pc->name, sizeof(fpath));
339 strncat(fpath, ".crt", 4);
340 fpath[sizeof(fpath)-1] = '\0';
342 if(our_stat(fpath, &sbuf) == 0){
343 if((sbuf.st_mode & S_IFMT) == S_IFREG)
344 setup_dir++;
346 else{ if(ps_global->smime->privatetype == Directory){
347 if(our_copy(fpath, pathkey) == 0)
348 setup_dir++;
349 } else { /* ps_global->smime->privatetype == Container */
350 BIO *out;
351 if((out = BIO_new_file(fpath, "w")) != NULL){
352 if(PEM_write_bio_X509(out, pc->cert))
353 setup_dir++;
354 BIO_free(out);
360 if(setup_dir)
361 if(pwdcert) *pwdcert = (void *) pc2;
362 else{
363 free_personal_certs(&pc2);
364 return;
366 } /* end of if(pkey != NULL) */
367 BIO_free(in);
368 } /* end of if(in != NULL) */
369 } /* end of if(ps_global->personal_certs) */
372 /* TODO: create self signed certificate */
373 if(setup_dir == 0)
374 q_status_message(SM_ORDER, 0, 2,
375 _("No key/certificate pair found. Password file not encrypted!"));
377 #endif /* PASSFILE */
379 /* smime_expunge_cert.
380 * Return values: < 0 there was an error.
381 * >=0 the number of messages expunged
384 smime_expunge_cert(WhichCerts ctype)
386 int count, removed;
387 CertList *cl, *dummy, *data;
388 char *path, *ext, buf[MAXPATH+1];
389 char *contents;
391 if(DATACERT(ctype)== NULL)
392 return -1;
394 /* data cert is the way we unify certificate management across functions, but it is
395 * not where we really save the information in the case ctype is equal to Private.
396 * What we will do is to update the datacert, and in the case of ctype equal to Private
397 * use the updated certdata to update the personal_certs data.
400 path = PATHCERTDIR(ctype);
401 ext = EXTCERT(ctype);
403 if(path){
404 /* add a fake certificate at the beginning of the list */
405 dummy = fs_get(sizeof(CertList));
406 memset((void *)dummy, 0, sizeof(CertList));
407 dummy->next = DATACERT(ctype);
409 for(cl = dummy, count = 0; cl && cl->next;){
410 if(cl->next->data.deleted == 0){
411 cl = cl->next;
412 continue;
415 removed = 1; /* assume success */
416 if(SMHOLDERTYPE(ctype) == Directory){
417 build_path(buf, path, cl->next->name, sizeof(buf));
418 if(ctype == Private && strlen(buf) + strlen(EXTCERT(Private)) < sizeof(buf))
419 strcat(buf, EXTCERT(Private));
421 if(our_unlink(buf) < 0){
422 q_status_message1(SM_ORDER, 3, 3, _("Error removing certificate %s"), cl->next->name);
423 cl = cl->next;
424 removed = 0;
427 else { /* SMHOLDERTYPE(ctype) == Container */
428 char *prefix= ctype == CACert ? CACERTSTORELEADER : EMAILADDRLEADER;
429 char tmp[MAILTMPLEN], *s, *t;
431 contents = CONTENTCERTLIST(ctype);
432 snprintf(tmp, sizeof(tmp), "%s%s", prefix, cl->next->name);
433 tmp[sizeof(tmp) - 1] = '\0';
434 if((s = strstr(contents, tmp)) != NULL){
435 if((t = strstr(s+strlen(tmp), prefix)) == NULL)
436 *s = '\0';
437 else
438 memmove(s, t, strlen(t)+1);
439 fs_resize((void **)&contents, strlen(contents)+1);
440 switch(ctype){
441 case Private: ps_global->smime->privatecontent = contents; break;
442 case Public : ps_global->smime->publiccontent = contents; break;
443 case CACert : ps_global->smime->cacontent = contents; break;
444 default : break;
447 else
448 removed = 0;
451 if(removed > 0){
452 count++; /* count it! */
453 data = cl->next;
454 cl->next = data->next;
455 if(data->name) fs_give((void **)&data->name);
456 fs_give((void **)&data);
459 } else
460 q_status_message(SM_ORDER, 3, 3, _("Error expunging certificate"));
462 switch(ctype){
463 case Private: ps_global->smime->privatecertlist = dummy->next; break;
464 case Public : ps_global->smime->publiccertlist = dummy->next; break;
465 case CACert : ps_global->smime->cacertlist = dummy->next; break;
466 default : break;
468 fs_give((void **)&dummy);
469 if(SMHOLDERTYPE(ctype) == Container){
470 if(copy_dir_to_container(ctype, contents) < 0)
471 count = 0;
473 if(count > 0){
474 q_status_message2(SM_ORDER, 3, 3, _("Removed %s certificate%s"), comatose(count), plural(count));
476 else
477 q_status_message(SM_ORDER, 3, 3, _("Error: No certificates were removed"));
478 return count;
481 void
482 mark_cert_deleted(WhichCerts ctype, int num, unsigned state)
484 CertList *cl;
485 int i;
487 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
488 cl->data.deleted = state;
491 unsigned
492 get_cert_deleted(WhichCerts ctype, int num)
494 CertList *cl;
495 int i;
497 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
498 return (cl && cl->data.deleted) ? 1 : 0;
501 EVP_PKEY *
502 load_pkey_with_prompt(char *fpath, char *text, char *prompt)
504 EVP_PKEY *pkey;
505 int rc = 0; /* rc == 1, cancel, rc == 0 success */
506 int we_clear = 0;
507 char pass[MAILTMPLEN+1];
508 BIO *in;
510 /* attempt to load with empty password */
511 in = text ? BIO_new_mem_buf(text, strlen(text)) : BIO_new_file(fpath, "r");
512 if(in != NULL){
513 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, "");
514 BIO_free(in);
515 if(pkey != NULL) return pkey;
518 if(pith_smime_enter_password)
519 while(pkey == NULL && rc != 1){
520 in = text ? BIO_new_mem_buf(text, strlen(text)) : BIO_new_file(fpath, "r");
521 if(in != NULL){
522 do {
523 rc = (*pith_smime_enter_password)(prompt, (char *)pass, sizeof(pass));
524 } while (rc!=0 && rc!=1 && rc>0);
526 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, (char *)pass);
527 BIO_free(in);
529 else rc = 1;
532 return pkey;
538 import_certificate(WhichCerts ctype)
540 int r = 1, rc;
541 char filename[MAXPATH+1], full_filename[MAXPATH+1], buf[MAXPATH+1];
543 if(pith_smime_import_certificate == NULL){
544 q_status_message(SM_ORDER, 0, 2,
545 _("import of certificates not implemented yet!"));
546 return -1;
549 smime_init();
551 r = (*pith_smime_import_certificate)(filename, full_filename, sizeof(filename) - 20);
553 ps_global->mangled_screen = 1;
555 if(r < 0)
556 return r;
557 else if (ctype == Private){
558 char prompt[500], *s, *t;
559 BIO *in;
560 EVP_PKEY *key = NULL;
561 PERSONAL_CERT *pc;
563 if(!ps_global->smime->privatecertlist){
564 ps_global->smime->privatecertlist = fs_get(sizeof(CertList));
565 memset((void *)DATACERT(ctype), 0, sizeof(CertList));
568 for(s = t = filename; (t = strstr(s, ".key")) != NULL; s = t + 1);
569 if(s) *(s-1) = 0;
571 snprintf(prompt, sizeof(prompt), _("Enter passphrase for <%s>: "), filename);
572 if((key = load_pkey_with_prompt(full_filename, NULL, prompt)) != NULL){
573 if(SMHOLDERTYPE(ctype) == Directory){
574 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
575 if(strcmp(buf + strlen(buf) - 4, EXTCERT(ctype)) != 0 && strlen(buf) + 4 < sizeof(buf))
576 strcat(buf, EXTCERT(ctype));
577 rc = our_copy(buf, full_filename);
579 else /* if(SMHOLDERTYPE(ctype) == Container){ */
580 rc = add_file_to_container(ctype, full_filename, NULL);
581 if(rc == 0)
582 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
583 else
584 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
585 if(ps_global->smime->publiccertlist)
586 ps_global->smime->publiccertlist->data.renew = 1;
588 else
589 q_status_message(SM_ORDER, 1, 3, _("Problem unlocking key (not a certificate and/or wrong password)"));
590 } else if (ctype == CACert){
591 BIO *ins;
592 X509 *cert;
594 if((ins = BIO_new_file(full_filename, "r")) != NULL){
595 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
596 if(SMHOLDERTYPE(ctype) == Directory){
597 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
598 if(strcmp(buf + strlen(buf) - 4, ".crt") != 0 && strlen(buf) + 4 < sizeof(buf))
599 strcat(buf, EXTCERT(ctype));
601 rc = our_copy(buf, full_filename);
603 else /* if(SMHOLDERTYPE(ctype) == Container){ */
604 rc = add_file_to_container(ctype, full_filename, NULL);
605 if(rc == 0)
606 q_status_message(SM_ORDER, 1, 3, _("Certificate saved"));
607 else
608 q_status_message(SM_ORDER, 1, 3, _("Error saving certificate"));
609 X509_free(cert); /* not needed anymore */
611 else
612 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
613 BIO_free(ins);
615 renew_store();
616 } else { /* ctype == Public. save certificate, but first validate that it is one */
617 BIO *ins;
618 X509 *cert;
620 if((ins = BIO_new_file(full_filename, "r")) != NULL){
621 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
622 if(SMHOLDERTYPE(ctype) == Directory){
623 char **email = get_x509_subject_email(cert);
624 int i;
626 for(i = 0; email[i] != NULL; i++){
627 save_cert_for(email[i], cert, Public);
628 fs_give((void **)&email[i]);
630 fs_give((void **)email);
632 else /* if(SMHOLDERTYPE(ctype) == Container){ */
633 add_file_to_container(ctype, full_filename, NULL);
634 X509_free(cert);
635 if(ps_global->smime->publiccertlist)
636 ps_global->smime->publiccertlist->data.renew = 1;
638 else
639 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
640 BIO_free(ins);
643 if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1;
644 return 0;
647 /* itype: information type to add: 0 - public, 1 - private.
648 * Memory freed by caller
650 BIO *
651 print_private_key_information(char *email, int itype)
653 BIO *out;
654 PERSONAL_CERT *pc;
656 if(ps_global->smime == NULL
657 || ps_global->smime->personal_certs == NULL
658 || (itype != 0 && itype != 1))
659 return NULL;
661 for(pc = ps_global->smime->personal_certs;
662 pc != NULL && strcmp(pc->name, email) != 0; pc = pc->next);
663 if(pc->key == NULL
664 && !load_private_key(pc)
665 && ps_global->smime
666 && ps_global->smime->need_passphrase){
667 if (*pith_opt_smime_get_passphrase)
668 (*pith_opt_smime_get_passphrase)();
669 load_private_key(pc);
672 if(pc->key == NULL)
673 return NULL;
675 out = BIO_new(BIO_s_mem());
676 if(itype == 0) /* 0 means public */
677 EVP_PKEY_print_public(out, pc->key, 0, NULL);
678 else if (itype == 1) /* 1 means private */
679 EVP_PKEY_print_private(out, pc->key, 0, NULL);
681 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
682 forget_private_keys();
684 return out;
688 * Forget any cached private keys
690 static void
691 forget_private_keys(void)
693 PERSONAL_CERT *pcert;
694 size_t len;
695 volatile char *p;
697 dprint((9, "forget_private_keys()"));
698 if(ps_global->smime){
699 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
700 pcert;
701 pcert=pcert->next){
703 if(pcert->key){
704 EVP_PKEY_free(pcert->key);
705 pcert->key = NULL;
709 ps_global->smime->entered_passphrase = 0;
710 len = sizeof(ps_global->smime->passphrase);
711 p = ps_global->smime->passphrase;
713 while(len-- > 0)
714 *p++ = '\0';
718 /* modelled after signature_path in reply.c, but uses home dir instead of the
719 * directory where the .pinerc is located, since according to documentation,
720 * the .alpine-smime directories are subdirectories of the home directory
722 int smime_path(char *rpath, char *fpath, size_t len)
724 *fpath = '\0';
725 if(rpath && *rpath){
726 size_t spl = strlen(rpath);
728 if(IS_REMOTE(rpath)){
729 if(spl < len - 1)
730 strncpy(fpath, rpath, len-1);
731 fpath[len-1] = '\0';
733 else if(is_absolute_path(rpath)){
734 strncpy(fpath, rpath, len-1);
735 fpath[len-1] = '\0';
736 fnexpand(fpath, len);
738 else if(ps_global->VAR_OPER_DIR){
739 if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
740 build_path(fpath, ps_global->VAR_OPER_DIR, rpath, len);
742 else if(ps_global->home_dir){
743 if(strlen(ps_global->home_dir) + spl < len - 1)
744 build_path(fpath, ps_global->home_dir, rpath, len);
747 return fpath && *fpath ? 1 : 0;
753 * taken from openssl/apps/app_rand.c
755 static int
756 app_RAND_load_file(const char *file)
758 char buffer[200];
760 if(file == NULL)
761 file = RAND_file_name(buffer, sizeof buffer);
762 else if(RAND_egd(file) > 0){
763 /* we try if the given filename is an EGD socket.
764 if it is, we don't write anything back to the file. */
765 egdsocket = 1;
766 return 1;
769 if(file == NULL || !RAND_load_file(file, -1)){
770 if(RAND_status() == 0){
771 dprint((1, "unable to load 'random state'\n"));
772 dprint((1, "This means that the random number generator has not been seeded\n"));
773 dprint((1, "with much random data.\n"));
776 return 0;
779 seeded = 1;
780 return 1;
785 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
787 static void
788 openssl_extra_randomness(void)
790 #if !defined(WIN32)
791 int fd;
792 unsigned long i;
793 char *tf = NULL;
794 char tmp[MAXPATH];
795 struct stat sbuf;
796 /* if system doesn't have /dev/urandom */
797 if(stat ("/dev/urandom", &sbuf)){
798 tmp[0] = '0';
799 tf = temp_nam(NULL, NULL);
800 if(tf){
801 strncpy(tmp, tf, sizeof(tmp));
802 tmp[sizeof(tmp)-1] = '\0';
803 fs_give((void **) &tf);
806 if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
807 i = (unsigned long) tmp;
808 else{
809 unlink(tmp); /* don't need the file */
810 fstat(fd, &sbuf); /* get information about the file */
811 i = sbuf.st_ino; /* remember its inode */
812 close(fd); /* or its descriptor */
814 /* not great but it'll have to do */
815 snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
816 tcp_serverhost (),i,
817 (unsigned long) (time (0) ^ gethostid ()),
818 (unsigned long) getpid ());
819 RAND_seed(tmp, strlen(tmp));
821 #endif
825 /* taken from openssl/apps/app_rand.c */
826 static int
827 app_RAND_write_file(const char *file)
829 char buffer[200];
831 if(egdsocket || !seeded)
833 * If we did not manage to read the seed file,
834 * we should not write a low-entropy seed file back --
835 * it would suppress a crucial warning the next time
836 * we want to use it.
838 return 0;
840 if(file == NULL)
841 file = RAND_file_name(buffer, sizeof buffer);
843 if(file == NULL || !RAND_write_file(file)){
844 dprint((1, "unable to write 'random state'\n"));
845 return 0;
848 return 1;
851 CertList *
852 certlist_from_personal_certs(PERSONAL_CERT *pc)
854 CertList *cl;
856 if(pc == NULL)
857 return NULL;
859 cl = fs_get(sizeof(CertList));
860 memset((void *)cl, 0, sizeof(CertList));
861 cl->name = cpystr(pc->name);
862 cl->next = certlist_from_personal_certs(pc->next);
864 return cl;
868 void
869 renew_cert_data(CertList **data, WhichCerts ctype)
871 smime_init();
872 if(ctype == Private){
873 if(data){
874 PERSONAL_CERT *pc = (PERSONAL_CERT *)ps_global->smime->personal_certs;
875 if(*data)
876 free_certlist(data);
877 free_personal_certs(&pc);
878 setup_privatekey_storage();
879 *data = certlist_from_personal_certs((PERSONAL_CERT *)ps_global->smime->personal_certs);
880 if(data && *data)
881 RENEWCERT(*data) = 0;
882 ps_global->smime->privatecertlist = *data;
884 if(ps_global->smime->privatecertlist)
885 RENEWCERT(ps_global->smime->privatecertlist) = 0;
886 } else {
887 X509_LOOKUP *lookup = NULL;
888 X509_STORE *store = NULL;
890 if((store = X509_STORE_new()) != NULL)
891 if((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL){
892 X509_STORE_free(store);
893 store = NULL;
895 else{
896 free_certlist(data);
897 if(SMHOLDERTYPE(ctype) == Directory)
898 add_certs_in_dir(lookup, PATHCERTDIR(ctype), EXTCERT(ctype), data);
899 else /* if(SMHOLDERTYPE(ctype) == Container) */
900 *data = mem_to_certlist(CONTENTCERTLIST(ctype), ctype);
901 if(data && *data)
902 RENEWCERT(*data) = 0;
903 if(ctype == Public)
904 ps_global->smime->publiccertlist = *data;
905 else
906 ps_global->smime->cacertlist = *data;
911 void
912 smime_reinit(void)
914 smime_deinit();
915 smime_init();
918 /* Installed as an atexit() handler to save the random data */
919 void
920 smime_deinit(void)
922 dprint((9, "smime_deinit()"));
923 app_RAND_write_file(NULL);
924 free_smime_struct(&ps_global->smime);
927 /* we renew the store when it has changed */
928 void renew_store(void)
930 if(ps_global->smime->inited){
931 if(s_cert_store != NULL)
932 X509_STORE_free(s_cert_store);
933 s_cert_store = get_ca_store();
937 /* Initialise openssl stuff if needed */
938 void
939 smime_init(void)
941 if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
943 dprint((9, "smime_init()"));
944 if(!ps_global->smime)
945 ps_global->smime = new_smime_struct();
947 setup_storage_locations();
949 s_cert_store = get_ca_store();
951 OpenSSL_add_all_algorithms();
952 ERR_load_crypto_strings();
954 app_RAND_load_file(NULL);
955 openssl_extra_randomness();
956 ps_global->smime->inited = 1;
959 ERR_clear_error();
963 /* validate a certificate. Return value : 0 for no error, -1 for error.
964 * In the latter case, set the openssl smime error in *error.
966 int smime_validate_cert(X509 *cert, long *error)
968 X509_STORE_CTX *csc;
970 ERR_clear_error();
971 *error = 0;
972 if((csc = X509_STORE_CTX_new()) != NULL){
973 X509_STORE_set_flags(s_cert_store, 0);
974 if(X509_STORE_CTX_init(csc,s_cert_store,cert,NULL)
975 && X509_verify_cert(csc) <= 0)
976 *error = X509_STORE_CTX_get_error(csc);
977 X509_STORE_CTX_free(csc);
979 return *error ? -1 : 0;
982 PERSONAL_CERT *
983 get_personal_certs(char *path)
985 PERSONAL_CERT *result = NULL;
986 char buf2[MAXPATH];
987 struct dirent *d;
988 DIR *dirp;
990 ps_global->smime->privatepath = cpystr(path);
991 dirp = opendir(path);
992 if(dirp){
993 while((d=readdir(dirp)) != NULL){
994 X509 *cert;
995 size_t ll;
997 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, ".key")){
999 /* copy file name to temp buffer */
1000 strncpy(buf2, d->d_name, sizeof(buf2)-1);
1001 buf2[sizeof(buf2)-1] = '\0';
1002 /* chop off ".key" trailier */
1003 buf2[strlen(buf2)-4] = 0;
1004 /* Look for certificate */
1005 cert = get_cert_for(buf2, Public);
1007 if(cert){
1008 PERSONAL_CERT *pc;
1010 /* create a new PERSONAL_CERT, fill it in */
1012 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1013 pc->cert = cert;
1014 pc->name = cpystr(buf2);
1016 /* Try to load the key with an empty password */
1017 pc->key = load_key(pc, "");
1019 pc->next = result;
1020 result = pc;
1024 closedir(dirp);
1026 return result;
1030 void
1031 setup_privatekey_storage(void)
1033 char path[MAXPATH+1], *contents;
1034 int privatekeycontainer = 0;
1036 /* private keys in a container */
1037 if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
1039 privatekeycontainer = 1;
1040 contents = NULL;
1041 path[0] = '\0';
1042 if(!smime_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
1043 privatekeycontainer = 0;
1045 if(privatekeycontainer && !IS_REMOTE(path)
1046 && ps_global->VAR_OPER_DIR
1047 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1048 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1049 /* TRANSLATORS: First arg is the directory name, second is
1050 the file user wants to read but can't. */
1051 _("Can't read file outside %s: %s"),
1052 ps_global->VAR_OPER_DIR, path);
1053 privatekeycontainer = 0;
1056 if(privatekeycontainer
1057 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1058 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1060 !(contents = read_file(path, READ_FROM_LOCALE)))
1061 privatekeycontainer = 0;
1064 if(privatekeycontainer && path[0]){
1065 ps_global->smime->privatetype = Container;
1066 ps_global->smime->privatepath = cpystr(path);
1068 if(contents){
1069 ps_global->smime->privatecontent = contents;
1070 ps_global->smime->personal_certs = mem_to_personal_certs(contents);
1075 /* private keys in a directory of files */
1076 if(!privatekeycontainer){
1077 PERSONAL_CERT *result = NULL;
1079 ps_global->smime->privatetype = Directory;
1081 path[0] = '\0';
1082 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1083 && !IS_REMOTE(path)))
1084 ps_global->smime->privatetype = Nada;
1085 else if(can_access(path, ACCESS_EXISTS)){
1086 if(our_mkpath(path, 0700)){
1087 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1088 ps_global->smime->privatetype = Nada;
1092 if(ps_global->smime->privatetype == Directory)
1093 ps_global->smime->personal_certs = get_personal_certs(path);
1099 static void
1100 setup_storage_locations(void)
1102 int publiccertcontainer = 0, cacertcontainer = 0;
1103 char path[MAXPATH+1], *contents;
1105 if(!ps_global->smime)
1106 return;
1108 #ifdef APPLEKEYCHAIN
1109 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1110 ps_global->smime->publictype = Keychain;
1112 else{
1113 #endif /* APPLEKEYCHAIN */
1114 /* Public certificates in a container */
1115 if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
1117 publiccertcontainer = 1;
1118 contents = NULL;
1119 path[0] = '\0';
1120 if(!smime_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
1121 publiccertcontainer = 0;
1123 if(publiccertcontainer && !IS_REMOTE(path)
1124 && ps_global->VAR_OPER_DIR
1125 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1126 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1127 /* TRANSLATORS: First arg is the directory name, second is
1128 the file user wants to read but can't. */
1129 _("Can't read file outside %s: %s"),
1130 ps_global->VAR_OPER_DIR, path);
1131 publiccertcontainer = 0;
1134 if(publiccertcontainer
1135 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1136 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1138 !(contents = read_file(path, READ_FROM_LOCALE)))
1139 publiccertcontainer = 0;
1142 if(publiccertcontainer && path[0]){
1143 ps_global->smime->publictype = Container;
1144 ps_global->smime->publicpath = cpystr(path);
1146 if(contents){
1147 ps_global->smime->publiccontent = contents;
1148 ps_global->smime->publiccertlist = mem_to_certlist(contents, Public);
1153 /* Public certificates in a directory of files */
1154 if(!publiccertcontainer){
1155 ps_global->smime->publictype = Directory;
1157 path[0] = '\0';
1158 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1159 && !IS_REMOTE(path)))
1160 ps_global->smime->publictype = Nada;
1161 else if(can_access(path, ACCESS_EXISTS)){
1162 if(our_mkpath(path, 0700)){
1163 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1164 ps_global->smime->publictype = Nada;
1168 if(ps_global->smime->publictype == Directory)
1169 ps_global->smime->publicpath = cpystr(path);
1172 #ifdef APPLEKEYCHAIN
1174 #endif /* APPLEKEYCHAIN */
1176 setup_privatekey_storage();
1178 /* extra cacerts in a container */
1179 if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
1181 cacertcontainer = 1;
1182 contents = NULL;
1183 path[0] = '\0';
1184 if(!smime_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
1185 cacertcontainer = 0;
1187 if(cacertcontainer && !IS_REMOTE(path)
1188 && ps_global->VAR_OPER_DIR
1189 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1190 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1191 /* TRANSLATORS: First arg is the directory name, second is
1192 the file user wants to read but can't. */
1193 _("Can't read file outside %s: %s"),
1194 ps_global->VAR_OPER_DIR, path);
1195 cacertcontainer = 0;
1198 if(cacertcontainer
1199 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1200 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1202 !(contents = read_file(path, READ_FROM_LOCALE)))
1203 cacertcontainer = 0;
1206 if(cacertcontainer && path[0]){
1207 ps_global->smime->catype = Container;
1208 ps_global->smime->capath = cpystr(path);
1209 ps_global->smime->cacontent = contents;
1210 if(contents)
1211 ps_global->smime->cacertlist = mem_to_certlist(contents, CACert);
1215 if(!cacertcontainer){
1216 ps_global->smime->catype = Directory;
1218 path[0] = '\0';
1219 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1220 && !IS_REMOTE(path)))
1221 ps_global->smime->catype = Nada;
1222 else if(can_access(path, ACCESS_EXISTS)){
1223 if(our_mkpath(path, 0700)){
1224 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1225 ps_global->smime->catype = Nada;
1229 if(ps_global->smime->catype == Directory)
1230 ps_global->smime->capath = cpystr(path);
1236 copy_publiccert_dir_to_container(void)
1238 return(copy_dir_to_container(Public, NULL));
1243 copy_publiccert_container_to_dir(void)
1245 return(copy_container_to_dir(Public));
1250 copy_privatecert_dir_to_container(void)
1252 return(copy_dir_to_container(Private, NULL));
1257 copy_privatecert_container_to_dir(void)
1259 return(copy_container_to_dir(Private));
1264 copy_cacert_dir_to_container(void)
1266 return(copy_dir_to_container(CACert, NULL));
1271 copy_cacert_container_to_dir(void)
1273 return(copy_container_to_dir(CACert));
1276 /* Add the contents of a file to a container. Do not check the content
1277 * of the file, just add it using the format for that container. The
1278 * caller must check the format, so that there is no data corruption
1279 * in the future.
1280 * return value: 0 - success,
1281 * != 0 - failure.
1284 add_file_to_container(WhichCerts ctype, char *fpath, char *altname)
1286 char *sep = (ctype == Public || ctype == Private)
1287 ? EMAILADDRLEADER : CACERTSTORELEADER;
1288 char *content = ctype == Public ? ps_global->smime->publiccontent
1289 : (ctype == Private ? ps_global->smime->privatecontent
1290 : ps_global->smime->cacontent);
1291 char *name;
1292 char *s, c;
1293 struct stat sbuf;
1294 STORE_S *in = NULL;
1295 int rv = -1; /* assume error */
1297 if(our_stat(fpath, &sbuf) < 0
1298 || (in = so_get(FileStar, fpath, READ_ACCESS | READ_FROM_LOCALE)) == NULL)
1299 goto endadd;
1301 if(altname != NULL)
1302 name = altname;
1303 else if((name = strrchr(fpath, '/')) != NULL){
1304 size_t ll;
1305 if((ll = strlen(++name)) > 4 && strucmp(name + ll - 4, EXTCERT(ctype)) == 0)
1306 name[ll-strlen(EXTCERT(ctype))] = '\0';
1308 else
1309 goto endadd;
1311 if(content){
1312 fs_resize((void **)&content, strlen(content) + strlen(sep) + strlen(name) + sbuf.st_size + 3); /* 2 = \n + \n + \0*/
1313 s = content;
1314 content += strlen(content);
1316 else{
1317 s = content = fs_get(strlen(sep) + strlen(name) + sbuf.st_size + 1); /* 2 = \n + \0 */
1318 *content = '\0';
1320 strncat(content, sep, strlen(sep));
1321 strncat(content, name, strlen(name));
1322 content += strlen(content);
1323 *content++ = '\n';
1325 while(so_readc(&c, in))
1326 *content++ = c;
1327 *content = '\0';
1329 switch(ctype){
1330 case Private: ps_global->smime->privatecontent = s; break;
1331 case Public : ps_global->smime->publiccontent = s; break;
1332 case CACert : ps_global->smime->cacontent = s; break;
1333 default : break;
1336 rv = copy_dir_to_container(ctype, s);
1338 endadd:
1339 if(in) so_give(&in);
1341 return rv;
1346 * returns 0 on success, -1 on failure
1347 * contents is an argument which tells this function to write the value
1348 * of this variable instead of reading the contents of the directory.
1349 * If the var contents is not null use its value as the value of the
1350 * container.
1353 copy_dir_to_container(WhichCerts which, char *contents)
1355 int ret = 0;
1356 BIO *bio_out = NULL, *bio_in = NULL;
1357 char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
1358 char *tempfile = NULL;
1359 DIR *dirp;
1360 struct dirent *d;
1361 REMDATA_S *rd = NULL;
1362 char *configdir = NULL;
1363 char *configpath = NULL;
1364 char *filesuffix = NULL;
1366 dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1367 smime_init();
1369 srcpath[0] = '\0';
1370 dstpath[0] = '\0';
1371 file[0] = '\0';
1372 emailaddr[0] = '\0';
1374 if(which == Public){
1375 configdir = ps_global->VAR_PUBLICCERT_DIR;
1376 configpath = ps_global->smime->publicpath;
1377 filesuffix = ".crt";
1379 else if(which == Private){
1380 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1381 configpath = ps_global->smime->privatepath;
1382 filesuffix = ".key";
1384 else if(which == CACert){
1385 configdir = ps_global->VAR_CACERT_DIR;
1386 configpath = ps_global->smime->capath;
1387 filesuffix = ".crt";
1390 if(!(configdir && configdir[0])){
1391 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1392 return -1;
1395 if(!(configpath && configpath[0])){
1396 #ifdef APPLEKEYCHAIN
1397 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1398 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1399 return -1;
1401 #endif /* APPLEKEYCHAIN */
1402 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1403 return -1;
1406 if(!(filesuffix && strlen(filesuffix) == 4)){
1407 return -1;
1412 * If there is a legit directory to read from set up the
1413 * container file to write to.
1415 if(smime_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
1417 if(IS_REMOTE(configpath)){
1418 rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
1419 NULL, "Error: ",
1420 _("Can't access remote smime configuration."));
1421 if(!rd)
1422 return -1;
1424 (void) rd_read_metadata(rd);
1426 if(rd->access == MaybeRorW){
1427 if(rd->read_status == 'R')
1428 rd->access = ReadOnly;
1429 else
1430 rd->access = ReadWrite;
1433 if(rd->access != NoExists){
1435 rd_check_remvalid(rd, 1L);
1438 * If the cached info says it is readonly but
1439 * it looks like it's been fixed now, change it to readwrite.
1441 if(rd->read_status == 'R'){
1442 rd_check_readonly_access(rd);
1443 if(rd->read_status == 'W'){
1444 rd->access = ReadWrite;
1445 rd->flags |= REM_OUTOFDATE;
1447 else
1448 rd->access = ReadOnly;
1452 if(rd->flags & REM_OUTOFDATE){
1453 if(rd_update_local(rd) != 0){
1455 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1456 rd_close_remdata(&rd);
1457 return -1;
1460 else
1461 rd_open_remote(rd);
1463 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1464 rd_close_remdata(&rd);
1465 return -1;
1468 rd->flags |= DO_REMTRIM;
1470 strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
1471 dstpath[sizeof(dstpath)-1] = '\0';
1473 else{
1474 strncpy(dstpath, configpath, sizeof(dstpath)-1);
1475 dstpath[sizeof(dstpath)-1] = '\0';
1479 * dstpath is either the local Container file or the local cache file
1480 * for the remote Container file.
1482 tempfile = tempfile_in_same_dir(dstpath, "az", NULL);
1486 * If there is a legit directory to read from and a tempfile
1487 * to write to we continue.
1489 if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
1491 if(contents != NULL){
1492 if(BIO_puts(bio_out, contents) < 0)
1493 ret = -1;
1495 else {
1496 if((dirp = opendir(srcpath)) != NULL){
1498 while((d=readdir(dirp)) && !ret){
1499 size_t ll;
1501 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, filesuffix)){
1503 /* copy file name to temp buffer */
1504 strncpy(emailaddr, d->d_name, sizeof(emailaddr)-1);
1505 emailaddr[sizeof(emailaddr)-1] = '\0';
1506 /* chop off suffix trailier */
1507 emailaddr[strlen(emailaddr)-4] = 0;
1510 * This is the separator between the contents of
1511 * different files.
1513 if(which == CACert){
1514 if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
1515 && (BIO_puts(bio_out, emailaddr) > 0)
1516 && (BIO_puts(bio_out, "\n") > 0)))
1517 ret = -1;
1519 else{
1520 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1521 && (BIO_puts(bio_out, emailaddr) > 0)
1522 && (BIO_puts(bio_out, "\n") > 0)))
1523 ret = -1;
1526 /* read then write contents of file */
1527 build_path(file, srcpath, d->d_name, sizeof(file));
1528 if(!(bio_in = BIO_new_file(file, "r")))
1529 ret = -1;
1531 if(!ret){
1532 int good_stuff = 0;
1534 while(BIO_gets(bio_in, line, sizeof(line)) > 0){
1535 if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
1536 good_stuff = 1;
1538 if(good_stuff)
1539 BIO_puts(bio_out, line);
1541 if(strncmp("-----END", line, strlen("-----END")) == 0)
1542 good_stuff = 0;
1546 BIO_free(bio_in);
1550 closedir(dirp);
1554 BIO_free(bio_out);
1556 if(!ret){
1557 if(rename_file(tempfile, dstpath) < 0){
1558 q_status_message2(SM_ORDER, 3, 3,
1559 _("Can't rename %s to %s"), tempfile, dstpath);
1560 ret = -1;
1563 /* if the container is remote, copy it */
1564 if(!ret && IS_REMOTE(configpath)){
1565 int e;
1566 char datebuf[200];
1568 datebuf[0] = '\0';
1570 if((e = rd_update_remote(rd, datebuf)) != 0){
1571 if(e == -1){
1572 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1573 _("Error opening temporary smime file %s: %s"),
1574 rd->lf, error_description(errno));
1575 dprint((1,
1576 "write_remote_smime: error opening temp file %s\n",
1577 rd->lf ? rd->lf : "?"));
1579 else{
1580 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1581 _("Error copying to %s: %s"),
1582 rd->rn, error_description(errno));
1583 dprint((1,
1584 "write_remote_smime: error copying from %s to %s\n",
1585 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1588 q_status_message(SM_ORDER | SM_DING, 5, 5,
1589 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1591 else{
1592 rd_update_metadata(rd, datebuf);
1593 rd->read_status = 'W';
1596 rd_close_remdata(&rd);
1601 if(tempfile)
1602 fs_give((void **) &tempfile);
1604 return ret;
1609 * returns 0 on success, -1 on failure
1612 copy_container_to_dir(WhichCerts which)
1614 char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
1615 char iobuf[4096];
1616 char *contents = NULL;
1617 char *leader = NULL;
1618 char *filesuffix = NULL;
1619 char *configdir = NULL;
1620 char *configpath = NULL;
1621 char *tempfile = NULL;
1622 char *p, *q, *line, *name, *certtext, *save_p;
1623 int len;
1624 BIO *in, *out;
1626 dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1627 smime_init();
1629 path[0] = '\0';
1631 if(which == Public){
1632 leader = EMAILADDRLEADER;
1633 contents = ps_global->smime->publiccontent;
1634 configdir = ps_global->VAR_PUBLICCERT_DIR;
1635 configpath = ps_global->smime->publicpath;
1636 filesuffix = ".crt";
1637 if(!(configpath && configpath[0])){
1638 #ifdef APPLEKEYCHAIN
1639 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1640 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1641 return -1;
1643 #endif /* APPLEKEYCHAIN */
1644 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1645 return -1;
1648 fs_give((void **) &ps_global->smime->publicpath);
1650 path[0] = '\0';
1651 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1652 && !IS_REMOTE(path))){
1653 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1654 return -1;
1657 if(can_access(path, ACCESS_EXISTS)){
1658 if(our_mkpath(path, 0700)){
1659 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1660 return -1;
1664 ps_global->smime->publicpath = cpystr(path);
1665 configpath = ps_global->smime->publicpath;
1667 else if(which == Private){
1668 leader = EMAILADDRLEADER;
1669 contents = ps_global->smime->privatecontent;
1670 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1671 configpath = ps_global->smime->privatepath;
1672 filesuffix = ".key";
1673 if(!(configpath && configpath[0])){
1674 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1675 return -1;
1678 fs_give((void **) &ps_global->smime->privatepath);
1680 path[0] = '\0';
1681 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1682 && !IS_REMOTE(path))){
1683 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1684 return -1;
1687 if(can_access(path, ACCESS_EXISTS)){
1688 if(our_mkpath(path, 0700)){
1689 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1690 return -1;
1694 ps_global->smime->privatepath = cpystr(path);
1695 configpath = ps_global->smime->privatepath;
1697 else if(which == CACert){
1698 leader = CACERTSTORELEADER;
1699 contents = ps_global->smime->cacontent;
1700 configdir = ps_global->VAR_CACERT_DIR;
1701 configpath = ps_global->smime->capath;
1702 filesuffix = ".crt";
1703 if(!(configpath && configpath[0])){
1704 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1705 return -1;
1708 fs_give((void **) &ps_global->smime->capath);
1710 path[0] = '\0';
1711 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1712 && !IS_REMOTE(path))){
1713 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1714 return -1;
1717 if(can_access(path, ACCESS_EXISTS)){
1718 if(our_mkpath(path, 0700)){
1719 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1720 return -1;
1724 ps_global->smime->capath = cpystr(path);
1725 configpath = ps_global->smime->capath;
1728 if(!(configdir && configdir[0])){
1729 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1730 return -1;
1733 if(!(configpath && configpath[0])){
1734 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1735 return -1;
1738 if(!(filesuffix && strlen(filesuffix) == 4)){
1739 return -1;
1743 if(contents && *contents){
1744 for(p = contents; *p != '\0';){
1745 line = p;
1747 while(*p && *p != '\n')
1748 p++;
1750 save_p = NULL;
1751 if(*p == '\n'){
1752 save_p = p;
1753 *p++ = '\0';
1756 if(strncmp(leader, line, strlen(leader)) == 0){
1757 name = line + strlen(leader);
1758 certtext = p;
1759 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
1760 if((q = strstr(certtext, leader)) != NULL){
1761 p = q;
1763 else{ /* end of file */
1764 q = certtext + strlen(certtext);
1765 p = q;
1768 strncpy(buf, name, sizeof(buf)-5);
1769 buf[sizeof(buf)-5] = '\0';
1770 strncat(buf, filesuffix, 5);
1771 build_path(file, configpath, buf, sizeof(file));
1773 in = BIO_new_mem_buf(certtext, q-certtext);
1774 if(in){
1775 tempfile = tempfile_in_same_dir(file, "az", NULL);
1776 out = NULL;
1777 if(tempfile)
1778 out = BIO_new_file(tempfile, "w");
1780 if(out){
1781 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1782 BIO_write(out, iobuf, len);
1784 BIO_free(out);
1786 if(rename_file(tempfile, file) < 0){
1787 q_status_message2(SM_ORDER, 3, 3,
1788 _("Can't rename %s to %s"),
1789 tempfile, file);
1790 return -1;
1793 fs_give((void **) &tempfile);
1796 BIO_free(in);
1801 if(save_p)
1802 *save_p = '\n';
1806 return 0;
1810 #ifdef APPLEKEYCHAIN
1813 copy_publiccert_container_to_keychain(void)
1815 /* NOT IMPLEMNTED */
1816 return -1;
1820 copy_publiccert_keychain_to_container(void)
1822 /* NOT IMPLEMNTED */
1823 return -1;
1826 #endif /* APPLEKEYCHAIN */
1830 * Get a pointer to a string describing the most recent OpenSSL error.
1831 * It's statically allocated, so don't change or attempt to free it.
1833 static const char *
1834 openssl_error_string(void)
1836 char *errs;
1837 const char *data = NULL;
1838 long errn;
1840 errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
1841 errs = (char*) ERR_reason_error_string(errn);
1843 if(errs)
1844 return errs;
1845 else if(data)
1846 return data;
1848 return "unknown error";
1852 /* Return true if the body looks like a PKCS7 object */
1854 is_pkcs7_body(BODY *body)
1856 int result;
1858 result = body->type==TYPEAPPLICATION &&
1859 body->subtype &&
1860 (strucmp(body->subtype,"pkcs7-mime")==0 ||
1861 strucmp(body->subtype,"x-pkcs7-mime")==0 ||
1862 strucmp(body->subtype,"pkcs7-signature")==0 ||
1863 strucmp(body->subtype,"x-pkcs7-signature")==0);
1865 return result;
1869 #ifdef notdef
1871 * Somewhat useful debug utility to dump the contents of a BIO to a file.
1872 * Note that a memory BIO will have its contents eliminated after they
1873 * are read so this will break the next step.
1875 static void
1876 dump_bio_to_file(BIO *in, char *filename)
1878 char iobuf[4096];
1879 int len;
1880 BIO *out;
1882 out = BIO_new_file(filename, "w");
1884 if(out){
1885 if(BIO_method_type(in) != BIO_TYPE_MEM)
1886 BIO_reset(in);
1888 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
1889 BIO_write(out, iobuf, len);
1891 BIO_free(out);
1894 BIO_reset(in);
1896 #endif
1900 * Recursively stash a pointer to the decrypted data in our
1901 * manufactured body.
1903 static void
1904 create_local_cache(char *h, char *base, BODY *b)
1906 if(b->type==TYPEMULTIPART){
1907 PART *p;
1909 #if 0
1910 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
1911 #else
1913 * We don't really want to copy the real body contents. It shouldn't be
1914 * used, and in the case of a message with attachments, we'll be
1915 * duplicating the files multiple times.
1917 cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
1918 #endif
1920 for(p=b->nested.part; p; p=p->next)
1921 create_local_cache(h, base, (BODY*) p);
1923 else{
1924 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
1925 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
1930 static long
1931 rfc822_output_func(void *b, char *string)
1933 BIO *bio = (BIO *) b;
1935 return(string ? *string ? (BIO_puts(bio, string) > 0 ? 1L : 0L)
1936 : (BIO_puts(bio, string) >= 0 ? 1L : 0L)
1937 : 0L);
1942 * Attempt to load the private key for the given PERSONAL_CERT.
1943 * This sets the appropriate passphrase globals in order to
1944 * interact with the user correctly.
1946 static int
1947 load_private_key(PERSONAL_CERT *pcert)
1949 if(!pcert->key){
1951 /* Try empty password by default */
1952 char *password = "";
1954 if(ps_global->smime
1955 && (ps_global->smime->need_passphrase
1956 || ps_global->smime->entered_passphrase)){
1957 /* We've already been in here and discovered we need a different password */
1959 if(ps_global->smime->entered_passphrase)
1960 password = (char *) ps_global->smime->passphrase; /* already entered */
1961 else
1962 return 0;
1965 ERR_clear_error();
1967 if(!(pcert->key = load_key(pcert, password))){
1968 long err = ERR_get_error();
1970 /* Couldn't load key... */
1972 if(ps_global->smime && ps_global->smime->entered_passphrase){
1974 /* The user got the password wrong maybe? */
1976 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
1977 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
1978 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
1979 else
1980 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
1982 /* This passphrase is no good; forget it */
1983 ps_global->smime->entered_passphrase = 0;
1986 if(ps_global->smime){
1987 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
1988 ps_global->smime->need_passphrase = 1;
1989 if(ps_global->smime->passphrase_emailaddr){
1990 int i;
1991 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
1992 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
1993 fs_give((void **) ps_global->smime->passphrase_emailaddr);
1996 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
1999 return 0;
2001 else{
2002 /* This key will be cached, so we won't be called again */
2003 if(ps_global->smime){
2004 ps_global->smime->entered_passphrase = 0;
2005 ps_global->smime->need_passphrase = 0;
2009 return 1;
2012 return 0;
2016 static void
2017 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename)
2019 b->type = TYPEAPPLICATION;
2020 b->subtype = cpystr(type);
2021 b->encoding = ENCBINARY;
2022 b->description = cpystr(description);
2024 b->disposition.type = cpystr("attachment");
2025 set_parameter(&b->disposition.parameter, "filename", filename);
2027 set_parameter(&b->parameter, "name", filename);
2032 * Look for a personal certificate matching the
2033 * given address
2035 PERSONAL_CERT *
2036 match_personal_cert_to_email(ADDRESS *a)
2038 PERSONAL_CERT *pcert = NULL;
2039 char buf[MAXPATH];
2040 char **email;
2041 int i, done;
2043 if(!a || !a->mailbox || !a->host)
2044 return NULL;
2046 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2048 if(ps_global->smime){
2049 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
2050 pcert;
2051 pcert=pcert->next){
2053 if(!pcert->cert)
2054 continue;
2056 email = get_x509_subject_email(pcert->cert);
2058 done = 0;
2059 if(email != NULL){
2060 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
2061 if(email[i] != NULL) done++;
2062 for(i = 0; email[i] != NULL; i++)
2063 fs_give((void **)&email[i]);
2064 fs_give((void **)email);
2067 if(done > 0)
2068 break;
2072 return pcert;
2077 * Look for a personal certificate matching the from
2078 * (or reply_to? in the given envelope)
2080 PERSONAL_CERT *
2081 match_personal_cert(ENVELOPE *env)
2083 PERSONAL_CERT *pcert;
2085 pcert = match_personal_cert_to_email(env->reply_to);
2086 if(!pcert)
2087 pcert = match_personal_cert_to_email(env->from);
2089 return pcert;
2094 * Flatten the given body into its MIME representation.
2095 * Return the result in a BIO.
2097 static BIO *
2098 body_to_bio(BODY *body)
2100 BIO *bio = NULL;
2101 int len;
2103 bio = BIO_new(BIO_s_mem());
2104 if(!bio)
2105 return NULL;
2107 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
2108 pine_write_body_header(body, rfc822_output_func, bio);
2109 pine_rfc822_output_body(body, rfc822_output_func, bio);
2112 * Now need to truncate by two characters since the above
2113 * appends CRLF.
2115 if((len=BIO_ctrl_pending(bio)) > 1){
2116 BUF_MEM *biobuf = NULL;
2118 BIO_get_mem_ptr(bio, &biobuf);
2119 if(biobuf){
2120 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
2124 return bio;
2128 static BIO *
2129 bio_from_store(STORE_S *store)
2131 BIO *ret = NULL;
2133 if(store && store->src == BioType && store->txt){
2134 ret = (BIO *) store->txt;
2137 return(ret);
2141 * Encrypt file; given a path (char *) fp, replace the file
2142 * by an encrypted version of it. If (char *) text is not null, then
2143 * replace the text of (char *) fp by the encrypted version of (char *) text.
2144 * certpath is the FULL path to the file containing the certificate used for
2145 * encryption.
2148 encrypt_file(char *fp, char *text, PERSONAL_CERT *pc)
2150 const EVP_CIPHER *cipher = NULL;
2151 STACK_OF(X509) *encerts = NULL;
2152 X509 *cert;
2153 BIO *out;
2154 PKCS7 *p7 = NULL;
2155 FILE *fpp;
2156 int i, rv = 0;
2157 char *fname;
2158 char emailaddress[MAILTMPLEN];
2160 smime_init();
2161 cipher = EVP_aes_256_cbc();
2162 encerts = sk_X509_new_null();
2164 if(pc == NULL)
2165 goto end;
2167 sk_X509_push(encerts, X509_dup(pc->cert));
2169 if(text){
2170 if((out = BIO_new(BIO_s_mem())) == NULL)
2171 goto end;
2172 (void) BIO_reset(out);
2173 BIO_puts(out, text);
2175 else{
2176 if(!(out = BIO_new_file(fp, "rb")))
2177 goto end;
2179 BIO_read_filename(out, fp);
2182 if((p7 = PKCS7_encrypt(encerts, out, cipher, 0)) == NULL)
2183 goto end;
2184 BIO_set_close(out, BIO_CLOSE);
2185 BIO_free(out);
2186 if(!(out = BIO_new_file(fp, "w")))
2187 goto end;
2188 BIO_reset(out);
2189 rv = PEM_write_bio_PKCS7(out, p7);
2190 BIO_flush(out);
2192 end:
2193 BIO_free(out);
2194 PKCS7_free(p7);
2195 sk_X509_pop_free(encerts, X509_free);
2197 return rv;
2201 * Encrypt a message on the way out. Called from call_mailer in send.c
2202 * The body may be reallocated.
2205 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
2207 PKCS7 *p7 = NULL;
2208 BIO *in = NULL;
2209 BIO *out = NULL;
2210 const EVP_CIPHER *cipher = NULL;
2211 STACK_OF(X509) *encerts = NULL;
2212 STORE_S *outs = NULL;
2213 PINEFIELD *pf;
2214 ADDRESS *a;
2215 BODY *body = *bodyP;
2216 BODY *newBody = NULL;
2217 int result = 0;
2219 dprint((9, "encrypt_outgoing_message()"));
2220 smime_init();
2222 cipher = EVP_aes_256_cbc();
2224 encerts = sk_X509_new_null();
2226 /* Look for a certificate for each of the recipients */
2227 for(pf = header->local; pf && pf->name; pf = pf->next)
2228 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
2229 for(a=*pf->addr; a; a=a->next){
2230 X509 *cert;
2231 char buf[MAXPATH];
2233 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2235 cert = get_cert_for(buf, Public);
2236 if(cert)
2237 sk_X509_push(encerts,cert);
2238 else{
2239 q_status_message2(SM_ORDER, 1, 1,
2240 _("Unable to find certificate for <%s@%s>"),
2241 a->mailbox, a->host);
2242 goto end;
2248 in = body_to_bio(body);
2250 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
2252 outs = so_get(BioType, NULL, EDIT_ACCESS);
2253 out = bio_from_store(outs);
2255 i2d_PKCS7_bio(out, p7);
2256 (void) BIO_flush(out);
2258 so_seek(outs, 0, SEEK_SET);
2260 newBody = mail_newbody();
2262 newBody->type = TYPEAPPLICATION;
2263 newBody->subtype = cpystr("pkcs7-mime");
2264 newBody->encoding = ENCBINARY;
2266 newBody->disposition.type = cpystr("attachment");
2267 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
2269 newBody->description = cpystr("S/MIME Encrypted Message");
2270 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
2271 set_parameter(&newBody->parameter, "name", "smime.p7m");
2273 newBody->contents.text.data = (unsigned char *) outs;
2275 *bodyP = newBody;
2277 result = 1;
2279 end:
2281 BIO_free(in);
2282 PKCS7_free(p7);
2283 sk_X509_pop_free(encerts, X509_free);
2285 dprint((9, "encrypt_outgoing_message returns %d", result));
2286 return result;
2291 Get (and decode) the body of the given section of msg
2293 static STORE_S*
2294 get_part_contents(long msgno, const char *section)
2296 long len;
2297 gf_io_t pc;
2298 STORE_S *store = NULL;
2299 char *err;
2301 store = so_get(CharStar, NULL, EDIT_ACCESS);
2302 if(store){
2303 gf_set_so_writec(&pc,store);
2305 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
2307 gf_clear_so_writec(store);
2309 so_seek(store, 0, SEEK_SET);
2311 if(err)
2312 so_give(&store);
2315 return store;
2319 static PKCS7 *
2320 get_pkcs7_from_part(long msgno,const char *section)
2322 STORE_S *store = NULL;
2323 PKCS7 *p7 = NULL;
2324 BIO *in = NULL;
2326 store = get_part_contents(msgno, section);
2328 if(store){
2329 if(store->src == CharStar){
2330 int len;
2333 * We're reaching inside the STORE_S structure. We should
2334 * probably have a way to get the length, instead.
2336 len = (int) (store->eod - store->dp);
2337 in = BIO_new_mem_buf(store->txt, len);
2339 else{ /* just copy it */
2340 unsigned char c;
2342 in = BIO_new(BIO_s_mem());
2343 (void) BIO_reset(in);
2345 so_seek(store, 0L, 0);
2346 while(so_readc(&c, store)){
2347 BIO_write(in, &c, 1);
2351 if(in){
2352 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2353 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
2354 /* error */
2357 BIO_free(in);
2360 so_give(&store);
2363 return p7;
2366 int same_cert(X509 *x, X509 *cert)
2368 char bufcert[256], bufx[256];
2369 int rv = 0;
2371 get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert));
2372 get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx));
2373 if(strcmp(bufx, bufcert) == 0)
2374 rv = 1;
2376 return rv;
2380 /* extract and save certificates from a PKCS7 package. The ctype variable
2381 * tells us if we want to extract it to a public/ or a ca/ directory. The
2382 * later makes sense only for recoverable errors (errors that can be fixed
2383 * by saving to the ca/ directory before we verify the signature).
2384 * Return value:
2385 * 0 - no errors (in public/) no need to try again,
2386 * or validated self signed certificate (in ca/)
2387 * < 0 - certificate error is not recoverable, don't even think about it.
2390 int smime_extract_and_save_cert(PKCS7 *p7)
2392 STACK_OF(X509) *signers;
2393 X509 *x, *cert;
2394 char **email;
2395 int i, j;
2396 long error;
2398 if((signers = PKCS7_get0_signers(p7, NULL, 0)) == NULL)
2399 return -1;
2401 for(i = 0; i < sk_X509_num(signers); i++){
2402 if((x = sk_X509_value(signers,i)) == NULL)
2403 continue;
2405 if((email = get_x509_subject_email(x)) != NULL){
2406 for(j = 0; email[j] != NULL; j++){
2407 if((cert = get_cert_for(email[j], Public)) == NULL
2408 || same_cert(x, cert) == 0)
2409 save_cert_for(email[j], x, Public);
2410 X509_free(cert);
2411 fs_give((void **) &email[i]);
2413 fs_give((void **) email);
2416 sk_X509_free(signers);
2418 return 0;
2422 * Try to verify a signature.
2424 * p7 - the pkcs7 object to verify
2425 * in - the plain data to verify (NULL if not detached)
2426 * out - BIO to which to write the opaque data
2427 * silent - if non zero, do not print errors, only print success.
2429 static int
2430 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
2432 STACK_OF(X509) *otherCerts = NULL;
2433 int result;
2434 const char *data;
2435 long err;
2437 if(!s_cert_store){
2438 if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
2439 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2441 return -1;
2444 smime_extract_and_save_cert(p7);
2446 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, 0);
2448 if(result){
2449 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
2451 else{
2452 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2454 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
2456 /* Retry verification so we can get the plain text */
2457 /* Might be better to reimplement PKCS7_verify here? */
2459 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY);
2461 if (!silent) q_status_message1(SM_ORDER | SM_DING, 3, 3,
2462 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
2465 return result;
2469 void
2470 free_smime_body_sparep(void **sparep)
2472 if(sparep && *sparep){
2473 PKCS7_free((PKCS7 *) (*sparep));
2474 *sparep = NULL;
2478 /* Big comment, explaining the mess that exists out there, and how we deal
2479 with it, and also how we solve the problems that are created this way.
2481 When Alpine sends a message, it constructs that message, computes the
2482 signature, but then it forgets the message it signed and reconstructs it
2483 again. Since it signs a message containing a notice about "mime aware
2484 tools", but it does not send that we do not include that in the part that
2485 is signed, and that takes care of much of the problems.
2487 Another problem is what is received from the servers. All servers tested
2488 seem to transmit the message that was signed intact and Alpine can check
2489 the signature correctly. That is not a problem. The problem arises when
2490 the message includes attachments. In this case different servers send
2491 different things, so it will be up to us to figure out what is the text
2492 that was actually signed. Confused? here is the story:
2494 When a message containing and attachment is sent by Alpine, UW-IMAP,
2495 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2496 that was sent by Alpine, but GMX.com, Exchange, and probably other servers
2497 add a trailing \r\n in the message, so when validating the signature,
2498 these messages will not validate. There are several things that can be
2499 done.
2501 1. Add a trailing \r\n to any message that contains attachments, sign that
2502 and send that. In this way, all messages will validate with all
2503 servers.
2505 2. Compatibility mode: If a message has an attachment, contains a trailing
2506 \r\n and does not validate (sent by an earlier version of Alpine),
2507 remove the trailing \r\n and try to revalidate again.
2509 3. We do not add \r\n to validate a message that we sent, because that
2510 would only work in Alpine, and not in any other client. That would not
2511 be a good thing to do.
2513 PART II
2515 Now we have to deal with encrypted and signed messages. The problem is that
2516 c-client makes all its pointers point to "on disk" content, but since we
2517 decrypted the data earlier, we have to make sure of two things. One is that we
2518 saved that data (so we do not have to decrypt it again) and second that we can
2519 use it.
2521 In order to save the data we use create_local_cache, so that we do not
2522 have to redecrypt the message. Once this is saved, c-client functions will
2523 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2528 * Given a multipart body of type multipart/signed, attempt to verify it.
2529 * Returns non-zero if the body was changed.
2531 static int
2532 do_detached_signature_verify(BODY *b, long msgno, char *section)
2534 PKCS7 *p7 = NULL;
2535 BIO *in = NULL;
2536 PART *p;
2537 int result, modified_the_body = 0;
2538 unsigned long mimelen, bodylen;
2539 char newSec[100], *mimetext, *bodytext;
2540 char *what_we_did;
2542 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"));
2543 smime_init();
2545 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
2547 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
2549 if(mimetext)
2550 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
2552 if (mimetext == NULL || bodytext == NULL)
2553 return modified_the_body;
2555 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
2557 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
2558 || (in = BIO_new(BIO_s_mem())) == NULL)
2559 return modified_the_body;
2561 (void) BIO_reset(in);
2562 BIO_write(in, mimetext, mimelen);
2563 BIO_write(in, bodytext, bodylen);
2565 /* Try compatibility with the past and check if this message
2566 * validates when we remove the last two characters. Silence
2567 * any failures first.
2569 if(((result = do_signature_verify(p7, in, NULL, 1)) == 0)
2570 && bodylen > 2
2571 && (strncmp(bodytext+bodylen-2,"\r\n", 2) == 0)){
2572 BUF_MEM *biobuf = NULL;
2574 BIO_get_mem_ptr(in, &biobuf);
2575 if(biobuf)
2576 BUF_MEM_grow(biobuf, mimelen + bodylen - 2);
2577 result = do_signature_verify(p7, in, NULL, 0);
2580 BIO_free(in);
2581 if(b->subtype)
2582 fs_give((void**) &b->subtype);
2584 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
2585 b->encoding = ENC8BIT;
2587 if(b->description)
2588 fs_give ((void**) &b->description);
2590 what_we_did = result ? _("This message was cryptographically signed.") :
2591 _("This message was cryptographically signed but the signature could not be verified.");
2593 b->description = cpystr(what_we_did);
2595 b->sparep = p7;
2597 p = b->nested.part;
2599 /* p is signed plaintext */
2600 if(p && p->next)
2601 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
2603 modified_the_body = 1;
2605 return modified_the_body;
2609 PERSONAL_CERT *
2610 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
2612 PERSONAL_CERT *x = NULL;
2614 if(ps_global->smime){
2615 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
2616 X509 *mine;
2618 mine = x->cert;
2620 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,mine->cert_info->issuer) &&
2621 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,mine->cert_info->serialNumber)){
2622 break;
2627 return x;
2631 static PERSONAL_CERT *
2632 find_certificate_matching_pkcs7(PKCS7 *p7)
2634 int i;
2635 STACK_OF(PKCS7_RECIP_INFO) *recips;
2636 PERSONAL_CERT *x = NULL;
2638 recips = p7->d.enveloped->recipientinfo;
2640 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
2641 PKCS7_RECIP_INFO *ri;
2643 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
2645 if((x=find_certificate_matching_recip_info(ri))!=0){
2646 break;
2650 return x;
2653 /* decrypt an encrypted file.
2654 Args: fp - the path to the encrypted file.
2655 rv - a code that tells the caller what happened inside the function
2656 pcert - a personal certificate that was used to encrypt this file
2657 Returns the decoded text allocated in a char *, whose memory must be
2658 freed by caller
2661 char *
2662 decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc)
2664 PKCS7 *p7 = NULL;
2665 char *text, *tmp, file_name[MAILTMPLEN];
2666 BIO *in = NULL, *out = NULL;
2667 EVP_PKEY *pkey = NULL;
2668 X509 *recip;
2669 int i, j;
2670 long unsigned int len;
2671 void *ret;
2673 smime_init();
2675 if(pc == NULL || (text = read_file(fp, 0)) == NULL)
2676 return NULL;
2678 tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1);
2679 for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0'
2680 && text[i] != '-'; j++, i++)
2681 tmp[j] = text[i];
2682 tmp[j] = '\0';
2684 ret = rfc822_base64(tmp, strlen(tmp), &len);
2686 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
2687 p7 = d2i_PKCS7_bio(in, NULL);
2688 BIO_free(in);
2691 if (text) fs_give((void **)&text);
2692 if (ret) fs_give((void **)&ret);
2694 if (rv) *rv = pc->key == NULL ? -1 : 1;
2696 out = BIO_new(BIO_s_mem());
2697 (void) BIO_reset(out);
2699 i = PKCS7_decrypt(p7, pc->key, pc->cert, out, 0);
2701 if(i == 0){
2702 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
2703 (char*) openssl_error_string());
2704 goto end;
2707 BIO_get_mem_data(out, &tmp);
2709 text = cpystr(tmp);
2710 BIO_free(out);
2712 end:
2713 PKCS7_free(p7);
2715 return text;
2719 * Try to decode (decrypt or verify a signature) a PKCS7 body
2720 * Returns non-zero if something was changed.
2722 static int
2723 do_decoding(BODY *b, long msgno, const char *section)
2725 int modified_the_body = 0;
2726 BIO *out = NULL;
2727 PKCS7 *p7 = NULL;
2728 X509 *recip = NULL;
2729 EVP_PKEY *key = NULL;
2730 PERSONAL_CERT *pcert = NULL;
2731 char *what_we_did = "";
2732 char null[1];
2734 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"));
2735 null[0] = '\0';
2736 smime_init();
2739 * Extract binary data from part to an in-memory store
2742 if(b->sparep){ /* already done */
2743 p7 = (PKCS7*) b->sparep;
2745 else{
2747 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
2748 if(!p7){
2749 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
2750 (char*) openssl_error_string());
2751 goto end;
2755 * Save the PKCS7 object for later dealings by the user interface.
2756 * It will be cleaned up when the body is garbage collected.
2758 b->sparep = p7;
2761 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
2763 if(PKCS7_type_is_signed(p7)){
2764 int sigok;
2766 out = BIO_new(BIO_s_mem());
2767 (void) BIO_reset(out);
2768 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
2770 sigok = do_signature_verify(p7, NULL, out, 0);
2772 what_we_did = sigok ? _("This message was cryptographically signed.") :
2773 _("This message was cryptographically signed but the signature could not be verified.");
2775 /* make sure it's null terminated */
2776 BIO_write(out, null, 1);
2778 else if(!PKCS7_type_is_enveloped(p7)){
2779 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
2780 goto end;
2782 else{ /* It *is* enveloped */
2783 int decrypt_result;
2785 what_we_did = _("This message was encrypted.");
2787 /* now need to find a cert that can decrypt this */
2788 pcert = find_certificate_matching_pkcs7(p7);
2790 if(!pcert){
2791 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
2792 goto end;
2795 recip = pcert->cert;
2797 if(!load_private_key(pcert)
2798 && ps_global->smime
2799 && ps_global->smime->need_passphrase
2800 && !ps_global->smime->already_auto_asked){
2801 /* Couldn't load key with blank password, ask user */
2802 ps_global->smime->already_auto_asked = 1;
2803 if(pith_opt_smime_get_passphrase){
2804 (*pith_opt_smime_get_passphrase)();
2805 load_private_key(pcert);
2809 key = pcert->key;
2810 if(!key)
2811 goto end;
2813 out = BIO_new(BIO_s_mem());
2814 (void) BIO_reset(out);
2815 BIO_puts(out, "MIME-Version: 1.0\r\n");
2817 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
2819 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
2820 forget_private_keys();
2822 if(!decrypt_result){
2823 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
2824 (char*) openssl_error_string());
2825 goto end;
2828 BIO_write(out, null, 1);
2832 * We've now produced a flattened MIME object in BIO out.
2833 * It needs to be turned back into a BODY.
2836 if(out){
2837 BODY *body;
2838 ENVELOPE *env;
2839 char *h = NULL;
2840 char *bstart;
2841 STRING s;
2842 BUF_MEM *bptr = NULL;
2844 BIO_get_mem_ptr(out, &bptr);
2845 if(bptr)
2846 h = bptr->data;
2848 /* look for start of body */
2849 bstart = strstr(h, "\r\n\r\n");
2851 if(!bstart){
2852 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
2854 else{
2855 bstart += 4; /* skip over CRLF*2 */
2857 INIT(&s, mail_string, bstart, strlen(bstart));
2858 rfc822_parse_msg_full(&env, &body, h, (bstart-h)-2, &s, BADHOST, 0, 0);
2859 mail_free_envelope(&env); /* Don't care about this */
2861 body->mime.offset = 0;
2862 body->mime.text.size = 0;
2864 * Now convert original body (application/pkcs7-mime)
2865 * to a multipart body with one sub-part (the decrypted body).
2866 * Note that the sub-part may also be multipart!
2869 b->type = TYPEMULTIPART;
2870 if(b->subtype)
2871 fs_give((void**) &b->subtype);
2874 * This subtype is used in mailview.c to annotate the display of
2875 * encrypted or signed messages. We know for sure then that it's a PKCS7
2876 * part because the sparep field is set to the PKCS7 object (see above).
2878 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
2879 b->encoding = ENC8BIT;
2881 if(b->description)
2882 fs_give((void**) &b->description);
2884 b->description = cpystr(what_we_did);
2886 if(b->disposition.type)
2887 fs_give((void **) &b->disposition.type);
2889 if(b->contents.text.data)
2890 fs_give((void **) &b->contents.text.data);
2892 if(b->parameter)
2893 mail_free_body_parameter(&b->parameter);
2895 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
2896 b->nested.part = fs_get(sizeof(PART));
2897 b->nested.part->body = *body;
2898 b->nested.part->next = NULL;
2900 fs_give((void**) &body);
2903 * IMPORTANT BIT: set the body->contents.text.data elements to contain the decrypted
2904 * data. Otherwise, it'll try to load it from the original data. Eek.
2906 create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body);
2908 modified_the_body = 1;
2912 end:
2913 if(out)
2914 BIO_free(out);
2916 return modified_the_body;
2921 * Recursively handle PKCS7 bodies in our message.
2923 * Returns non-zero if some fiddling was done.
2925 static int
2926 do_fiddle_smime_message(BODY *b, long msgno, char *section)
2928 int modified_the_body = 0;
2930 if(!b)
2931 return 0;
2933 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"));
2935 if(is_pkcs7_body(b)){
2937 if(do_decoding(b, msgno, section)){
2939 * b should now be a multipart message:
2940 * fiddle it too in case it's been multiply-encrypted!
2943 /* fallthru */
2944 modified_the_body = 1;
2948 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
2950 PART *p;
2951 int partNum;
2952 char newSec[100];
2954 if(MIME_MULT_SIGNED(b->type, b->subtype)){
2958 * Ahah. We have a multipart signed entity.
2960 * Multipart/signed
2961 * part 1 (signed thing)
2962 * part 2 (the pkcs7 signature)
2964 * We're going to convert that to
2966 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
2967 * part 1 (signed thing)
2968 * part 2 has been freed
2970 * We also extract the signature from part 2 and save it
2971 * in the multipart body->sparep, and we add a description
2972 * in the multipart body->description.
2975 * The results of a decrypted message will be similar. It
2976 * will be
2978 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
2979 * part 1 (decrypted thing)
2982 modified_the_body += do_detached_signature_verify(b, msgno, section);
2984 else if(MIME_MSG(b->type, b->subtype)){
2985 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
2987 else{
2989 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
2991 /* Append part number to the section string */
2993 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
2995 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
3000 return modified_the_body;
3005 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3006 * Returns non-zero if something was changed.
3009 fiddle_smime_message(BODY *b, long msgno)
3011 return do_fiddle_smime_message(b, msgno, "");
3015 /********************************************************************************/
3019 * Output a string in a distinctive style
3021 void
3022 gf_puts_uline(char *txt, gf_io_t pc)
3024 pc(TAG_EMBED); pc(TAG_BOLDON);
3025 gf_puts(txt, pc);
3026 pc(TAG_EMBED); pc(TAG_BOLDOFF);
3030 STACK_OF(X509) *
3031 get_chain_for_cert(X509 *cert, int *error)
3033 STACK_OF(X509) *chain = NULL;
3034 X509_STORE_CTX *ctx;
3035 X509 *x, *xtmp;
3036 int rc; /* return code */
3038 *error = 0;
3039 ERR_clear_error();
3040 if((ctx = X509_STORE_CTX_new()) != NULL){
3041 X509_STORE_set_flags(s_cert_store, 0);
3042 if(!X509_STORE_CTX_init(ctx, s_cert_store, cert, NULL))
3043 *error = X509_STORE_CTX_get_error(ctx);
3044 else if((chain = sk_X509_new_null()) != NULL){
3045 for(x = cert; ; x = xtmp){
3046 sk_X509_push(chain, X509_dup(x));
3047 rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x);
3048 if(rc < 0)
3049 *error = 1;
3050 if(rc <= 0)
3051 break;
3052 if(!X509_check_issued(xtmp, xtmp))
3053 break;
3056 if(*error && chain != NULL)
3057 sk_X509_pop_free(chain, X509_free);
3058 X509_STORE_CTX_free(ctx);
3060 return chain;
3065 * Sign a message. Called from call_mailer in send.c.
3067 * This takes the header for the outgoing message as well as a pointer
3068 * to the current body (which may be reallocated).
3071 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach)
3073 STORE_S *outs = NULL;
3074 BODY *body = *bodyP;
3075 BODY *newBody = NULL;
3076 PART *p1 = NULL;
3077 PART *p2 = NULL;
3078 PERSONAL_CERT *pcert;
3079 BIO *in = NULL;
3080 BIO *out = NULL;
3081 PKCS7 *p7 = NULL;
3082 STACK_OF(X509) *chain;
3083 int result = 0, error;
3084 int flags = dont_detach ? 0 : PKCS7_DETACHED;
3086 dprint((9, "sign_outgoing_message()"));
3088 smime_init();
3090 /* Look for a private key matching the sender address... */
3092 pcert = match_personal_cert(header->env);
3094 if(!pcert){
3095 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
3096 goto end;
3099 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
3100 /* Couldn't load key with blank password, try again */
3101 if(pith_opt_smime_get_passphrase){
3102 (*pith_opt_smime_get_passphrase)();
3103 load_private_key(pcert);
3107 if(!pcert->key)
3108 goto end;
3110 in = body_to_bio(body);
3112 chain = get_chain_for_cert(pcert->cert, &error);
3114 p7 = PKCS7_sign(pcert->cert, pcert->key, chain, in, flags);
3116 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3117 forget_private_keys();
3119 if(chain)
3120 sk_X509_pop_free(chain, X509_free);
3122 if(!p7){
3123 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
3124 goto end;
3127 if(error)
3128 q_status_message(SM_ORDER, 1, 1, _("Not all certificates needed to verify signature included in signed message"));
3130 outs = so_get(BioType, NULL, EDIT_ACCESS);
3131 out = bio_from_store(outs);
3133 i2d_PKCS7_bio(out, p7);
3134 (void) BIO_flush(out);
3136 so_seek(outs, 0, SEEK_SET);
3138 if((flags&PKCS7_DETACHED)==0){
3140 /* the simple case: the signed data is in the pkcs7 object */
3142 newBody = mail_newbody();
3144 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m");
3146 newBody->contents.text.data = (unsigned char *) outs;
3147 *bodyP = newBody;
3149 result = 1;
3151 else{
3154 * OK.
3155 * We have to create a new body as follows:
3157 * multipart/signed; blah blah blah
3158 * reference to existing body
3160 * pkcs7 object
3163 newBody = mail_newbody();
3165 newBody->type = TYPEMULTIPART;
3166 newBody->subtype = cpystr("signed");
3167 newBody->encoding = ENC7BIT;
3169 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
3170 set_parameter(&newBody->parameter, "micalg", "sha1");
3172 p1 = mail_newbody_part();
3173 p2 = mail_newbody_part();
3176 * This is nasty. We're just copying the body in here,
3177 * but since our newBody is freed at the end of call_mailer,
3178 * we mustn't let this body (the original one) be freed twice.
3180 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
3182 p1->next = p2;
3184 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s");
3185 p2->body.contents.text.data = (unsigned char *) outs;
3187 newBody->nested.part = p1;
3189 *bodyP = newBody;
3191 result = 1;
3194 end:
3196 PKCS7_free(p7);
3197 BIO_free(in);
3199 dprint((9, "sign_outgoing_message returns %d", result));
3200 return result;
3204 SMIME_STUFF_S *
3205 new_smime_struct(void)
3207 SMIME_STUFF_S *ret = NULL;
3209 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
3210 memset((void *) ret, 0, sizeof(*ret));
3211 ret->publictype = Nada;
3213 return ret;
3217 static void
3218 free_smime_struct(SMIME_STUFF_S **smime)
3220 if(smime && *smime){
3221 if((*smime)->passphrase_emailaddr){
3222 int i;
3223 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
3224 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
3225 fs_give((void **) (*smime)->passphrase_emailaddr);
3228 if((*smime)->publicpath)
3229 fs_give((void **) &(*smime)->publicpath);
3231 if((*smime)->publiccertlist)
3232 free_certlist(&(*smime)->publiccertlist);
3234 if((*smime)->cacertlist)
3235 free_certlist(&(*smime)->cacertlist);
3237 if((*smime)->privatecertlist)
3238 free_certlist(&(*smime)->privatecertlist);
3240 if((*smime)->publiccontent)
3241 fs_give((void **) &(*smime)->publiccontent);
3243 if((*smime)->privatepath)
3244 fs_give((void **) &(*smime)->privatepath);
3246 if((*smime)->personal_certs){
3247 PERSONAL_CERT *pc;
3249 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
3250 free_personal_certs(&pc);
3251 (*smime)->personal_certs = NULL;
3254 #ifdef PASSFILE
3255 if((*smime)->pwdcert){
3256 PERSONAL_CERT *pc;
3258 pc = (PERSONAL_CERT *) (*smime)->pwdcert;
3259 free_personal_certs(&pc);
3260 (*smime)->pwdcert = NULL;
3262 #endif /* PASSFILE */
3264 if((*smime)->privatecontent)
3265 fs_give((void **) &(*smime)->privatecontent);
3267 if((*smime)->capath)
3268 fs_give((void **) &(*smime)->capath);
3270 if((*smime)->cacontent)
3271 fs_give((void **) &(*smime)->cacontent);
3273 fs_give((void **) smime);
3277 #endif /* SMIME */