* Alpine failed to read an encrypted password file if too many
[alpine.git] / pith / smime.c
blob389ce0121a70a4e4a6f6bf22c0b5fd7641a6b633
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 *, 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];
613 char *what;
615 if(pith_smime_import_certificate == NULL){
616 q_status_message(SM_ORDER, 0, 2,
617 _("import of certificates not implemented yet!"));
618 return 0;
621 what = ctype == Public || ctype == CACert ? "certificate" : "key";
622 r = (*pith_smime_import_certificate)(filename, full_filename, what, sizeof(filename) - 20);
624 if(r < 0)
625 return 0;
627 /* we are trying to import a new key for the password file. First we ask for the
628 * private key. Once this is loaded, we make a reasonable attempt to find the
629 * public key in the same directory as the key was loaded from. We do this by
630 * looking for a file with the correct public certificate name, then we look
631 * in the same private key, and if not, we ask the user for its location. If all
632 * of this works, we import the key and public to the password directory.
634 #ifdef PASSFILE
635 if(ctype == Password){
636 char PrivateKeyPath[MAXPATH+1], PublicCertPath[MAXPATH+1], s[MAXPATH+1];
637 char full_name_key[MAXPATH+1], full_name_cert[MAXPATH+1];
638 char *use_this_file;
639 char prompt[500];
640 EVP_PKEY *key = NULL;
642 rc = 1; /* assume success :) */
643 if(strlen(filename) > 4){
644 strncpy(s, filename, sizeof(s));
645 s[sizeof(s)-1] = '\0';
646 if(!strcmp(s + strlen(s) - strlen(EXTCERT(Private)), EXTCERT(Private)))
647 s[strlen(s) - strlen(EXTCERT(Private))] = '\0';
648 else
649 rc = 0;
650 } else rc = 0;
652 if(rc == 0){
653 q_status_message(SM_ORDER, 1, 3, _("Error in key name. Check file extension"));
654 return 0;
657 snprintf(prompt, sizeof(prompt), _("Enter passphrase for <%s>: "), filename);
658 prompt[sizeof(prompt)-1] = '\0';
659 if((key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){
660 BIO *ins = NULL;
661 X509 *cert = NULL;
663 strncpy(full_name_key, full_filename, sizeof(full_filename));
664 full_name_key[sizeof(full_name_key)-1] = '\0';
666 build_path(buf, PATHCERTDIR(ctype), s, sizeof(buf));
668 strncpy(PrivateKeyPath, buf, sizeof(PrivateKeyPath));
669 PrivateKeyPath[sizeof(PrivateKeyPath)-1] = '\0';
670 if(strlen(PrivateKeyPath) + 4 < sizeof(PrivateKeyPath)){
671 strncat(PrivateKeyPath, EXTCERT(Private), 4);
672 PrivateKeyPath[sizeof(PrivateKeyPath)-1] = '\0';
675 /* remove .key extension and replace it with .crt extension */
676 strncpy(full_name_cert, full_name_key, sizeof(full_name_key));
677 full_name_cert[sizeof(full_name_cert)-1] = '\0';
678 full_name_cert[strlen(full_name_cert) - strlen(EXTCERT(Private))] = '\0';
679 strncat(full_name_cert, EXTCERT(Public), 4);
680 full_name_cert[sizeof(full_name_cert)-1] = '\0';
683 /* set up path to location where we will save public cert */
684 strncpy(PublicCertPath, buf, sizeof(PublicCertPath));
685 PublicCertPath[sizeof(PublicCertPath)-1] = '\0';
686 if(strlen(PublicCertPath) + 4 < sizeof(PublicCertPath)){
687 strncat(PublicCertPath, EXTCERT(Public), 4);
688 PublicCertPath[sizeof(PublicCertPath)-1] = '\0';
691 /* attempt #1 to guess public cert name, use .crt extension */
692 if((ins = BIO_new_file(full_name_cert, "r")) != NULL){
693 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
694 use_this_file = &full_name_cert[0];
697 else{
698 /* attempt #2 to guess public cert name: user the original key */
699 if((ins = BIO_new_file(full_name_key, "r")) != NULL){
700 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
701 use_this_file = &full_name_key[0];
704 else {
705 int done = 0;
706 /* attempt #3, ask the user */
707 do {
708 r = (*pith_smime_import_certificate)(filename, use_this_file, "certificate", sizeof(filename) - 20);
709 if(r < 0){
710 if(ins != NULL) BIO_free(ins);
711 if(cert != NULL) X509_free(cert);
712 return 0;
714 if((ins = BIO_new_file(use_this_file, "r")) != NULL){
715 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL)
716 done++;
717 else
718 q_status_message(SM_ORDER, 1, 3, _("Error parsing certificate"));
720 else
721 q_status_message(SM_ORDER, 1, 3, _("Error reading certificate"));
722 } while (done == 0);
725 if(ins != NULL){
726 if(cert != NULL){ /* check that certificate matches key */
727 if(!X509_check_private_key(cert, key)){
728 rc = 0;
729 q_status_message(SM_ORDER, 1, 3, _("Certificate does not match key"));
731 else
732 rc = 1; /* Success! */
734 else
735 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
737 if(rc == 1){ /* if everything has been successful,
738 * copy the files to their final destination */
739 if(our_copy(PrivateKeyPath, full_filename) == 0){ /* <-- save the private key */
740 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
741 if(our_copy(PublicCertPath, use_this_file) == 0){
742 char tmp[MAILTMPLEN];
743 FILE *fp;
745 if(!passfile_name(ps_global->pinerc, tmp, sizeof(tmp))
746 || !(fp = our_fopen(tmp, "rb"))){
747 q_status_message(SM_ORDER, 1, 3, _("Error reading password file!"));
748 rc = 0;
750 else {
751 char tmp2[MAILTMPLEN];
752 int encrypted = 0;
753 char *text;
754 PERSONAL_CERT *pwdcert, *pc;
756 pwdcert = (PERSONAL_CERT *) ps_global->pwdcert;
757 if(pwdcert == NULL)
758 setup_pwdcert((void **)&pwdcert);
760 tmp2[0] = '\0';
761 fgets(tmp2, sizeof(tmp2), fp);
762 fclose(fp);
763 if(strcmp(tmp2, "-----BEGIN PKCS7-----\n")){
764 if(encrypt_file((char *)tmp, NULL, pwdcert))
765 encrypted++;
767 else
768 encrypted++;
770 if(encrypted){
771 text = decrypt_file((char *)tmp, NULL, pwdcert);
772 if(text != NULL){
773 pc = fs_get(sizeof(PERSONAL_CERT));
774 memset((void *)pc, 0, sizeof(PERSONAL_CERT));
775 filename[strlen(filename)-strlen(EXTCERT(Private))] = '\0';
776 pc->name = cpystr(filename);
777 snprintf(buf, sizeof(buf), "%s%s", filename, EXTCERT(Public));
778 buf[sizeof(buf)-1] = '\0';
779 pc->cname = cpystr(buf);
780 pc->key = key;
781 pc->cert = cert;
783 if(encrypt_file((char *)tmp, text, pc)){ /* we did it! */
784 build_path(buf, PATHCERTDIR(ctype), pwdcert->name, sizeof(buf));
785 strncat(buf, EXTCERT(Private), 4);
786 buf[sizeof(buf)-1] = '\0';
787 if(strcmp(PrivateKeyPath, buf)){
788 if (unlink(buf) < 0)
789 q_status_message(SM_ORDER, 1, 3, _("Failed to remove old key"));
791 build_path(buf, PATHCERTDIR(ctype), pwdcert->cname, sizeof(buf));
792 if(strcmp(PublicCertPath, buf)){
793 if(unlink(buf) < 0)
794 q_status_message(SM_ORDER, 1, 3, _("Failed to remove old certificate"));
796 free_personal_certs((PERSONAL_CERT **)&ps_global->pwdcert);
797 ps_global->pwdcert = pc;
798 rc = 1;
799 q_status_message(SM_ORDER, 1, 3, _("Password file reencrypted"));
800 } else {
801 q_status_message(SM_ORDER, 1, 3, _("Failed to reencrypt password file"));
802 rc = 0;
804 } else {
805 q_status_message(SM_ORDER, 1, 3, _("Error decrypting Password file"));
807 } else {
808 q_status_message(SM_ORDER, 1, 3, _("Password file not encrypted and coulr not encrypt"));
809 rc = 0;
813 else{
814 q_status_message(SM_ORDER, 1, 3, _("Error saving public certificate"));
815 if(our_unlink(PrivateKeyPath) < 0)
816 q_status_message(SM_ORDER, 1, 3, _("Error while cleaning private key"));
817 rc = 0;
820 else{
821 rc = 0;
822 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
824 if(ins != NULL) BIO_free(ins);
825 if(rc == 0 && cert != NULL) X509_free(cert);
827 } else {
828 rc = 0;
829 q_status_message(SM_ORDER, 1, 3, _("Error unlocking private key"));
832 return rc;
834 #endif /* PASSFILE */
836 smime_init();
837 ps_global->mangled_screen = 1;
839 if (ctype == Private){
840 char prompt[500], *s, *t;
841 EVP_PKEY *key = NULL;
843 if(!ps_global->smime->privatecertlist){
844 ps_global->smime->privatecertlist = fs_get(sizeof(CertList));
845 memset((void *)DATACERT(ctype), 0, sizeof(CertList));
848 for(s = t = filename; (t = strstr(s, ".key")) != NULL; s = t + 1);
849 if(s) *(s-1) = 0;
851 snprintf(prompt, sizeof(prompt), _("Enter passphrase for <%s>: "), filename);
852 prompt[sizeof(prompt)-1] = '\0';
853 if((key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){
854 if(SMHOLDERTYPE(ctype) == Directory){
855 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
856 if(strcmp(buf + strlen(buf) - 4, EXTCERT(ctype)) != 0 && strlen(buf) + 4 < sizeof(buf)){
857 strncat(buf, EXTCERT(ctype), 4);
858 buf[sizeof(buf)-1] = '\0';
860 rc = our_copy(buf, full_filename);
862 else /* if(SMHOLDERTYPE(ctype) == Container){ */
863 rc = add_file_to_container(ctype, full_filename, NULL);
864 if(rc == 0)
865 q_status_message(SM_ORDER, 1, 3, _("Private key saved"));
866 else
867 q_status_message(SM_ORDER, 1, 3, _("Error saving private key"));
868 if(ps_global->smime->publiccertlist)
869 ps_global->smime->publiccertlist->data.renew = 1;
871 else
872 q_status_message(SM_ORDER, 1, 3, _("Problem unlocking key (not a certificate or wrong password)"));
873 } else if (ctype == CACert){
874 BIO *ins;
875 X509 *cert;
877 if((ins = BIO_new_file(full_filename, "r")) != NULL){
878 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
879 if(SMHOLDERTYPE(ctype) == Directory){
880 build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
881 if(strcmp(buf + strlen(buf) - 4, ".crt") != 0 && strlen(buf) + 4 < sizeof(buf)){
882 strncat(buf, EXTCERT(ctype), 4);
883 buf[sizeof(buf)-1] = '\0';
886 rc = our_copy(buf, full_filename);
888 else /* if(SMHOLDERTYPE(ctype) == Container){ */
889 rc = add_file_to_container(ctype, full_filename, NULL);
890 if(rc == 0)
891 q_status_message(SM_ORDER, 1, 3, _("Certificate saved"));
892 else
893 q_status_message(SM_ORDER, 1, 3, _("Error saving certificate"));
894 X509_free(cert); /* not needed anymore */
896 else
897 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
898 BIO_free(ins);
900 renew_store();
901 } else { /* ctype == Public. save certificate, but first validate that it is one */
902 BIO *ins;
903 X509 *cert;
905 if((ins = BIO_new_file(full_filename, "r")) != NULL){
906 if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
907 if(SMHOLDERTYPE(ctype) == Directory){
908 char **email;
910 if((email = get_x509_subject_email(cert)) != NULL){
911 int i;
912 for(i = 0; email[i] != NULL; i++){
913 save_cert_for(email[i], cert, Public);
914 fs_give((void **)&email[i]);
916 fs_give((void **)email);
918 if(strcmp(filename + strlen(filename) - 4, ".crt") == 0)
919 filename[strlen(filename) - 4] = '\0';
920 save_cert_for(filename, cert, Public);
922 else /* if(SMHOLDERTYPE(ctype) == Container){ */
923 add_file_to_container(ctype, full_filename, NULL);
924 X509_free(cert);
925 if(ps_global->smime->publiccertlist)
926 ps_global->smime->publiccertlist->data.renew = 1;
928 else
929 q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
930 BIO_free(ins);
933 if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1;
934 return 1;
937 /* itype: information type to add: 0 - public, 1 - private.
938 * Memory freed by caller
940 BIO *
941 print_private_key_information(char *email, int itype)
943 BIO *out;
944 PERSONAL_CERT *pc;
946 if(ps_global->smime == NULL
947 || ps_global->smime->personal_certs == NULL
948 || (itype != 0 && itype != 1))
949 return NULL;
951 for(pc = ps_global->smime->personal_certs;
952 pc != NULL && strcmp(pc->name, email) != 0; pc = pc->next);
953 if(pc->key == NULL
954 && !load_private_key(pc)
955 && ps_global->smime
956 && ps_global->smime->need_passphrase){
957 if (*pith_opt_smime_get_passphrase)
958 (*pith_opt_smime_get_passphrase)();
959 load_private_key(pc);
962 if(pc->key == NULL)
963 return NULL;
965 out = BIO_new(BIO_s_mem());
966 if(itype == 0) /* 0 means public */
967 EVP_PKEY_print_public(out, pc->key, 0, NULL);
968 else if (itype == 1) /* 1 means private */
969 EVP_PKEY_print_private(out, pc->key, 0, NULL);
971 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
972 forget_private_keys();
974 return out;
978 * Forget any cached private keys
980 static void
981 forget_private_keys(void)
983 PERSONAL_CERT *pcert;
984 size_t len;
985 volatile char *p;
987 dprint((9, "forget_private_keys()"));
988 if(ps_global->smime){
989 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
990 pcert;
991 pcert=pcert->next){
993 if(pcert->key){
994 EVP_PKEY_free(pcert->key);
995 pcert->key = NULL;
999 ps_global->smime->entered_passphrase = 0;
1000 len = sizeof(ps_global->smime->passphrase);
1001 p = ps_global->smime->passphrase;
1003 while(len-- > 0)
1004 *p++ = '\0';
1008 /* modelled after signature_path in reply.c, but uses home dir instead of the
1009 * directory where the .pinerc is located, since according to documentation,
1010 * the .alpine-smime directories are subdirectories of the home directory
1012 int smime_path(char *rpath, char *fpath, size_t len)
1014 *fpath = '\0';
1015 if(rpath && *rpath){
1016 size_t spl = strlen(rpath);
1018 if(IS_REMOTE(rpath)){
1019 if(spl < len - 1)
1020 strncpy(fpath, rpath, len-1);
1021 fpath[len-1] = '\0';
1023 else if(is_absolute_path(rpath)){
1024 strncpy(fpath, rpath, len-1);
1025 fpath[len-1] = '\0';
1026 fnexpand(fpath, len);
1028 else if(ps_global->VAR_OPER_DIR){
1029 if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
1030 build_path(fpath, ps_global->VAR_OPER_DIR, rpath, len);
1032 else if(ps_global->home_dir){
1033 if(strlen(ps_global->home_dir) + spl < len - 1)
1034 build_path(fpath, ps_global->home_dir, rpath, len);
1037 return fpath && *fpath ? 1 : 0;
1043 * taken from openssl/apps/app_rand.c
1045 static int
1046 app_RAND_load_file(const char *file)
1048 #define RANDBUFLEN 200
1049 char buffer[RANDBUFLEN];
1051 if(file == NULL)
1052 file = RAND_file_name(buffer, RANDBUFLEN);
1054 if(file == NULL || !RAND_load_file(file, -1)){
1055 if(RAND_status() == 0){
1056 dprint((1, "unable to load 'random state'\n"));
1057 dprint((1, "This means that the random number generator has not been seeded\n"));
1058 dprint((1, "with much random data.\n"));
1061 return 0;
1064 seeded = 1;
1065 return 1;
1070 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
1072 static void
1073 openssl_extra_randomness(void)
1075 #if !defined(WIN32)
1076 int fd;
1077 unsigned long i;
1078 char *tf = NULL;
1079 char tmp[MAXPATH];
1080 struct stat sbuf;
1081 /* if system doesn't have /dev/urandom */
1082 if(stat ("/dev/urandom", &sbuf)){
1083 tmp[0] = '0';
1084 tf = temp_nam(NULL, NULL);
1085 if(tf){
1086 strncpy(tmp, tf, sizeof(tmp));
1087 tmp[sizeof(tmp)-1] = '\0';
1088 fs_give((void **) &tf);
1091 if((fd = open(tmp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0)
1092 i = (unsigned long) tmp;
1093 else{
1094 unlink(tmp); /* don't need the file */
1095 fstat(fd, &sbuf); /* get information about the file */
1096 i = sbuf.st_ino; /* remember its inode */
1097 close(fd); /* or its descriptor */
1099 /* not great but it'll have to do */
1100 snprintf(tmp+strlen(tmp), sizeof(tmp)-strlen(tmp), "%.80s%lx%lx%lx",
1101 tcp_serverhost (),i,
1102 (unsigned long) (time (0) ^ gethostid ()),
1103 (unsigned long) getpid ());
1104 RAND_seed(tmp, strlen(tmp));
1106 #endif
1110 /* taken from openssl/apps/app_rand.c */
1111 static int
1112 app_RAND_write_file(const char *file)
1114 char buffer[200];
1116 if(!seeded)
1118 * If we did not manage to read the seed file,
1119 * we should not write a low-entropy seed file back --
1120 * it would suppress a crucial warning the next time
1121 * we want to use it.
1123 return 0;
1125 if(file == NULL)
1126 file = RAND_file_name(buffer, sizeof buffer);
1128 if(file == NULL || !RAND_write_file(file)){
1129 dprint((1, "unable to write 'random state'\n"));
1130 return 0;
1133 return 1;
1136 CertList *
1137 certlist_from_personal_certs(PERSONAL_CERT *pc)
1139 CertList *cl;
1140 X509 *x;
1142 if(pc == NULL)
1143 return NULL;
1145 if((x = get_cert_for(pc->name, Public, 1)) != NULL)
1146 cl = smime_X509_to_cert_info(x, pc->name);
1147 cl->next = certlist_from_personal_certs(pc->next);
1149 return cl;
1152 void
1153 renew_cert_data(CertList **data, WhichCerts ctype)
1155 smime_init();
1156 if(ctype == Private){
1157 if(data){
1158 PERSONAL_CERT *pc = (PERSONAL_CERT *)ps_global->smime->personal_certs;
1159 if(*data)
1160 free_certlist(data);
1161 free_personal_certs(&pc);
1162 setup_privatekey_storage();
1163 *data = certlist_from_personal_certs((PERSONAL_CERT *)ps_global->smime->personal_certs);
1164 if(data && *data){
1165 resort_certificates(data, ctype);
1166 RENEWCERT(*data) = 0;
1168 ps_global->smime->privatecertlist = *data;
1170 if(ps_global->smime->privatecertlist)
1171 RENEWCERT(ps_global->smime->privatecertlist) = 0;
1172 } else {
1173 X509_LOOKUP *lookup = NULL;
1174 X509_STORE *store = NULL;
1176 if((store = X509_STORE_new()) != NULL){
1177 if((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL){
1178 X509_STORE_free(store);
1179 store = NULL;
1180 } else{
1181 free_certlist(data);
1182 if(SMHOLDERTYPE(ctype) == Directory)
1183 add_certs_in_dir(lookup, PATHCERTDIR(ctype), EXTCERT(ctype), data);
1184 else /* if(SMHOLDERTYPE(ctype) == Container) */
1185 *data = mem_to_certlist(CONTENTCERTLIST(ctype), ctype);
1186 if(data && *data){
1187 resort_certificates(data, ctype);
1188 RENEWCERT(*data) = 0;
1190 if(ctype == Public)
1191 ps_global->smime->publiccertlist = *data;
1192 else
1193 ps_global->smime->cacertlist = *data;
1197 setup_certs_backup_by_type(ctype);
1200 void
1201 smime_reinit(void)
1203 smime_deinit();
1204 smime_init();
1207 /* Installed as an atexit() handler to save the random data */
1208 void
1209 smime_deinit(void)
1211 dprint((9, "smime_deinit()"));
1212 app_RAND_write_file(NULL);
1213 free_smime_struct(&ps_global->smime);
1216 /* we renew the store when it has changed */
1217 void renew_store(void)
1219 if(ps_global->smime->inited){
1220 if(s_cert_store != NULL)
1221 X509_STORE_free(s_cert_store);
1222 s_cert_store = get_ca_store();
1226 /* Initialise openssl stuff if needed */
1227 void
1228 smime_init(void)
1230 if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
1232 dprint((9, "smime_init()"));
1233 if(!ps_global->smime)
1234 ps_global->smime = new_smime_struct();
1236 setup_storage_locations();
1238 s_cert_store = get_ca_store();
1239 setup_certs_backup_by_type(CACert);
1241 #ifdef OPENSSL_1_1_0
1242 OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS|OPENSSL_INIT_ADD_ALL_DIGESTS|OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
1243 #else
1244 OpenSSL_add_all_algorithms();
1245 ERR_load_crypto_strings();
1246 #endif /* OPENSSL_1_1_0 */
1248 app_RAND_load_file(NULL);
1249 openssl_extra_randomness();
1250 ps_global->smime->inited = 1;
1253 ERR_clear_error();
1257 /* validate a certificate. Return value : 0 for no error, -1 for error.
1258 * In the latter case, set the openssl smime error in *error.
1260 int smime_validate_cert(X509 *cert, long *error)
1262 X509_STORE_CTX *csc;
1264 ERR_clear_error();
1265 *error = 0;
1266 if((csc = X509_STORE_CTX_new()) != NULL){
1267 X509_STORE_set_flags(s_cert_store, 0);
1268 if(X509_STORE_CTX_init(csc,s_cert_store,cert,NULL)
1269 && X509_verify_cert(csc) <= 0)
1270 *error = X509_STORE_CTX_get_error(csc);
1271 X509_STORE_CTX_free(csc);
1273 return *error ? -1 : 0;
1276 PERSONAL_CERT *
1277 get_personal_certs(char *path)
1279 PERSONAL_CERT *result = NULL;
1280 char buf2[MAXPATH];
1281 struct dirent *d;
1282 DIR *dirp;
1284 ps_global->smime->privatepath = cpystr(path);
1285 dirp = opendir(path);
1286 if(dirp){
1287 while((d=readdir(dirp)) != NULL){
1288 X509 *cert;
1289 size_t ll;
1291 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, ".key")){
1293 /* copy file name to temp buffer */
1294 strncpy(buf2, d->d_name, sizeof(buf2)-1);
1295 buf2[sizeof(buf2)-1] = '\0';
1296 /* chop off ".key" trailier */
1297 buf2[strlen(buf2)-4] = '\0';
1298 /* Look for certificate */
1299 cert = get_cert_for(buf2, Public, 1);
1301 if(cert){
1302 PERSONAL_CERT *pc;
1304 /* create a new PERSONAL_CERT, fill it in */
1306 pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
1307 pc->cert = cert;
1308 pc->name = cpystr(buf2);
1309 strncat(buf2, EXTCERT(Public), 4);
1310 pc->cname = cpystr(buf2);
1312 /* Try to load the key with an empty password */
1313 pc->key = load_key(pc, "", SM_NORMALCERT);
1315 pc->next = result;
1316 result = pc;
1320 closedir(dirp);
1322 return result;
1326 void
1327 setup_privatekey_storage(void)
1329 char path[MAXPATH+1], *contents;
1330 int privatekeycontainer = 0;
1332 /* private keys in a container */
1333 if(ps_global->VAR_PRIVATEKEY_CONTAINER && ps_global->VAR_PRIVATEKEY_CONTAINER[0]){
1335 privatekeycontainer = 1;
1336 contents = NULL;
1337 path[0] = '\0';
1338 if(!smime_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
1339 privatekeycontainer = 0;
1341 if(privatekeycontainer && !IS_REMOTE(path)
1342 && ps_global->VAR_OPER_DIR
1343 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1344 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1345 /* TRANSLATORS: First arg is the directory name, second is
1346 the file user wants to read but can't. */
1347 _("Can't read file outside %s: %s"),
1348 ps_global->VAR_OPER_DIR, path);
1349 privatekeycontainer = 0;
1352 if(privatekeycontainer
1353 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1354 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1356 !(contents = read_file(path, READ_FROM_LOCALE)))
1357 privatekeycontainer = 0;
1360 if(privatekeycontainer && path[0]){
1361 ps_global->smime->privatetype = Container;
1362 ps_global->smime->privatepath = cpystr(path);
1364 if(contents){
1365 ps_global->smime->privatecontent = contents;
1366 ps_global->smime->personal_certs = mem_to_personal_certs(contents);
1371 /* private keys in a directory of files */
1372 if(!privatekeycontainer){
1373 ps_global->smime->privatetype = Directory;
1375 path[0] = '\0';
1376 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
1377 && !IS_REMOTE(path)))
1378 ps_global->smime->privatetype = Nada;
1379 else if(can_access(path, ACCESS_EXISTS)){
1380 if(our_mkpath(path, 0700)){
1381 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1382 ps_global->smime->privatetype = Nada;
1386 if(ps_global->smime->privatetype == Directory)
1387 ps_global->smime->personal_certs = get_personal_certs(path);
1389 setup_certs_backup_by_type(Private);
1394 static void
1395 setup_storage_locations(void)
1397 int publiccertcontainer = 0, cacertcontainer = 0;
1398 char path[MAXPATH+1], *contents;
1400 if(!ps_global->smime)
1401 return;
1403 #ifdef APPLEKEYCHAIN
1404 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1405 ps_global->smime->publictype = Keychain;
1407 else{
1408 #endif /* APPLEKEYCHAIN */
1409 /* Public certificates in a container */
1410 if(ps_global->VAR_PUBLICCERT_CONTAINER && ps_global->VAR_PUBLICCERT_CONTAINER[0]){
1412 publiccertcontainer = 1;
1413 contents = NULL;
1414 path[0] = '\0';
1415 if(!smime_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
1416 publiccertcontainer = 0;
1418 if(publiccertcontainer && !IS_REMOTE(path)
1419 && ps_global->VAR_OPER_DIR
1420 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1421 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1422 /* TRANSLATORS: First arg is the directory name, second is
1423 the file user wants to read but can't. */
1424 _("Can't read file outside %s: %s"),
1425 ps_global->VAR_OPER_DIR, path);
1426 publiccertcontainer = 0;
1429 if(publiccertcontainer
1430 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1431 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1433 !(contents = read_file(path, READ_FROM_LOCALE)))
1434 publiccertcontainer = 0;
1437 if(publiccertcontainer && path[0]){
1438 ps_global->smime->publictype = Container;
1439 ps_global->smime->publicpath = cpystr(path);
1441 if(contents){
1442 ps_global->smime->publiccontent = contents;
1443 ps_global->smime->publiccertlist = mem_to_certlist(contents, Public);
1448 /* Public certificates in a directory of files */
1449 if(!publiccertcontainer){
1450 ps_global->smime->publictype = Directory;
1452 path[0] = '\0';
1453 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1454 && !IS_REMOTE(path)))
1455 ps_global->smime->publictype = Nada;
1456 else if(can_access(path, ACCESS_EXISTS)){
1457 if(our_mkpath(path, 0700)){
1458 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1459 ps_global->smime->publictype = Nada;
1463 if(ps_global->smime->publictype == Directory)
1464 ps_global->smime->publicpath = cpystr(path);
1467 #ifdef APPLEKEYCHAIN
1469 #endif /* APPLEKEYCHAIN */
1471 setup_privatekey_storage();
1473 /* extra cacerts in a container */
1474 if(ps_global->VAR_CACERT_CONTAINER && ps_global->VAR_CACERT_CONTAINER[0]){
1476 cacertcontainer = 1;
1477 contents = NULL;
1478 path[0] = '\0';
1479 if(!smime_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
1480 cacertcontainer = 0;
1482 if(cacertcontainer && !IS_REMOTE(path)
1483 && ps_global->VAR_OPER_DIR
1484 && !in_dir(ps_global->VAR_OPER_DIR, path)){
1485 q_status_message2(SM_ORDER | SM_DING, 3, 4,
1486 /* TRANSLATORS: First arg is the directory name, second is
1487 the file user wants to read but can't. */
1488 _("Can't read file outside %s: %s"),
1489 ps_global->VAR_OPER_DIR, path);
1490 cacertcontainer = 0;
1493 if(cacertcontainer
1494 && (IS_REMOTE(path) || can_access(path, ACCESS_EXISTS) == 0)){
1495 if(!(IS_REMOTE(path) && (contents = simple_read_remote_file(path, REMOTE_SMIME_SUBTYPE)))
1497 !(contents = read_file(path, READ_FROM_LOCALE)))
1498 cacertcontainer = 0;
1501 if(cacertcontainer && path[0]){
1502 ps_global->smime->catype = Container;
1503 ps_global->smime->capath = cpystr(path);
1504 ps_global->smime->cacontent = contents;
1505 if(contents)
1506 ps_global->smime->cacertlist = mem_to_certlist(contents, CACert);
1510 if(!cacertcontainer){
1511 ps_global->smime->catype = Directory;
1513 path[0] = '\0';
1514 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
1515 && !IS_REMOTE(path)))
1516 ps_global->smime->catype = Nada;
1517 else if(can_access(path, ACCESS_EXISTS)){
1518 if(our_mkpath(path, 0700)){
1519 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1520 ps_global->smime->catype = Nada;
1524 if(ps_global->smime->catype == Directory)
1525 ps_global->smime->capath = cpystr(path);
1531 copy_publiccert_dir_to_container(void)
1533 return(copy_dir_to_container(Public, NULL));
1538 copy_publiccert_container_to_dir(void)
1540 return(copy_container_to_dir(Public));
1545 copy_privatecert_dir_to_container(void)
1547 return(copy_dir_to_container(Private, NULL));
1552 copy_privatecert_container_to_dir(void)
1554 return(copy_container_to_dir(Private));
1559 copy_cacert_dir_to_container(void)
1561 return(copy_dir_to_container(CACert, NULL));
1566 copy_cacert_container_to_dir(void)
1568 return(copy_container_to_dir(CACert));
1571 /* Add the contents of a file to a container. Do not check the content
1572 * of the file, just add it using the format for that container. The
1573 * caller must check the format, so that there is no data corruption
1574 * in the future.
1575 * return value: 0 - success,
1576 * != 0 - failure.
1579 add_file_to_container(WhichCerts ctype, char *fpath, char *altname)
1581 char *sep = (ctype == Public || ctype == Private)
1582 ? EMAILADDRLEADER : CACERTSTORELEADER;
1583 char *content = ctype == Public ? ps_global->smime->publiccontent
1584 : (ctype == Private ? ps_global->smime->privatecontent
1585 : ps_global->smime->cacontent);
1586 char *name;
1587 char *s;
1588 unsigned char c;
1589 struct stat sbuf;
1590 STORE_S *in = NULL;
1591 int rv = -1; /* assume error */
1593 if(our_stat(fpath, &sbuf) < 0
1594 || (in = so_get(FileStar, fpath, READ_ACCESS | READ_FROM_LOCALE)) == NULL)
1595 goto endadd;
1597 if(altname != NULL)
1598 name = altname;
1599 else if((name = strrchr(fpath, '/')) != NULL){
1600 size_t ll;
1601 if((ll = strlen(++name)) > 4 && strucmp(name + ll - 4, EXTCERT(ctype)) == 0)
1602 name[ll-strlen(EXTCERT(ctype))] = '\0';
1604 else
1605 goto endadd;
1607 if(content){
1608 fs_resize((void **)&content, strlen(content) + strlen(sep) + strlen(name) + sbuf.st_size + 3); /* 2 = \n + \n + \0*/
1609 s = content;
1610 content += strlen(content);
1612 else{
1613 s = content = fs_get(strlen(sep) + strlen(name) + sbuf.st_size + 1); /* 2 = \n + \0 */
1614 *content = '\0';
1616 strncat(content, sep, strlen(sep));
1617 strncat(content, name, strlen(name));
1618 content += strlen(content);
1619 *content++ = '\n';
1621 while(so_readc(&c, in))
1622 *content++ = (char) c;
1623 *content = '\0';
1625 switch(ctype){
1626 case Private: ps_global->smime->privatecontent = s; break;
1627 case Public : ps_global->smime->publiccontent = s; break;
1628 case CACert : ps_global->smime->cacontent = s; break;
1629 default : break;
1632 rv = copy_dir_to_container(ctype, s);
1634 endadd:
1635 if(in) so_give(&in);
1637 return rv;
1642 * returns 0 on success, -1 on failure
1643 * contents is an argument which tells this function to write the value
1644 * of this variable instead of reading the contents of the directory.
1645 * If the var contents is not null use its value as the value of the
1646 * container.
1649 copy_dir_to_container(WhichCerts which, char *contents)
1651 int ret = 0, container = 0;
1652 BIO *bio_out = NULL, *bio_in = NULL;
1653 char srcpath[MAXPATH+1], dstpath[MAXPATH+1], emailaddr[MAXPATH], file[MAXPATH], line[4096];
1654 char *tempfile = NULL, fpath[MAXPATH+1];
1655 DIR *dirp;
1656 struct dirent *d;
1657 REMDATA_S *rd = NULL;
1658 char *configdir = NULL;
1659 char *configpath = NULL;
1660 char *configcontainer = NULL;
1661 char *filesuffix = NULL;
1662 char *ret_dir = NULL;
1664 dprint((9, "copy_dir_to_container(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1665 smime_init();
1667 srcpath[0] = '\0';
1668 dstpath[0] = '\0';
1669 file[0] = '\0';
1670 emailaddr[0] = '\0';
1672 if(which == Public){
1673 configdir = ps_global->VAR_PUBLICCERT_DIR;
1674 configpath = ps_global->smime->publicpath;
1675 configcontainer = cpystr(DF_PUBLIC_CONTAINER);
1676 filesuffix = ".crt";
1678 else if(which == Private){
1679 configdir = ps_global->VAR_PRIVATEKEY_DIR;
1680 configpath = ps_global->smime->privatepath;
1681 configcontainer = cpystr(DF_PRIVATE_CONTAINER);
1682 filesuffix = ".key";
1684 else if(which == CACert){
1685 configdir = ps_global->VAR_CACERT_DIR;
1686 configpath = ps_global->smime->capath;
1687 configcontainer = cpystr(DF_CA_CONTAINER);
1688 filesuffix = ".crt";
1690 container = SMHOLDERTYPE(which) == Container;
1692 if(!(configdir && configdir[0])){
1693 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
1694 return -1;
1697 if(!(configpath && configpath[0])){
1698 #ifdef APPLEKEYCHAIN
1699 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1700 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1701 return -1;
1703 #endif /* APPLEKEYCHAIN */
1704 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1705 return -1;
1708 if(!(filesuffix && strlen(filesuffix) == 4)){
1709 return -1;
1714 * If there is a legit directory to read from set up the
1715 * container file to write to.
1717 if(smime_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
1719 if(IS_REMOTE(configpath)){
1720 rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
1721 NULL, "Error: ",
1722 _("Can't access remote smime configuration."));
1723 if(!rd)
1724 return -1;
1726 (void) rd_read_metadata(rd);
1728 if(rd->access == MaybeRorW){
1729 if(rd->read_status == 'R')
1730 rd->access = ReadOnly;
1731 else
1732 rd->access = ReadWrite;
1735 if(rd->access != NoExists){
1737 rd_check_remvalid(rd, 1L);
1740 * If the cached info says it is readonly but
1741 * it looks like it's been fixed now, change it to readwrite.
1743 if(rd->read_status == 'R'){
1744 rd_check_readonly_access(rd);
1745 if(rd->read_status == 'W'){
1746 rd->access = ReadWrite;
1747 rd->flags |= REM_OUTOFDATE;
1749 else
1750 rd->access = ReadOnly;
1754 if(rd->flags & REM_OUTOFDATE){
1755 if(rd_update_local(rd) != 0){
1757 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1758 rd_close_remdata(&rd);
1759 return -1;
1762 else
1763 rd_open_remote(rd);
1765 if(rd->access != ReadWrite || rd_remote_is_readonly(rd)){
1766 rd_close_remdata(&rd);
1767 return -1;
1770 rd->flags |= DO_REMTRIM;
1772 strncpy(dstpath, rd->lf, sizeof(dstpath)-1);
1773 dstpath[sizeof(dstpath)-1] = '\0';
1775 else{
1776 strncpy(dstpath, configpath, sizeof(dstpath)-1);
1777 dstpath[sizeof(dstpath)-1] = '\0';
1781 * dstpath is either the local Container file or the local cache file
1782 * for the remote Container file.
1784 tempfile = tempfile_in_same_dir(dstpath, "az", &ret_dir);
1788 * If there is a legit directory to read from and a tempfile
1789 * to write to we continue.
1791 if(tempfile && (bio_out=BIO_new_file(tempfile, "w")) != NULL){
1793 if(contents != NULL){
1794 if(BIO_puts(bio_out, contents) < 0)
1795 ret = -1;
1797 else {
1798 if((dirp = opendir(srcpath)) != NULL){
1800 while((d=readdir(dirp)) && !ret){
1801 size_t ll;
1803 if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, filesuffix)){
1805 /* copy file name to temp buffer */
1806 strncpy(emailaddr, d->d_name, sizeof(emailaddr)-1);
1807 emailaddr[sizeof(emailaddr)-1] = '\0';
1808 /* chop off suffix trailier */
1809 emailaddr[strlen(emailaddr)-4] = 0;
1812 * This is the separator between the contents of
1813 * different files.
1815 if(which == CACert){
1816 if(!((BIO_puts(bio_out, CACERTSTORELEADER) > 0)
1817 && (BIO_puts(bio_out, emailaddr) > 0)
1818 && (BIO_puts(bio_out, "\n") > 0)))
1819 ret = -1;
1821 else{
1822 if(!((BIO_puts(bio_out, EMAILADDRLEADER) > 0)
1823 && (BIO_puts(bio_out, emailaddr) > 0)
1824 && (BIO_puts(bio_out, "\n") > 0)))
1825 ret = -1;
1828 /* read then write contents of file */
1829 build_path(file, srcpath, d->d_name, sizeof(file));
1830 if(!(bio_in = BIO_new_file(file, "r")))
1831 ret = -1;
1833 if(!ret){
1834 int good_stuff = 0;
1836 while(BIO_gets(bio_in, line, sizeof(line)) > 0){
1837 if(strncmp("-----BEGIN", line, strlen("-----BEGIN")) == 0)
1838 good_stuff = 1;
1840 if(good_stuff)
1841 BIO_puts(bio_out, line);
1843 if(strncmp("-----END", line, strlen("-----END")) == 0)
1844 good_stuff = 0;
1848 BIO_free(bio_in);
1852 closedir(dirp);
1856 BIO_free(bio_out);
1858 if(!ret){
1859 if(container && configpath && *configpath){
1860 strncpy(fpath, configpath, sizeof(fpath));
1861 fpath[sizeof(fpath) - 1] = '\0';
1863 else if(ret_dir){
1864 if(strlen(dstpath) + strlen(configcontainer) - strlen(ret_dir) + 1 < sizeof(dstpath))
1865 snprintf(fpath, sizeof(fpath), "%s%c%s",
1866 dstpath, tempfile[strlen(ret_dir)], configcontainer);
1867 else
1868 ret = -1;
1870 else ret = -1;
1872 if(!ret){
1873 if(!IS_REMOTE(configpath)){
1874 if(rename_file(tempfile, fpath) < 0){
1875 q_status_message2(SM_ORDER, 3, 3,
1876 _("Can't rename %s to %s"), tempfile, fpath);
1877 ret = -1;
1878 } else q_status_message1(SM_ORDER, 3, 3,
1879 _("saved container to %s"), fpath);
1881 else { /* if the container is remote, copy it */
1882 int e;
1883 char datebuf[200];
1885 if(rd != NULL && rename_file(tempfile, rd->lf) < 0){
1886 q_status_message2(SM_ORDER, 3, 3,
1887 _("Can't rename %s to %s"), tempfile, rd->lf);
1888 ret = -1;
1891 datebuf[0] = '\0';
1893 if((e = rd_update_remote(rd, datebuf)) != 0){
1894 if(e == -1){
1895 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1896 _("Error opening temporary smime file %s: %s"),
1897 rd->lf, error_description(errno));
1898 dprint((1,
1899 "write_remote_smime: error opening temp file %s\n",
1900 rd->lf ? rd->lf : "?"));
1902 else{
1903 q_status_message2(SM_ORDER | SM_DING, 3, 5,
1904 _("Error copying to %s: %s"),
1905 rd->rn, error_description(errno));
1906 dprint((1,
1907 "write_remote_smime: error copying from %s to %s\n",
1908 rd->lf ? rd->lf : "?", rd->rn ? rd->rn : "?"));
1911 q_status_message(SM_ORDER | SM_DING, 5, 5,
1912 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1914 else{
1915 rd_update_metadata(rd, datebuf);
1916 rd->read_status = 'W';
1919 rd_close_remdata(&rd);
1925 if(tempfile)
1926 fs_give((void **) &tempfile);
1928 if(ret_dir)
1929 fs_give((void **) &ret_dir);
1931 if(configcontainer)
1932 fs_give((void **) &configcontainer);
1934 return ret;
1939 * returns 0 on success, -1 on failure
1942 copy_container_to_dir(WhichCerts which)
1944 char path[MAXPATH+1], file[MAXPATH+1], buf[MAXPATH+1];
1945 char iobuf[4096];
1946 char *contents = NULL;
1947 char *leader = NULL;
1948 char *filesuffix = NULL;
1949 char *configdir = NULL;
1950 char *configpath = NULL;
1951 char *tempfile = NULL;
1952 char *p, *q, *line, *name, *certtext, *save_p;
1953 int len;
1954 BIO *in, *out;
1956 dprint((9, "copy_container_to_dir(%s)", which==Public ? "Public" : which==Private ? "Private" : which==CACert ? "CACert" : "?"));
1957 smime_init();
1959 path[0] = '\0';
1961 if(which == Public){
1962 leader = EMAILADDRLEADER;
1963 contents = ps_global->smime->publiccontent;
1964 configdir = ps_global->VAR_PUBLICCERT_DIR;
1965 configpath = ps_global->smime->publicpath;
1966 filesuffix = ".crt";
1967 if(!(configpath && configpath[0])){
1968 #ifdef APPLEKEYCHAIN
1969 if(which == Public && F_ON(F_PUBLICCERTS_IN_KEYCHAIN, ps_global)){
1970 q_status_message(SM_ORDER, 3, 3, _("Turn off the Keychain feature above first"));
1971 return -1;
1973 #endif /* APPLEKEYCHAIN */
1974 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
1975 return -1;
1978 fs_give((void **) &ps_global->smime->publicpath);
1980 path[0] = '\0';
1981 if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
1982 && !IS_REMOTE(path))){
1983 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
1984 return -1;
1987 if(can_access(path, ACCESS_EXISTS)){
1988 if(our_mkpath(path, 0700)){
1989 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
1990 return -1;
1994 ps_global->smime->publicpath = cpystr(path);
1995 configpath = ps_global->smime->publicpath;
1997 else if(which == Private){
1998 leader = EMAILADDRLEADER;
1999 contents = ps_global->smime->privatecontent;
2000 configdir = ps_global->VAR_PRIVATEKEY_DIR;
2001 configpath = ps_global->smime->privatepath;
2002 filesuffix = ".key";
2003 if(!(configpath && configpath[0])){
2004 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2005 return -1;
2008 fs_give((void **) &ps_global->smime->privatepath);
2010 path[0] = '\0';
2011 if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
2012 && !IS_REMOTE(path))){
2013 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2014 return -1;
2017 if(can_access(path, ACCESS_EXISTS)){
2018 if(our_mkpath(path, 0700)){
2019 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2020 return -1;
2024 ps_global->smime->privatepath = cpystr(path);
2025 configpath = ps_global->smime->privatepath;
2027 else if(which == CACert){
2028 leader = CACERTSTORELEADER;
2029 contents = ps_global->smime->cacontent;
2030 configdir = ps_global->VAR_CACERT_DIR;
2031 configpath = ps_global->smime->capath;
2032 filesuffix = ".crt";
2033 if(!(configpath && configpath[0])){
2034 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2035 return -1;
2038 fs_give((void **) &ps_global->smime->capath);
2040 path[0] = '\0';
2041 if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
2042 && !IS_REMOTE(path))){
2043 q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
2044 return -1;
2047 if(can_access(path, ACCESS_EXISTS)){
2048 if(our_mkpath(path, 0700)){
2049 q_status_message1(SM_ORDER, 3, 3, _("Can't create directory %s"), path);
2050 return -1;
2054 ps_global->smime->capath = cpystr(path);
2055 configpath = ps_global->smime->capath;
2058 if(!(configdir && configdir[0])){
2059 q_status_message(SM_ORDER, 3, 3, _("Directory not defined"));
2060 return -1;
2063 if(!(configpath && configpath[0])){
2064 q_status_message(SM_ORDER, 3, 3, _("Container path is not defined"));
2065 return -1;
2068 if(!(filesuffix && strlen(filesuffix) == 4)){
2069 return -1;
2073 if(contents && *contents){
2074 for(p = contents; *p != '\0';){
2075 line = p;
2077 while(*p && *p != '\n')
2078 p++;
2080 save_p = NULL;
2081 if(*p == '\n'){
2082 save_p = p;
2083 *p++ = '\0';
2086 if(strncmp(leader, line, strlen(leader)) == 0){
2087 name = line + strlen(leader);
2088 certtext = p;
2089 if(strncmp("-----BEGIN", certtext, strlen("-----BEGIN")) == 0){
2090 if((q = strstr(certtext, leader)) != NULL){
2091 p = q;
2093 else{ /* end of file */
2094 q = certtext + strlen(certtext);
2095 p = q;
2098 strncpy(buf, name, sizeof(buf)-5);
2099 buf[sizeof(buf)-5] = '\0';
2100 strncat(buf, filesuffix, 5);
2101 build_path(file, configpath, buf, sizeof(file));
2103 in = BIO_new_mem_buf(certtext, q-certtext);
2104 if(in){
2105 tempfile = tempfile_in_same_dir(file, "az", NULL);
2106 out = NULL;
2107 if(tempfile)
2108 out = BIO_new_file(tempfile, "w");
2110 if(out){
2111 while((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0)
2112 BIO_write(out, iobuf, len);
2114 BIO_free(out);
2116 if(rename_file(tempfile, file) < 0){
2117 q_status_message2(SM_ORDER, 3, 3,
2118 _("Can't rename %s to %s"),
2119 tempfile, file);
2120 return -1;
2123 fs_give((void **) &tempfile);
2126 BIO_free(in);
2131 if(save_p)
2132 *save_p = '\n';
2136 return 0;
2140 #ifdef APPLEKEYCHAIN
2143 copy_publiccert_container_to_keychain(void)
2145 /* NOT IMPLEMNTED */
2146 return -1;
2150 copy_publiccert_keychain_to_container(void)
2152 /* NOT IMPLEMNTED */
2153 return -1;
2156 #endif /* APPLEKEYCHAIN */
2160 * Get a pointer to a string describing the most recent OpenSSL error.
2161 * It's statically allocated, so don't change or attempt to free it.
2163 static const char *
2164 openssl_error_string(void)
2166 char *errs;
2167 const char *data = NULL;
2168 long errn;
2170 errn = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2171 errs = (char*) ERR_reason_error_string(errn);
2173 if(errs)
2174 return errs;
2175 else if(data)
2176 return data;
2178 return "unknown error";
2182 /* Return true if the body looks like a PKCS7 object */
2184 is_pkcs7_body(BODY *body)
2186 int result;
2188 result = body->type==TYPEAPPLICATION &&
2189 body->subtype &&
2190 (strucmp(body->subtype,"pkcs7-mime")==0 ||
2191 strucmp(body->subtype,"x-pkcs7-mime")==0 ||
2192 strucmp(body->subtype,"pkcs7-signature")==0 ||
2193 strucmp(body->subtype,"x-pkcs7-signature")==0);
2195 return result;
2200 * Recursively stash a pointer to the decrypted data in our
2201 * manufactured body.
2202 * parameters: type: call of type 1, save the base and header for multipart messages
2203 call of type 0, do not save the base and header for multipart messages
2205 static void
2206 create_local_cache(char *h, char *base, BODY *b, int type)
2208 if(b->type==TYPEMULTIPART){
2209 PART *p;
2211 if(type == 1){
2212 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2213 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2214 } else if(type == 0){
2216 * We don't really want to copy the real body contents. It shouldn't be
2217 * used, and in the case of a message with attachments, we'll be
2218 * duplicating the files multiple times.
2220 cpytxt(&b->contents.text, "BODY UNAVAILABLE", 16);
2222 for(p=b->nested.part; p; p=p->next)
2223 create_local_cache(h, base, (BODY *) p, type);
2226 else{
2227 cpytxt(&b->mime.text, h+b->mime.offset, b->mime.text.size);
2228 cpytxt(&b->contents.text, base + b->contents.offset, b->size.bytes);
2233 static long
2234 rfc822_output_func(void *b, char *string)
2236 BIO *bio = (BIO *) b;
2238 return(string ? *string ? (BIO_puts(bio, string) > 0 ? 1L : 0L)
2239 : (BIO_puts(bio, string) >= 0 ? 1L : 0L)
2240 : 0L);
2245 * Attempt to load the private key for the given PERSONAL_CERT.
2246 * This sets the appropriate passphrase globals in order to
2247 * interact with the user correctly.
2249 static int
2250 load_private_key(PERSONAL_CERT *pcert)
2252 if(!pcert->key){
2254 /* Try empty password by default */
2255 char *password = "";
2257 if(ps_global->smime
2258 && (ps_global->smime->need_passphrase
2259 || ps_global->smime->entered_passphrase)){
2260 /* We've already been in here and discovered we need a different password */
2262 if(ps_global->smime->entered_passphrase)
2263 password = (char *) ps_global->smime->passphrase; /* already entered */
2264 else
2265 return 0;
2268 ERR_clear_error();
2270 if(!(pcert->key = load_key(pcert, password, SM_NORMALCERT))){
2271 long err = ERR_get_error();
2273 /* Couldn't load key... */
2275 if(ps_global->smime && ps_global->smime->entered_passphrase){
2277 /* The user got the password wrong maybe? */
2279 if((ERR_GET_LIB(err)==ERR_LIB_EVP && ERR_GET_REASON(err)==EVP_R_BAD_DECRYPT) ||
2280 (ERR_GET_LIB(err)==ERR_LIB_PEM && ERR_GET_REASON(err)==PEM_R_BAD_DECRYPT))
2281 q_status_message(SM_ORDER | SM_DING, 4, 4, _("Incorrect passphrase"));
2282 else
2283 q_status_message1(SM_ORDER, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2285 /* This passphrase is no good; forget it */
2286 ps_global->smime->entered_passphrase = 0;
2289 if(ps_global->smime){
2290 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2291 ps_global->smime->need_passphrase = 1;
2292 if(ps_global->smime->passphrase_emailaddr){
2293 int i;
2294 for(i = 0; ps_global->smime->passphrase_emailaddr[i] != NULL; i++)
2295 fs_give((void **)&ps_global->smime->passphrase_emailaddr[i]);
2296 fs_give((void **) ps_global->smime->passphrase_emailaddr);
2299 ps_global->smime->passphrase_emailaddr = get_x509_subject_email(pcert->cert);
2302 return 0;
2304 else{
2305 /* This key will be cached, so we won't be called again */
2306 if(ps_global->smime){
2307 ps_global->smime->entered_passphrase = 0;
2308 ps_global->smime->need_passphrase = 0;
2312 return 1;
2315 return 0;
2319 static void
2320 setup_pkcs7_body_for_signature(BODY *b, char *description, char *type, char *filename, char *smime_type)
2322 b->type = TYPEAPPLICATION;
2323 b->subtype = cpystr(type);
2324 b->encoding = ENCBINARY;
2325 b->description = cpystr(description);
2327 b->disposition.type = cpystr("attachment");
2328 set_parameter(&b->disposition.parameter, "filename", filename);
2330 set_parameter(&b->parameter, "name", filename);
2331 if(smime_type && *smime_type)
2332 set_parameter(&b->parameter, "smime-type", smime_type);
2337 * Look for a personal certificate matching the
2338 * given address
2340 PERSONAL_CERT *
2341 match_personal_cert_to_email(ADDRESS *a)
2343 PERSONAL_CERT *pcert = NULL;
2344 char buf[MAXPATH];
2345 char **email;
2346 int i, done;
2348 if(!a || !a->mailbox || !a->host)
2349 return NULL;
2351 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2353 if(ps_global->smime){
2354 for(pcert=(PERSONAL_CERT *) ps_global->smime->personal_certs;
2355 pcert;
2356 pcert=pcert->next){
2358 if(!pcert->cert)
2359 continue;
2361 email = get_x509_subject_email(pcert->cert);
2363 done = 0;
2364 if(email != NULL){
2365 for(i = 0; email[i] && strucmp(email[i], buf) != 0; i++);
2366 if(email[i] != NULL) done++;
2367 for(i = 0; email[i] != NULL; i++)
2368 fs_give((void **)&email[i]);
2369 fs_give((void **)email);
2372 if(done > 0)
2373 break;
2377 return pcert;
2382 * Look for a personal certificate matching the from
2383 * (or reply_to? in the given envelope)
2385 PERSONAL_CERT *
2386 match_personal_cert(ENVELOPE *env)
2388 PERSONAL_CERT *pcert;
2390 pcert = match_personal_cert_to_email(env->reply_to);
2391 if(!pcert)
2392 pcert = match_personal_cert_to_email(env->from);
2394 return pcert;
2399 * Flatten the given body into its MIME representation.
2400 * Return the result in a BIO.
2402 static BIO *
2403 body_to_bio(BODY *body)
2405 BIO *bio = NULL;
2406 int len;
2408 bio = BIO_new(BIO_s_mem());
2409 if(!bio)
2410 return NULL;
2412 pine_encode_body(body); /* this attaches random boundary strings to multiparts */
2413 pine_write_body_header(body, rfc822_output_func, bio);
2414 pine_rfc822_output_body(body, rfc822_output_func, bio);
2417 * Now need to truncate by two characters since the above
2418 * appends CRLF.
2420 if((len=BIO_ctrl_pending(bio)) > 1){
2421 BUF_MEM *biobuf = NULL;
2423 BIO_get_mem_ptr(bio, &biobuf);
2424 if(biobuf){
2425 BUF_MEM_grow(biobuf, len-2); /* remove CRLF */
2429 return bio;
2433 static BIO *
2434 bio_from_store(STORE_S *store)
2436 BIO *ret = NULL;
2438 if(store && store->src == BioType && store->txt){
2439 ret = (BIO *) store->txt;
2442 return(ret);
2446 * Encrypt file; given a path (char *) fp, replace the file
2447 * by an encrypted version of it. If (char *) text is not null, then
2448 * replace the text of (char *) fp by the encrypted version of (char *) text.
2449 * certpath is the FULL path to the file containing the certificate used for
2450 * encryption.
2453 encrypt_file(char *fp, char *text, PERSONAL_CERT *pc)
2455 const EVP_CIPHER *cipher = NULL;
2456 STACK_OF(X509) *encerts = NULL;
2457 BIO *out = NULL;
2458 PKCS7 *p7 = NULL;
2459 int rv = 0;
2461 if(pc == NULL)
2462 return 0;
2464 cipher = EVP_aes_256_cbc();
2465 encerts = sk_X509_new_null();
2467 sk_X509_push(encerts, X509_dup(pc->cert));
2469 if(text){
2470 if((out = BIO_new(BIO_s_mem())) != NULL){
2471 (void) BIO_reset(out);
2472 BIO_puts(out, text);
2475 else if((out = BIO_new_file(fp, "rb")) != NULL)
2476 BIO_read_filename(out, fp);
2478 if((p7 = PKCS7_encrypt(encerts, out, cipher, 0)) != NULL){
2479 BIO_set_close(out, BIO_CLOSE);
2480 BIO_free(out);
2481 if((out = BIO_new_file(fp, "w")) != NULL){
2482 BIO_reset(out);
2483 rv = PEM_write_bio_PKCS7(out, p7);
2484 BIO_flush(out);
2488 if(out != NULL)
2489 BIO_free(out);
2490 PKCS7_free(p7);
2491 sk_X509_pop_free(encerts, X509_free);
2493 return rv;
2497 * Encrypt a message on the way out. Called from call_mailer in send.c
2498 * The body may be reallocated.
2501 encrypt_outgoing_message(METAENV *header, BODY **bodyP)
2503 PKCS7 *p7 = NULL;
2504 BIO *in = NULL;
2505 BIO *out = NULL;
2506 const EVP_CIPHER *cipher = NULL;
2507 STACK_OF(X509) *encerts = NULL;
2508 STORE_S *outs = NULL;
2509 PINEFIELD *pf;
2510 ADDRESS *a;
2511 BODY *body = *bodyP;
2512 BODY *newBody = NULL;
2513 int result = 0;
2514 X509 *cert;
2515 char buf[MAXPATH];
2517 dprint((9, "encrypt_outgoing_message()"));
2518 smime_init();
2520 cipher = EVP_aes_256_cbc();
2522 encerts = sk_X509_new_null();
2524 /* Look for a certificate for each of the recipients */
2525 for(pf = header->local; pf && pf->name; pf = pf->next)
2526 if(pf->type == Address && pf->rcptto && pf->addr && *pf->addr){
2527 for(a=*pf->addr; a; a=a->next){
2528 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2530 if((cert = get_cert_for(buf, Public, 1)) != NULL){
2531 sk_X509_push(encerts,cert);
2532 }else{
2533 q_status_message2(SM_ORDER, 1, 1,
2534 _("Unable to find certificate for <%s@%s>"),
2535 a->mailbox, a->host);
2536 goto end;
2541 /* add the sender's certificate so that they can decrypt the message too */
2542 for(a=header->env->from; a ; a = a->next){
2543 snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
2545 if((cert = get_cert_for(buf, Public, 1)) != NULL
2546 && sk_X509_find(encerts, cert) == -1)
2547 sk_X509_push(encerts,cert);
2550 in = body_to_bio(body);
2552 p7 = PKCS7_encrypt(encerts, in, cipher, 0);
2554 outs = so_get(BioType, NULL, EDIT_ACCESS);
2555 out = bio_from_store(outs);
2557 i2d_PKCS7_bio(out, p7);
2558 (void) BIO_flush(out);
2560 so_seek(outs, 0, SEEK_SET);
2562 newBody = mail_newbody();
2564 newBody->type = TYPEAPPLICATION;
2565 newBody->subtype = cpystr("pkcs7-mime");
2566 newBody->encoding = ENCBINARY;
2568 newBody->disposition.type = cpystr("attachment");
2569 set_parameter(&newBody->disposition.parameter, "filename", "smime.p7m");
2571 newBody->description = cpystr("S/MIME Encrypted Message");
2572 set_parameter(&newBody->parameter, "smime-type", "enveloped-data");
2573 set_parameter(&newBody->parameter, "name", "smime.p7m");
2575 newBody->contents.text.data = (unsigned char *) outs;
2577 *bodyP = newBody;
2579 result = 1;
2581 end:
2583 BIO_free(in);
2584 PKCS7_free(p7);
2585 sk_X509_pop_free(encerts, X509_free);
2587 dprint((9, "encrypt_outgoing_message returns %d", result));
2588 return result;
2593 Get (and decode) the body of the given section of msg
2595 static STORE_S*
2596 get_part_contents(long msgno, const char *section)
2598 long len;
2599 gf_io_t pc;
2600 STORE_S *store = NULL;
2601 char *err;
2603 store = so_get(CharStar, NULL, EDIT_ACCESS);
2604 if(store){
2605 gf_set_so_writec(&pc,store);
2607 err = detach(ps_global->mail_stream, msgno, (char*) section, 0L, &len, pc, NULL, 0L);
2609 gf_clear_so_writec(store);
2611 so_seek(store, 0, SEEK_SET);
2613 if(err)
2614 so_give(&store);
2617 return store;
2621 static PKCS7 *
2622 get_pkcs7_from_part(long msgno,const char *section)
2624 STORE_S *store = NULL;
2625 PKCS7 *p7 = NULL;
2626 BIO *in = NULL;
2628 store = get_part_contents(msgno, section);
2630 if(store){
2631 if(store->src == CharStar){
2632 int len;
2635 * We're reaching inside the STORE_S structure. We should
2636 * probably have a way to get the length, instead.
2638 len = (int) (store->eod - store->dp);
2639 in = BIO_new_mem_buf(store->txt, len);
2641 else{ /* just copy it */
2642 unsigned char c;
2644 in = BIO_new(BIO_s_mem());
2645 (void) BIO_reset(in);
2647 so_seek(store, 0L, 0);
2648 while(so_readc(&c, store)){
2649 BIO_write(in, &c, 1);
2653 if(in){
2654 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2655 if((p7=d2i_PKCS7_bio(in,NULL)) == NULL){
2656 /* error */
2659 BIO_free(in);
2662 so_give(&store);
2665 return p7;
2668 int same_cert(X509 *x, X509 *cert)
2670 char bufcert[256], bufx[256];
2671 int rv = 0;
2673 get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert), ":");
2674 get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx), ":");
2675 if(strcmp(bufx, bufcert) == 0)
2676 rv = 1;
2678 return rv;
2682 /* extract and save certificates from a PKCS7 package. The ctype variable
2683 * tells us if we want to extract it to a public/ or a ca/ directory. The
2684 * later makes sense only for recoverable errors (errors that can be fixed
2685 * by saving to the ca/ directory before we verify the signature).
2686 * Return value:
2687 * 0 - no errors (in public/) no need to try again,
2688 * or validated self signed certificate (in ca/)
2689 * < 0 - certificate error is not recoverable, don't even think about it.
2692 int smime_extract_and_save_cert(PKCS7 *p7, int check_cert)
2694 STACK_OF(X509) *signers;
2695 X509 *x, *cert;
2696 char **email;
2697 int i, j;
2698 long error;
2700 if((signers = PKCS7_get0_signers(p7, NULL, 0)) == NULL)
2701 return -1;
2703 for(i = 0; i < sk_X509_num(signers); i++){
2704 if((x = sk_X509_value(signers,i)) == NULL)
2705 continue;
2707 if((email = get_x509_subject_email(x)) != NULL){
2708 for(j = 0; email[j] != NULL; j++){
2709 if((cert = get_cert_for(email[j], Public, 1)) == NULL
2710 || same_cert(x, cert) == 0){
2711 if(check_cert == 0
2712 || smime_validate_cert(x, &error) == 0
2713 || (*pith_smime_confirm_save)(email[j]) == 1)
2714 save_cert_for(email[j], x, Public);
2716 if(cert != NULL)
2717 X509_free(cert);
2718 fs_give((void **) &email[j]);
2720 fs_give((void **) email);
2723 sk_X509_free(signers);
2725 return 0;
2729 * Try to verify a signature.
2731 * p7 - the pkcs7 object to verify
2732 * in - the plain data to verify (NULL if not detached)
2733 * out - BIO to which to write the opaque data
2734 * silent - if non zero, do not print errors, only print success.
2736 static int
2737 do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
2739 STACK_OF(X509) *otherCerts = NULL;
2740 CertList *cl;
2741 int result, flags;
2742 const char *data;
2743 long err;
2745 if(!s_cert_store){
2746 if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
2747 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2749 return -1;
2752 flags = F_ON(F_USE_CERT_STORE_ONLY, ps_global) ? PKCS7_NOINTERN : 0;
2754 if(ps_global->smime->publiccertlist == NULL){
2755 renew_cert_data(&ps_global->smime->publiccertlist, Public);
2756 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next){
2757 if(cl->x509_cert == NULL){
2758 char *s = strrchr(cl->name, '.');
2759 *s = '\0';
2760 cl->x509_cert = get_cert_for(cl->name, Public, 1);
2761 *s = '.';
2766 if(ps_global->smime->publiccertlist){
2767 otherCerts = sk_X509_new_null();
2768 for(cl = ps_global->smime->publiccertlist; cl ; cl = cl->next)
2769 if(cl->x509_cert != NULL)
2770 sk_X509_push(otherCerts, X509_dup(cl->x509_cert));
2773 result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, flags);
2775 sk_X509_pop_free(otherCerts, X509_free);
2777 if(result){
2778 q_status_message(SM_ORDER, 1, 1, _("S/MIME signature verified ok"));
2780 else{
2781 err = ERR_peek_error_line_data(NULL, NULL, &data, NULL);
2783 if(out && err==ERR_PACK(ERR_LIB_PKCS7,PKCS7_F_PKCS7_VERIFY,PKCS7_R_CERTIFICATE_VERIFY_ERROR)){
2785 /* Retry verification so we can get the plain text */
2786 /* Might be better to reimplement PKCS7_verify here? */
2788 PKCS7_verify(p7, otherCerts, s_cert_store, in, out, PKCS7_NOVERIFY);
2790 if (!silent) q_status_message1(SM_ORDER | SM_DING, 3, 3,
2791 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
2794 return result;
2798 void
2799 free_smime_body_sparep(void **sparep)
2801 char *s;
2802 SIZEDTEXT *st;
2803 if(sparep && *sparep){
2804 switch(get_smime_sparep_type(*sparep)){
2805 case P7Type: PKCS7_free((PKCS7 *) get_smime_sparep_data(*sparep));
2806 break;
2807 case CharType: s = (char *)get_smime_sparep_data(*sparep);
2808 fs_give((void **) &s);
2809 break;
2810 case SizedText : st = (SIZEDTEXT *)get_smime_sparep_data(*sparep);
2811 fs_give((void **) &st->data);
2812 fs_give((void **) &st);
2813 break;
2814 default : break;
2816 ((SMIME_SPARE_S *)(*sparep))->data = NULL;
2817 fs_give(sparep);
2821 /* Big comment, explaining the mess that exists out there, and how we deal
2822 with it, and also how we solve the problems that are created this way.
2824 When Alpine sends a message, it constructs that message, computes the
2825 signature, but then it forgets the message it signed and reconstructs it
2826 again. Since it signs a message containing a notice about "mime aware
2827 tools", but it does not send that we do not include that in the part
2828 that is signed, and that takes care of much of the problems.
2830 Another problem is what is received from the servers. All servers tested
2831 seem to transmit the message that was signed intact and Alpine can check
2832 the signature correctly. That is not a problem. The problem arises when
2833 the message includes attachments. In this case different servers send
2834 different things, so it will be up to us to figure out what is the text
2835 that was actually signed. Confused? here is the story:
2837 When a message containing and attachment is sent by Alpine, UW-IMAP,
2838 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2839 that was sent by Alpine, but GMX.com, Exchange, and probably other
2840 servers add a trailing \r\n in the message, so when validating the
2841 signature, these messages will not validate. There are several things
2842 that can be done.
2844 1. Add a trailing \r\n to any message that contains attachments, sign that
2845 and send that. In this way, all messages will validate with all
2846 servers.
2848 2. Compatibility mode: If a message has an attachment, contains a trailing
2849 \r\n and does not validate (sent by an earlier version of Alpine),
2850 remove the trailing \r\n and try to revalidate again.
2852 3. We do not add \r\n to validate a message that we sent, because that
2853 would only work in Alpine, and not in any other client. That would
2854 not be a good thing to do.
2856 PART II
2858 Now we have to deal with encrypted and signed messages. The problem is
2859 that c-client makes all its pointers point to "on disk" content, but
2860 since we decrypted the data earlier, we have to make sure of two things.
2861 One is that we saved that data (so we do not have to decrypt it again)
2862 and second that we can use it.
2864 In order to save the data we use create_local_cache, so that we do not
2865 have to redecrypt the message. Once this is saved, c-client functions will
2866 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2868 PART III
2870 When we are trying to verify messages with detached signatures, some
2871 imap servers send incorrect information in the mail_fetch_mime call. By
2872 incorrect I mean that this is not fetched directly from the message, but
2873 it is read from the message, processed, and then the processed part is
2874 sent to us, so this text might not agree with what is in the message,
2875 and so the validation of the signature might fail. However, the good
2876 news is that the message validates if saved to a local folder. This
2877 means that if normal validation does not work we can make it work by
2878 saving the message locally and validating that. This is implemented
2879 below, and causes delay in the display of the message. I am considering
2880 at this time not to do this automatically, but wait for the user to tell
2881 us to do it for them by means of a command available in the
2882 mail_view_screen. This might help in other situations, where a message
2883 is supposed to have an attachment, but it can not be seen in the
2884 processed text. Nevertheless, at this time, this is automatic, and is
2885 causing a delay in the processing of the message, but it is validating
2886 correctly all messages.
2888 PART IV
2890 When the user sends a message as encrypted and signed, this code used to
2891 encrypt first, and then sign the pkcs7 body, but it turns out that some
2892 other clients can not handle these messages. While we could argue that the
2893 other clients need to improve, we will support reading messages in both
2894 ways, and will send messages using this technique; that is, signed first,
2895 encrypted second. It seems that all tested clients support this way, so it
2896 should be safe to do so.
2899 typedef struct smime_filter_s {
2900 void (*filter)();
2901 } SMIME_FILTER_S;
2903 SMIME_FILTER_S sig_filter[] = {
2904 {smime_remove_trailing_crlf},
2905 {smime_remove_folding_space}
2908 #define TOTAL_FILTERS (sizeof(sig_filter)/sizeof(sig_filter[0]))
2909 #define TOTAL_SIGFLTR (1 << TOTAL_FILTERS) /* not good, keep filters to a low number */
2911 void
2912 smime_remove_trailing_crlf(char **mimetext, unsigned long *mimelen,
2913 char **bodytext, unsigned long *bodylen)
2915 if(*bodylen > 2 && !strncmp(*bodytext+*bodylen-2, "\r\n", 2))
2916 *bodylen -= 2;
2919 void
2920 smime_remove_folding_space(char **mimetext, unsigned long *mimelen,
2921 char **bodytext, unsigned long *bodylen)
2923 char *s = NULL, *t;
2924 unsigned long mlen = *mimelen;
2926 if(*mimetext){
2927 for (s = t = *mimetext; t - *mimetext < *mimelen; ){
2928 if(*t == '\r' && *(t+1) == '\n' && (*(t+2) == '\t' || *(t+2) == ' ')){
2929 *s++ = ' ';
2930 t += 3;
2931 mlen -= 2;
2933 else
2934 *s++ = *t++;
2936 *mimelen = mlen;
2941 smime_validate_extra_test(char *mimetext, unsigned long mimelen, char *bodytext, unsigned long bodylen, PKCS7 *p7, int nflag)
2943 int result, i, j, flag;
2944 char *mtext, *btext;
2945 unsigned long mlen, blen;
2946 BIO *in;
2948 mtext = mimelen ? fs_get(mimelen+1) : NULL;
2949 btext = fs_get(bodylen+1);
2951 flag = 1; /* silence all failures */
2952 for(i = 1; result == 0 && i < TOTAL_SIGFLTR; i++){
2953 if((in = BIO_new(BIO_s_mem())) == NULL)
2954 return -1;
2956 (void) BIO_reset(in);
2958 if(i+1 == TOTAL_SIGFLTR)
2959 flag = nflag;
2961 if(mimelen)
2962 strncpy(mtext, mimetext, mlen = mimelen);
2963 strncpy(btext, bodytext, blen = bodylen);
2964 for(j = 0; j < TOTAL_FILTERS; j++)
2965 if((i >> j) & 1)
2966 (sig_filter[j].filter)(&mtext, &mlen, &btext, &blen);
2967 if(mtext != NULL)
2968 BIO_write(in, mtext, mlen);
2969 BIO_write(in, btext, blen);
2970 result = do_signature_verify(p7, in, NULL, flag);
2971 BIO_free(in);
2973 if(mtext) fs_give((void **)&mtext);
2974 if(btext) fs_give((void **)&btext);
2975 return result;
2979 * Given a multipart body of type multipart/signed, attempt to verify it.
2980 * Returns non-zero if the body was changed.
2982 static int
2983 do_detached_signature_verify(BODY *b, long msgno, char *section)
2985 PKCS7 *p7 = NULL;
2986 BIO *in = NULL;
2987 PART *p;
2988 int result, modified_the_body = 0;
2989 int flag; /* 1 silent, 0 not silent */
2990 int saved = 0;
2991 unsigned long mimelen, bodylen;
2992 char newSec[100], *mimetext, *bodytext;
2993 char *what_we_did;
2994 SIZEDTEXT *st;
2996 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"));
2998 smime_init();
3000 /* if it was signed and then encrypted, use the decrypted text
3001 * to check the validity of the signature
3003 if(b->sparep){
3004 if(get_smime_sparep_type(b->sparep) == SizedText){
3005 /* bodytext includes mimetext */
3006 st = (SIZEDTEXT *) get_smime_sparep_data(b->sparep);
3007 bodytext = (char *) st->data;
3008 bodylen = st->size;
3009 mimetext = NULL;
3010 mimelen = 0L;
3013 else{
3014 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3015 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3016 if(mimetext)
3017 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3019 if(mimetext == NULL || bodytext == NULL)
3020 return modified_the_body;
3023 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3025 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL
3026 || (in = BIO_new(BIO_s_mem())) == NULL)
3027 return modified_the_body;
3029 (void) BIO_reset(in);
3030 if(mimetext != NULL)
3031 BIO_write(in, mimetext, mimelen);
3032 BIO_write(in, bodytext, bodylen);
3034 saved = smime_extract_and_save_cert(p7, F_ON(F_USE_CERT_STORE_ONLY, ps_global));
3035 if(saved < 0 && F_ON(F_USE_CERT_STORE_ONLY, ps_global))
3036 return modified_the_body;
3038 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3039 flag = (mimelen == 0 || !IS_REMOTE(ps_global->mail_stream->mailbox))
3040 ? 0 : 1;
3041 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, flag);
3042 if(result < 0)
3043 return modified_the_body;
3044 if(result == 0
3045 && mimelen > 0 /* do not do this for encrypted messages */
3046 && IS_REMOTE(ps_global->mail_stream->mailbox)){
3047 char *fetch;
3048 unsigned long hlen, tlen;
3049 STORE_S *msg_so;
3051 BIO_free(in);
3052 if((in = BIO_new(BIO_s_mem())) != NULL
3053 && (fetch = mail_fetch_header(ps_global->mail_stream, msgno, NULL,
3054 NULL, &hlen, FT_PEEK)) != NULL
3055 && (msg_so = so_get(CharStar, NULL, WRITE_ACCESS)) != NULL
3056 && so_nputs(msg_so, fetch, (long) hlen)
3057 && (fetch = pine_mail_fetch_text(ps_global->mail_stream, msgno, NULL,
3058 &tlen, FT_PEEK)) != NULL
3059 && so_nputs(msg_so, fetch, tlen)){
3060 STRING bs;
3061 char *h = (char *) so_text(msg_so);
3062 char *bstart = strstr(h, "\r\n\r\n");
3063 ENVELOPE *env;
3064 BODY *body, *tmpB;
3066 bstart += 4;
3067 INIT(&bs, mail_string, bstart, tlen);
3068 rfc822_parse_msg_full(&env, &body, h, bstart-h-4, &bs, BADHOST, 0, 0);
3069 mail_free_envelope(&env);
3071 mail_free_body_part(&b->nested.part);
3072 tmpB = mail_body_section(body, (unsigned char *) section);
3073 if(MIME_MSG(tmpB->type, tmpB->subtype))
3074 b->nested.part = tmpB->nested.msg->body->nested.part;
3075 else
3076 b->nested.part = tmpB->nested.part;
3077 create_local_cache(bstart, bstart, &b->nested.part->body, 1);
3078 modified_the_body = 1;
3080 snprintf(newSec, sizeof(newSec), "%s%s1", section ? section : "", (section && *section) ? "." : "");
3082 mimetext = mail_fetch_mime(ps_global->mail_stream, msgno, (char*) newSec, &mimelen, 0);
3084 if(mimetext)
3085 bodytext = mail_fetch_body (ps_global->mail_stream, msgno, (char*) newSec, &bodylen, 0);
3087 if (mimetext == NULL || bodytext == NULL)
3088 return modified_the_body;
3090 snprintf(newSec, sizeof(newSec), "%s%s2", section ? section : "", (section && *section) ? "." : "");
3092 if((p7 = get_pkcs7_from_part(msgno, newSec)) == NULL)
3093 return modified_the_body;
3095 (void) BIO_reset(in);
3096 BIO_write(in, mimetext, mimelen);
3097 BIO_write(in, bodytext, bodylen);
3098 so_give(&msg_so);
3100 if((result = do_signature_verify(p7, in, NULL, 1)) == 0){
3101 result = smime_validate_extra_test(mimetext, mimelen, bodytext, bodylen, p7, 0);
3102 if(result < 0)
3103 return modified_the_body;
3109 BIO_free(in);
3110 if(b->subtype)
3111 fs_give((void**) &b->subtype);
3113 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3114 b->encoding = ENC8BIT;
3116 if(b->description)
3117 fs_give ((void**) &b->description);
3119 what_we_did = result ? _("This message was cryptographically signed.") :
3120 _("This message was cryptographically signed but the signature could not be verified.");
3122 b->description = cpystr(what_we_did);
3124 b->sparep = create_smime_sparep(P7Type, p7);
3126 p = b->nested.part;
3128 /* p is signed plaintext */
3129 if(p && p->next)
3130 mail_free_body_part(&p->next); /* hide the pkcs7 from the viewer */
3132 modified_the_body = 1;
3134 return modified_the_body;
3138 PERSONAL_CERT *
3139 find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri)
3141 PERSONAL_CERT *x = NULL;
3143 if(ps_global->smime){
3144 for(x = (PERSONAL_CERT *) ps_global->smime->personal_certs; x; x=x->next){
3145 X509 *mine;
3147 mine = x->cert;
3149 if(!X509_NAME_cmp(ri->issuer_and_serial->issuer,X509_get_issuer_name(mine)) &&
3150 !ASN1_INTEGER_cmp(ri->issuer_and_serial->serial,X509_get_serialNumber(mine))){
3151 break;
3156 return x;
3160 static PERSONAL_CERT *
3161 find_certificate_matching_pkcs7(PKCS7 *p7)
3163 int i;
3164 STACK_OF(PKCS7_RECIP_INFO) *recips;
3165 PERSONAL_CERT *x = NULL;
3167 recips = p7->d.enveloped->recipientinfo;
3169 for(i=0; i<sk_PKCS7_RECIP_INFO_num(recips); i++){
3170 PKCS7_RECIP_INFO *ri;
3172 ri = sk_PKCS7_RECIP_INFO_value(recips, i);
3174 if((x=find_certificate_matching_recip_info(ri))!=0){
3175 break;
3179 return x;
3182 /* decrypt an encrypted file.
3183 Args: fp - the path to the encrypted file.
3184 rv - a code that tells the caller what happened inside the function
3185 pcert - a personal certificate that was used to encrypt this file
3186 Returns the decoded text allocated in a char *, whose memory must be
3187 freed by caller
3190 char *
3191 decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc)
3193 PKCS7 *p7 = NULL;
3194 char *text, *tmp;
3195 BIO *in = NULL, *out = NULL;
3196 int i, j;
3197 long unsigned int len;
3198 void *ret;
3200 if(pc == NULL || (text = read_file(fp, 0)) == NULL || *text == '\0')
3201 return NULL;
3203 tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1);
3204 for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0'
3205 && text[i] != '-'; j++, i++)
3206 tmp[j] = text[i];
3207 tmp[j] = '\0';
3209 ret = rfc822_base64((unsigned char *)tmp, strlen(tmp), &len);
3211 if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){
3212 p7 = d2i_PKCS7_bio(in, NULL);
3213 BIO_free(in);
3216 if (text) fs_give((void **)&text);
3217 if (ret) fs_give((void **)&ret);
3219 if (rv) *rv = pc->key == NULL ? -1 : 1;
3221 out = BIO_new(BIO_s_mem());
3222 (void) BIO_reset(out);
3224 if(PKCS7_decrypt(p7, pc->key, pc->cert, out, 0) != 0){
3225 BIO_get_mem_data(out, &tmp);
3226 text = cpystr(tmp);
3227 BIO_free(out);
3228 } else
3229 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3230 (char *) openssl_error_string());
3231 PKCS7_free(p7);
3233 return text;
3237 * Try to decode (decrypt or verify a signature) a PKCS7 body
3238 * Returns non-zero if something was changed.
3240 static int
3241 do_decoding(BODY *b, long msgno, const char *section)
3243 int modified_the_body = 0;
3244 BIO *out = NULL;
3245 PKCS7 *p7 = NULL;
3246 X509 *recip = NULL;
3247 EVP_PKEY *key = NULL;
3248 PERSONAL_CERT *pcert = NULL;
3249 char *what_we_did = "";
3250 char null[1];
3252 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"));
3253 null[0] = '\0';
3254 smime_init();
3257 * Extract binary data from part to an in-memory store
3260 if(b->sparep){
3261 if(get_smime_sparep_type(b->sparep) == P7Type)
3262 p7 = (PKCS7*) get_smime_sparep_data(b->sparep);
3264 else{
3265 p7 = get_pkcs7_from_part(msgno, section && *section ? section : "1");
3266 if(!p7){
3267 q_status_message1(SM_ORDER, 2, 2, "Couldn't load PKCS7 object: %s",
3268 (char*) openssl_error_string());
3269 goto end;
3273 * Save the PKCS7 object for later dealings by the user interface.
3274 * It will be cleaned up when the body is garbage collected.
3276 b->sparep = create_smime_sparep(P7Type, p7);
3279 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7), PKCS7_type_is_enveloped(p7)));
3281 if(PKCS7_type_is_signed(p7)){
3282 int sigok;
3284 out = BIO_new(BIO_s_mem());
3285 (void) BIO_reset(out);
3286 BIO_puts(out, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3288 sigok = do_signature_verify(p7, NULL, out, 0);
3290 what_we_did = sigok ? _("This message was cryptographically signed.") :
3291 _("This message was cryptographically signed but the signature could not be verified.");
3293 /* make sure it's null terminated */
3294 BIO_write(out, null, 1);
3296 else if(!PKCS7_type_is_enveloped(p7)){
3297 q_status_message(SM_ORDER, 1, 1, "PKCS7 object not recognised.");
3298 goto end;
3300 else{ /* It *is* enveloped */
3301 int decrypt_result;
3303 what_we_did = _("This message was encrypted.");
3305 /* now need to find a cert that can decrypt this */
3306 pcert = find_certificate_matching_pkcs7(p7);
3308 if(!pcert){
3309 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3310 goto end;
3313 recip = pcert->cert;
3315 if(!load_private_key(pcert)
3316 && ps_global->smime
3317 && ps_global->smime->need_passphrase
3318 && !ps_global->smime->already_auto_asked){
3319 /* Couldn't load key with blank password, ask user */
3320 ps_global->smime->already_auto_asked = 1;
3321 if(pith_opt_smime_get_passphrase){
3322 (*pith_opt_smime_get_passphrase)();
3323 load_private_key(pcert);
3327 key = pcert->key;
3328 if(!key)
3329 goto end;
3331 out = BIO_new(BIO_s_mem());
3332 (void) BIO_reset(out);
3333 BIO_puts(out, "MIME-Version: 1.0\r\n");
3335 decrypt_result = PKCS7_decrypt(p7, key, recip, out, 0);
3337 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3338 forget_private_keys();
3340 if(!decrypt_result){
3341 q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"),
3342 (char*) openssl_error_string());
3343 goto end; }
3345 BIO_write(out, null, 1);
3349 * We've now produced a flattened MIME object in BIO out.
3350 * It needs to be turned back into a BODY.
3353 if(out){
3354 BODY *body;
3355 ENVELOPE *env;
3356 char *h = NULL;
3357 char *bstart;
3358 STRING s;
3359 BUF_MEM *bptr = NULL;
3361 BIO_get_mem_ptr(out, &bptr);
3362 if(bptr)
3363 h = bptr->data;
3365 /* look for start of body */
3366 bstart = strstr(h, "\r\n\r\n");
3368 if(!bstart){
3369 q_status_message(SM_ORDER, 3, 3, _("Encrypted data couldn't be parsed."));
3371 else{
3372 SIZEDTEXT *st;
3373 bstart += 4; /* skip over CRLF*2 */
3375 INIT(&s, mail_string, bstart, strlen(bstart));
3376 rfc822_parse_msg_full(&env, &body, h, bstart-h-2, &s, BADHOST, 0, 0);
3377 mail_free_envelope(&env); /* Don't care about this */
3379 if(body->type == TYPEMULTIPART
3380 && !strucmp(body->subtype, "SIGNED")){
3381 char *cookie = NULL;
3382 PARAMETER *param;
3383 for (param = body->parameter; param && !cookie; param = param->next)
3384 if (!strucmp (param->attribute,"BOUNDARY")) cookie = param->value;
3385 if(cookie != NULL){
3386 st = fs_get(sizeof(SIZEDTEXT));
3387 st->data = (void *) cpystr(bstart + strlen(cookie)+4); /* 4 = strlen("--\r\n") */
3388 st->size = body->nested.part->next->body.mime.offset - 2*(strlen(cookie) + 4);
3389 body->sparep = create_smime_sparep(SizedText, (void *)st);
3391 else
3392 q_status_message(SM_ORDER, 3, 3, _("Couldn't find cookie in attachment list."));
3394 body->mime.offset = 0;
3395 body->mime.text.size = 0;
3398 * Now convert original body (application/pkcs7-mime)
3399 * to a multipart body with one sub-part (the decrypted body).
3400 * Note that the sub-part may also be multipart!
3403 b->type = TYPEMULTIPART;
3404 if(b->subtype)
3405 fs_give((void**) &b->subtype);
3408 * This subtype is used in mailview.c to annotate the display of
3409 * encrypted or signed messages. We know for sure then that it's a PKCS7
3410 * part because the sparep field is set to the PKCS7 object (see above).
3412 b->subtype = cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE);
3413 b->encoding = ENC8BIT;
3415 if(b->description)
3416 fs_give((void**) &b->description);
3418 b->description = cpystr(what_we_did);
3420 if(b->disposition.type)
3421 fs_give((void **) &b->disposition.type);
3423 if(b->contents.text.data)
3424 fs_give((void **) &b->contents.text.data);
3426 if(b->parameter)
3427 mail_free_body_parameter(&b->parameter);
3429 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3430 b->nested.part = fs_get(sizeof(PART));
3431 b->nested.part->body = *body;
3432 b->nested.part->next = NULL;
3434 fs_give((void**) &body);
3437 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3438 * the decrypted data. Otherwise, it'll try to load it from the original
3439 * data. Eek.
3441 create_local_cache(bstart-b->nested.part->body.mime.offset, bstart, &b->nested.part->body, 0);
3443 modified_the_body = 1;
3447 end:
3448 if(out)
3449 BIO_free(out);
3451 return modified_the_body;
3456 * Recursively handle PKCS7 bodies in our message.
3458 * Returns non-zero if some fiddling was done.
3460 static int
3461 do_fiddle_smime_message(BODY *b, long msgno, char *section)
3463 int modified_the_body = 0;
3465 if(!b)
3466 return 0;
3468 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"));
3470 if(is_pkcs7_body(b)){
3472 if(do_decoding(b, msgno, section)){
3474 * b should now be a multipart message:
3475 * fiddle it too in case it's been multiply-encrypted!
3478 /* fallthru */
3479 modified_the_body = 1;
3483 if(b->type==TYPEMULTIPART || MIME_MSG(b->type, b->subtype)){
3485 PART *p;
3486 int partNum;
3487 char newSec[100];
3489 if(MIME_MULT_SIGNED(b->type, b->subtype)){
3493 * Ahah. We have a multipart signed entity.
3495 * Multipart/signed
3496 * part 1 (signed thing)
3497 * part 2 (the pkcs7 signature)
3499 * We're going to convert that to
3501 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3502 * part 1 (signed thing)
3503 * part 2 has been freed
3505 * We also extract the signature from part 2 and save it
3506 * in the multipart body->sparep, and we add a description
3507 * in the multipart body->description.
3510 * The results of a decrypted message will be similar. It
3511 * will be
3513 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3514 * part 1 (decrypted thing)
3517 modified_the_body += do_detached_signature_verify(b, msgno, section);
3519 else if(MIME_MSG(b->type, b->subtype)){
3520 modified_the_body += do_fiddle_smime_message(b->nested.msg->body, msgno, section);
3522 else{
3524 for(p=b->nested.part,partNum=1; p; p=p->next,partNum++){
3525 /* Append part number to the section string */
3527 snprintf(newSec, sizeof(newSec), "%s%s%d", section, *section ? "." : "", partNum);
3529 modified_the_body += do_fiddle_smime_message(&p->body, msgno, newSec);
3534 return modified_the_body;
3539 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3540 * Returns non-zero if something was changed.
3543 fiddle_smime_message(BODY *b, long msgno)
3545 return do_fiddle_smime_message(b, msgno, "");
3549 /********************************************************************************/
3553 * Output a string in a distinctive style
3555 void
3556 gf_puts_uline(char *txt, gf_io_t pc)
3558 pc(TAG_EMBED); pc(TAG_BOLDON);
3559 gf_puts(txt, pc);
3560 pc(TAG_EMBED); pc(TAG_BOLDOFF);
3563 /* get_chain_for_cert: error and level are mandatory arguments */
3564 STACK_OF(X509) *
3565 get_chain_for_cert(X509 *cert, int *error, int *level)
3567 STACK_OF(X509) *chain = NULL;
3568 X509_STORE_CTX *ctx;
3569 X509 *x, *xtmp;
3570 int rc; /* return code */
3572 *level = -1;
3573 *error = 0;
3574 ERR_clear_error();
3575 if((ctx = X509_STORE_CTX_new()) != NULL){
3576 X509_STORE_set_flags(s_cert_store, 0);
3577 if(!X509_STORE_CTX_init(ctx, s_cert_store, cert, NULL))
3578 *error = X509_STORE_CTX_get_error(ctx);
3579 else if((chain = sk_X509_new_null()) != NULL){
3580 for(x = cert; ; x = xtmp){
3581 if(++*level > 0)
3582 sk_X509_push(chain, X509_dup(x));
3583 rc = X509_STORE_CTX_get1_issuer(&xtmp, ctx, x);
3584 if(rc < 0)
3585 *error = 1;
3586 if(rc <= 0)
3587 break;
3588 if(!X509_check_issued(xtmp, xtmp))
3589 break;
3592 X509_STORE_CTX_free(ctx);
3594 return chain;
3599 * Sign a message. Called from call_mailer in send.c.
3601 * This takes the header for the outgoing message as well as a pointer
3602 * to the current body (which may be reallocated).
3603 * The last argument (BODY **bp) is an argument that tells Alpine
3604 * if the body has 8 bit. if *bp is not null we compute two signatures
3605 * one for the quoted-printable encoded message, and another for the
3606 * 8bit encoded message. We return the signature for the 8bit encoded
3607 * part in p2->body.mime.text.data.
3608 * The reason why we compute two signatures is so that we can decide
3609 * which one to use later, and we only do it in the case that *bp is
3610 * not null. If we did not do this, then we might not be able to sign
3611 * a message until we log in to the smtp server, so instead of doing
3612 * that, we get ready for any possible situation we might find.
3615 sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach, BODY **bp)
3617 STORE_S *outs = NULL;
3618 STORE_S *outs_2 = NULL;
3619 BODY *body = *bodyP;
3620 BODY *newBody = NULL;
3621 PART *p1 = NULL;
3622 PART *p2 = NULL;
3623 PERSONAL_CERT *pcert;
3624 BIO *in = NULL;
3625 BIO *in_2 = NULL;
3626 BIO *out = NULL;
3627 BIO *out_2 = NULL;
3628 PKCS7 *p7 = NULL;
3629 PKCS7 *p7_2 = NULL;
3630 STACK_OF(X509) *chain;
3631 int result = 0, error;
3632 int flags = dont_detach ? 0 : PKCS7_DETACHED;
3633 int level;
3635 dprint((9, "sign_outgoing_message()"));
3637 smime_init();
3639 /* Look for a private key matching the sender address... */
3641 pcert = match_personal_cert(header->env);
3643 if(!pcert){
3644 q_status_message(SM_ORDER, 3, 3, _("Couldn't find the certificate needed to sign."));
3645 goto end;
3648 if(!load_private_key(pcert) && ps_global->smime && ps_global->smime->need_passphrase){
3649 /* Couldn't load key with blank password, try again */
3650 if(pith_opt_smime_get_passphrase){
3651 (*pith_opt_smime_get_passphrase)();
3652 load_private_key(pcert);
3656 if(!pcert->key)
3657 goto end;
3659 if(((chain = get_chain_for_cert(pcert->cert, &error, &level)) != NULL && error)
3660 || level == 0){
3661 sk_X509_pop_free(chain, X509_free);
3662 chain = NULL;
3665 if(error)
3666 q_status_message(SM_ORDER, 1, 1,
3667 _("Not all certificates needed to verify signature included in signed message"));
3669 in = body_to_bio(body);
3671 p7 = PKCS7_sign(pcert->cert, pcert->key, chain, in, flags);
3673 if(bp && *bp){
3674 int i, save_encoding;
3676 for(i = 0; (i <= ENCMAX) && body_encodings[i]; i++);
3678 if(i > ENCMAX){ /* no empty encoding slots! */
3679 *bp = NULL;
3681 else {
3682 save_encoding = (*bp)->encoding;
3683 body_encodings[(*bp)->encoding = i] = body_encodings[ENC8BIT];
3685 in_2 = body_to_bio(body);
3687 body_encodings[i] = NULL;
3688 (*bp)->encoding = save_encoding;
3692 if(bp && *bp)
3693 p7_2 = PKCS7_sign(pcert->cert, pcert->key, chain, in_2, flags);
3695 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
3696 forget_private_keys();
3698 if(chain)
3699 sk_X509_pop_free(chain, X509_free);
3701 if(!p7){
3702 q_status_message(SM_ORDER, 1, 1, _("Error creating signed object."));
3703 goto end;
3706 outs = so_get(BioType, NULL, EDIT_ACCESS);
3707 out = bio_from_store(outs);
3709 i2d_PKCS7_bio(out, p7);
3710 (void) BIO_flush(out);
3712 so_seek(outs, 0, SEEK_SET);
3714 if(bp && *bp && p7_2){
3715 outs_2 = so_get(BioType, NULL, EDIT_ACCESS);
3716 out_2 = bio_from_store(outs_2);
3718 i2d_PKCS7_bio(out_2, p7_2);
3719 (void) BIO_flush(out_2);
3721 so_seek(outs_2, 0, SEEK_SET);
3724 if((flags&PKCS7_DETACHED)==0){
3726 /* the simple case: the signed data is in the pkcs7 object */
3728 newBody = mail_newbody();
3730 setup_pkcs7_body_for_signature(newBody, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3732 newBody->contents.text.data = (unsigned char *) outs;
3733 *bodyP = newBody;
3735 result = 1;
3737 else{
3740 * OK.
3741 * We have to create a new body as follows:
3743 * multipart/signed; blah blah blah
3744 * reference to existing body
3746 * pkcs7 object
3749 newBody = mail_newbody();
3751 newBody->type = TYPEMULTIPART;
3752 newBody->subtype = cpystr("signed");
3753 newBody->encoding = ENC7BIT;
3755 set_parameter(&newBody->parameter, "protocol", "application/pkcs7-signature");
3756 set_parameter(&newBody->parameter, "micalg", "sha1");
3758 p1 = mail_newbody_part();
3759 p2 = mail_newbody_part();
3762 * This is nasty. We're just copying the body in here,
3763 * but since our newBody is freed at the end of call_mailer,
3764 * we mustn't let this body (the original one) be freed twice.
3766 p1->body = *body; /* ARRGH. This is special cased at the end of call_mailer */
3768 p1->next = p2;
3770 setup_pkcs7_body_for_signature(&p2->body, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL);
3771 p2->body.mime.text.data = (unsigned char *) outs_2;
3772 p2->body.contents.text.data = (unsigned char *) outs;
3774 newBody->nested.part = p1;
3776 *bodyP = newBody;
3778 result = 1;
3781 end:
3783 PKCS7_free(p7);
3784 BIO_free(in);
3786 if(bp && *bp){
3787 if(p7_2) PKCS7_free(p7_2);
3788 BIO_free(in_2);
3791 dprint((9, "sign_outgoing_message returns %d", result));
3792 return result;
3796 SMIME_STUFF_S *
3797 new_smime_struct(void)
3799 SMIME_STUFF_S *ret = NULL;
3801 ret = (SMIME_STUFF_S *) fs_get(sizeof(*ret));
3802 memset((void *) ret, 0, sizeof(*ret));
3803 ret->publictype = Nada;
3805 return ret;
3809 static void
3810 free_smime_struct(SMIME_STUFF_S **smime)
3812 if(smime && *smime){
3813 if((*smime)->passphrase_emailaddr){
3814 int i;
3815 for(i = 0; (*smime)->passphrase_emailaddr[i] != NULL; i++)
3816 fs_give((void **) &(*smime)->passphrase_emailaddr[i]);
3817 fs_give((void **) (*smime)->passphrase_emailaddr);
3820 if((*smime)->publicpath)
3821 fs_give((void **) &(*smime)->publicpath);
3823 if((*smime)->publiccertlist)
3824 free_certlist(&(*smime)->publiccertlist);
3826 if((*smime)->backuppubliccertlist)
3827 free_certlist(&(*smime)->backuppubliccertlist);
3829 if((*smime)->cacertlist)
3830 free_certlist(&(*smime)->cacertlist);
3832 if((*smime)->backupcacertlist)
3833 free_certlist(&(*smime)->backupcacertlist);
3835 if((*smime)->privatecertlist)
3836 free_certlist(&(*smime)->privatecertlist);
3838 if((*smime)->backupprivatecertlist)
3839 free_certlist(&(*smime)->backupprivatecertlist);
3841 if((*smime)->publiccontent)
3842 fs_give((void **) &(*smime)->publiccontent);
3844 if((*smime)->privatepath)
3845 fs_give((void **) &(*smime)->privatepath);
3847 if((*smime)->personal_certs){
3848 PERSONAL_CERT *pc;
3850 pc = (PERSONAL_CERT *) (*smime)->personal_certs;
3851 free_personal_certs(&pc);
3852 (*smime)->personal_certs = NULL;
3855 if((*smime)->privatecontent)
3856 fs_give((void **) &(*smime)->privatecontent);
3858 if((*smime)->capath)
3859 fs_give((void **) &(*smime)->capath);
3861 if((*smime)->cacontent)
3862 fs_give((void **) &(*smime)->cacontent);
3864 fs_give((void **) smime);
3868 #endif /* SMIME */