* Add the ability to change the private key and certificates used
[alpine.git] / pith / smime.c
blobd32a97c9417adb79a77975042fcbaf1d202dcc2d
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: smime.c 1176 2008-09-29 21:16:42Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2016 Eduardo Chappa
8 * Copyright 2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
20 * This is based on a contribution from Jonathan Paisley
22 * File: smime.c
23 * Author: paisleyj@dcs.gla.ac.uk
24 * Date: 01/2001
28 #include "../pith/headers.h"
30 #ifdef SMIME
32 #include "../pith/osdep/canaccess.h"
33 #include "../pith/helptext.h"
34 #include "../pith/store.h"
35 #include "../pith/status.h"
36 #include "../pith/detach.h"
37 #include "../pith/conf.h"
38 #include "../pith/smkeys.h"
39 #include "../pith/smime.h"
40 #include "../pith/mailpart.h"
41 #include "../pith/reply.h"
42 #include "../pith/tempfile.h"
43 #include "../pith/readfile.h"
44 #include "../pith/remote.h"
45 #ifdef PASSFILE
46 #include "../pith/imap.h"
47 #endif /* PASSFILE */
49 #include <openssl/buffer.h>
50 #include <openssl/x509v3.h>
52 /* internal prototypes */
53 static void forget_private_keys(void);
54 static int app_RAND_load_file(const char *file);
55 static void openssl_extra_randomness(void);
56 static int app_RAND_write_file(const char *file);
57 static const char *openssl_error_string(void);
58 static int load_private_key(PERSONAL_CERT *pcert);
59 static void create_local_cache(char *h, char *base, BODY *b, int type);
60 static long rfc822_output_func(void *b, char *string);
61 static void setup_pkcs7_body_for_signature(BODY *b, char *description,
62 char *type, char *filename, char *smime_type);
63 static BIO *body_to_bio(BODY *body);
64 static BIO *bio_from_store(STORE_S *store);
65 static STORE_S *get_part_contents(long msgno, const char *section);
66 static PKCS7 *get_pkcs7_from_part(long msgno, const char *section);
67 static int do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent);
68 static int do_detached_signature_verify(BODY *b, long msgno, char *section);
69 static PERSONAL_CERT *find_certificate_matching_pkcs7(PKCS7 *p7);
70 static int do_decoding(BODY *b, long msgno, const char *section);
71 static void free_smime_struct(SMIME_STUFF_S **smime);
72 static void setup_storage_locations(void);
73 static int copy_container_to_dir(WhichCerts which);
74 static int do_fiddle_smime_message(BODY *b, long msgno, char *section);
75 void setup_privatekey_storage(void);
76 int smime_extract_and_save_cert(PKCS7 *p7, int check_cert);
77 int same_cert(X509 *, X509 *);
78 #ifdef PASSFILE
79 int load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile, char **certfile, EVP_PKEY **pkey, X509 **pcert);
80 #endif /* PASSFILE */
81 EVP_PKEY *load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *);
82 void smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen);
83 void smime_remove_folding_space(char **mimetext, unsigned long *mimelen, char **bodytext, unsigned long *bodylen);
84 int smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag);
86 int (*pith_opt_smime_get_passphrase)(void);
87 int (*pith_smime_import_certificate)(char *, char *, size_t);
88 int (*pith_smime_enter_password)(char *prompt, char *, size_t);
89 int (*pith_smime_confirm_save)(char *email);
91 static X509_STORE *s_cert_store;
93 /* State management for randomness functions below */
94 static int seeded = 0;
96 void *
97 create_smime_sparep(SpareType stype, void *s)
99 SMIME_SPARE_S *rv;
101 rv = fs_get(sizeof(SMIME_SPARE_S));
102 rv->sptype = stype;
103 rv->data = s;
104 return (void *) rv;
107 SpareType
108 get_smime_sparep_type(void *s)
110 return ((SMIME_SPARE_S *)s)->sptype;
113 void *
114 get_smime_sparep_data(void *s)
116 return ((SMIME_SPARE_S *)s)->data;
120 #ifdef PASSFILE
122 * load key from pathkeydir and cert from pathcertdir. It chooses the first
123 * key/certificate pair that matches. Delete pairs that you do not want used,
124 * if you do not want them selected. All parameters must be non-null.
125 * Memory freed by caller.
126 * Return values:
127 * -1 : user cancelled load
128 * 0 : load was successful
129 * 1 : there was an error in the loading.
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, ret = 1; /* assume error */
140 if(pathkeydir == NULL || pathcertdir == NULL || keyfile == NULL
141 || pkey == NULL || certfile == NULL || pcert == NULL)
142 return 1;
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, &ret)) != 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);
177 return ret;
181 /* setup a key and certificate to encrypt and decrypt a password file.
182 * These files will be saved in the .alpine-smime/.pwd directory, but its
183 * location can be setup in the command line with the -pwdcertdir option.
184 * Here are the rules:
186 * Check if the .alpine-smime/.pwd (or -pwdcertdir directory) exists,
187 * if not create it. If we are successful, move to the next step
189 * - If the user has a key/cert pair, in the .alpine-smime/.pwd dir
190 * setup is successful;
191 * - if the user does not have a key/cert pair, look to see if
192 * ps_global->smime->personal_certs is already setup, if so, use it.
193 * - if ps_global->smime->personal_certs is not set up, see if we can
194 * find a certificate/cert pair in the default locations at compilation
195 * time. (~/.alpine-smime/private and ~/.alpine-smime/public
196 * - if none of this is successful, create a key/certificate pair
197 * (TODO: implement this)
198 * - in any other case, setup is not successful.
200 * If setup is successful, setup ps_global->pwdcert.
201 * If any of this fails, ps_global->pwdcert will be null.
202 * Ok, that should do it.
204 void
205 setup_pwdcert(void **pwdcert)
207 int we_inited = 0;
208 int setup_dir = 0; /* make it non zero if we know which dir to use */
209 struct stat sbuf;
210 char pathdir[MAXPATH+1], pathkey[MAXPATH+1], fpath[MAXPATH+1], pathcert[MAXPATH+1];
211 char fpath2[MAXPATH+1], prompt[MAILTMPLEN];
212 char *keyfile, *certfile, *text;
213 EVP_PKEY *pkey = NULL;
214 X509 *pcert = NULL;
215 PERSONAL_CERT *pc, *pc2 = NULL;
216 static int was_here = 0;
218 if(pwdcert == NULL || was_here == 1)
219 return;
221 was_here++;
222 if(ps_global->pwdcertdir){
223 if(our_stat(ps_global->pwdcertdir, &sbuf) == 0
224 && ((sbuf.st_mode & S_IFMT) == S_IFDIR)){
225 setup_dir++;
226 strncpy(pathdir, ps_global->pwdcertdir, sizeof(pathdir));
227 pathdir[sizeof(pathdir)-1] = '\0';
229 } else {
230 smime_path(DF_PASSWORD_DIR, pathdir, sizeof(pathdir));
231 if(our_stat(pathdir, &sbuf) == 0){
232 if((sbuf.st_mode & S_IFMT) == S_IFDIR)
233 setup_dir++;
234 } else if(can_access(pathdir, ACCESS_EXISTS) != 0
235 && our_mkpath(pathdir, 0700) == 0)
236 setup_dir++;
239 if(setup_dir == 0){
240 was_here = 0;
241 return;
244 if(load_key_and_cert(pathdir, pathdir, &keyfile, &certfile, &pkey, &pcert) < 0){
245 was_here = 0;
246 return;
250 if(ps_global->pwdcertdir == NULL) /* save the result of pwdcertdir */
251 ps_global->pwdcertdir = cpystr(pathdir);
253 if(certfile && keyfile){
254 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
255 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
256 pc->name = keyfile;
257 pc->key = pkey;
258 pc->cert = pcert;
259 pc->cname = certfile;
260 *pwdcert = (void *) pc;
261 was_here = 0;
262 return;
265 /* if the user gave a pwdcertdir and there is nothing there, do not
266 * continue. Let the user initialize on their own this directory.
268 if(ps_global->pwdcertdir != NULL){
269 was_here = 0;
270 return;
273 /* look to see if there are any certificates lying around, first
274 * we try to load ps_global->smime to see if that has information
275 * we can use. If we are the process filling the smime structure
276 * we deinit at the end, since this might not do a full init.
278 if(ps_global && ps_global->smime && !ps_global->smime->inited){
279 we_inited++;
280 smime_init();
283 /* at this point ps_global->smime->inited == 1 */
284 if(ps_global->smime && ps_global->smime->personal_certs != NULL){
285 pc = (PERSONAL_CERT *) ps_global->smime->personal_certs;
286 if(ps_global->smime->privatetype == Directory){
287 build_path(pathkey, ps_global->smime->privatepath, pc->name, sizeof(pathkey));
288 strncat(pathkey, ".key", 4);
289 pathkey[sizeof(pathkey)-1] = '\0';
290 text = NULL;
291 } else if (ps_global->smime->privatetype == Container){
292 if(pc->keytext == NULL){ /* we should *never* be here, but just in case */
293 if(ps_global->smime->privatecontent != NULL){
294 char tmp[MAILTMPLEN], *s, *t, c;
295 snprintf(tmp, sizeof(tmp), "%s%s", EMAILADDRLEADER, pc->name);
296 tmp[sizeof(tmp)-1] = '\0';
297 if((s = strstr(ps_global->smime->privatecontent, tmp)) != NULL){
298 if((t = strstr(s+strlen(tmp), EMAILADDRLEADER)) != NULL){
299 c = *t;
300 *t = '\0';
301 pc->keytext = cpystr(s + strlen(tmp) + 1); /* 1 = strlen("\n") */
302 *t = c;
304 else
305 pc->keytext = cpystr(s + strlen(tmp) + 1); /* 1 = strlen("\n") */
309 if(pc->keytext != NULL) /* we should go straigth here */
310 text = pc->keytext;
311 } else if (ps_global->smime->privatetype == Keychain){
312 pathkey[0] = '\0'; /* no apple key chain support yet */
313 text = NULL;
315 if((pathkey && *pathkey) || text){
316 snprintf(prompt, sizeof(prompt),
317 _("Enter password of key <%s> to unlock password file: "), pc->name);
319 if((pkey = load_pkey_with_prompt(pathkey, text, prompt, NULL)) != NULL){
320 pc2 = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
321 memset((void *)pc2, 0, sizeof(PERSONAL_CERT));
322 pc2->name = cpystr(pc->name);
323 pc2->key = pkey;
324 pc2->cert = X509_dup(pc->cert);
326 /* now copy the keys and certs, starting by the key... */
327 build_path(fpath, pathdir, pc->name, sizeof(fpath));
328 strncat(fpath, ".key", 4);
329 fpath[sizeof(fpath)-1] = '\0';
330 if(our_stat(fpath, &sbuf) == 0){ /* if fpath exists */
331 if((sbuf.st_mode & S_IFMT) == S_IFREG) /* and is a regular file */
332 setup_dir++; /* we are done */
333 } else if(ps_global->smime->privatetype == Directory){
334 if(our_copy(fpath, pathkey) == 0)
335 setup_dir++;
336 } else if(ps_global->smime->privatetype == Container){
337 BIO *out;
338 if((out = BIO_new_file(fpath, "w")) != NULL){
339 if(BIO_puts(out, pc->keytext) > 0)
340 setup_dir++;
341 BIO_free(out);
343 } else if(ps_global->smime->privatetype == Keychain){
344 /* add support for Apple Mac OS X */
348 /* successful copy of key, now continue with certificate */
349 if(setup_dir){
350 setup_dir = 0;
352 build_path(pathkey, ps_global->smime->publicpath, pc->name, sizeof(pathkey));
353 strncat(pathkey, ".crt", 4);
354 pathkey[sizeof(pathkey)-1] = '\0';
356 build_path(fpath, pathdir, pc->name, sizeof(fpath));
357 strncat(fpath, ".crt", 4);
358 fpath[sizeof(fpath)-1] = '\0';
360 if(our_stat(fpath, &sbuf) == 0){
361 if((sbuf.st_mode & S_IFMT) == S_IFREG)
362 setup_dir++;
364 else if(ps_global->smime->privatetype == Directory){
365 if(our_copy(fpath, pathkey) == 0)
366 setup_dir++;
367 } else if(ps_global->smime->privatetype == Container) {
368 BIO *out;
369 if((out = BIO_new_file(fpath, "w")) != NULL){
370 if(PEM_write_bio_X509(out, pc->cert))
371 setup_dir++;
372 BIO_free(out);
374 } else if (ps_global->smime->privatetype == Keychain) {
375 /* add support for Mac OS X */
379 if(setup_dir){
380 *pwdcert = (void *) pc2;
381 was_here = 0;
382 return;
384 else if(pc2 != NULL)
385 free_personal_certs(&pc2);
386 } /* if (pathkey...) */
387 } /* if(ps_global->smime->personal_certs) */
390 if(setup_dir == 0){
391 /* PATHCERTDIR(Private) must be null, so create a path */
392 set_current_val(&ps_global->vars[V_PRIVATEKEY_DIR], TRUE, TRUE);
393 smime_path(ps_global->VAR_PRIVATEKEY_DIR, pathkey, sizeof(pathkey));
395 /* PATHCERTDIR(Public) must be null, so create a path */
396 set_current_val(&ps_global->vars[V_PUBLICCERT_DIR], TRUE, TRUE);
397 smime_path(ps_global->VAR_PUBLICCERT_DIR, pathcert, sizeof(pathcert));
399 /* BUG: this does not support local containers */
400 load_key_and_cert(pathkey, pathcert, &keyfile, &certfile, &pkey, &pcert);
402 if(certfile && keyfile){
403 build_path(fpath, pathdir, keyfile, sizeof(fpath));
404 strncat(fpath, ".key", 4);
405 fpath[sizeof(fpath)-1] = '\0';
407 build_path(fpath2, pathkey, keyfile, sizeof(fpath));
408 strncat(fpath2, ".key", 4);
409 fpath2[sizeof(fpath2)-1] = '\0';
411 if(our_copy(fpath, fpath2) == 0)
412 setup_dir++;
414 if(setup_dir){
415 setup_dir = 0;
417 build_path(fpath, pathdir, certfile, sizeof(fpath));
418 build_path(fpath2, pathcert, certfile, sizeof(fpath2));
420 if(our_copy(fpath, fpath2) == 0)
421 setup_dir++;
426 if(keyfile && certfile){
427 pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT));
428 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
429 pc->name = keyfile;
430 pc->key = pkey;
431 pc->cert = pcert;
432 *pwdcert = (void *) pc;
433 fs_give((void **)&certfile);
434 was_here = 0;
435 return;
438 /* TODO: create self signed certificate
439 q_status_message(SM_ORDER, 2, 2,
440 _("No key/certificate pair found for password file encryption support"));
442 was_here = 0;
443 if(we_inited)
444 smime_deinit();
446 #endif /* PASSFILE */
448 /* smime_expunge_cert.
449 * Return values: < 0 there was an error.
450 * >=0 the number of messages expunged
453 smime_expunge_cert(WhichCerts ctype)
455 int count, removed;
456 CertList *cl, *dummy, *data;
457 char *path, buf[MAXPATH+1];
458 char *contents;
460 if(DATACERT(ctype)== NULL)
461 return -1;
463 /* data cert is the way we unify certificate management across
464 * functions, but it is not where we really save the information in the
465 * case ctype is equal to Private. What we will do is to update the
466 * datacert, and in the case of ctype equal to Private use the updated
467 * certdata to update the personal_certs data.
470 path = PATHCERTDIR(ctype);
472 if(path){
473 /* add a fake certificate at the beginning of the list */
474 dummy = fs_get(sizeof(CertList));
475 memset((void *)dummy, 0, sizeof(CertList));
476 dummy->next = DATACERT(ctype);
478 for(cl = dummy, count = 0; cl && cl->next;){
479 if(cl->next->data.deleted == 0){
480 cl = cl->next;
481 continue;
484 removed = 1; /* assume success */
485 if(SMHOLDERTYPE(ctype) == Directory){
486 build_path(buf, path, cl->next->name, sizeof(buf));
487 if(ctype == Private && strlen(buf) + strlen(EXTCERT(Private)) < sizeof(buf)){
488 strncat(buf, EXTCERT(Private), 4);
489 buf[sizeof(buf)-1] = '\0';
492 if(our_unlink(buf) < 0){
493 q_status_message1(SM_ORDER, 3, 3, _("Error removing certificate %s"), cl->next->name);
494 cl = cl->next;
495 removed = 0;
498 else if(SMHOLDERTYPE(ctype) == Container){
499 char *prefix= ctype == CACert ? CACERTSTORELEADER : EMAILADDRLEADER;
500 char tmp[MAILTMPLEN], *s, *t;
502 contents = CONTENTCERTLIST(ctype);
503 snprintf(tmp, sizeof(tmp), "%s%s", prefix, cl->next->name);
504 tmp[sizeof(tmp) - 1] = '\0';
505 if((s = strstr(contents, tmp)) != NULL){
506 if((t = strstr(s+strlen(tmp), prefix)) == NULL)
507 *s = '\0';
508 else
509 memmove(s, t, strlen(t)+1);
510 fs_resize((void **)&contents, strlen(contents)+1);
511 switch(ctype){
512 case Private: ps_global->smime->privatecontent = contents; break;
513 case Public : ps_global->smime->publiccontent = contents; break;
514 case CACert : ps_global->smime->cacontent = contents; break;
515 default : break;
518 else
519 removed = 0;
520 } else { /* unhandled case */
523 if(removed > 0){
524 count++; /* count it! */
525 data = cl->next;
526 cl->next = data->next;
527 if(data->name) fs_give((void **)&data->name);
528 fs_give((void **)&data);
531 } else
532 q_status_message(SM_ORDER, 3, 3, _("Error expunging certificate"));
534 switch(ctype){
535 case Private: ps_global->smime->privatecertlist = dummy->next; break;
536 case Public : ps_global->smime->publiccertlist = dummy->next; break;
537 case CACert : ps_global->smime->cacertlist = dummy->next; break;
538 default : break;
540 fs_give((void **)&dummy);
541 if(SMHOLDERTYPE(ctype) == Container){
542 if(copy_dir_to_container(ctype, contents) < 0)
543 count = 0;
545 if(count > 0){
546 q_status_message2(SM_ORDER, 3, 3, _("Removed %s certificate%s"), comatose(count), plural(count));
548 else
549 q_status_message(SM_ORDER, 3, 3, _("Error: No certificates were removed"));
550 return count;
553 void
554 mark_cert_deleted(WhichCerts ctype, int num, unsigned state)
556 CertList *cl;
557 int i;
559 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
560 cl->data.deleted = state;
563 unsigned
564 get_cert_deleted(WhichCerts ctype, int num)
566 CertList *cl;
567 int i;
569 for(cl = DATACERT(ctype), i = 0; cl != NULL && i < num; cl = cl->next, i++);
570 return (cl && cl->data.deleted) ? 1 : 0;
573 EVP_PKEY *
574 load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *ret)
576 EVP_PKEY *pkey;
577 int rc = 0; /* rc == 1, cancel, rc == 0 success */
578 char pass[MAILTMPLEN+1];
579 BIO *in;
581 /* attempt to load with empty password */
582 in = text ? BIO_new_mem_buf(text, strlen(text)) : BIO_new_file(fpath, "r");
583 if(in != NULL){
584 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, "");
585 if(pkey != NULL) return pkey;
586 } else return NULL;
588 if(pith_smime_enter_password)
589 while(pkey == NULL && rc != 1){
590 do {
591 rc = (*pith_smime_enter_password)(prompt, (char *)pass, sizeof(pass));
592 } while (rc!=0 && rc!=1 && rc>0);
594 (void) BIO_reset(in);
595 pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, (char *)pass);
598 BIO_free(in);
600 if(ret) *ret = rc == 1 ? -1 : pkey != NULL ? 0 : 1;
601 return pkey;
604 /* This is a tool for conf_screen, The return value must be zero when
605 * nothing changed, so if there is a failure in the import return 0
606 * and return 1 when we succeeded
609 import_certificate(WhichCerts ctype)
611 int r = 1, rc;
612 char filename[MAXPATH+1], full_filename[MAXPATH+1], buf[MAXPATH+1];
614 if(pith_smime_import_certificate == NULL){
615 q_status_message(SM_ORDER, 0, 2,
616 _("import of certificates not implemented yet!"));
617 return 0;
620 r = (*pith_smime_import_certificate)(filename, full_filename, sizeof(filename) - 20);
622 if(r < 0)
623 return 0;
625 /* we are trying to import a new key for the password file. First we ask for the
626 * private key. Once this is loaded, we make a reasonable attempt to find the
627 * public key in the same directory as the key was loaded from. We do this by
628 * looking for a file with the correct public certificate name, then we look
629 * in the same private key, and if not, we ask the user for its location. If all
630 * of this works, we import the key and public to the password directory.
632 if(ctype == Password){
633 char PrivateKeyPath[MAXPATH+1], PublicCertPath[MAXPATH+1], s[MAXPATH+1];
634 char full_name_key[MAXPATH+1], full_name_cert[MAXPATH+1];
635 char *use_this_file;
636 char prompt[500];
637 EVP_PKEY *key = NULL;
639 rc = 1; /* assume success :) */
640 if(strlen(filename) > 4){
641 strncpy(s, filename, sizeof(s));
642 s[sizeof(s)-1] = '\0';
643 if(!strcmp(s + strlen(s) - strlen(EXTCERT(Private)), EXTCERT(Private)))
644 s[strlen(s) - strlen(EXTCERT(Private))] = '\0';
645 else
646 rc = 0;
647 } else rc = 0;
649 if(rc == 0){
650 q_status_message(SM_ORDER, 1, 3, _("Error in key name. Check file extension"));
651 return 0;
654 snprintf(prompt, sizeof(prompt), _("Enter passphrase for <%s>: "), filename);
655 prompt[sizeof(prompt)-1] = '\0';
656 if((key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){
657 BIO *ins = NULL;
658 X509 *cert = NULL;
660 strncpy(full_name_key, full_filename, sizeof(full_filename));
661 full_name_key[sizeof(full_name_key)-1] = '\0';
663 build_path(buf, PATHCERTDIR(ctype), s, sizeof(buf));
665 strncpy(PrivateKeyPath, buf, sizeof(PrivateKeyPath));
666 PrivateKeyPath[sizeof(PrivateKeyPath)-1] = '\0';
667 if(strlen(PrivateKeyPath) + 4 < sizeof(PrivateKeyPath)){
668 strncat(PrivateKeyPath, EXTCERT(Private), 4);
669 PrivateKeyPath[sizeof(PrivateKeyPath)-1] = '\0';
672 /* remove .key extension and replace it with .crt extension */
673 strncpy(full_name_cert, full_name_key, sizeof(full_name_key));
674 full_name_cert[sizeof(full_name_cert)-1] = '\0';
675 full_name_cert[strlen(full_name_cert) - strlen(EXTCERT(Private))] = '\0';
676 strncat(full_name_cert, EXTCERT(Public), 4);
677 full_name_cert[sizeof(full_name_cert)-1] = '\0';
680 /* set up path to location where we will save public cert */
681 strncpy(PublicCertPath, buf, sizeof(PublicCertPath));
682 PublicCertPath[sizeof(PublicCertPath)-1] = '\0';
683 if(strlen(PublicCertPath) + 4 < sizeof(PublicCertPath)){
684 strncat(PublicCertPath, EXTCERT(Public), 4);
685 PublicCertPath[sizeof(PublicCertPath)-1] = '\0';
688 /* attempt #1 to guess public cert name, use .crt extension */
689 if((ins = BIO_new_file(full_name_cert, "r")) != NULL){
690 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
691 use_this_file = &full_name_cert[0];
694 else{
695 /* attempt #2 to guess public cert name: user the original key */
696 if((ins = BIO_new_file(full_name_key, "r")) != NULL){
697 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
698 use_this_file = &full_name_key[0];
701 else {
702 int done = 0;
703 /* attempt #3, ask the user */
704 do {
705 r = (*pith_smime_import_certificate)(filename, use_this_file, sizeof(filename) - 20);
706 if(r < 0){
707 if(ins != NULL) BIO_free(ins);
708 if(cert != NULL) X509_free(cert);
709 return 0;
711 if((ins = BIO_new_file(use_this_file, "r")) != NULL){
712 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL)
713 done++;
714 else
715 q_status_message(SM_ORDER, 1, 3, _("Error parsing certificate"));
717 else
718 q_status_message(SM_ORDER, 1, 3, _("Error reading certificate"));
719 } while (done == 0);
722 if(ins != NULL){
723 if(cert != NULL){ /* check that certificate matches key */
724 if(!X509_check_private_key(cert, key)){
725 rc = 0;
726 q_status_message(SM_ORDER, 1, 3, _("Certificate does not match key"));
728 else
729 rc = 1; /* Success! */
731 else
732 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
734 if(rc == 1){ /* if everything has been successful,
735 * copy the files to their final destination */
736 if(our_copy(PrivateKeyPath, full_filename) == 0){ /* <-- save the private key */
737 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
738 if(our_copy(PublicCertPath, use_this_file) == 0){
739 char tmp[MAILTMPLEN];
740 FILE *fp;
742 if(!passfile_name(ps_global->pinerc, tmp, sizeof(tmp))
743 || !(fp = our_fopen(tmp, "rb"))){
744 q_status_message(SM_ORDER, 1, 3, _("Error reading password file!"));
745 rc = 0;
747 else {
748 char tmp2[MAILTMPLEN];
749 int encrypted = 0;
750 char *text;
751 PERSONAL_CERT *pwdcert, *pc;
753 pwdcert = (PERSONAL_CERT *) ps_global->pwdcert;
754 if(pwdcert == NULL)
755 setup_pwdcert((void **)&pwdcert);
757 tmp2[0] = '\0';
758 fgets(tmp2, sizeof(tmp2), fp);
759 fclose(fp);
760 if(strcmp(tmp2, "-----BEGIN PKCS7-----\n")){
761 if(encrypt_file((char *)tmp, NULL, pwdcert))
762 encrypted++;
764 else
765 encrypted++;
767 if(encrypted){
768 text = decrypt_file((char *)tmp, NULL, pwdcert);
769 if(text != NULL){
770 pc = fs_get(sizeof(PERSONAL_CERT));
771 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
772 filename[strlen(filename)-strlen(EXTCERT(Private))] = '\0';
773 pc->name = cpystr(filename);
774 snprintf(buf, sizeof(buf), "%s%s", filename, EXTCERT(Public));
775 buf[sizeof(buf)-1] = '\0';
776 pc->cname = cpystr(buf);
777 pc->key = key;
778 pc->cert = cert;
780 if(encrypt_file((char *)tmp, text, pc)){ /* we did it! */
781 build_path(buf, PATHCERTDIR(ctype), pwdcert->name, sizeof(buf));
782 strncat(buf, EXTCERT(Private), 4);
783 buf[sizeof(buf)-1] = '\0';
784 if(unlink(buf) < 0)
785 q_status_message(SM_ORDER, 1, 3, _("Failed to remove old key"));
786 build_path(buf, PATHCERTDIR(ctype), pwdcert->cname, sizeof(buf));
787 if(unlink(buf) < 0)
788 q_status_message(SM_ORDER, 1, 3, _("Failed to remove old certificate"));
789 free_personal_certs((PERSONAL_CERT **)&ps_global->pwdcert);
790 ps_global->pwdcert = pc;
791 rc = 1;
792 q_status_message(SM_ORDER, 1, 3, _("Password file reencrypted"));
793 } else {
794 q_status_message(SM_ORDER, 1, 3, _("Failed to reencrypt password file"));
795 rc = 0;
797 } else {
798 q_status_message(SM_ORDER, 1, 3, _("Error decrypting Password file"));
800 } else {
801 q_status_message(SM_ORDER, 1, 3, _("Password file not encrypted and coulr not encrypt"));
802 rc = 0;
806 else{
807 q_status_message(SM_ORDER, 1, 3, _("Error saving public certificate"));
808 if(our_unlink(PrivateKeyPath) < 0)
809 q_status_message(SM_ORDER, 1, 3, _("Error while cleaning private key"));
810 rc = 0;
813 else{
814 rc = 0;
815 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
817 if(ins != NULL) BIO_free(ins);
818 if(rc == 0 && cert != NULL) X509_free(cert);
820 } else {
821 rc = 0;
822 q_status_message(SM_ORDER, 1, 3, _("Error unlocking private key"));
825 return rc;
828 smime_init();
829 ps_global->mangled_screen = 1;
831 if (ctype == Private){
832 char prompt[500], *s, *t;
833 EVP_PKEY *key = NULL;
835 if(!ps_global->smime->privatecertlist){
836 ps_global->smime->privatecertlist = fs_get(sizeof(CertList));
837 memset((void *)DATACERT(ctype), 0, sizeof(CertList));
840 for(s = t = filename; (t = strstr(s, ".key")) != NULL; s = t + 1);
841 if(s) *(s-1) = 0;
843 snprintf(prompt, sizeof(prompt), _("Enter passphrase for <%s>: "), filename);
844 prompt[sizeof(prompt)-1] = '\0';
845 if((key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){
846 if(SMHOLDERTYPE(ctype) == Directory){
847 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
848 if(strcmp(buf + strlen(buf) - 4, EXTCERT(ctype)) != 0 && strlen(buf) + 4 < sizeof(buf)){
849 strncat(buf, EXTCERT(ctype), 4);
850 buf[sizeof(buf)-1] = '\0';
852 rc = our_copy(buf, full_filename);
854 else /* if(SMHOLDERTYPE(ctype) == Container){ */
855 rc = add_file_to_container(ctype, full_filename, NULL);
856 if(rc == 0)
857 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
858 else
859 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
860 if(ps_global->smime->publiccertlist)
861 ps_global->smime->publiccertlist->data.renew = 1;
863 else
864 q_status_message(SM_ORDER, 1, 3, _("Problem unlocking key (not a certificate or wrong password)"));
865 } else if (ctype == CACert){
866 BIO *ins;
867 X509 *cert;
869 if((ins = BIO_new_file(full_filename, "r")) != NULL){
870 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
871 if(SMHOLDERTYPE(ctype) == Directory){
872 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
873 if(strcmp(buf + strlen(buf) - 4, ".crt") != 0 && strlen(buf) + 4 < sizeof(buf)){
874 strncat(buf, EXTCERT(ctype), 4);
875 buf[sizeof(buf)-1] = '\0';
878 rc = our_copy(buf, full_filename);
880 else /* if(SMHOLDERTYPE(ctype) == Container){ */
881 rc = add_file_to_container(ctype, full_filename, NULL);
882 if(rc == 0)
883 q_status_message(SM_ORDER, 1, 3, _("Certificate saved"));
884 else
885 q_status_message(SM_ORDER, 1, 3, _("Error saving certificate"));
886 X509_free(cert); /* not needed anymore */
888 else
889 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
890 BIO_free(ins);
892 renew_store();
893 } else { /* ctype == Public. save certificate, but first validate that it is one */
894 BIO *ins;
895 X509 *cert;
897 if((ins = BIO_new_file(full_filename, "r")) != NULL){
898 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
899 if(SMHOLDERTYPE(ctype) == Directory){
900 char **email;
902 if((email = get_x509_subject_email(cert)) != NULL){
903 int i;
904 for(i = 0; email[i] != NULL; i++){
905 save_cert_for(email[i], cert, Public);
906 fs_give((void **)&email[i]);
908 fs_give((void **)email);
910 if(strcmp(filename + strlen(filename) - 4, ".crt") == 0)
911 filename[strlen(filename) - 4] = '\0';
912 save_cert_for(filename, cert, Public);
914 else /* if(SMHOLDERTYPE(ctype) == Container){ */
915 add_file_to_container(ctype, full_filename, NULL);
916 X509_free(cert);
917 if(ps_global->smime->publiccertlist)
918 ps_global->smime->publiccertlist->data.renew = 1;
920 else
921 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
922 BIO_free(ins);
925 if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1;
926 return 1;
929 /* itype: information type to add: 0 - public, 1 - private.
930 * Memory freed by caller
932 BIO *
933 print_private_key_information(char *email, int itype)
935 BIO *out;
936 PERSONAL_CERT *pc;
938 if(ps_global->smime == NULL
939 || ps_global->smime->personal_certs == NULL
940 || (itype != 0 && itype != 1))
941 return NULL;
943 for(pc = ps_global->smime->personal_certs;
944 pc != NULL && strcmp(pc->name, email) != 0; pc = pc->next);
945 if(pc->key == NULL
946 && !load_private_key(pc)
947 && ps_global->smime
948 && ps_global->smime->need_passphrase){
949 if (*pith_opt_smime_get_passphrase)
950 (*pith_opt_smime_get_passphrase)();
951 load_private_key(pc);
954 if(pc->key == NULL)
955 return NULL;
957 out = BIO_new(BIO_s_mem());
958 if(itype == 0) /* 0 means public */
959 EVP_PKEY_print_public(out, pc->key, 0, NULL);
960 else if (itype == 1) /* 1 means private */
961 EVP_PKEY_print_private(out, pc->key, 0, NULL);
963 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
964 forget_private_keys();
966 return out;
970 * Forget any cached private keys
972 static void
973 forget_private_keys(void)
975 PERSONAL_CERT *pcert;
976 size_t len;
977 volatile char *p;
979 dprint((9, "forget_private_keys()"));
980 if(ps_global->smime){
981 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
982 pcert;
983 pcert=pcert->next){
985 if(pcert->key){
986 EVP_PKEY_free(pcert->key);
987 pcert->key = NULL;
991 ps_global->smime->entered_passphrase = 0;
992 len = sizeof(ps_global->smime->passphrase);
993 p = ps_global->smime->passphrase;
995 while(len-- > 0)
996 *p++ = '\0';
1000 /* modelled after signature_path in reply.c, but uses home dir instead of the
1001 * directory where the .pinerc is located, since according to documentation,
1002 * the .alpine-smime directories are subdirectories of the home directory
1004 int smime_path(char *rpath, char *fpath, size_t len)
1006 *fpath = '\0';
1007 if(rpath && *rpath){
1008 size_t spl = strlen(rpath);
1010 if(IS_REMOTE(rpath)){
1011 if(spl < len - 1)
1012 strncpy(fpath, rpath, len-1);
1013 fpath[len-1] = '\0';
1015 else if(is_absolute_path(rpath)){
1016 strncpy(fpath, rpath, len-1);
1017 fpath[len-1] = '\0';
1018 fnexpand(fpath, len);
1020 else if(ps_global->VAR_OPER_DIR){
1021 if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
1022 build_path(fpath, ps_global->VAR_OPER_DIR, rpath, len);
1024 else if(ps_global->home_dir){
1025 if(strlen(ps_global->home_dir) + spl < len - 1)
1026 build_path(fpath, ps_global->home_dir, rpath, len);
1029 return fpath && *fpath ? 1 : 0;
1035 * taken from openssl/apps/app_rand.c
1037 static int
1038 app_RAND_load_file(const char *file)
1040 #define RANDBUFLEN 200
1041 char buffer[RANDBUFLEN];
1043 if(file == NULL)
1044 file = RAND_file_name(buffer, RANDBUFLEN);
1046 if(file == NULL || !RAND_load_file(file, -1)){
1047 if(RAND_status() == 0){
1048 dprint((1, "unable to load 'random state'\n"));
1049 dprint((1, "This means that the random number generator has not been seeded\n"));
1050 dprint((1, "with much random data.\n"));
1053 return 0;
1056 seeded = 1;
1057 return 1;
1062 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
1064 static void
1065 openssl_extra_randomness(void)
1067 #if !defined(WIN32)
1068 int fd;
1069 unsigned long i;
1070 char *tf = NULL;
1071 char tmp[MAXPATH];
1072 struct stat sbuf;
1073 /* if system doesn't have /dev/urandom */
1074 if(stat ("/dev/urandom", &sbuf)){
1075 tmp[0] = '0';
1076 tf = temp_nam(NULL, NULL);
1077 if(tf){
1078 strncpy(tmp, tf, sizeof(tmp));
1079 tmp[sizeof(tmp)-1] = '\0';
1080 fs_give((void **) &tf);
1083 if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
1084 i = (unsigned long) tmp;
1085 else{
1086 unlink(tmp); /* don't need the file */
1087 fstat(fd, &sbuf); /* get information about the file */
1088 i = sbuf.st_ino; /* remember its inode */
1089 close(fd); /* or its descriptor */
1091 /* not great but it'll have to do */
1092 snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
1093 tcp_serverhost (),i,
1094 (unsigned long) (time (0) ^ gethostid ()),
1095 (unsigned long) getpid ());
1096 RAND_seed(tmp, strlen(tmp));
1098 #endif
1102 /* taken from openssl/apps/app_rand.c */
1103 static int
1104 app_RAND_write_file(const char *file)
1106 char buffer[200];
1108 if(!seeded)
1110 * If we did not manage to read the seed file,
1111 * we should not write a low-entropy seed file back --
1112 * it would suppress a crucial warning the next time
1113 * we want to use it.
1115 return 0;
1117 if(file == NULL)
1118 file = RAND_file_name(buffer, sizeof buffer);
1120 if(file == NULL || !RAND_write_file(file)){
1121 dprint((1, "unable to write 'random state'\n"));
1122 return 0;
1125 return 1;
1128 CertList *
1129 certlist_from_personal_certs(PERSONAL_CERT *pc)
1131 CertList *cl;
1132 X509 *x;
1134 if(pc == NULL)
1135 return NULL;
1137 if((x = get_cert_for(pc->name, Public, 1)) != NULL){
1138 cl = smime_X509_to_cert_info(x, pc->name);
1139 X509_free(x);
1141 cl->next = certlist_from_personal_certs(pc->next);
1143 return cl;
1146 void
1147 renew_cert_data(CertList **data, WhichCerts ctype)
1149 smime_init();
1150 if(ctype == Private){
1151 if(data){
1152 PERSONAL_CERT *pc = (PERSONAL_CERT *)ps_global->smime->personal_certs;
1153 if(*data)
1154 free_certlist(data);
1155 free_personal_certs(&pc);
1156 setup_privatekey_storage();
1157 *data = certlist_from_personal_certs((PERSONAL_CERT *)ps_global->smime->personal_certs);
1158 if(data && *data){
1159 resort_certificates(data, ctype);
1160 RENEWCERT(*data) = 0;
1162 ps_global->smime->privatecertlist = *data;
1164 if(ps_global->smime->privatecertlist)
1165 RENEWCERT(ps_global->smime->privatecertlist) = 0;
1166 } else {
1167 X509_LOOKUP *lookup = NULL;
1168 X509_STORE *store = NULL;
1170 if((store = X509_STORE_new()) != NULL){
1171 if((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL){
1172 X509_STORE_free(store);
1173 store = NULL;
1174 } else{
1175 free_certlist(data);
1176 if(SMHOLDERTYPE(ctype) == Directory)
1177 add_certs_in_dir(lookup, PATHCERTDIR(ctype), EXTCERT(ctype), data);
1178 else /* if(SMHOLDERTYPE(ctype) == Container) */
1179 *data = mem_to_certlist(CONTENTCERTLIST(ctype), ctype);
1180 if(data && *data){
1181 resort_certificates(data, ctype);
1182 RENEWCERT(*data) = 0;
1184 if(ctype == Public)
1185 ps_global->smime->publiccertlist = *data;
1186 else
1187 ps_global->smime->cacertlist = *data;
1191 setup_certs_backup_by_type(ctype);
1194 void
1195 smime_reinit(void)
1197 smime_deinit();
1198 smime_init();
1201 /* Installed as an atexit() handler to save the random data */
1202 void
1203 smime_deinit(void)
1205 dprint((9, "smime_deinit()"));
1206 app_RAND_write_file(NULL);
1207 free_smime_struct(&ps_global->smime);
1210 /* we renew the store when it has changed */
1211 void renew_store(void)
1213 if(ps_global->smime->inited){
1214 if(s_cert_store != NULL)
1215 X509_STORE_free(s_cert_store);
1216 s_cert_store = get_ca_store();
1220 /* Initialise openssl stuff if needed */
1221 void
1222 smime_init(void)
1224 if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
1226 dprint((9, "smime_init()"));
1227 if(!ps_global->smime)
1228 ps_global->smime = new_smime_struct();
1230 setup_storage_locations();
1232 s_cert_store = get_ca_store();
1233 setup_certs_backup_by_type(CACert);
1235 OpenSSL_add_all_algorithms();
1236 ERR_load_crypto_strings();
1238 app_RAND_load_file(NULL);
1239 openssl_extra_randomness();
1240 ps_global->smime->inited = 1;
1243 ERR_clear_error();
1247 /* validate a certificate. Return value : 0 for no error, -1 for error.
1248 * In the latter case, set the openssl smime error in *error.
1250 int smime_validate_cert(X509 *cert, long *error)
1252 X509_STORE_CTX *csc;
1254 ERR_clear_error();
1255 *error = 0;
1256 if((csc = X509_STORE_CTX_new()) != NULL){
1257 X509_STORE_set_flags(s_cert_store, 0);
1258 if(X509_STORE_CTX_init(csc,s_cert_store,cert,NULL)
1259 && X509_verify_cert(csc) <= 0)
1260 *error = X509_STORE_CTX_get_error(csc);
1261 X509_STORE_CTX_free(csc);
1263 return *error ? -1 : 0;
1266 PERSONAL_CERT *
1267 get_personal_certs(char *path)
1269 PERSONAL_CERT *result = NULL;
1270 char buf2[MAXPATH];
1271 struct dirent *d;
1272 DIR *dirp;
1274 ps_global->smime->privatepath = cpystr(path);
1275 dirp = opendir(path);
1276 if(dirp){
1277 while((d=readdir(dirp)) != NULL){
1278 X509 *cert;
1279 size_t ll;
1281 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, ".key")){
1283 /* copy file name to temp buffer */
1284 strncpy(buf2, d->d_name, sizeof(buf2)-1);
1285 buf2[sizeof(buf2)-1] = '\0';
1286 /* chop off ".key" trailier */
1287 buf2[strlen(buf2)-4] = 0;
1288 /* Look for certificate */
1289 cert = get_cert_for(buf2, Public, 1);
1291 if(cert){
1292 PERSONAL_CERT *pc;
1294 /* create a new PERSONAL_CERT, fill it in */
1296 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1297 pc->cert = cert;
1298 pc->name = cpystr(buf2);
1300 /* Try to load the key with an empty password */
1301 pc->key = load_key(pc, "", SM_NORMALCERT);
1303 pc->next = result;
1304 result = pc;
1308 closedir(dirp);
1310 return result;
1314 void
1315 setup_privatekey_storage(void)
1317 char path[MAXPATH+1], *contents;
1318 int privatekeycontainer = 0;
1320 /* private keys in a container */
1321 if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
1323 privatekeycontainer = 1;
1324 contents = NULL;
1325 path[0] = '\0';
1326 if(!smime_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
1327 privatekeycontainer = 0;
1329 if(privatekeycontainer && !IS_REMOTE(path)
1330 && ps_global->VAR_OPER_DIR
1331 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1332 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1333 /* TRANSLATORS: First arg is the directory name, second is
1334 the file user wants to read but can't. */
1335 _("Can't read file outside %s: %s"),
1336 ps_global->VAR_OPER_DIR, path);
1337 privatekeycontainer = 0;
1340 if(privatekeycontainer
1341 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1342 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1344 !(contents = read_file(path, READ_FROM_LOCALE)))
1345 privatekeycontainer = 0;
1348 if(privatekeycontainer && path[0]){
1349 ps_global->smime->privatetype = Container;
1350 ps_global->smime->privatepath = cpystr(path);
1352 if(contents){
1353 ps_global->smime->privatecontent = contents;
1354 ps_global->smime->personal_certs = mem_to_personal_certs(contents);
1359 /* private keys in a directory of files */
1360 if(!privatekeycontainer){
1361 ps_global->smime->privatetype = Directory;
1363 path[0] = '\0';
1364 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1365 && !IS_REMOTE(path)))
1366 ps_global->smime->privatetype = Nada;
1367 else if(can_access(path, ACCESS_EXISTS)){
1368 if(our_mkpath(path, 0700)){
1369 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1370 ps_global->smime->privatetype = Nada;
1374 if(ps_global->smime->privatetype == Directory)
1375 ps_global->smime->personal_certs = get_personal_certs(path);
1377 setup_certs_backup_by_type(Private);
1382 static void
1383 setup_storage_locations(void)
1385 int publiccertcontainer = 0, cacertcontainer = 0;
1386 char path[MAXPATH+1], *contents;
1388 if(!ps_global->smime)
1389 return;
1391 #ifdef APPLEKEYCHAIN
1392 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1393 ps_global->smime->publictype = Keychain;
1395 else{
1396 #endif /* APPLEKEYCHAIN */
1397 /* Public certificates in a container */
1398 if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
1400 publiccertcontainer = 1;
1401 contents = NULL;
1402 path[0] = '\0';
1403 if(!smime_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
1404 publiccertcontainer = 0;
1406 if(publiccertcontainer && !IS_REMOTE(path)
1407 && ps_global->VAR_OPER_DIR
1408 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1409 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1410 /* TRANSLATORS: First arg is the directory name, second is
1411 the file user wants to read but can't. */
1412 _("Can't read file outside %s: %s"),
1413 ps_global->VAR_OPER_DIR, path);
1414 publiccertcontainer = 0;
1417 if(publiccertcontainer
1418 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1419 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1421 !(contents = read_file(path, READ_FROM_LOCALE)))
1422 publiccertcontainer = 0;
1425 if(publiccertcontainer && path[0]){
1426 ps_global->smime->publictype = Container;
1427 ps_global->smime->publicpath = cpystr(path);
1429 if(contents){
1430 ps_global->smime->publiccontent = contents;
1431 ps_global->smime->publiccertlist = mem_to_certlist(contents, Public);
1436 /* Public certificates in a directory of files */
1437 if(!publiccertcontainer){
1438 ps_global->smime->publictype = Directory;
1440 path[0] = '\0';
1441 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1442 && !IS_REMOTE(path)))
1443 ps_global->smime->publictype = Nada;
1444 else if(can_access(path, ACCESS_EXISTS)){
1445 if(our_mkpath(path, 0700)){
1446 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1447 ps_global->smime->publictype = Nada;
1451 if(ps_global->smime->publictype == Directory)
1452 ps_global->smime->publicpath = cpystr(path);
1455 #ifdef APPLEKEYCHAIN
1457 #endif /* APPLEKEYCHAIN */
1459 setup_privatekey_storage();
1461 /* extra cacerts in a container */
1462 if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
1464 cacertcontainer = 1;
1465 contents = NULL;
1466 path[0] = '\0';
1467 if(!smime_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
1468 cacertcontainer = 0;
1470 if(cacertcontainer && !IS_REMOTE(path)
1471 && ps_global->VAR_OPER_DIR
1472 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1473 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1474 /* TRANSLATORS: First arg is the directory name, second is
1475 the file user wants to read but can't. */
1476 _("Can't read file outside %s: %s"),
1477 ps_global->VAR_OPER_DIR, path);
1478 cacertcontainer = 0;
1481 if(cacertcontainer
1482 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1483 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1485 !(contents = read_file(path, READ_FROM_LOCALE)))
1486 cacertcontainer = 0;
1489 if(cacertcontainer && path[0]){
1490 ps_global->smime->catype = Container;
1491 ps_global->smime->capath = cpystr(path);
1492 ps_global->smime->cacontent = contents;
1493 if(contents)
1494 ps_global->smime->cacertlist = mem_to_certlist(contents, CACert);
1498 if(!cacertcontainer){
1499 ps_global->smime->catype = Directory;
1501 path[0] = '\0';
1502 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1503 && !IS_REMOTE(path)))
1504 ps_global->smime->catype = Nada;
1505 else if(can_access(path, ACCESS_EXISTS)){
1506 if(our_mkpath(path, 0700)){
1507 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1508 ps_global->smime->catype = Nada;
1512 if(ps_global->smime->catype == Directory)
1513 ps_global->smime->capath = cpystr(path);
1519 copy_publiccert_dir_to_container(void)
1521 return(copy_dir_to_container(Public, NULL));
1526 copy_publiccert_container_to_dir(void)
1528 return(copy_container_to_dir(Public));
1533 copy_privatecert_dir_to_container(void)
1535 return(copy_dir_to_container(Private, NULL));
1540 copy_privatecert_container_to_dir(void)
1542 return(copy_container_to_dir(Private));
1547 copy_cacert_dir_to_container(void)
1549 return(copy_dir_to_container(CACert, NULL));
1554 copy_cacert_container_to_dir(void)
1556 return(copy_container_to_dir(CACert));
1559 /* Add the contents of a file to a container. Do not check the content
1560 * of the file, just add it using the format for that container. The
1561 * caller must check the format, so that there is no data corruption
1562 * in the future.
1563 * return value: 0 - success,
1564 * != 0 - failure.
1567 add_file_to_container(WhichCerts ctype, char *fpath, char *altname)
1569 char *sep = (ctype == Public || ctype == Private)
1570 ? EMAILADDRLEADER : CACERTSTORELEADER;
1571 char *content = ctype == Public ? ps_global->smime->publiccontent
1572 : (ctype == Private ? ps_global->smime->privatecontent
1573 : ps_global->smime->cacontent);
1574 char *name;
1575 char *s;
1576 unsigned char c;
1577 struct stat sbuf;
1578 STORE_S *in = NULL;
1579 int rv = -1; /* assume error */
1581 if(our_stat(fpath, &sbuf) < 0
1582 || (in = so_get(FileStar, fpath, READ_ACCESS | READ_FROM_LOCALE)) == NULL)
1583 goto endadd;
1585 if(altname != NULL)
1586 name = altname;
1587 else if((name = strrchr(fpath, '/')) != NULL){
1588 size_t ll;
1589 if((ll = strlen(++name)) > 4 && strucmp(name + ll - 4, EXTCERT(ctype)) == 0)
1590 name[ll-strlen(EXTCERT(ctype))] = '\0';
1592 else
1593 goto endadd;
1595 if(content){
1596 fs_resize((void **)&content, strlen(content) + strlen(sep) + strlen(name) + sbuf.st_size + 3); /* 2 = \n + \n + \0*/
1597 s = content;
1598 content += strlen(content);
1600 else{
1601 s = content = fs_get(strlen(sep) + strlen(name) + sbuf.st_size + 1); /* 2 = \n + \0 */
1602 *content = '\0';
1604 strncat(content, sep, strlen(sep));
1605 strncat(content, name, strlen(name));
1606 content += strlen(content);
1607 *content++ = '\n';
1609 while(so_readc(&c, in))
1610 *content++ = (char) c;
1611 *content = '\0';
1613 switch(ctype){
1614 case Private: ps_global->smime->privatecontent = s; break;
1615 case Public : ps_global->smime->publiccontent = s; break;
1616 case CACert : ps_global->smime->cacontent = s; break;
1617 default : break;
1620 rv = copy_dir_to_container(ctype, s);
1622 endadd:
1623 if(in) so_give(&in);
1625 return rv;
1630 * returns 0 on success, -1 on failure
1631 * contents is an argument which tells this function to write the value
1632 * of this variable instead of reading the contents of the directory.
1633 * If the var contents is not null use its value as the value of the
1634 * container.
1637 copy_dir_to_container(WhichCerts which, char *contents)
1639 int ret = 0, container = 0;
1640 BIO *bio_out = NULL, *bio_in = NULL;
1641 char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
1642 char *tempfile = NULL, fpath[MAXPATH+1];
1643 DIR *dirp;
1644 struct dirent *d;
1645 REMDATA_S *rd = NULL;
1646 char *configdir = NULL;
1647 char *configpath = NULL;
1648 char *configcontainer = NULL;
1649 char *filesuffix = NULL;
1650 char *ret_dir = NULL;
1652 dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1653 smime_init();
1655 srcpath[0] = '\0';
1656 dstpath[0] = '\0';
1657 file[0] = '\0';
1658 emailaddr[0] = '\0';
1660 if(which == Public){
1661 configdir = ps_global->VAR_PUBLICCERT_DIR;
1662 configpath = ps_global->smime->publicpath;
1663 configcontainer = cpystr(DF_PUBLIC_CONTAINER);
1664 filesuffix = ".crt";
1666 else if(which == Private){
1667 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1668 configpath = ps_global->smime->privatepath;
1669 configcontainer = cpystr(DF_PRIVATE_CONTAINER);
1670 filesuffix = ".key";
1672 else if(which == CACert){
1673 configdir = ps_global->VAR_CACERT_DIR;
1674 configpath = ps_global->smime->capath;
1675 configcontainer = cpystr(DF_CA_CONTAINER);
1676 filesuffix = ".crt";
1678 container = SMHOLDERTYPE(which) == Container;
1680 if(!(configdir && configdir[0])){
1681 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1682 return -1;
1685 if(!(configpath && configpath[0])){
1686 #ifdef APPLEKEYCHAIN
1687 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1688 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1689 return -1;
1691 #endif /* APPLEKEYCHAIN */
1692 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1693 return -1;
1696 if(!(filesuffix && strlen(filesuffix) == 4)){
1697 return -1;
1702 * If there is a legit directory to read from set up the
1703 * container file to write to.
1705 if(smime_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
1707 if(IS_REMOTE(configpath)){
1708 rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
1709 NULL, "Error: ",
1710 _("Can't access remote smime configuration."));
1711 if(!rd)
1712 return -1;
1714 (void) rd_read_metadata(rd);
1716 if(rd->access == MaybeRorW){
1717 if(rd->read_status == 'R')
1718 rd->access = ReadOnly;
1719 else
1720 rd->access = ReadWrite;
1723 if(rd->access != NoExists){
1725 rd_check_remvalid(rd, 1L);
1728 * If the cached info says it is readonly but
1729 * it looks like it's been fixed now, change it to readwrite.
1731 if(rd->read_status == 'R'){
1732 rd_check_readonly_access(rd);
1733 if(rd->read_status == 'W'){
1734 rd->access = ReadWrite;
1735 rd->flags |= REM_OUTOFDATE;
1737 else
1738 rd->access = ReadOnly;
1742 if(rd->flags & REM_OUTOFDATE){
1743 if(rd_update_local(rd) != 0){
1745 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1746 rd_close_remdata(&rd);
1747 return -1;
1750 else
1751 rd_open_remote(rd);
1753 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1754 rd_close_remdata(&rd);
1755 return -1;
1758 rd->flags |= DO_REMTRIM;
1760 strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
1761 dstpath[sizeof(dstpath)-1] = '\0';
1763 else{
1764 strncpy(dstpath, configpath, sizeof(dstpath)-1);
1765 dstpath[sizeof(dstpath)-1] = '\0';
1769 * dstpath is either the local Container file or the local cache file
1770 * for the remote Container file.
1772 tempfile = tempfile_in_same_dir(dstpath, "az", &ret_dir);
1776 * If there is a legit directory to read from and a tempfile
1777 * to write to we continue.
1779 if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
1781 if(contents != NULL){
1782 if(BIO_puts(bio_out, contents) < 0)
1783 ret = -1;
1785 else {
1786 if((dirp = opendir(srcpath)) != NULL){
1788 while((d=readdir(dirp)) && !ret){
1789 size_t ll;
1791 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, filesuffix)){
1793 /* copy file name to temp buffer */
1794 strncpy(emailaddr, d->d_name, sizeof(emailaddr)-1);
1795 emailaddr[sizeof(emailaddr)-1] = '\0';
1796 /* chop off suffix trailier */
1797 emailaddr[strlen(emailaddr)-4] = 0;
1800 * This is the separator between the contents of
1801 * different files.
1803 if(which == CACert){
1804 if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
1805 && (BIO_puts(bio_out, emailaddr) > 0)
1806 && (BIO_puts(bio_out, "\n") > 0)))
1807 ret = -1;
1809 else{
1810 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1811 && (BIO_puts(bio_out, emailaddr) > 0)
1812 && (BIO_puts(bio_out, "\n") > 0)))
1813 ret = -1;
1816 /* read then write contents of file */
1817 build_path(file, srcpath, d->d_name, sizeof(file));
1818 if(!(bio_in = BIO_new_file(file, "r")))
1819 ret = -1;
1821 if(!ret){
1822 int good_stuff = 0;
1824 while(BIO_gets(bio_in, line, sizeof(line)) > 0){
1825 if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
1826 good_stuff = 1;
1828 if(good_stuff)
1829 BIO_puts(bio_out, line);
1831 if(strncmp("-----END", line, strlen("-----END")) == 0)
1832 good_stuff = 0;
1836 BIO_free(bio_in);
1840 closedir(dirp);
1844 BIO_free(bio_out);
1846 if(!ret){
1847 if(container && configpath && *configpath){
1848 strncpy(fpath, configpath, sizeof(fpath));
1849 fpath[sizeof(fpath) - 1] = '\0';
1851 else if(ret_dir){
1852 if(strlen(dstpath) + strlen(configcontainer) - strlen(ret_dir) + 1 < sizeof(dstpath))
1853 snprintf(fpath, sizeof(fpath), "%s%c%s",
1854 dstpath, tempfile[strlen(ret_dir)], configcontainer);
1855 else
1856 ret = -1;
1858 else ret = -1;
1860 if(!ret){
1861 if(!IS_REMOTE(configpath)){
1862 if(rename_file(tempfile, fpath) < 0){
1863 q_status_message2(SM_ORDER, 3, 3,
1864 _("Can't rename %s to %s"), tempfile, fpath);
1865 ret = -1;
1866 } else q_status_message1(SM_ORDER, 3, 3,
1867 _("saved container to %s"), fpath);
1869 else { /* if the container is remote, copy it */
1870 int e;
1871 char datebuf[200];
1873 if(rd != NULL && rename_file(tempfile, rd->lf) < 0){
1874 q_status_message2(SM_ORDER, 3, 3,
1875 _("Can't rename %s to %s"), tempfile, rd->lf);
1876 ret = -1;
1879 datebuf[0] = '\0';
1881 if((e = rd_update_remote(rd, datebuf)) != 0){
1882 if(e == -1){
1883 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1884 _("Error opening temporary smime file %s: %s"),
1885 rd->lf, error_description(errno));
1886 dprint((1,
1887 "write_remote_smime: error opening temp file %s\n",
1888 rd->lf ? rd->lf : "?"));
1890 else{
1891 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1892 _("Error copying to %s: %s"),
1893 rd->rn, error_description(errno));
1894 dprint((1,
1895 "write_remote_smime: error copying from %s to %s\n",
1896 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1899 q_status_message(SM_ORDER | SM_DING, 5, 5,
1900 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1902 else{
1903 rd_update_metadata(rd, datebuf);
1904 rd->read_status = 'W';
1907 rd_close_remdata(&rd);
1913 if(tempfile)
1914 fs_give((void **) &tempfile);
1916 if(ret_dir)
1917 fs_give((void **) &ret_dir);
1919 if(configcontainer)
1920 fs_give((void **) &configcontainer);
1922 return ret;
1927 * returns 0 on success, -1 on failure
1930 copy_container_to_dir(WhichCerts which)
1932 char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
1933 char iobuf[4096];
1934 char *contents = NULL;
1935 char *leader = NULL;
1936 char *filesuffix = NULL;
1937 char *configdir = NULL;
1938 char *configpath = NULL;
1939 char *tempfile = NULL;
1940 char *p, *q, *line, *name, *certtext, *save_p;
1941 int len;
1942 BIO *in, *out;
1944 dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1945 smime_init();
1947 path[0] = '\0';
1949 if(which == Public){
1950 leader = EMAILADDRLEADER;
1951 contents = ps_global->smime->publiccontent;
1952 configdir = ps_global->VAR_PUBLICCERT_DIR;
1953 configpath = ps_global->smime->publicpath;
1954 filesuffix = ".crt";
1955 if(!(configpath && configpath[0])){
1956 #ifdef APPLEKEYCHAIN
1957 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1958 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1959 return -1;
1961 #endif /* APPLEKEYCHAIN */
1962 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1963 return -1;
1966 fs_give((void **) &ps_global->smime->publicpath);
1968 path[0] = '\0';
1969 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1970 && !IS_REMOTE(path))){
1971 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1972 return -1;
1975 if(can_access(path, ACCESS_EXISTS)){
1976 if(our_mkpath(path, 0700)){
1977 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1978 return -1;
1982 ps_global->smime->publicpath = cpystr(path);
1983 configpath = ps_global->smime->publicpath;
1985 else if(which == Private){
1986 leader = EMAILADDRLEADER;
1987 contents = ps_global->smime->privatecontent;
1988 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1989 configpath = ps_global->smime->privatepath;
1990 filesuffix = ".key";
1991 if(!(configpath && configpath[0])){
1992 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1993 return -1;
1996 fs_give((void **) &ps_global->smime->privatepath);
1998 path[0] = '\0';
1999 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
2000 && !IS_REMOTE(path))){
2001 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2002 return -1;
2005 if(can_access(path, ACCESS_EXISTS)){
2006 if(our_mkpath(path, 0700)){
2007 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2008 return -1;
2012 ps_global->smime->privatepath = cpystr(path);
2013 configpath = ps_global->smime->privatepath;
2015 else if(which == CACert){
2016 leader = CACERTSTORELEADER;
2017 contents = ps_global->smime->cacontent;
2018 configdir = ps_global->VAR_CACERT_DIR;
2019 configpath = ps_global->smime->capath;
2020 filesuffix = ".crt";
2021 if(!(configpath && configpath[0])){
2022 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2023 return -1;
2026 fs_give((void **) &ps_global->smime->capath);
2028 path[0] = '\0';
2029 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
2030 && !IS_REMOTE(path))){
2031 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2032 return -1;
2035 if(can_access(path, ACCESS_EXISTS)){
2036 if(our_mkpath(path, 0700)){
2037 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2038 return -1;
2042 ps_global->smime->capath = cpystr(path);
2043 configpath = ps_global->smime->capath;
2046 if(!(configdir && configdir[0])){
2047 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
2048 return -1;
2051 if(!(configpath && configpath[0])){
2052 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2053 return -1;
2056 if(!(filesuffix && strlen(filesuffix) == 4)){
2057 return -1;
2061 if(contents && *contents){
2062 for(p = contents; *p != '\0';){
2063 line = p;
2065 while(*p && *p != '\n')
2066 p++;
2068 save_p = NULL;
2069 if(*p == '\n'){
2070 save_p = p;
2071 *p++ = '\0';
2074 if(strncmp(leader, line, strlen(leader)) == 0){
2075 name = line + strlen(leader);
2076 certtext = p;
2077 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
2078 if((q = strstr(certtext, leader)) != NULL){
2079 p = q;
2081 else{ /* end of file */
2082 q = certtext + strlen(certtext);
2083 p = q;
2086 strncpy(buf, name, sizeof(buf)-5);
2087 buf[sizeof(buf)-5] = '\0';
2088 strncat(buf, filesuffix, 5);
2089 build_path(file, configpath, buf, sizeof(file));
2091 in = BIO_new_mem_buf(certtext, q-certtext);
2092 if(in){
2093 tempfile = tempfile_in_same_dir(file, "az", NULL);
2094 out = NULL;
2095 if(tempfile)
2096 out = BIO_new_file(tempfile, "w");
2098 if(out){
2099 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
2100 BIO_write(out, iobuf, len);
2102 BIO_free(out);
2104 if(rename_file(tempfile, file) < 0){
2105 q_status_message2(SM_ORDER, 3, 3,
2106 _("Can't rename %s to %s"),
2107 tempfile, file);
2108 return -1;
2111 fs_give((void **) &tempfile);
2114 BIO_free(in);
2119 if(save_p)
2120 *save_p = '\n';
2124 return 0;
2128 #ifdef APPLEKEYCHAIN
2131 copy_publiccert_container_to_keychain(void)
2133 /* NOT IMPLEMNTED */
2134 return -1;
2138 copy_publiccert_keychain_to_container(void)
2140 /* NOT IMPLEMNTED */
2141 return -1;
2144 #endif /* APPLEKEYCHAIN */
2148 * Get a pointer to a string describing the most recent OpenSSL error.
2149 * It's statically allocated, so don't change or attempt to free it.
2151 static const char *
2152 openssl_error_string(void)
2154 char *errs;
2155 const char *data = NULL;
2156 long errn;
2158 errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2159 errs = (char*) ERR_reason_error_string(errn);
2161 if(errs)
2162 return errs;
2163 else if(data)
2164 return data;
2166 return "unknown error";
2170 /* Return true if the body looks like a PKCS7 object */
2172 is_pkcs7_body(BODY *body)
2174 int result;
2176 result = body->type==TYPEAPPLICATION &&
2177 body->subtype &&
2178 (strucmp(body->subtype,"pkcs7-mime")==0 ||
2179 strucmp(body->subtype,"x-pkcs7-mime")==0 ||
2180 strucmp(body->subtype,"pkcs7-signature")==0 ||
2181 strucmp(body->subtype,"x-pkcs7-signature")==0);
2183 return result;
2188 * Recursively stash a pointer to the decrypted data in our
2189 * manufactured body.
2190 * parameters: type: call of type 1, save the base and header for multipart messages
2191 call of type 0, do not save the base and header for multipart messages
2193 static void
2194 create_local_cache(char *h, char *base, BODY *b, int type)
2196 if(b->type==TYPEMULTIPART){
2197 PART *p;
2199 if(type == 1){
2200 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2201 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2202 } else if(type == 0){
2204 * We don't really want to copy the real body contents. It shouldn't be
2205 * used, and in the case of a message with attachments, we'll be
2206 * duplicating the files multiple times.
2208 cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
2210 for(p=b->nested.part; p; p=p->next)
2211 create_local_cache(h, base, (BODY *) p, type);
2214 else{
2215 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2216 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2221 static long
2222 rfc822_output_func(void *b, char *string)
2224 BIO *bio = (BIO *) b;
2226 return(string ? *string ? (BIO_puts(bio, string) > 0 ? 1L : 0L)
2227 : (BIO_puts(bio, string) >= 0 ? 1L : 0L)
2228 : 0L);
2233 * Attempt to load the private key for the given PERSONAL_CERT.
2234 * This sets the appropriate passphrase globals in order to
2235 * interact with the user correctly.
2237 static int
2238 load_private_key(PERSONAL_CERT *pcert)
2240 if(!pcert->key){
2242 /* Try empty password by default */
2243 char *password = "";
2245 if(ps_global->smime
2246 && (ps_global->smime->need_passphrase
2247 || ps_global->smime->entered_passphrase)){
2248 /* We've already been in here and discovered we need a different password */
2250 if(ps_global->smime->entered_passphrase)
2251 password = (char *) ps_global->smime->passphrase; /* already entered */
2252 else
2253 return 0;
2256 ERR_clear_error();
2258 if(!(pcert->key = load_key(pcert, password, SM_NORMALCERT))){
2259 long err = ERR_get_error();
2261 /* Couldn't load key... */
2263 if(ps_global->smime && ps_global->smime->entered_passphrase){
2265 /* The user got the password wrong maybe? */
2267 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
2268 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
2269 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
2270 else
2271 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2273 /* This passphrase is no good; forget it */
2274 ps_global->smime->entered_passphrase = 0;
2277 if(ps_global->smime){
2278 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2279 ps_global->smime->need_passphrase = 1;
2280 if(ps_global->smime->passphrase_emailaddr){
2281 int i;
2282 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
2283 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
2284 fs_give((void **) ps_global->smime->passphrase_emailaddr);
2287 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
2290 return 0;
2292 else{
2293 /* This key will be cached, so we won't be called again */
2294 if(ps_global->smime){
2295 ps_global->smime->entered_passphrase = 0;
2296 ps_global->smime->need_passphrase = 0;
2300 return 1;
2303 return 0;
2307 static void
2308 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename, char *smime_type)
2310 b->type = TYPEAPPLICATION;
2311 b->subtype = cpystr(type);
2312 b->encoding = ENCBINARY;
2313 b->description = cpystr(description);
2315 b->disposition.type = cpystr("attachment");
2316 set_parameter(&b->disposition.parameter, "filename", filename);
2318 set_parameter(&b->parameter, "name", filename);
2319 if(smime_type && *smime_type)
2320 set_parameter(&b->parameter, "smime-type", smime_type);
2325 * Look for a personal certificate matching the
2326 * given address
2328 PERSONAL_CERT *
2329 match_personal_cert_to_email(ADDRESS *a)
2331 PERSONAL_CERT *pcert = NULL;
2332 char buf[MAXPATH];
2333 char **email;
2334 int i, done;
2336 if(!a || !a->mailbox || !a->host)
2337 return NULL;
2339 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2341 if(ps_global->smime){
2342 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
2343 pcert;
2344 pcert=pcert->next){
2346 if(!pcert->cert)
2347 continue;
2349 email = get_x509_subject_email(pcert->cert);
2351 done = 0;
2352 if(email != NULL){
2353 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
2354 if(email[i] != NULL) done++;
2355 for(i = 0; email[i] != NULL; i++)
2356 fs_give((void **)&email[i]);
2357 fs_give((void **)email);
2360 if(done > 0)
2361 break;
2365 return pcert;
2370 * Look for a personal certificate matching the from
2371 * (or reply_to? in the given envelope)
2373 PERSONAL_CERT *
2374 match_personal_cert(ENVELOPE *env)
2376 PERSONAL_CERT *pcert;
2378 pcert = match_personal_cert_to_email(env->reply_to);
2379 if(!pcert)
2380 pcert = match_personal_cert_to_email(env->from);
2382 return pcert;
2387 * Flatten the given body into its MIME representation.
2388 * Return the result in a BIO.
2390 static BIO *
2391 body_to_bio(BODY *body)
2393 BIO *bio = NULL;
2394 int len;
2396 bio = BIO_new(BIO_s_mem());
2397 if(!bio)
2398 return NULL;
2400 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
2401 pine_write_body_header(body, rfc822_output_func, bio);
2402 pine_rfc822_output_body(body, rfc822_output_func, bio);
2405 * Now need to truncate by two characters since the above
2406 * appends CRLF.
2408 if((len=BIO_ctrl_pending(bio)) > 1){
2409 BUF_MEM *biobuf = NULL;
2411 BIO_get_mem_ptr(bio, &biobuf);
2412 if(biobuf){
2413 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
2417 return bio;
2421 static BIO *
2422 bio_from_store(STORE_S *store)
2424 BIO *ret = NULL;
2426 if(store && store->src == BioType && store->txt){
2427 ret = (BIO *) store->txt;
2430 return(ret);
2434 * Encrypt file; given a path (char *) fp, replace the file
2435 * by an encrypted version of it. If (char *) text is not null, then
2436 * replace the text of (char *) fp by the encrypted version of (char *) text.
2437 * certpath is the FULL path to the file containing the certificate used for
2438 * encryption.
2441 encrypt_file(char *fp, char *text, PERSONAL_CERT *pc)
2443 const EVP_CIPHER *cipher = NULL;
2444 STACK_OF(X509) *encerts = NULL;
2445 BIO *out = NULL;
2446 PKCS7 *p7 = NULL;
2447 int rv = 0;
2449 if(pc == NULL)
2450 return 0;
2452 cipher = EVP_aes_256_cbc();
2453 encerts = sk_X509_new_null();
2455 sk_X509_push(encerts, X509_dup(pc->cert));
2457 if(text){
2458 if((out = BIO_new(BIO_s_mem())) != NULL){
2459 (void) BIO_reset(out);
2460 BIO_puts(out, text);
2463 else if((out = BIO_new_file(fp, "rb")) != NULL)
2464 BIO_read_filename(out, fp);
2466 if((p7 = PKCS7_encrypt(encerts, out, cipher, 0)) != NULL){
2467 BIO_set_close(out, BIO_CLOSE);
2468 BIO_free(out);
2469 if((out = BIO_new_file(fp, "w")) != NULL){
2470 BIO_reset(out);
2471 rv = PEM_write_bio_PKCS7(out, p7);
2472 BIO_flush(out);
2476 if(out != NULL)
2477 BIO_free(out);
2478 PKCS7_free(p7);
2479 sk_X509_pop_free(encerts, X509_free);
2481 return rv;
2485 * Encrypt a message on the way out. Called from call_mailer in send.c
2486 * The body may be reallocated.
2489 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
2491 PKCS7 *p7 = NULL;
2492 BIO *in = NULL;
2493 BIO *out = NULL;
2494 const EVP_CIPHER *cipher = NULL;
2495 STACK_OF(X509) *encerts = NULL;
2496 STORE_S *outs = NULL;
2497 PINEFIELD *pf;
2498 ADDRESS *a;
2499 BODY *body = *bodyP;
2500 BODY *newBody = NULL;
2501 int result = 0;
2502 X509 *cert;
2503 char buf[MAXPATH];
2505 dprint((9, "encrypt_outgoing_message()"));
2506 smime_init();
2508 cipher = EVP_aes_256_cbc();
2510 encerts = sk_X509_new_null();
2512 /* Look for a certificate for each of the recipients */
2513 for(pf = header->local; pf && pf->name; pf = pf->next)
2514 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
2515 for(a=*pf->addr; a; a=a->next){
2516 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2518 if((cert = get_cert_for(buf, Public, 1)) != NULL){
2519 sk_X509_push(encerts,cert);
2520 }else{
2521 q_status_message2(SM_ORDER, 1, 1,
2522 _("Unable to find certificate for <%s@%s>"),
2523 a->mailbox, a->host);
2524 goto end;
2529 /* add the sender's certificate so that they can decrypt the message too */
2530 for(a=header->env->from; a ; a = a->next){
2531 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2533 if((cert = get_cert_for(buf, Public, 1)) != NULL
2534 && sk_X509_find(encerts, cert) == -1)
2535 sk_X509_push(encerts,cert);
2538 in = body_to_bio(body);
2540 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
2542 outs = so_get(BioType, NULL, EDIT_ACCESS);
2543 out = bio_from_store(outs);
2545 i2d_PKCS7_bio(out, p7);
2546 (void) BIO_flush(out);
2548 so_seek(outs, 0, SEEK_SET);
2550 newBody = mail_newbody();
2552 newBody->type = TYPEAPPLICATION;
2553 newBody->subtype = cpystr("pkcs7-mime");
2554 newBody->encoding = ENCBINARY;
2556 newBody->disposition.type = cpystr("attachment");
2557 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
2559 newBody->description = cpystr("S/MIME Encrypted Message");
2560 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
2561 set_parameter(&newBody->parameter, "name", "smime.p7m");
2563 newBody->contents.text.data = (unsigned char *) outs;
2565 *bodyP = newBody;
2567 result = 1;
2569 end:
2571 BIO_free(in);
2572 PKCS7_free(p7);
2573 sk_X509_pop_free(encerts, X509_free);
2575 dprint((9, "encrypt_outgoing_message returns %d", result));
2576 return result;
2581 Get (and decode) the body of the given section of msg
2583 static STORE_S*
2584 get_part_contents(long msgno, const char *section)
2586 long len;
2587 gf_io_t pc;
2588 STORE_S *store = NULL;
2589 char *err;
2591 store = so_get(CharStar, NULL, EDIT_ACCESS);
2592 if(store){
2593 gf_set_so_writec(&pc,store);
2595 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
2597 gf_clear_so_writec(store);
2599 so_seek(store, 0, SEEK_SET);
2601 if(err)
2602 so_give(&store);
2605 return store;
2609 static PKCS7 *
2610 get_pkcs7_from_part(long msgno,const char *section)
2612 STORE_S *store = NULL;
2613 PKCS7 *p7 = NULL;
2614 BIO *in = NULL;
2616 store = get_part_contents(msgno, section);
2618 if(store){
2619 if(store->src == CharStar){
2620 int len;
2623 * We're reaching inside the STORE_S structure. We should
2624 * probably have a way to get the length, instead.
2626 len = (int) (store->eod - store->dp);
2627 in = BIO_new_mem_buf(store->txt, len);
2629 else{ /* just copy it */
2630 unsigned char c;
2632 in = BIO_new(BIO_s_mem());
2633 (void) BIO_reset(in);
2635 so_seek(store, 0L, 0);
2636 while(so_readc(&c, store)){
2637 BIO_write(in, &c, 1);
2641 if(in){
2642 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2643 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
2644 /* error */
2647 BIO_free(in);
2650 so_give(&store);
2653 return p7;
2656 int same_cert(X509 *x, X509 *cert)
2658 char bufcert[256], bufx[256];
2659 int rv = 0;
2661 get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert), ":");
2662 get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx), ":");
2663 if(strcmp(bufx, bufcert) == 0)
2664 rv = 1;
2666 return rv;
2670 /* extract and save certificates from a PKCS7 package. The ctype variable
2671 * tells us if we want to extract it to a public/ or a ca/ directory. The
2672 * later makes sense only for recoverable errors (errors that can be fixed
2673 * by saving to the ca/ directory before we verify the signature).
2674 * Return value:
2675 * 0 - no errors (in public/) no need to try again,
2676 * or validated self signed certificate (in ca/)
2677 * < 0 - certificate error is not recoverable, don't even think about it.
2680 int smime_extract_and_save_cert(PKCS7 *p7, int check_cert)
2682 STACK_OF(X509) *signers;
2683 X509 *x, *cert;
2684 char **email;
2685 int i, j;
2686 long error;
2688 if((signers = PKCS7_get0_signers(p7, NULL, 0)) == NULL)
2689 return -1;
2691 for(i = 0; i < sk_X509_num(signers); i++){
2692 if((x = sk_X509_value(signers,i)) == NULL)
2693 continue;
2695 if((email = get_x509_subject_email(x)) != NULL){
2696 for(j = 0; email[j] != NULL; j++){
2697 if((cert = get_cert_for(email[j], Public, 1)) == NULL
2698 || same_cert(x, cert) == 0){
2699 if(check_cert == 0
2700 || smime_validate_cert(x, &error) == 0
2701 || (*pith_smime_confirm_save)(email[j]) == 1)
2702 save_cert_for(email[j], x, Public);
2704 if(cert != NULL)
2705 X509_free(cert);
2706 fs_give((void **) &email[j]);
2708 fs_give((void **) email);
2711 sk_X509_free(signers);
2713 return 0;
2717 * Try to verify a signature.
2719 * p7 - the pkcs7 object to verify
2720 * in - the plain data to verify (NULL if not detached)
2721 * out - BIO to which to write the opaque data
2722 * silent - if non zero, do not print errors, only print success.
2724 static int
2725 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
2727 STACK_OF(X509) *otherCerts = NULL;
2728 CertList *cl;
2729 int result, flags;
2730 const char *data;
2731 long err;
2733 if(!s_cert_store){
2734 if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
2735 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2737 return -1;
2740 flags = F_ON(F_USE_CERT_STORE_ONLY, ps_global) ? PKCS7_NOINTERN : 0;
2742 if(ps_global->smime->publiccertlist == NULL){
2743 renew_cert_data(&ps_global->smime->publiccertlist, Public);
2744 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next){
2745 if(cl->x509_cert == NULL){
2746 char *s = strrchr(cl->name, '.');
2747 *s = '\0';
2748 cl->x509_cert = get_cert_for(cl->name, Public, 1);
2749 *s = '.';
2754 if(ps_global->smime->publiccertlist){
2755 otherCerts = sk_X509_new_null();
2756 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next)
2757 if(cl->x509_cert != NULL)
2758 sk_X509_push(otherCerts, X509_dup(cl->x509_cert));
2761 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, flags);
2763 sk_X509_pop_free(otherCerts, X509_free);
2765 if(result){
2766 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
2768 else{
2769 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2771 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
2773 /* Retry verification so we can get the plain text */
2774 /* Might be better to reimplement PKCS7_verify here? */
2776 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY);
2778 if (!silent) q_status_message1(SM_ORDER | SM_DING, 3, 3,
2779 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
2782 return result;
2786 void
2787 free_smime_body_sparep(void **sparep)
2789 char *s;
2790 SIZEDTEXT *st;
2791 if(sparep && *sparep){
2792 switch(get_smime_sparep_type(*sparep)){
2793 case P7Type: PKCS7_free((PKCS7 *) get_smime_sparep_data(*sparep));
2794 break;
2795 case CharType: s = (char *)get_smime_sparep_data(*sparep);
2796 fs_give((void **) &s);
2797 break;
2798 case SizedText : st = (SIZEDTEXT *)get_smime_sparep_data(*sparep);
2799 fs_give((void **) &st->data);
2800 fs_give((void **) &st);
2801 break;
2802 default : break;
2804 ((SMIME_SPARE_S *)(*sparep))->data = NULL;
2805 fs_give(sparep);
2809 /* Big comment, explaining the mess that exists out there, and how we deal
2810 with it, and also how we solve the problems that are created this way.
2812 When Alpine sends a message, it constructs that message, computes the
2813 signature, but then it forgets the message it signed and reconstructs it
2814 again. Since it signs a message containing a notice about "mime aware
2815 tools", but it does not send that we do not include that in the part
2816 that is signed, and that takes care of much of the problems.
2818 Another problem is what is received from the servers. All servers tested
2819 seem to transmit the message that was signed intact and Alpine can check
2820 the signature correctly. That is not a problem. The problem arises when
2821 the message includes attachments. In this case different servers send
2822 different things, so it will be up to us to figure out what is the text
2823 that was actually signed. Confused? here is the story:
2825 When a message containing and attachment is sent by Alpine, UW-IMAP,
2826 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2827 that was sent by Alpine, but GMX.com, Exchange, and probably other
2828 servers add a trailing \r\n in the message, so when validating the
2829 signature, these messages will not validate. There are several things
2830 that can be done.
2832 1. Add a trailing \r\n to any message that contains attachments, sign that
2833 and send that. In this way, all messages will validate with all
2834 servers.
2836 2. Compatibility mode: If a message has an attachment, contains a trailing
2837 \r\n and does not validate (sent by an earlier version of Alpine),
2838 remove the trailing \r\n and try to revalidate again.
2840 3. We do not add \r\n to validate a message that we sent, because that
2841 would only work in Alpine, and not in any other client. That would
2842 not be a good thing to do.
2844 PART II
2846 Now we have to deal with encrypted and signed messages. The problem is
2847 that c-client makes all its pointers point to "on disk" content, but
2848 since we decrypted the data earlier, we have to make sure of two things.
2849 One is that we saved that data (so we do not have to decrypt it again)
2850 and second that we can use it.
2852 In order to save the data we use create_local_cache, so that we do not
2853 have to redecrypt the message. Once this is saved, c-client functions will
2854 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2856 PART III
2858 When we are trying to verify messages with detached signatures, some
2859 imap servers send incorrect information in the mail_fetch_mime call. By
2860 incorrect I mean that this is not fetched directly from the message, but
2861 it is read from the message, processed, and then the processed part is
2862 sent to us, so this text might not agree with what is in the message,
2863 and so the validation of the signature might fail. However, the good
2864 news is that the message validates if saved to a local folder. This
2865 means that if normal validation does not work we can make it work by
2866 saving the message locally and validating that. This is implemented
2867 below, and causes delay in the display of the message. I am considering
2868 at this time not to do this automatically, but wait for the user to tell
2869 us to do it for them by means of a command available in the
2870 mail_view_screen. This might help in other situations, where a message
2871 is supposed to have an attachment, but it can not be seen in the
2872 processed text. Nevertheless, at this time, this is automatic, and is
2873 causing a delay in the processing of the message, but it is validating
2874 correctly all messages.
2876 PART IV
2878 When the user sends a message as encrypted and signed, this code used to
2879 encrypt first, and then sign the pkcs7 body, but it turns out that some
2880 other clients can not handle these messages. While we could argue that the
2881 other clients need to improve, we will support reading messages in both
2882 ways, and will send messages using this technique; that is, signed first,
2883 encrypted second. It seems that all tested clients support this way, so it
2884 should be safe to do so.
2887 typedef struct smime_filter_s {
2888 void (*filter)();
2889 } SMIME_FILTER_S;
2891 SMIME_FILTER_S sig_filter[] = {
2892 {smime_remove_trailing_crlf},
2893 {smime_remove_folding_space}
2896 #define TOTAL_FILTERS (sizeof(sig_filter)/sizeof(sig_filter[0]))
2897 #define TOTAL_SIGFLTR (1 << TOTAL_FILTERS) /* not good, keep filters to a low number */
2899 void
2900 smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen,
2901 char **bodytext, unsigned long *bodylen)
2903 if(*bodylen > 2 && !strncmp(*bodytext+*bodylen-2, "\r\n", 2))
2904 *bodylen -= 2;
2907 void
2908 smime_remove_folding_space(char **mimetext, unsigned long *mimelen,
2909 char **bodytext, unsigned long *bodylen)
2911 char *s = NULL, *t;
2912 unsigned long mlen = *mimelen;
2914 if(*mimetext){
2915 for (s = t = *mimetext; t - *mimetext < *mimelen; ){
2916 if(*t == '\r' && *(t+1) == '\n' && (*(t+2) == '\t' || *(t+2) == ' ')){
2917 *s++ = ' ';
2918 t += 3;
2919 mlen -= 2;
2921 else
2922 *s++ = *t++;
2924 *mimelen = mlen;
2929 smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag)
2931 int result, i, j, flag;
2932 char *mtext, *btext;
2933 unsigned long mlen, blen;
2934 BIO *in;
2936 mtext = mimelen ? fs_get(mimelen+1) : NULL;
2937 btext = fs_get(bodylen+1);
2939 flag = 1; /* silence all failures */
2940 for(i = 1; result == 0 && i < TOTAL_SIGFLTR; i++){
2941 if((in = BIO_new(BIO_s_mem())) == NULL)
2942 return -1;
2944 (void) BIO_reset(in);
2946 if(i+1 == TOTAL_SIGFLTR)
2947 flag = nflag;
2949 if(mimelen)
2950 strncpy(mtext, mimetext, mlen = mimelen);
2951 strncpy(btext, bodytext, blen = bodylen);
2952 for(j = 0; j < TOTAL_FILTERS; j++)
2953 if((i >> j) & 1)
2954 (sig_filter[j].filter)(&mtext, &mlen, &btext, &blen);
2955 if(mtext != NULL)
2956 BIO_write(in, mtext, mlen);
2957 BIO_write(in, btext, blen);
2958 result = do_signature_verify(p7, in, NULL, flag);
2959 BIO_free(in);
2961 if(mtext) fs_give((void **)&mtext);
2962 if(btext) fs_give((void **)&btext);
2963 return result;
2967 * Given a multipart body of type multipart/signed, attempt to verify it.
2968 * Returns non-zero if the body was changed.
2970 static int
2971 do_detached_signature_verify(BODY *b, long msgno, char *section)
2973 PKCS7 *p7 = NULL;
2974 BIO *in = NULL;
2975 PART *p;
2976 int result, modified_the_body = 0;
2977 int flag; /* 1 silent, 0 not silent */
2978 int saved = 0;
2979 unsigned long mimelen, bodylen;
2980 char newSec[100], *mimetext, *bodytext;
2981 char *what_we_did;
2982 SIZEDTEXT *st;
2984 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"));
2986 smime_init();
2988 /* if it was signed and then encrypted, use the decrypted text
2989 * to check the validity of the signature
2991 if(b->sparep){
2992 if(get_smime_sparep_type(b->sparep) == SizedText){
2993 /* bodytext includes mimetext */
2994 st = (SIZEDTEXT *) get_smime_sparep_data(b->sparep);
2995 bodytext = (char *) st->data;
2996 bodylen = st->size;
2997 mimetext = NULL;
2998 mimelen = 0L;
3001 else{
3002 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3003 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3004 if(mimetext)
3005 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3007 if(mimetext == NULL || bodytext == NULL)
3008 return modified_the_body;
3011 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3013 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
3014 || (in = BIO_new(BIO_s_mem())) == NULL)
3015 return modified_the_body;
3017 (void) BIO_reset(in);
3018 if(mimetext != NULL)
3019 BIO_write(in, mimetext, mimelen);
3020 BIO_write(in, bodytext, bodylen);
3022 saved = smime_extract_and_save_cert(p7, F_ON(F_USE_CERT_STORE_ONLY, ps_global));
3023 if(saved < 0 && F_ON(F_USE_CERT_STORE_ONLY, ps_global))
3024 return modified_the_body;
3026 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3027 flag = (mimelen == 0 || !IS_REMOTE(ps_global->mail_stream->mailbox))
3028 ? 0 : 1;
3029 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, flag);
3030 if(result < 0)
3031 return modified_the_body;
3032 if(result == 0
3033 && mimelen > 0 /* do not do this for encrypted messages */
3034 && IS_REMOTE(ps_global->mail_stream->mailbox)){
3035 char *fetch;
3036 unsigned long hlen, tlen;
3037 STORE_S *msg_so;
3039 BIO_free(in);
3040 if((in = BIO_new(BIO_s_mem())) != NULL
3041 && (fetch = mail_fetch_header(ps_global->mail_stream, msgno, NULL,
3042 NULL, &hlen, FT_PEEK)) != NULL
3043 && (msg_so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL
3044 && so_nputs(msg_so, fetch, (long) hlen)
3045 && (fetch = pine_mail_fetch_text(ps_global->mail_stream, msgno, NULL,
3046 &tlen, FT_PEEK)) != NULL
3047 && so_nputs(msg_so, fetch, tlen)){
3048 STRING bs;
3049 char *h = (char *) so_text(msg_so);
3050 char *bstart = strstr(h, "\r\n\r\n");
3051 ENVELOPE *env;
3052 BODY *body, *tmpB;
3054 bstart += 4;
3055 INIT(&bs, mail_string, bstart, tlen);
3056 rfc822_parse_msg_full(&env, &body, h, bstart-h-4, &bs, BADHOST, 0, 0);
3057 mail_free_envelope(&env);
3059 mail_free_body_part(&b->nested.part);
3060 tmpB = mail_body_section(body, (unsigned char *) section);
3061 if(MIME_MSG(tmpB->type, tmpB->subtype))
3062 b->nested.part = tmpB->nested.msg->body->nested.part;
3063 else
3064 b->nested.part = tmpB->nested.part;
3065 create_local_cache(bstart, bstart, &b->nested.part->body, 1);
3066 modified_the_body = 1;
3068 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3070 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3072 if(mimetext)
3073 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3075 if (mimetext == NULL || bodytext == NULL)
3076 return modified_the_body;
3078 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3080 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL)
3081 return modified_the_body;
3083 (void) BIO_reset(in);
3084 BIO_write(in, mimetext, mimelen);
3085 BIO_write(in, bodytext, bodylen);
3086 so_give(&msg_so);
3088 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3089 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, 0);
3090 if(result < 0)
3091 return modified_the_body;
3097 BIO_free(in);
3098 if(b->subtype)
3099 fs_give((void**) &b->subtype);
3101 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3102 b->encoding = ENC8BIT;
3104 if(b->description)
3105 fs_give ((void**) &b->description);
3107 what_we_did = result ? _("This message was cryptographically signed.") :
3108 _("This message was cryptographically signed but the signature could not be verified.");
3110 b->description = cpystr(what_we_did);
3112 b->sparep = create_smime_sparep(P7Type, p7);
3114 p = b->nested.part;
3116 /* p is signed plaintext */
3117 if(p && p->next)
3118 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
3120 modified_the_body = 1;
3122 return modified_the_body;
3126 PERSONAL_CERT *
3127 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
3129 PERSONAL_CERT *x = NULL;
3131 if(ps_global->smime){
3132 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
3133 X509 *mine;
3135 mine = x->cert;
3137 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,mine->cert_info->issuer) &&
3138 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,mine->cert_info->serialNumber)){
3139 break;
3144 return x;
3148 static PERSONAL_CERT *
3149 find_certificate_matching_pkcs7(PKCS7 *p7)
3151 int i;
3152 STACK_OF(PKCS7_RECIP_INFO) *recips;
3153 PERSONAL_CERT *x = NULL;
3155 recips = p7->d.enveloped->recipientinfo;
3157 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
3158 PKCS7_RECIP_INFO *ri;
3160 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
3162 if((x=find_certificate_matching_recip_info(ri))!=0){
3163 break;
3167 return x;
3170 /* decrypt an encrypted file.
3171 Args: fp - the path to the encrypted file.
3172 rv - a code that tells the caller what happened inside the function
3173 pcert - a personal certificate that was used to encrypt this file
3174 Returns the decoded text allocated in a char *, whose memory must be
3175 freed by caller
3178 char *
3179 decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc)
3181 PKCS7 *p7 = NULL;
3182 char *text, *tmp;
3183 BIO *in = NULL, *out = NULL;
3184 int i, j;
3185 long unsigned int len;
3186 void *ret;
3188 if(pc == NULL || (text = read_file(fp, 0)) == NULL || *text == '\0')
3189 return NULL;
3191 tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1);
3192 for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0'
3193 && text[i] != '-'; j++, i++)
3194 tmp[j] = text[i];
3195 tmp[j] = '\0';
3197 ret = rfc822_base64((unsigned char *)tmp, strlen(tmp), &len);
3199 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
3200 p7 = d2i_PKCS7_bio(in, NULL);
3201 BIO_free(in);
3204 if (text) fs_give((void **)&text);
3205 if (ret) fs_give((void **)&ret);
3207 if (rv) *rv = pc->key == NULL ? -1 : 1;
3209 out = BIO_new(BIO_s_mem());
3210 (void) BIO_reset(out);
3212 if(PKCS7_decrypt(p7, pc->key, pc->cert, out, 0) != 0){
3213 BIO_get_mem_data(out, &tmp);
3214 text = cpystr(tmp);
3215 BIO_free(out);
3216 } else
3217 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3218 (char *) openssl_error_string());
3219 PKCS7_free(p7);
3221 return text;
3225 * Try to decode (decrypt or verify a signature) a PKCS7 body
3226 * Returns non-zero if something was changed.
3228 static int
3229 do_decoding(BODY *b, long msgno, const char *section)
3231 int modified_the_body = 0;
3232 BIO *out = NULL;
3233 PKCS7 *p7 = NULL;
3234 X509 *recip = NULL;
3235 EVP_PKEY *key = NULL;
3236 PERSONAL_CERT *pcert = NULL;
3237 char *what_we_did = "";
3238 char null[1];
3240 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"));
3241 null[0] = '\0';
3242 smime_init();
3245 * Extract binary data from part to an in-memory store
3248 if(b->sparep){
3249 if(get_smime_sparep_type(b->sparep) == P7Type)
3250 p7 = (PKCS7*) get_smime_sparep_data(b->sparep);
3252 else{
3253 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
3254 if(!p7){
3255 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
3256 (char*) openssl_error_string());
3257 goto end;
3261 * Save the PKCS7 object for later dealings by the user interface.
3262 * It will be cleaned up when the body is garbage collected.
3264 b->sparep = create_smime_sparep(P7Type, p7);
3267 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
3269 if(PKCS7_type_is_signed(p7)){
3270 int sigok;
3272 out = BIO_new(BIO_s_mem());
3273 (void) BIO_reset(out);
3274 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3276 sigok = do_signature_verify(p7, NULL, out, 0);
3278 what_we_did = sigok ? _("This message was cryptographically signed.") :
3279 _("This message was cryptographically signed but the signature could not be verified.");
3281 /* make sure it's null terminated */
3282 BIO_write(out, null, 1);
3284 else if(!PKCS7_type_is_enveloped(p7)){
3285 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
3286 goto end;
3288 else{ /* It *is* enveloped */
3289 int decrypt_result;
3291 what_we_did = _("This message was encrypted.");
3293 /* now need to find a cert that can decrypt this */
3294 pcert = find_certificate_matching_pkcs7(p7);
3296 if(!pcert){
3297 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3298 goto end;
3301 recip = pcert->cert;
3303 if(!load_private_key(pcert)
3304 && ps_global->smime
3305 && ps_global->smime->need_passphrase
3306 && !ps_global->smime->already_auto_asked){
3307 /* Couldn't load key with blank password, ask user */
3308 ps_global->smime->already_auto_asked = 1;
3309 if(pith_opt_smime_get_passphrase){
3310 (*pith_opt_smime_get_passphrase)();
3311 load_private_key(pcert);
3315 key = pcert->key;
3316 if(!key)
3317 goto end;
3319 out = BIO_new(BIO_s_mem());
3320 (void) BIO_reset(out);
3321 BIO_puts(out, "MIME-Version: 1.0\r\n");
3323 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
3325 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3326 forget_private_keys();
3328 if(!decrypt_result){
3329 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3330 (char*) openssl_error_string());
3331 goto end; }
3333 BIO_write(out, null, 1);
3337 * We've now produced a flattened MIME object in BIO out.
3338 * It needs to be turned back into a BODY.
3341 if(out){
3342 BODY *body;
3343 ENVELOPE *env;
3344 char *h = NULL;
3345 char *bstart;
3346 STRING s;
3347 BUF_MEM *bptr = NULL;
3349 BIO_get_mem_ptr(out, &bptr);
3350 if(bptr)
3351 h = bptr->data;
3353 /* look for start of body */
3354 bstart = strstr(h, "\r\n\r\n");
3356 if(!bstart){
3357 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
3359 else{
3360 SIZEDTEXT *st;
3361 bstart += 4; /* skip over CRLF*2 */
3363 INIT(&s, mail_string, bstart, strlen(bstart));
3364 rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
3365 mail_free_envelope(&env); /* Don't care about this */
3367 if(body->type == TYPEMULTIPART
3368 && !strucmp(body->subtype, "SIGNED")){
3369 char *cookie = NULL;
3370 PARAMETER *param;
3371 for (param = body->parameter; param && !cookie; param = param->next)
3372 if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value;
3373 if(cookie != NULL){
3374 st = fs_get(sizeof(SIZEDTEXT));
3375 st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */
3376 st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4);
3377 body->sparep = create_smime_sparep(SizedText, (void *)st);
3379 else
3380 q_status_message(SM_ORDER, 3, 3, _("Couldn't find cookie in attachment list."));
3382 body->mime.offset = 0;
3383 body->mime.text.size = 0;
3386 * Now convert original body (application/pkcs7-mime)
3387 * to a multipart body with one sub-part (the decrypted body).
3388 * Note that the sub-part may also be multipart!
3391 b->type = TYPEMULTIPART;
3392 if(b->subtype)
3393 fs_give((void**) &b->subtype);
3396 * This subtype is used in mailview.c to annotate the display of
3397 * encrypted or signed messages. We know for sure then that it's a PKCS7
3398 * part because the sparep field is set to the PKCS7 object (see above).
3400 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3401 b->encoding = ENC8BIT;
3403 if(b->description)
3404 fs_give((void**) &b->description);
3406 b->description = cpystr(what_we_did);
3408 if(b->disposition.type)
3409 fs_give((void **) &b->disposition.type);
3411 if(b->contents.text.data)
3412 fs_give((void **) &b->contents.text.data);
3414 if(b->parameter)
3415 mail_free_body_parameter(&b->parameter);
3417 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3418 b->nested.part = fs_get(sizeof(PART));
3419 b->nested.part->body = *body;
3420 b->nested.part->next = NULL;
3422 fs_give((void**) &body);
3425 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3426 * the decrypted data. Otherwise, it'll try to load it from the original
3427 * data. Eek.
3429 create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body, 0);
3431 modified_the_body = 1;
3435 end:
3436 if(out)
3437 BIO_free(out);
3439 return modified_the_body;
3444 * Recursively handle PKCS7 bodies in our message.
3446 * Returns non-zero if some fiddling was done.
3448 static int
3449 do_fiddle_smime_message(BODY *b, long msgno, char *section)
3451 int modified_the_body = 0;
3453 if(!b)
3454 return 0;
3456 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"));
3458 if(is_pkcs7_body(b)){
3460 if(do_decoding(b, msgno, section)){
3462 * b should now be a multipart message:
3463 * fiddle it too in case it's been multiply-encrypted!
3466 /* fallthru */
3467 modified_the_body = 1;
3471 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
3473 PART *p;
3474 int partNum;
3475 char newSec[100];
3477 if(MIME_MULT_SIGNED(b->type, b->subtype)){
3481 * Ahah. We have a multipart signed entity.
3483 * Multipart/signed
3484 * part 1 (signed thing)
3485 * part 2 (the pkcs7 signature)
3487 * We're going to convert that to
3489 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3490 * part 1 (signed thing)
3491 * part 2 has been freed
3493 * We also extract the signature from part 2 and save it
3494 * in the multipart body->sparep, and we add a description
3495 * in the multipart body->description.
3498 * The results of a decrypted message will be similar. It
3499 * will be
3501 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3502 * part 1 (decrypted thing)
3505 modified_the_body += do_detached_signature_verify(b, msgno, section);
3507 else if(MIME_MSG(b->type, b->subtype)){
3508 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
3510 else{
3512 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
3513 /* Append part number to the section string */
3515 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
3517 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
3522 return modified_the_body;
3527 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3528 * Returns non-zero if something was changed.
3531 fiddle_smime_message(BODY *b, long msgno)
3533 return do_fiddle_smime_message(b, msgno, "");
3537 /********************************************************************************/
3541 * Output a string in a distinctive style
3543 void
3544 gf_puts_uline(char *txt, gf_io_t pc)
3546 pc(TAG_EMBED); pc(TAG_BOLDON);
3547 gf_puts(txt, pc);
3548 pc(TAG_EMBED); pc(TAG_BOLDOFF);
3551 /* get_chain_for_cert: error and level are mandatory arguments */
3552 STACK_OF(X509) *
3553 get_chain_for_cert(X509 *cert, int *error, int *level)
3555 STACK_OF(X509) *chain = NULL;
3556 X509_STORE_CTX *ctx;
3557 X509 *x, *xtmp;
3558 int rc; /* return code */
3560 *level = -1;
3561 *error = 0;
3562 ERR_clear_error();
3563 if((ctx = X509_STORE_CTX_new()) != NULL){
3564 X509_STORE_set_flags(s_cert_store, 0);
3565 if(!X509_STORE_CTX_init(ctx, s_cert_store, cert, NULL))
3566 *error = X509_STORE_CTX_get_error(ctx);
3567 else if((chain = sk_X509_new_null()) != NULL){
3568 for(x = cert; ; x = xtmp){
3569 if(++*level > 0)
3570 sk_X509_push(chain, X509_dup(x));
3571 rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x);
3572 if(rc < 0)
3573 *error = 1;
3574 if(rc <= 0)
3575 break;
3576 if(!X509_check_issued(xtmp, xtmp))
3577 break;
3580 X509_STORE_CTX_free(ctx);
3582 return chain;
3587 * Sign a message. Called from call_mailer in send.c.
3589 * This takes the header for the outgoing message as well as a pointer
3590 * to the current body (which may be reallocated).
3591 * The last argument (BODY **bp) is an argument that tells Alpine
3592 * if the body has 8 bit. if *bp is not null we compute two signatures
3593 * one for the quoted-printable encoded message, and another for the
3594 * 8bit encoded message. We return the signature for the 8bit encoded
3595 * part in p2->body.mime.text.data.
3596 * The reason why we compute two signatures is so that we can decide
3597 * which one to use later, and we only do it in the case that *bp is
3598 * not null. If we did not do this, then we might not be able to sign
3599 * a message until we log in to the smtp server, so instead of doing
3600 * that, we get ready for any possible situation we might find.
3603 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp)
3605 STORE_S *outs = NULL;
3606 STORE_S *outs_2 = NULL;
3607 BODY *body = *bodyP;
3608 BODY *newBody = NULL;
3609 PART *p1 = NULL;
3610 PART *p2 = NULL;
3611 PERSONAL_CERT *pcert;
3612 BIO *in = NULL;
3613 BIO *in_2 = NULL;
3614 BIO *out = NULL;
3615 BIO *out_2 = NULL;
3616 PKCS7 *p7 = NULL;
3617 PKCS7 *p7_2 = NULL;
3618 STACK_OF(X509) *chain;
3619 int result = 0, error;
3620 int flags = dont_detach ? 0 : PKCS7_DETACHED;
3621 int level;
3623 dprint((9, "sign_outgoing_message()"));
3625 smime_init();
3627 /* Look for a private key matching the sender address... */
3629 pcert = match_personal_cert(header->env);
3631 if(!pcert){
3632 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
3633 goto end;
3636 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
3637 /* Couldn't load key with blank password, try again */
3638 if(pith_opt_smime_get_passphrase){
3639 (*pith_opt_smime_get_passphrase)();
3640 load_private_key(pcert);
3644 if(!pcert->key)
3645 goto end;
3647 if(((chain = get_chain_for_cert(pcert->cert, &error, &level)) != NULL && error)
3648 || level == 0){
3649 sk_X509_pop_free(chain, X509_free);
3650 chain = NULL;
3653 if(error)
3654 q_status_message(SM_ORDER, 1, 1,
3655 _("Not all certificates needed to verify signature included in signed message"));
3657 in = body_to_bio(body);
3659 p7 = PKCS7_sign(pcert->cert, pcert->key, chain, in, flags);
3661 if(bp && *bp){
3662 int i, save_encoding;
3664 for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++);
3666 if(i > ENCMAX){ /* no empty encoding slots! */
3667 *bp = NULL;
3669 else {
3670 save_encoding = (*bp)->encoding;
3671 body_encodings[(*bp)->encoding = i] = body_encodings[ENC8BIT];
3673 in_2 = body_to_bio(body);
3675 body_encodings[i] = NULL;
3676 (*bp)->encoding = save_encoding;
3680 if(bp && *bp)
3681 p7_2 = PKCS7_sign(pcert->cert, pcert->key, chain, in_2, flags);
3683 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3684 forget_private_keys();
3686 if(chain)
3687 sk_X509_pop_free(chain, X509_free);
3689 if(!p7){
3690 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
3691 goto end;
3694 outs = so_get(BioType, NULL, EDIT_ACCESS);
3695 out = bio_from_store(outs);
3697 i2d_PKCS7_bio(out, p7);
3698 (void) BIO_flush(out);
3700 so_seek(outs, 0, SEEK_SET);
3702 if(bp && *bp && p7_2){
3703 outs_2 = so_get(BioType, NULL, EDIT_ACCESS);
3704 out_2 = bio_from_store(outs_2);
3706 i2d_PKCS7_bio(out_2, p7_2);
3707 (void) BIO_flush(out_2);
3709 so_seek(outs_2, 0, SEEK_SET);
3712 if((flags&PKCS7_DETACHED)==0){
3714 /* the simple case: the signed data is in the pkcs7 object */
3716 newBody = mail_newbody();
3718 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3720 newBody->contents.text.data = (unsigned char *) outs;
3721 *bodyP = newBody;
3723 result = 1;
3725 else{
3728 * OK.
3729 * We have to create a new body as follows:
3731 * multipart/signed; blah blah blah
3732 * reference to existing body
3734 * pkcs7 object
3737 newBody = mail_newbody();
3739 newBody->type = TYPEMULTIPART;
3740 newBody->subtype = cpystr("signed");
3741 newBody->encoding = ENC7BIT;
3743 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
3744 set_parameter(&newBody->parameter, "micalg", "sha1");
3746 p1 = mail_newbody_part();
3747 p2 = mail_newbody_part();
3750 * This is nasty. We're just copying the body in here,
3751 * but since our newBody is freed at the end of call_mailer,
3752 * we mustn't let this body (the original one) be freed twice.
3754 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
3756 p1->next = p2;
3758 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL);
3759 p2->body.mime.text.data = (unsigned char *) outs_2;
3760 p2->body.contents.text.data = (unsigned char *) outs;
3762 newBody->nested.part = p1;
3764 *bodyP = newBody;
3766 result = 1;
3769 end:
3771 PKCS7_free(p7);
3772 BIO_free(in);
3774 if(bp && *bp){
3775 if(p7_2) PKCS7_free(p7_2);
3776 BIO_free(in_2);
3779 dprint((9, "sign_outgoing_message returns %d", result));
3780 return result;
3784 SMIME_STUFF_S *
3785 new_smime_struct(void)
3787 SMIME_STUFF_S *ret = NULL;
3789 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
3790 memset((void *) ret, 0, sizeof(*ret));
3791 ret->publictype = Nada;
3793 return ret;
3797 static void
3798 free_smime_struct(SMIME_STUFF_S **smime)
3800 if(smime && *smime){
3801 if((*smime)->passphrase_emailaddr){
3802 int i;
3803 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
3804 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
3805 fs_give((void **) (*smime)->passphrase_emailaddr);
3808 if((*smime)->publicpath)
3809 fs_give((void **) &(*smime)->publicpath);
3811 if((*smime)->publiccertlist)
3812 free_certlist(&(*smime)->publiccertlist);
3814 if((*smime)->backuppubliccertlist)
3815 free_certlist(&(*smime)->backuppubliccertlist);
3817 if((*smime)->cacertlist)
3818 free_certlist(&(*smime)->cacertlist);
3820 if((*smime)->backupcacertlist)
3821 free_certlist(&(*smime)->backupcacertlist);
3823 if((*smime)->privatecertlist)
3824 free_certlist(&(*smime)->privatecertlist);
3826 if((*smime)->backupprivatecertlist)
3827 free_certlist(&(*smime)->backupprivatecertlist);
3829 if((*smime)->publiccontent)
3830 fs_give((void **) &(*smime)->publiccontent);
3832 if((*smime)->privatepath)
3833 fs_give((void **) &(*smime)->privatepath);
3835 if((*smime)->personal_certs){
3836 PERSONAL_CERT *pc;
3838 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
3839 free_personal_certs(&pc);
3840 (*smime)->personal_certs = NULL;
3843 if((*smime)->privatecontent)
3844 fs_give((void **) &(*smime)->privatecontent);
3846 if((*smime)->capath)
3847 fs_give((void **) &(*smime)->capath);
3849 if((*smime)->cacontent)
3850 fs_give((void **) &(*smime)->cacontent);
3852 fs_give((void **) smime);
3856 #endif /* SMIME */