1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: smime.c 1176 2008-09-29 21:16:42Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2013-2014 Eduardo Chappa
8 * Copyright 2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
20 * This is based on a contribution from Jonathan Paisley
23 * Author: paisleyj@dcs.gla.ac.uk
28 #include "../pith/headers.h"
32 #include "../pith/osdep/canaccess.h"
33 #include "../pith/helptext.h"
34 #include "../pith/store.h"
35 #include "../pith/status.h"
36 #include "../pith/detach.h"
37 #include "../pith/conf.h"
38 #include "../pith/smkeys.h"
39 #include "../pith/smime.h"
40 #include "../pith/mailpart.h"
41 #include "../pith/reply.h"
42 #include "../pith/tempfile.h"
43 #include "../pith/readfile.h"
44 #include "../pith/remote.h"
46 #include <openssl/buffer.h>
47 #include <openssl/x509v3.h>
49 /* internal prototypes */
50 static void forget_private_keys(void);
51 static int app_RAND_load_file(const char *file
);
52 static void openssl_extra_randomness(void);
53 static int app_RAND_write_file(const char *file
);
54 static const char *openssl_error_string(void);
55 static int load_private_key(PERSONAL_CERT
*pcert
);
56 static void create_local_cache(char *h
, char *base
, BODY
*b
);
57 static long rfc822_output_func(void *b
, char *string
);
58 static void setup_pkcs7_body_for_signature(BODY
*b
, char *description
,
59 char *type
, char *filename
);
60 static BIO
*body_to_bio(BODY
*body
);
61 static BIO
*bio_from_store(STORE_S
*store
);
62 static STORE_S
*get_part_contents(long msgno
, const char *section
);
63 static PKCS7
*get_pkcs7_from_part(long msgno
, const char *section
);
64 static int do_signature_verify(PKCS7
*p7
, BIO
*in
, BIO
*out
, int silent
);
65 static int do_detached_signature_verify(BODY
*b
, long msgno
, char *section
);
66 static PERSONAL_CERT
*find_certificate_matching_pkcs7(PKCS7
*p7
);
67 static int do_decoding(BODY
*b
, long msgno
, const char *section
);
68 static void free_smime_struct(SMIME_STUFF_S
**smime
);
69 static void setup_storage_locations(void);
70 static int copy_container_to_dir(WhichCerts which
);
71 void setup_privatekey_storage(void);
72 int smime_path(char *rpath
, char *fpath
, size_t len
);
73 int smime_extract_and_save_cert(PKCS7
*p7
);
74 int same_cert(X509
*, X509
*);
75 CertList
* certlist_from_personal_certs(PERSONAL_CERT
*pc
);
77 void load_key_and_cert(char *pathdir
, char **keyfile
, char **certfile
, EVP_PKEY
**pkey
, X509
**pcert
);
79 STACK_OF(X509
) *get_chain_for_cert(X509
*cert
, int *error
);
80 EVP_PKEY
*load_pkey_with_prompt(char *fpath
, char *text
, char *prompt
);
82 int (*pith_opt_smime_get_passphrase
)(void);
83 int (*pith_smime_import_certificate
)(char *, char *, size_t);
84 int (*pith_smime_enter_password
)(char *prompt
, char *, size_t);
86 static X509_STORE
*s_cert_store
;
88 /* State management for randomness functions below */
89 static int seeded
= 0;
90 static int egdsocket
= 0;
95 load_key_and_cert(char *pathdir
, char **keyfile
,
96 char **certfile
, EVP_PKEY
**pkey
, X509
**pcert
)
99 char buf
[MAXPATH
+1], pathkey
[MAXPATH
+1], prompt
[MAILTMPLEN
];
103 if(keyfile
) *keyfile
= NULL
;
104 if(certfile
) *certfile
= NULL
;
105 if(pkey
) *pkey
= NULL
;
106 if(pcert
) *pcert
= NULL
;
108 if(pathdir
== NULL
) return;
110 if((dirp
= opendir(pathdir
)) != NULL
){
111 while((d
=readdir(dirp
)) != NULL
){
114 if((ll
=strlen(d
->d_name
)) && ll
> 4){
115 if(pkey
&& *pkey
== NULL
&& keyfile
&& !strcmp(d
->d_name
+ll
-4, ".key")){
116 strncpy(buf
, d
->d_name
, sizeof(buf
));
117 buf
[sizeof(buf
)-1] = '\0';
118 build_path(pathkey
, pathdir
, buf
, sizeof(pathkey
));
119 buf
[strlen(buf
)-4] = '\0';
120 snprintf(prompt
, sizeof(prompt
),
121 _("Enter password of key <%s> to unlock password file: "), buf
);
122 if((*pkey
= load_pkey_with_prompt(pathkey
, NULL
, prompt
)) != NULL
)
123 *keyfile
= cpystr(buf
);
126 if(pcert
&& *pcert
== NULL
&& certfile
&& !strcmp(d
->d_name
+ll
-4, ".crt")){
127 strncpy(buf
, d
->d_name
, sizeof(buf
));
128 buf
[sizeof(buf
)-1] = '\0';
129 build_path(pathkey
, pathdir
, buf
, sizeof(pathkey
));
130 if((in
= BIO_new_file(pathkey
, "r")) != NULL
){
131 if((*pcert
= PEM_read_bio_X509(in
, NULL
, NULL
, NULL
)) != NULL
)
132 *certfile
= cpystr(buf
);
134 q_status_message1(SM_ORDER
, 0, 2,
135 _("loading public certificate %s failed. Continuing..."), buf
);
146 /* setup a key and certificate to encrypt and decrypt a password file.
147 * These files will be saved in the .alpine-smime/.pwd directory, but its
148 * location can be setup in the command line with the -pwdcertdir option.
149 * Here are the rules:
151 * Check if the .alpine-smime/.pwd (or -pwdcertdir directory) exists,
152 * if not create it. If we are successful, move to the next step
154 * - If the user has a key/cert pair, setup is successful;
155 * - if the used does not have a key/cert pair, or one cannot be found
156 * create one, setup is successful; (TODO: implement this)
157 * - in any other case, setup is not successful.
159 * If setup is successful, setup ps_global->smime->pwdcert.
160 * If any of this fails, ps_global->smime->pwdcert will be null.
161 * Ok, that should do it.
164 setup_pwdcert(void **pwdcert
)
166 int setup_dir
= 0; /* make it non zero if we know which dir to use */
169 char pathdir
[MAXPATH
+1], pathkey
[MAXPATH
+1], fpath
[MAXPATH
+1];
170 char prompt
[MAILTMPLEN
], buf
[MAXPATH
+1];
171 char *keyfile
, *certfile
, *text
;
174 EVP_PKEY
*pkey
= NULL
;
177 PERSONAL_CERT
*pc
, *pc2
= NULL
;
181 if(ps_global
->pwdcertdir
){
182 if(our_stat(ps_global
->pwdcertdir
, &sbuf
) == 0
183 && ((sbuf
.st_mode
& S_IFMT
) == S_IFDIR
)){
185 strncpy(pathdir
, ps_global
->pwdcertdir
, sizeof(pathdir
));
186 pathdir
[sizeof(pathdir
)-1] = '\0';
189 smime_path(DF_PASSWORD_DIR
, pathdir
, sizeof(pathdir
));
190 if(our_stat(pathdir
, &sbuf
) == 0){
191 if((sbuf
.st_mode
& S_IFMT
) == S_IFDIR
)
194 if(can_access(pathdir
, ACCESS_EXISTS
) != 0
195 && our_mkpath(pathdir
, 0700) == 0)
203 /* reuse setup dir to mean setup_key */
206 /* BUG: add container support */
207 load_key_and_cert(pathdir
, &keyfile
, &certfile
, &pkey
, &pcert
);
209 if(certfile
&& keyfile
)
214 /* PATHCERTDIR(Private) must be null, so create a path */
215 set_current_val(&ps_global
->vars
[V_PRIVATEKEY_DIR
], TRUE
, TRUE
);
216 smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, pathkey
, MAXPATH
);
217 load_key_and_cert(pathkey
, &keyfile
, NULL
, &pkey
, NULL
);
219 if(certfile
== NULL
){
220 /* PATHCERTDIR(Public) must be null, so create a path */
221 set_current_val(&ps_global
->vars
[V_PUBLICCERT_DIR
], TRUE
, TRUE
);
222 smime_path(ps_global
->VAR_PUBLICCERT_DIR
, pathkey
, MAXPATH
);
223 load_key_and_cert(pathkey
, NULL
, &certfile
, NULL
, &pcert
);
226 if(certfile
&& keyfile
){
227 build_path(fpath
, pathdir
, keyfile
, sizeof(fpath
));
228 strncat(fpath
, ".key", 4);
229 fpath
[sizeof(fpath
)-1] = '\0';
231 smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, pathkey
, MAXPATH
);
232 strncat(pathkey
, "/", 1);
233 strncat(pathkey
, keyfile
, strlen(keyfile
));
234 strncat(pathkey
, ".key", 4);
235 pathkey
[sizeof(pathkey
)-1] = '\0';
237 if(our_copy(fpath
, pathkey
) == 0)
243 build_path(fpath
, pathdir
, certfile
, sizeof(fpath
));
245 smime_path(ps_global
->VAR_PUBLICCERT_DIR
, pathkey
, MAXPATH
);
246 strncat(pathkey
, "/", 1);
247 strncat(pathkey
, keyfile
, strlen(keyfile
));
248 strncat(pathkey
, ".crt", 4);
249 pathkey
[sizeof(pathkey
)-1] = '\0';
251 if(our_copy(fpath
, pathkey
) == 0)
258 pc2
= (PERSONAL_CERT
*) fs_get(sizeof(PERSONAL_CERT
));
259 memset((void *)pc2
, 0, sizeof(PERSONAL_CERT
));
263 if(pwdcert
) *pwdcert
= (void *) pc2
;
267 fs_give((void **)&certfile
);
269 if(setup_dir
== 0){ /* try looking for an existent key */
270 if(ps_global
->smime
->personal_certs
){
271 pc
= (PERSONAL_CERT
*) ps_global
->smime
->personal_certs
;
272 if(ps_global
->smime
->privatetype
== Directory
){
273 build_path(pathkey
, ps_global
->smime
->privatepath
, pc
->name
, sizeof(pathkey
));
274 strncat(pathkey
, ".key", 4);
275 pathkey
[sizeof(pathkey
)-1] = '\0';
277 } else { /* ps_global->smime->privatetype == Container */
278 if(pc
->keytext
== NULL
){ /* we should *never* be here, but just in case */
279 if(ps_global
->smime
->privatecontent
!= NULL
){
280 char tmp
[MAILTMPLEN
], *s
, *t
, c
;
281 snprintf(tmp
, sizeof(tmp
), "%s%s", EMAILADDRLEADER
, pc
->name
);
282 tmp
[sizeof(tmp
)-1] = '\0';
283 if((s
= strstr(ps_global
->smime
->privatecontent
, tmp
)) != NULL
){
284 if((t
= strstr(s
+strlen(tmp
), EMAILADDRLEADER
)) != NULL
){
287 pc
->keytext
= cpystr(s
+ strlen(tmp
) + 1); /* 1 = strlen("\n") */
291 pc
->keytext
= cpystr(s
+ strlen(tmp
) + 1); /* 1 = strlen("\n") */
295 if(pc
->keytext
!= NULL
) /* we should go straigth here */
298 if((pathkey
&& *pathkey
) || text
){
299 snprintf(prompt
, sizeof(prompt
),
300 _("Enter password of key <%s> to unlock password file: "), pc
->name
);
302 if((pkey
= load_pkey_with_prompt(pathkey
, text
, prompt
)) != NULL
){
303 pc2
= (PERSONAL_CERT
*) fs_get(sizeof(PERSONAL_CERT
));
304 memset((void *)pc2
, 0, sizeof(PERSONAL_CERT
));
305 pc2
->name
= cpystr(pc
->name
);
307 pc2
->cert
= X509_dup(pc
->cert
);
309 /* now copy the keys and certs, starting by the key... */
310 build_path(fpath
, pathdir
, pc
->name
, sizeof(fpath
));
311 strncat(fpath
, ".key", 4);
312 fpath
[sizeof(fpath
)-1] = '\0';
313 if(our_stat(fpath
, &sbuf
) == 0){
314 if((sbuf
.st_mode
& S_IFMT
) == S_IFREG
)
317 else { if(ps_global
->smime
->privatetype
== Directory
){
318 if(our_copy(fpath
, pathkey
) == 0)
320 } else { /* ps_global->smime->privatetype == Container */
322 if((out
= BIO_new_file(fpath
, "w")) != NULL
){
323 if(BIO_puts(out
, pc
->keytext
) > 0)
330 /* successful copy of key, now continue with certificate */
334 build_path(pathkey
, ps_global
->smime
->publicpath
, pc
->name
, sizeof(pathkey
));
335 strncat(pathkey
, ".crt", 4);
336 pathkey
[sizeof(pathkey
)-1] = '\0';
338 build_path(fpath
, pathdir
, pc
->name
, sizeof(fpath
));
339 strncat(fpath
, ".crt", 4);
340 fpath
[sizeof(fpath
)-1] = '\0';
342 if(our_stat(fpath
, &sbuf
) == 0){
343 if((sbuf
.st_mode
& S_IFMT
) == S_IFREG
)
346 else{ if(ps_global
->smime
->privatetype
== Directory
){
347 if(our_copy(fpath
, pathkey
) == 0)
349 } else { /* ps_global->smime->privatetype == Container */
351 if((out
= BIO_new_file(fpath
, "w")) != NULL
){
352 if(PEM_write_bio_X509(out
, pc
->cert
))
361 if(pwdcert
) *pwdcert
= (void *) pc2
;
363 free_personal_certs(&pc2
);
366 } /* end of if(pkey != NULL) */
368 } /* end of if(in != NULL) */
369 } /* end of if(ps_global->personal_certs) */
372 /* TODO: create self signed certificate */
374 q_status_message(SM_ORDER
, 0, 2,
375 _("No key/certificate pair found. Password file not encrypted!"));
377 #endif /* PASSFILE */
379 /* smime_expunge_cert.
380 * Return values: < 0 there was an error.
381 * >=0 the number of messages expunged
384 smime_expunge_cert(WhichCerts ctype
)
387 CertList
*cl
, *dummy
, *data
;
388 char *path
, *ext
, buf
[MAXPATH
+1];
391 if(DATACERT(ctype
)== NULL
)
394 /* data cert is the way we unify certificate management across functions, but it is
395 * not where we really save the information in the case ctype is equal to Private.
396 * What we will do is to update the datacert, and in the case of ctype equal to Private
397 * use the updated certdata to update the personal_certs data.
400 path
= PATHCERTDIR(ctype
);
401 ext
= EXTCERT(ctype
);
404 /* add a fake certificate at the beginning of the list */
405 dummy
= fs_get(sizeof(CertList
));
406 memset((void *)dummy
, 0, sizeof(CertList
));
407 dummy
->next
= DATACERT(ctype
);
409 for(cl
= dummy
, count
= 0; cl
&& cl
->next
;){
410 if(cl
->next
->data
.deleted
== 0){
415 removed
= 1; /* assume success */
416 if(SMHOLDERTYPE(ctype
) == Directory
){
417 build_path(buf
, path
, cl
->next
->name
, sizeof(buf
));
418 if(ctype
== Private
&& strlen(buf
) + strlen(EXTCERT(Private
)) < sizeof(buf
))
419 strcat(buf
, EXTCERT(Private
));
421 if(our_unlink(buf
) < 0){
422 q_status_message1(SM_ORDER
, 3, 3, _("Error removing certificate %s"), cl
->next
->name
);
427 else { /* SMHOLDERTYPE(ctype) == Container */
428 char *prefix
= ctype
== CACert
? CACERTSTORELEADER
: EMAILADDRLEADER
;
429 char tmp
[MAILTMPLEN
], *s
, *t
;
431 contents
= CONTENTCERTLIST(ctype
);
432 snprintf(tmp
, sizeof(tmp
), "%s%s", prefix
, cl
->next
->name
);
433 tmp
[sizeof(tmp
) - 1] = '\0';
434 if((s
= strstr(contents
, tmp
)) != NULL
){
435 if((t
= strstr(s
+strlen(tmp
), prefix
)) == NULL
)
438 memmove(s
, t
, strlen(t
)+1);
439 fs_resize((void **)&contents
, strlen(contents
)+1);
441 case Private
: ps_global
->smime
->privatecontent
= contents
; break;
442 case Public
: ps_global
->smime
->publiccontent
= contents
; break;
443 case CACert
: ps_global
->smime
->cacontent
= contents
; break;
452 count
++; /* count it! */
454 cl
->next
= data
->next
;
455 if(data
->name
) fs_give((void **)&data
->name
);
456 fs_give((void **)&data
);
460 q_status_message(SM_ORDER
, 3, 3, _("Error expunging certificate"));
463 case Private
: ps_global
->smime
->privatecertlist
= dummy
->next
; break;
464 case Public
: ps_global
->smime
->publiccertlist
= dummy
->next
; break;
465 case CACert
: ps_global
->smime
->cacertlist
= dummy
->next
; break;
468 fs_give((void **)&dummy
);
469 if(SMHOLDERTYPE(ctype
) == Container
){
470 if(copy_dir_to_container(ctype
, contents
) < 0)
474 q_status_message2(SM_ORDER
, 3, 3, _("Removed %s certificate%s"), comatose(count
), plural(count
));
477 q_status_message(SM_ORDER
, 3, 3, _("Error: No certificates were removed"));
482 mark_cert_deleted(WhichCerts ctype
, int num
, unsigned state
)
487 for(cl
= DATACERT(ctype
), i
= 0; cl
!= NULL
&& i
< num
; cl
= cl
->next
, i
++);
488 cl
->data
.deleted
= state
;
492 get_cert_deleted(WhichCerts ctype
, int num
)
497 for(cl
= DATACERT(ctype
), i
= 0; cl
!= NULL
&& i
< num
; cl
= cl
->next
, i
++);
498 return (cl
&& cl
->data
.deleted
) ? 1 : 0;
502 load_pkey_with_prompt(char *fpath
, char *text
, char *prompt
)
505 int rc
= 0; /* rc == 1, cancel, rc == 0 success */
507 char pass
[MAILTMPLEN
+1];
510 /* attempt to load with empty password */
511 in
= text
? BIO_new_mem_buf(text
, strlen(text
)) : BIO_new_file(fpath
, "r");
513 pkey
= PEM_read_bio_PrivateKey(in
, NULL
, NULL
, "");
515 if(pkey
!= NULL
) return pkey
;
518 if(pith_smime_enter_password
)
519 while(pkey
== NULL
&& rc
!= 1){
520 in
= text
? BIO_new_mem_buf(text
, strlen(text
)) : BIO_new_file(fpath
, "r");
523 rc
= (*pith_smime_enter_password
)(prompt
, (char *)pass
, sizeof(pass
));
524 } while (rc
!=0 && rc
!=1 && rc
>0);
526 pkey
= PEM_read_bio_PrivateKey(in
, NULL
, NULL
, (char *)pass
);
538 import_certificate(WhichCerts ctype
)
541 char filename
[MAXPATH
+1], full_filename
[MAXPATH
+1], buf
[MAXPATH
+1];
543 if(pith_smime_import_certificate
== NULL
){
544 q_status_message(SM_ORDER
, 0, 2,
545 _("import of certificates not implemented yet!"));
551 r
= (*pith_smime_import_certificate
)(filename
, full_filename
, sizeof(filename
) - 20);
553 ps_global
->mangled_screen
= 1;
557 else if (ctype
== Private
){
558 char prompt
[500], *s
, *t
;
560 EVP_PKEY
*key
= NULL
;
563 if(!ps_global
->smime
->privatecertlist
){
564 ps_global
->smime
->privatecertlist
= fs_get(sizeof(CertList
));
565 memset((void *)DATACERT(ctype
), 0, sizeof(CertList
));
568 for(s
= t
= filename
; (t
= strstr(s
, ".key")) != NULL
; s
= t
+ 1);
571 snprintf(prompt
, sizeof(prompt
), _("Enter passphrase for <%s>: "), filename
);
572 if((key
= load_pkey_with_prompt(full_filename
, NULL
, prompt
)) != NULL
){
573 if(SMHOLDERTYPE(ctype
) == Directory
){
574 build_path(buf
, PATHCERTDIR(ctype
), filename
, sizeof(buf
));
575 if(strcmp(buf
+ strlen(buf
) - 4, EXTCERT(ctype
)) != 0 && strlen(buf
) + 4 < sizeof(buf
))
576 strcat(buf
, EXTCERT(ctype
));
577 rc
= our_copy(buf
, full_filename
);
579 else /* if(SMHOLDERTYPE(ctype) == Container){ */
580 rc
= add_file_to_container(ctype
, full_filename
, NULL
);
582 q_status_message(SM_ORDER
, 1, 3, _("Private key saved"));
584 q_status_message(SM_ORDER
, 1, 3, _("Error saving private key"));
585 if(ps_global
->smime
->publiccertlist
)
586 ps_global
->smime
->publiccertlist
->data
.renew
= 1;
589 q_status_message(SM_ORDER
, 1, 3, _("Problem unlocking key (not a certificate and/or wrong password)"));
590 } else if (ctype
== CACert
){
594 if((ins
= BIO_new_file(full_filename
, "r")) != NULL
){
595 if((cert
= PEM_read_bio_X509(ins
, NULL
, NULL
, NULL
)) != NULL
){
596 if(SMHOLDERTYPE(ctype
) == Directory
){
597 build_path(buf
, PATHCERTDIR(ctype
), filename
, sizeof(buf
));
598 if(strcmp(buf
+ strlen(buf
) - 4, ".crt") != 0 && strlen(buf
) + 4 < sizeof(buf
))
599 strcat(buf
, EXTCERT(ctype
));
601 rc
= our_copy(buf
, full_filename
);
603 else /* if(SMHOLDERTYPE(ctype) == Container){ */
604 rc
= add_file_to_container(ctype
, full_filename
, NULL
);
606 q_status_message(SM_ORDER
, 1, 3, _("Certificate saved"));
608 q_status_message(SM_ORDER
, 1, 3, _("Error saving certificate"));
609 X509_free(cert
); /* not needed anymore */
612 q_status_message(SM_ORDER
, 1, 3, _("Error in certificate file (not a certificate?)"));
616 } else { /* ctype == Public. save certificate, but first validate that it is one */
620 if((ins
= BIO_new_file(full_filename
, "r")) != NULL
){
621 if((cert
= PEM_read_bio_X509(ins
, NULL
, NULL
, NULL
)) != NULL
){
622 if(SMHOLDERTYPE(ctype
) == Directory
){
623 char **email
= get_x509_subject_email(cert
);
626 for(i
= 0; email
[i
] != NULL
; i
++){
627 save_cert_for(email
[i
], cert
, Public
);
628 fs_give((void **)&email
[i
]);
630 fs_give((void **)email
);
632 else /* if(SMHOLDERTYPE(ctype) == Container){ */
633 add_file_to_container(ctype
, full_filename
, NULL
);
635 if(ps_global
->smime
->publiccertlist
)
636 ps_global
->smime
->publiccertlist
->data
.renew
= 1;
639 q_status_message(SM_ORDER
, 1, 3, _("Error in certificate file (not a certificate?)"));
643 if(DATACERT(ctype
)) RENEWCERT(DATACERT(ctype
)) = 1;
647 /* itype: information type to add: 0 - public, 1 - private.
648 * Memory freed by caller
651 print_private_key_information(char *email
, int itype
)
656 if(ps_global
->smime
== NULL
657 || ps_global
->smime
->personal_certs
== NULL
658 || (itype
!= 0 && itype
!= 1))
661 for(pc
= ps_global
->smime
->personal_certs
;
662 pc
!= NULL
&& strcmp(pc
->name
, email
) != 0; pc
= pc
->next
);
664 && !load_private_key(pc
)
666 && ps_global
->smime
->need_passphrase
){
667 if (*pith_opt_smime_get_passphrase
)
668 (*pith_opt_smime_get_passphrase
)();
669 load_private_key(pc
);
675 out
= BIO_new(BIO_s_mem());
676 if(itype
== 0) /* 0 means public */
677 EVP_PKEY_print_public(out
, pc
->key
, 0, NULL
);
678 else if (itype
== 1) /* 1 means private */
679 EVP_PKEY_print_private(out
, pc
->key
, 0, NULL
);
681 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
682 forget_private_keys();
688 * Forget any cached private keys
691 forget_private_keys(void)
693 PERSONAL_CERT
*pcert
;
697 dprint((9, "forget_private_keys()"));
698 if(ps_global
->smime
){
699 for(pcert
=(PERSONAL_CERT
*) ps_global
->smime
->personal_certs
;
704 EVP_PKEY_free(pcert
->key
);
709 ps_global
->smime
->entered_passphrase
= 0;
710 len
= sizeof(ps_global
->smime
->passphrase
);
711 p
= ps_global
->smime
->passphrase
;
718 /* modelled after signature_path in reply.c, but uses home dir instead of the
719 * directory where the .pinerc is located, since according to documentation,
720 * the .alpine-smime directories are subdirectories of the home directory
722 int smime_path(char *rpath
, char *fpath
, size_t len
)
726 size_t spl
= strlen(rpath
);
728 if(IS_REMOTE(rpath
)){
730 strncpy(fpath
, rpath
, len
-1);
733 else if(is_absolute_path(rpath
)){
734 strncpy(fpath
, rpath
, len
-1);
736 fnexpand(fpath
, len
);
738 else if(ps_global
->VAR_OPER_DIR
){
739 if(strlen(ps_global
->VAR_OPER_DIR
) + spl
< len
- 1)
740 build_path(fpath
, ps_global
->VAR_OPER_DIR
, rpath
, len
);
742 else if(ps_global
->home_dir
){
743 if(strlen(ps_global
->home_dir
) + spl
< len
- 1)
744 build_path(fpath
, ps_global
->home_dir
, rpath
, len
);
747 return fpath
&& *fpath
? 1 : 0;
753 * taken from openssl/apps/app_rand.c
756 app_RAND_load_file(const char *file
)
761 file
= RAND_file_name(buffer
, sizeof buffer
);
762 else if(RAND_egd(file
) > 0){
763 /* we try if the given filename is an EGD socket.
764 if it is, we don't write anything back to the file. */
769 if(file
== NULL
|| !RAND_load_file(file
, -1)){
770 if(RAND_status() == 0){
771 dprint((1, "unable to load 'random state'\n"));
772 dprint((1, "This means that the random number generator has not been seeded\n"));
773 dprint((1, "with much random data.\n"));
785 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
788 openssl_extra_randomness(void)
796 /* if system doesn't have /dev/urandom */
797 if(stat ("/dev/urandom", &sbuf
)){
799 tf
= temp_nam(NULL
, NULL
);
801 strncpy(tmp
, tf
, sizeof(tmp
));
802 tmp
[sizeof(tmp
)-1] = '\0';
803 fs_give((void **) &tf
);
806 if((fd
= open(tmp
, O_WRONLY
|O_CREAT
|O_EXCL
, 0600)) < 0)
807 i
= (unsigned long) tmp
;
809 unlink(tmp
); /* don't need the file */
810 fstat(fd
, &sbuf
); /* get information about the file */
811 i
= sbuf
.st_ino
; /* remember its inode */
812 close(fd
); /* or its descriptor */
814 /* not great but it'll have to do */
815 snprintf(tmp
+strlen(tmp
), sizeof(tmp
)-strlen(tmp
), "%.80s%lx%lx%lx",
817 (unsigned long) (time (0) ^ gethostid ()),
818 (unsigned long) getpid ());
819 RAND_seed(tmp
, strlen(tmp
));
825 /* taken from openssl/apps/app_rand.c */
827 app_RAND_write_file(const char *file
)
831 if(egdsocket
|| !seeded
)
833 * If we did not manage to read the seed file,
834 * we should not write a low-entropy seed file back --
835 * it would suppress a crucial warning the next time
841 file
= RAND_file_name(buffer
, sizeof buffer
);
843 if(file
== NULL
|| !RAND_write_file(file
)){
844 dprint((1, "unable to write 'random state'\n"));
852 certlist_from_personal_certs(PERSONAL_CERT
*pc
)
859 cl
= fs_get(sizeof(CertList
));
860 memset((void *)cl
, 0, sizeof(CertList
));
861 cl
->name
= cpystr(pc
->name
);
862 cl
->next
= certlist_from_personal_certs(pc
->next
);
869 renew_cert_data(CertList
**data
, WhichCerts ctype
)
872 if(ctype
== Private
){
874 PERSONAL_CERT
*pc
= (PERSONAL_CERT
*)ps_global
->smime
->personal_certs
;
877 free_personal_certs(&pc
);
878 setup_privatekey_storage();
879 *data
= certlist_from_personal_certs((PERSONAL_CERT
*)ps_global
->smime
->personal_certs
);
881 RENEWCERT(*data
) = 0;
882 ps_global
->smime
->privatecertlist
= *data
;
884 if(ps_global
->smime
->privatecertlist
)
885 RENEWCERT(ps_global
->smime
->privatecertlist
) = 0;
887 X509_LOOKUP
*lookup
= NULL
;
888 X509_STORE
*store
= NULL
;
890 if((store
= X509_STORE_new()) != NULL
)
891 if((lookup
= X509_STORE_add_lookup(store
, X509_LOOKUP_file())) == NULL
){
892 X509_STORE_free(store
);
897 if(SMHOLDERTYPE(ctype
) == Directory
)
898 add_certs_in_dir(lookup
, PATHCERTDIR(ctype
), EXTCERT(ctype
), data
);
899 else /* if(SMHOLDERTYPE(ctype) == Container) */
900 *data
= mem_to_certlist(CONTENTCERTLIST(ctype
), ctype
);
902 RENEWCERT(*data
) = 0;
904 ps_global
->smime
->publiccertlist
= *data
;
906 ps_global
->smime
->cacertlist
= *data
;
918 /* Installed as an atexit() handler to save the random data */
922 dprint((9, "smime_deinit()"));
923 app_RAND_write_file(NULL
);
924 free_smime_struct(&ps_global
->smime
);
927 /* we renew the store when it has changed */
928 void renew_store(void)
930 if(ps_global
->smime
->inited
){
931 if(s_cert_store
!= NULL
)
932 X509_STORE_free(s_cert_store
);
933 s_cert_store
= get_ca_store();
937 /* Initialise openssl stuff if needed */
941 if(F_OFF(F_DONT_DO_SMIME
, ps_global
) && !(ps_global
->smime
&& ps_global
->smime
->inited
)){
943 dprint((9, "smime_init()"));
944 if(!ps_global
->smime
)
945 ps_global
->smime
= new_smime_struct();
947 setup_storage_locations();
949 s_cert_store
= get_ca_store();
951 OpenSSL_add_all_algorithms();
952 ERR_load_crypto_strings();
954 app_RAND_load_file(NULL
);
955 openssl_extra_randomness();
956 ps_global
->smime
->inited
= 1;
963 /* validate a certificate. Return value : 0 for no error, -1 for error.
964 * In the latter case, set the openssl smime error in *error.
966 int smime_validate_cert(X509
*cert
, long *error
)
972 if((csc
= X509_STORE_CTX_new()) != NULL
){
973 X509_STORE_set_flags(s_cert_store
, 0);
974 if(X509_STORE_CTX_init(csc
,s_cert_store
,cert
,NULL
)
975 && X509_verify_cert(csc
) <= 0)
976 *error
= X509_STORE_CTX_get_error(csc
);
977 X509_STORE_CTX_free(csc
);
979 return *error
? -1 : 0;
983 get_personal_certs(char *path
)
985 PERSONAL_CERT
*result
= NULL
;
990 ps_global
->smime
->privatepath
= cpystr(path
);
991 dirp
= opendir(path
);
993 while((d
=readdir(dirp
)) != NULL
){
997 if((ll
=strlen(d
->d_name
)) && ll
> 4 && !strcmp(d
->d_name
+ll
-4, ".key")){
999 /* copy file name to temp buffer */
1000 strncpy(buf2
, d
->d_name
, sizeof(buf2
)-1);
1001 buf2
[sizeof(buf2
)-1] = '\0';
1002 /* chop off ".key" trailier */
1003 buf2
[strlen(buf2
)-4] = 0;
1004 /* Look for certificate */
1005 cert
= get_cert_for(buf2
, Public
);
1010 /* create a new PERSONAL_CERT, fill it in */
1012 pc
= (PERSONAL_CERT
*) fs_get(sizeof(*pc
));
1014 pc
->name
= cpystr(buf2
);
1016 /* Try to load the key with an empty password */
1017 pc
->key
= load_key(pc
, "");
1031 setup_privatekey_storage(void)
1033 char path
[MAXPATH
+1], *contents
;
1034 int privatekeycontainer
= 0;
1036 /* private keys in a container */
1037 if(ps_global
->VAR_PRIVATEKEY_CONTAINER
&& ps_global
->VAR_PRIVATEKEY_CONTAINER
[0]){
1039 privatekeycontainer
= 1;
1042 if(!smime_path(ps_global
->VAR_PRIVATEKEY_CONTAINER
, path
, MAXPATH
))
1043 privatekeycontainer
= 0;
1045 if(privatekeycontainer
&& !IS_REMOTE(path
)
1046 && ps_global
->VAR_OPER_DIR
1047 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1048 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1049 /* TRANSLATORS: First arg is the directory name, second is
1050 the file user wants to read but can't. */
1051 _("Can't read file outside %s: %s"),
1052 ps_global
->VAR_OPER_DIR
, path
);
1053 privatekeycontainer
= 0;
1056 if(privatekeycontainer
1057 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1058 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1060 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1061 privatekeycontainer
= 0;
1064 if(privatekeycontainer
&& path
[0]){
1065 ps_global
->smime
->privatetype
= Container
;
1066 ps_global
->smime
->privatepath
= cpystr(path
);
1069 ps_global
->smime
->privatecontent
= contents
;
1070 ps_global
->smime
->personal_certs
= mem_to_personal_certs(contents
);
1075 /* private keys in a directory of files */
1076 if(!privatekeycontainer
){
1077 PERSONAL_CERT
*result
= NULL
;
1079 ps_global
->smime
->privatetype
= Directory
;
1082 if(!(smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, path
, MAXPATH
)
1083 && !IS_REMOTE(path
)))
1084 ps_global
->smime
->privatetype
= Nada
;
1085 else if(can_access(path
, ACCESS_EXISTS
)){
1086 if(our_mkpath(path
, 0700)){
1087 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1088 ps_global
->smime
->privatetype
= Nada
;
1092 if(ps_global
->smime
->privatetype
== Directory
)
1093 ps_global
->smime
->personal_certs
= get_personal_certs(path
);
1100 setup_storage_locations(void)
1102 int publiccertcontainer
= 0, cacertcontainer
= 0;
1103 char path
[MAXPATH
+1], *contents
;
1105 if(!ps_global
->smime
)
1108 #ifdef APPLEKEYCHAIN
1109 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
1110 ps_global
->smime
->publictype
= Keychain
;
1113 #endif /* APPLEKEYCHAIN */
1114 /* Public certificates in a container */
1115 if(ps_global
->VAR_PUBLICCERT_CONTAINER
&& ps_global
->VAR_PUBLICCERT_CONTAINER
[0]){
1117 publiccertcontainer
= 1;
1120 if(!smime_path(ps_global
->VAR_PUBLICCERT_CONTAINER
, path
, MAXPATH
))
1121 publiccertcontainer
= 0;
1123 if(publiccertcontainer
&& !IS_REMOTE(path
)
1124 && ps_global
->VAR_OPER_DIR
1125 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1126 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1127 /* TRANSLATORS: First arg is the directory name, second is
1128 the file user wants to read but can't. */
1129 _("Can't read file outside %s: %s"),
1130 ps_global
->VAR_OPER_DIR
, path
);
1131 publiccertcontainer
= 0;
1134 if(publiccertcontainer
1135 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1136 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1138 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1139 publiccertcontainer
= 0;
1142 if(publiccertcontainer
&& path
[0]){
1143 ps_global
->smime
->publictype
= Container
;
1144 ps_global
->smime
->publicpath
= cpystr(path
);
1147 ps_global
->smime
->publiccontent
= contents
;
1148 ps_global
->smime
->publiccertlist
= mem_to_certlist(contents
, Public
);
1153 /* Public certificates in a directory of files */
1154 if(!publiccertcontainer
){
1155 ps_global
->smime
->publictype
= Directory
;
1158 if(!(smime_path(ps_global
->VAR_PUBLICCERT_DIR
, path
, MAXPATH
)
1159 && !IS_REMOTE(path
)))
1160 ps_global
->smime
->publictype
= Nada
;
1161 else if(can_access(path
, ACCESS_EXISTS
)){
1162 if(our_mkpath(path
, 0700)){
1163 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1164 ps_global
->smime
->publictype
= Nada
;
1168 if(ps_global
->smime
->publictype
== Directory
)
1169 ps_global
->smime
->publicpath
= cpystr(path
);
1172 #ifdef APPLEKEYCHAIN
1174 #endif /* APPLEKEYCHAIN */
1176 setup_privatekey_storage();
1178 /* extra cacerts in a container */
1179 if(ps_global
->VAR_CACERT_CONTAINER
&& ps_global
->VAR_CACERT_CONTAINER
[0]){
1181 cacertcontainer
= 1;
1184 if(!smime_path(ps_global
->VAR_CACERT_CONTAINER
, path
, MAXPATH
))
1185 cacertcontainer
= 0;
1187 if(cacertcontainer
&& !IS_REMOTE(path
)
1188 && ps_global
->VAR_OPER_DIR
1189 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1190 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1191 /* TRANSLATORS: First arg is the directory name, second is
1192 the file user wants to read but can't. */
1193 _("Can't read file outside %s: %s"),
1194 ps_global
->VAR_OPER_DIR
, path
);
1195 cacertcontainer
= 0;
1199 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1200 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1202 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1203 cacertcontainer
= 0;
1206 if(cacertcontainer
&& path
[0]){
1207 ps_global
->smime
->catype
= Container
;
1208 ps_global
->smime
->capath
= cpystr(path
);
1209 ps_global
->smime
->cacontent
= contents
;
1211 ps_global
->smime
->cacertlist
= mem_to_certlist(contents
, CACert
);
1215 if(!cacertcontainer
){
1216 ps_global
->smime
->catype
= Directory
;
1219 if(!(smime_path(ps_global
->VAR_CACERT_DIR
, path
, MAXPATH
)
1220 && !IS_REMOTE(path
)))
1221 ps_global
->smime
->catype
= Nada
;
1222 else if(can_access(path
, ACCESS_EXISTS
)){
1223 if(our_mkpath(path
, 0700)){
1224 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1225 ps_global
->smime
->catype
= Nada
;
1229 if(ps_global
->smime
->catype
== Directory
)
1230 ps_global
->smime
->capath
= cpystr(path
);
1236 copy_publiccert_dir_to_container(void)
1238 return(copy_dir_to_container(Public
, NULL
));
1243 copy_publiccert_container_to_dir(void)
1245 return(copy_container_to_dir(Public
));
1250 copy_privatecert_dir_to_container(void)
1252 return(copy_dir_to_container(Private
, NULL
));
1257 copy_privatecert_container_to_dir(void)
1259 return(copy_container_to_dir(Private
));
1264 copy_cacert_dir_to_container(void)
1266 return(copy_dir_to_container(CACert
, NULL
));
1271 copy_cacert_container_to_dir(void)
1273 return(copy_container_to_dir(CACert
));
1276 /* Add the contents of a file to a container. Do not check the content
1277 * of the file, just add it using the format for that container. The
1278 * caller must check the format, so that there is no data corruption
1280 * return value: 0 - success,
1284 add_file_to_container(WhichCerts ctype
, char *fpath
, char *altname
)
1286 char *sep
= (ctype
== Public
|| ctype
== Private
)
1287 ? EMAILADDRLEADER
: CACERTSTORELEADER
;
1288 char *content
= ctype
== Public
? ps_global
->smime
->publiccontent
1289 : (ctype
== Private
? ps_global
->smime
->privatecontent
1290 : ps_global
->smime
->cacontent
);
1295 int rv
= -1; /* assume error */
1297 if(our_stat(fpath
, &sbuf
) < 0
1298 || (in
= so_get(FileStar
, fpath
, READ_ACCESS
| READ_FROM_LOCALE
)) == NULL
)
1303 else if((name
= strrchr(fpath
, '/')) != NULL
){
1305 if((ll
= strlen(++name
)) > 4 && strucmp(name
+ ll
- 4, EXTCERT(ctype
)) == 0)
1306 name
[ll
-strlen(EXTCERT(ctype
))] = '\0';
1312 fs_resize((void **)&content
, strlen(content
) + strlen(sep
) + strlen(name
) + sbuf
.st_size
+ 3); /* 2 = \n + \n + \0*/
1314 content
+= strlen(content
);
1317 s
= content
= fs_get(strlen(sep
) + strlen(name
) + sbuf
.st_size
+ 1); /* 2 = \n + \0 */
1320 strncat(content
, sep
, strlen(sep
));
1321 strncat(content
, name
, strlen(name
));
1322 content
+= strlen(content
);
1325 while(so_readc(&c
, in
))
1330 case Private
: ps_global
->smime
->privatecontent
= s
; break;
1331 case Public
: ps_global
->smime
->publiccontent
= s
; break;
1332 case CACert
: ps_global
->smime
->cacontent
= s
; break;
1336 rv
= copy_dir_to_container(ctype
, s
);
1339 if(in
) so_give(&in
);
1346 * returns 0 on success, -1 on failure
1347 * contents is an argument which tells this function to write the value
1348 * of this variable instead of reading the contents of the directory.
1349 * If the var contents is not null use its value as the value of the
1353 copy_dir_to_container(WhichCerts which
, char *contents
)
1356 BIO
*bio_out
= NULL
, *bio_in
= NULL
;
1357 char srcpath
[MAXPATH
+1], dstpath
[MAXPATH
+1], emailaddr
[MAXPATH
], file
[MAXPATH
], line
[4096];
1358 char *tempfile
= NULL
;
1361 REMDATA_S
*rd
= NULL
;
1362 char *configdir
= NULL
;
1363 char *configpath
= NULL
;
1364 char *filesuffix
= NULL
;
1366 dprint((9, "copy_dir_to_container(%s)", which
==Public
? "Public" : which
==Private
? "Private" : which
==CACert
? "CACert" : "?"));
1372 emailaddr
[0] = '\0';
1374 if(which
== Public
){
1375 configdir
= ps_global
->VAR_PUBLICCERT_DIR
;
1376 configpath
= ps_global
->smime
->publicpath
;
1377 filesuffix
= ".crt";
1379 else if(which
== Private
){
1380 configdir
= ps_global
->VAR_PRIVATEKEY_DIR
;
1381 configpath
= ps_global
->smime
->privatepath
;
1382 filesuffix
= ".key";
1384 else if(which
== CACert
){
1385 configdir
= ps_global
->VAR_CACERT_DIR
;
1386 configpath
= ps_global
->smime
->capath
;
1387 filesuffix
= ".crt";
1390 if(!(configdir
&& configdir
[0])){
1391 q_status_message(SM_ORDER
, 3, 3, _("Directory not defined"));
1395 if(!(configpath
&& configpath
[0])){
1396 #ifdef APPLEKEYCHAIN
1397 if(which
== Public
&& F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
1398 q_status_message(SM_ORDER
, 3, 3, _("Turn off the Keychain feature above first"));
1401 #endif /* APPLEKEYCHAIN */
1402 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
1406 if(!(filesuffix
&& strlen(filesuffix
) == 4)){
1412 * If there is a legit directory to read from set up the
1413 * container file to write to.
1415 if(smime_path(configdir
, srcpath
, MAXPATH
) && !IS_REMOTE(srcpath
)){
1417 if(IS_REMOTE(configpath
)){
1418 rd
= rd_create_remote(RemImap
, configpath
, REMOTE_SMIME_SUBTYPE
,
1420 _("Can't access remote smime configuration."));
1424 (void) rd_read_metadata(rd
);
1426 if(rd
->access
== MaybeRorW
){
1427 if(rd
->read_status
== 'R')
1428 rd
->access
= ReadOnly
;
1430 rd
->access
= ReadWrite
;
1433 if(rd
->access
!= NoExists
){
1435 rd_check_remvalid(rd
, 1L);
1438 * If the cached info says it is readonly but
1439 * it looks like it's been fixed now, change it to readwrite.
1441 if(rd
->read_status
== 'R'){
1442 rd_check_readonly_access(rd
);
1443 if(rd
->read_status
== 'W'){
1444 rd
->access
= ReadWrite
;
1445 rd
->flags
|= REM_OUTOFDATE
;
1448 rd
->access
= ReadOnly
;
1452 if(rd
->flags
& REM_OUTOFDATE
){
1453 if(rd_update_local(rd
) != 0){
1455 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1456 rd_close_remdata(&rd
);
1463 if(rd
->access
!= ReadWrite
|| rd_remote_is_readonly(rd
)){
1464 rd_close_remdata(&rd
);
1468 rd
->flags
|= DO_REMTRIM
;
1470 strncpy(dstpath
, rd
->lf
, sizeof(dstpath
)-1);
1471 dstpath
[sizeof(dstpath
)-1] = '\0';
1474 strncpy(dstpath
, configpath
, sizeof(dstpath
)-1);
1475 dstpath
[sizeof(dstpath
)-1] = '\0';
1479 * dstpath is either the local Container file or the local cache file
1480 * for the remote Container file.
1482 tempfile
= tempfile_in_same_dir(dstpath
, "az", NULL
);
1486 * If there is a legit directory to read from and a tempfile
1487 * to write to we continue.
1489 if(tempfile
&& (bio_out
=BIO_new_file(tempfile
, "w")) != NULL
){
1491 if(contents
!= NULL
){
1492 if(BIO_puts(bio_out
, contents
) < 0)
1496 if((dirp
= opendir(srcpath
)) != NULL
){
1498 while((d
=readdir(dirp
)) && !ret
){
1501 if((ll
=strlen(d
->d_name
)) && ll
> 4 && !strcmp(d
->d_name
+ll
-4, filesuffix
)){
1503 /* copy file name to temp buffer */
1504 strncpy(emailaddr
, d
->d_name
, sizeof(emailaddr
)-1);
1505 emailaddr
[sizeof(emailaddr
)-1] = '\0';
1506 /* chop off suffix trailier */
1507 emailaddr
[strlen(emailaddr
)-4] = 0;
1510 * This is the separator between the contents of
1513 if(which
== CACert
){
1514 if(!((BIO_puts(bio_out
, CACERTSTORELEADER
) > 0)
1515 && (BIO_puts(bio_out
, emailaddr
) > 0)
1516 && (BIO_puts(bio_out
, "\n") > 0)))
1520 if(!((BIO_puts(bio_out
, EMAILADDRLEADER
) > 0)
1521 && (BIO_puts(bio_out
, emailaddr
) > 0)
1522 && (BIO_puts(bio_out
, "\n") > 0)))
1526 /* read then write contents of file */
1527 build_path(file
, srcpath
, d
->d_name
, sizeof(file
));
1528 if(!(bio_in
= BIO_new_file(file
, "r")))
1534 while(BIO_gets(bio_in
, line
, sizeof(line
)) > 0){
1535 if(strncmp("-----BEGIN", line
, strlen("-----BEGIN")) == 0)
1539 BIO_puts(bio_out
, line
);
1541 if(strncmp("-----END", line
, strlen("-----END")) == 0)
1557 if(rename_file(tempfile
, dstpath
) < 0){
1558 q_status_message2(SM_ORDER
, 3, 3,
1559 _("Can't rename %s to %s"), tempfile
, dstpath
);
1563 /* if the container is remote, copy it */
1564 if(!ret
&& IS_REMOTE(configpath
)){
1570 if((e
= rd_update_remote(rd
, datebuf
)) != 0){
1572 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1573 _("Error opening temporary smime file %s: %s"),
1574 rd
->lf
, error_description(errno
));
1576 "write_remote_smime: error opening temp file %s\n",
1577 rd
->lf
? rd
->lf
: "?"));
1580 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1581 _("Error copying to %s: %s"),
1582 rd
->rn
, error_description(errno
));
1584 "write_remote_smime: error copying from %s to %s\n",
1585 rd
->lf
? rd
->lf
: "?", rd
->rn
? rd
->rn
: "?"));
1588 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
1589 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1592 rd_update_metadata(rd
, datebuf
);
1593 rd
->read_status
= 'W';
1596 rd_close_remdata(&rd
);
1602 fs_give((void **) &tempfile
);
1609 * returns 0 on success, -1 on failure
1612 copy_container_to_dir(WhichCerts which
)
1614 char path
[MAXPATH
+1], file
[MAXPATH
+1], buf
[MAXPATH
+1];
1616 char *contents
= NULL
;
1617 char *leader
= NULL
;
1618 char *filesuffix
= NULL
;
1619 char *configdir
= NULL
;
1620 char *configpath
= NULL
;
1621 char *tempfile
= NULL
;
1622 char *p
, *q
, *line
, *name
, *certtext
, *save_p
;
1626 dprint((9, "copy_container_to_dir(%s)", which
==Public
? "Public" : which
==Private
? "Private" : which
==CACert
? "CACert" : "?"));
1631 if(which
== Public
){
1632 leader
= EMAILADDRLEADER
;
1633 contents
= ps_global
->smime
->publiccontent
;
1634 configdir
= ps_global
->VAR_PUBLICCERT_DIR
;
1635 configpath
= ps_global
->smime
->publicpath
;
1636 filesuffix
= ".crt";
1637 if(!(configpath
&& configpath
[0])){
1638 #ifdef APPLEKEYCHAIN
1639 if(which
== Public
&& F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
1640 q_status_message(SM_ORDER
, 3, 3, _("Turn off the Keychain feature above first"));
1643 #endif /* APPLEKEYCHAIN */
1644 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
1648 fs_give((void **) &ps_global
->smime
->publicpath
);
1651 if(!(smime_path(ps_global
->VAR_PUBLICCERT_DIR
, path
, MAXPATH
)
1652 && !IS_REMOTE(path
))){
1653 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
1657 if(can_access(path
, ACCESS_EXISTS
)){
1658 if(our_mkpath(path
, 0700)){
1659 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1664 ps_global
->smime
->publicpath
= cpystr(path
);
1665 configpath
= ps_global
->smime
->publicpath
;
1667 else if(which
== Private
){
1668 leader
= EMAILADDRLEADER
;
1669 contents
= ps_global
->smime
->privatecontent
;
1670 configdir
= ps_global
->VAR_PRIVATEKEY_DIR
;
1671 configpath
= ps_global
->smime
->privatepath
;
1672 filesuffix
= ".key";
1673 if(!(configpath
&& configpath
[0])){
1674 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
1678 fs_give((void **) &ps_global
->smime
->privatepath
);
1681 if(!(smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, path
, MAXPATH
)
1682 && !IS_REMOTE(path
))){
1683 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
1687 if(can_access(path
, ACCESS_EXISTS
)){
1688 if(our_mkpath(path
, 0700)){
1689 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1694 ps_global
->smime
->privatepath
= cpystr(path
);
1695 configpath
= ps_global
->smime
->privatepath
;
1697 else if(which
== CACert
){
1698 leader
= CACERTSTORELEADER
;
1699 contents
= ps_global
->smime
->cacontent
;
1700 configdir
= ps_global
->VAR_CACERT_DIR
;
1701 configpath
= ps_global
->smime
->capath
;
1702 filesuffix
= ".crt";
1703 if(!(configpath
&& configpath
[0])){
1704 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
1708 fs_give((void **) &ps_global
->smime
->capath
);
1711 if(!(smime_path(ps_global
->VAR_CACERT_DIR
, path
, MAXPATH
)
1712 && !IS_REMOTE(path
))){
1713 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
1717 if(can_access(path
, ACCESS_EXISTS
)){
1718 if(our_mkpath(path
, 0700)){
1719 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1724 ps_global
->smime
->capath
= cpystr(path
);
1725 configpath
= ps_global
->smime
->capath
;
1728 if(!(configdir
&& configdir
[0])){
1729 q_status_message(SM_ORDER
, 3, 3, _("Directory not defined"));
1733 if(!(configpath
&& configpath
[0])){
1734 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
1738 if(!(filesuffix
&& strlen(filesuffix
) == 4)){
1743 if(contents
&& *contents
){
1744 for(p
= contents
; *p
!= '\0';){
1747 while(*p
&& *p
!= '\n')
1756 if(strncmp(leader
, line
, strlen(leader
)) == 0){
1757 name
= line
+ strlen(leader
);
1759 if(strncmp("-----BEGIN", certtext
, strlen("-----BEGIN")) == 0){
1760 if((q
= strstr(certtext
, leader
)) != NULL
){
1763 else{ /* end of file */
1764 q
= certtext
+ strlen(certtext
);
1768 strncpy(buf
, name
, sizeof(buf
)-5);
1769 buf
[sizeof(buf
)-5] = '\0';
1770 strncat(buf
, filesuffix
, 5);
1771 build_path(file
, configpath
, buf
, sizeof(file
));
1773 in
= BIO_new_mem_buf(certtext
, q
-certtext
);
1775 tempfile
= tempfile_in_same_dir(file
, "az", NULL
);
1778 out
= BIO_new_file(tempfile
, "w");
1781 while((len
= BIO_read(in
, iobuf
, sizeof(iobuf
))) > 0)
1782 BIO_write(out
, iobuf
, len
);
1786 if(rename_file(tempfile
, file
) < 0){
1787 q_status_message2(SM_ORDER
, 3, 3,
1788 _("Can't rename %s to %s"),
1793 fs_give((void **) &tempfile
);
1810 #ifdef APPLEKEYCHAIN
1813 copy_publiccert_container_to_keychain(void)
1815 /* NOT IMPLEMNTED */
1820 copy_publiccert_keychain_to_container(void)
1822 /* NOT IMPLEMNTED */
1826 #endif /* APPLEKEYCHAIN */
1830 * Get a pointer to a string describing the most recent OpenSSL error.
1831 * It's statically allocated, so don't change or attempt to free it.
1834 openssl_error_string(void)
1837 const char *data
= NULL
;
1840 errn
= ERR_peek_error_line_data(NULL
, NULL
, &data
, NULL
);
1841 errs
= (char*) ERR_reason_error_string(errn
);
1848 return "unknown error";
1852 /* Return true if the body looks like a PKCS7 object */
1854 is_pkcs7_body(BODY
*body
)
1858 result
= body
->type
==TYPEAPPLICATION
&&
1860 (strucmp(body
->subtype
,"pkcs7-mime")==0 ||
1861 strucmp(body
->subtype
,"x-pkcs7-mime")==0 ||
1862 strucmp(body
->subtype
,"pkcs7-signature")==0 ||
1863 strucmp(body
->subtype
,"x-pkcs7-signature")==0);
1871 * Somewhat useful debug utility to dump the contents of a BIO to a file.
1872 * Note that a memory BIO will have its contents eliminated after they
1873 * are read so this will break the next step.
1876 dump_bio_to_file(BIO
*in
, char *filename
)
1882 out
= BIO_new_file(filename
, "w");
1885 if(BIO_method_type(in
) != BIO_TYPE_MEM
)
1888 while((len
= BIO_read(in
, iobuf
, sizeof(iobuf
))) > 0)
1889 BIO_write(out
, iobuf
, len
);
1900 * Recursively stash a pointer to the decrypted data in our
1901 * manufactured body.
1904 create_local_cache(char *h
, char *base
, BODY
*b
)
1906 if(b
->type
==TYPEMULTIPART
){
1910 cpytxt(&b
->contents
.text
, base
+ b
->contents
.offset
, b
->size
.bytes
);
1913 * We don't really want to copy the real body contents. It shouldn't be
1914 * used, and in the case of a message with attachments, we'll be
1915 * duplicating the files multiple times.
1917 cpytxt(&b
->contents
.text
, "BODY UNAVAILABLE", 16);
1920 for(p
=b
->nested
.part
; p
; p
=p
->next
)
1921 create_local_cache(h
, base
, (BODY
*) p
);
1924 cpytxt(&b
->mime
.text
, h
+b
->mime
.offset
, b
->mime
.text
.size
);
1925 cpytxt(&b
->contents
.text
, base
+ b
->contents
.offset
, b
->size
.bytes
);
1931 rfc822_output_func(void *b
, char *string
)
1933 BIO
*bio
= (BIO
*) b
;
1935 return(string
? *string
? (BIO_puts(bio
, string
) > 0 ? 1L : 0L)
1936 : (BIO_puts(bio
, string
) >= 0 ? 1L : 0L)
1942 * Attempt to load the private key for the given PERSONAL_CERT.
1943 * This sets the appropriate passphrase globals in order to
1944 * interact with the user correctly.
1947 load_private_key(PERSONAL_CERT
*pcert
)
1951 /* Try empty password by default */
1952 char *password
= "";
1955 && (ps_global
->smime
->need_passphrase
1956 || ps_global
->smime
->entered_passphrase
)){
1957 /* We've already been in here and discovered we need a different password */
1959 if(ps_global
->smime
->entered_passphrase
)
1960 password
= (char *) ps_global
->smime
->passphrase
; /* already entered */
1967 if(!(pcert
->key
= load_key(pcert
, password
))){
1968 long err
= ERR_get_error();
1970 /* Couldn't load key... */
1972 if(ps_global
->smime
&& ps_global
->smime
->entered_passphrase
){
1974 /* The user got the password wrong maybe? */
1976 if((ERR_GET_LIB(err
)==ERR_LIB_EVP
&& ERR_GET_REASON(err
)==EVP_R_BAD_DECRYPT
) ||
1977 (ERR_GET_LIB(err
)==ERR_LIB_PEM
&& ERR_GET_REASON(err
)==PEM_R_BAD_DECRYPT
))
1978 q_status_message(SM_ORDER
| SM_DING
, 4, 4, _("Incorrect passphrase"));
1980 q_status_message1(SM_ORDER
, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
1982 /* This passphrase is no good; forget it */
1983 ps_global
->smime
->entered_passphrase
= 0;
1986 if(ps_global
->smime
){
1987 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
1988 ps_global
->smime
->need_passphrase
= 1;
1989 if(ps_global
->smime
->passphrase_emailaddr
){
1991 for(i
= 0; ps_global
->smime
->passphrase_emailaddr
[i
] != NULL
; i
++)
1992 fs_give((void **)&ps_global
->smime
->passphrase_emailaddr
[i
]);
1993 fs_give((void **) ps_global
->smime
->passphrase_emailaddr
);
1996 ps_global
->smime
->passphrase_emailaddr
= get_x509_subject_email(pcert
->cert
);
2002 /* This key will be cached, so we won't be called again */
2003 if(ps_global
->smime
){
2004 ps_global
->smime
->entered_passphrase
= 0;
2005 ps_global
->smime
->need_passphrase
= 0;
2017 setup_pkcs7_body_for_signature(BODY
*b
, char *description
, char *type
, char *filename
)
2019 b
->type
= TYPEAPPLICATION
;
2020 b
->subtype
= cpystr(type
);
2021 b
->encoding
= ENCBINARY
;
2022 b
->description
= cpystr(description
);
2024 b
->disposition
.type
= cpystr("attachment");
2025 set_parameter(&b
->disposition
.parameter
, "filename", filename
);
2027 set_parameter(&b
->parameter
, "name", filename
);
2032 * Look for a personal certificate matching the
2036 match_personal_cert_to_email(ADDRESS
*a
)
2038 PERSONAL_CERT
*pcert
= NULL
;
2043 if(!a
|| !a
->mailbox
|| !a
->host
)
2046 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2048 if(ps_global
->smime
){
2049 for(pcert
=(PERSONAL_CERT
*) ps_global
->smime
->personal_certs
;
2056 email
= get_x509_subject_email(pcert
->cert
);
2060 for(i
= 0; email
[i
] && strucmp(email
[i
], buf
) != 0; i
++);
2061 if(email
[i
] != NULL
) done
++;
2062 for(i
= 0; email
[i
] != NULL
; i
++)
2063 fs_give((void **)&email
[i
]);
2064 fs_give((void **)email
);
2077 * Look for a personal certificate matching the from
2078 * (or reply_to? in the given envelope)
2081 match_personal_cert(ENVELOPE
*env
)
2083 PERSONAL_CERT
*pcert
;
2085 pcert
= match_personal_cert_to_email(env
->reply_to
);
2087 pcert
= match_personal_cert_to_email(env
->from
);
2094 * Flatten the given body into its MIME representation.
2095 * Return the result in a BIO.
2098 body_to_bio(BODY
*body
)
2103 bio
= BIO_new(BIO_s_mem());
2107 pine_encode_body(body
); /* this attaches random boundary strings to multiparts */
2108 pine_write_body_header(body
, rfc822_output_func
, bio
);
2109 pine_rfc822_output_body(body
, rfc822_output_func
, bio
);
2112 * Now need to truncate by two characters since the above
2115 if((len
=BIO_ctrl_pending(bio
)) > 1){
2116 BUF_MEM
*biobuf
= NULL
;
2118 BIO_get_mem_ptr(bio
, &biobuf
);
2120 BUF_MEM_grow(biobuf
, len
-2); /* remove CRLF */
2129 bio_from_store(STORE_S
*store
)
2133 if(store
&& store
->src
== BioType
&& store
->txt
){
2134 ret
= (BIO
*) store
->txt
;
2141 * Encrypt file; given a path (char *) fp, replace the file
2142 * by an encrypted version of it. If (char *) text is not null, then
2143 * replace the text of (char *) fp by the encrypted version of (char *) text.
2144 * certpath is the FULL path to the file containing the certificate used for
2148 encrypt_file(char *fp
, char *text
, PERSONAL_CERT
*pc
)
2150 const EVP_CIPHER
*cipher
= NULL
;
2151 STACK_OF(X509
) *encerts
= NULL
;
2158 char emailaddress
[MAILTMPLEN
];
2161 cipher
= EVP_aes_256_cbc();
2162 encerts
= sk_X509_new_null();
2167 sk_X509_push(encerts
, X509_dup(pc
->cert
));
2170 if((out
= BIO_new(BIO_s_mem())) == NULL
)
2172 (void) BIO_reset(out
);
2173 BIO_puts(out
, text
);
2176 if(!(out
= BIO_new_file(fp
, "rb")))
2179 BIO_read_filename(out
, fp
);
2182 if((p7
= PKCS7_encrypt(encerts
, out
, cipher
, 0)) == NULL
)
2184 BIO_set_close(out
, BIO_CLOSE
);
2186 if(!(out
= BIO_new_file(fp
, "w")))
2189 rv
= PEM_write_bio_PKCS7(out
, p7
);
2195 sk_X509_pop_free(encerts
, X509_free
);
2201 * Encrypt a message on the way out. Called from call_mailer in send.c
2202 * The body may be reallocated.
2205 encrypt_outgoing_message(METAENV
*header
, BODY
**bodyP
)
2210 const EVP_CIPHER
*cipher
= NULL
;
2211 STACK_OF(X509
) *encerts
= NULL
;
2212 STORE_S
*outs
= NULL
;
2215 BODY
*body
= *bodyP
;
2216 BODY
*newBody
= NULL
;
2219 dprint((9, "encrypt_outgoing_message()"));
2222 cipher
= EVP_aes_256_cbc();
2224 encerts
= sk_X509_new_null();
2226 /* Look for a certificate for each of the recipients */
2227 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
2228 if(pf
->type
== Address
&& pf
->rcptto
&& pf
->addr
&& *pf
->addr
){
2229 for(a
=*pf
->addr
; a
; a
=a
->next
){
2233 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2235 cert
= get_cert_for(buf
, Public
);
2237 sk_X509_push(encerts
,cert
);
2239 q_status_message2(SM_ORDER
, 1, 1,
2240 _("Unable to find certificate for <%s@%s>"),
2241 a
->mailbox
, a
->host
);
2248 in
= body_to_bio(body
);
2250 p7
= PKCS7_encrypt(encerts
, in
, cipher
, 0);
2252 outs
= so_get(BioType
, NULL
, EDIT_ACCESS
);
2253 out
= bio_from_store(outs
);
2255 i2d_PKCS7_bio(out
, p7
);
2256 (void) BIO_flush(out
);
2258 so_seek(outs
, 0, SEEK_SET
);
2260 newBody
= mail_newbody();
2262 newBody
->type
= TYPEAPPLICATION
;
2263 newBody
->subtype
= cpystr("pkcs7-mime");
2264 newBody
->encoding
= ENCBINARY
;
2266 newBody
->disposition
.type
= cpystr("attachment");
2267 set_parameter(&newBody
->disposition
.parameter
, "filename", "smime.p7m");
2269 newBody
->description
= cpystr("S/MIME Encrypted Message");
2270 set_parameter(&newBody
->parameter
, "smime-type", "enveloped-data");
2271 set_parameter(&newBody
->parameter
, "name", "smime.p7m");
2273 newBody
->contents
.text
.data
= (unsigned char *) outs
;
2283 sk_X509_pop_free(encerts
, X509_free
);
2285 dprint((9, "encrypt_outgoing_message returns %d", result
));
2291 Get (and decode) the body of the given section of msg
2294 get_part_contents(long msgno
, const char *section
)
2298 STORE_S
*store
= NULL
;
2301 store
= so_get(CharStar
, NULL
, EDIT_ACCESS
);
2303 gf_set_so_writec(&pc
,store
);
2305 err
= detach(ps_global
->mail_stream
, msgno
, (char*) section
, 0L, &len
, pc
, NULL
, 0L);
2307 gf_clear_so_writec(store
);
2309 so_seek(store
, 0, SEEK_SET
);
2320 get_pkcs7_from_part(long msgno
,const char *section
)
2322 STORE_S
*store
= NULL
;
2326 store
= get_part_contents(msgno
, section
);
2329 if(store
->src
== CharStar
){
2333 * We're reaching inside the STORE_S structure. We should
2334 * probably have a way to get the length, instead.
2336 len
= (int) (store
->eod
- store
->dp
);
2337 in
= BIO_new_mem_buf(store
->txt
, len
);
2339 else{ /* just copy it */
2342 in
= BIO_new(BIO_s_mem());
2343 (void) BIO_reset(in
);
2345 so_seek(store
, 0L, 0);
2346 while(so_readc(&c
, store
)){
2347 BIO_write(in
, &c
, 1);
2352 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2353 if((p7
=d2i_PKCS7_bio(in
,NULL
)) == NULL
){
2366 int same_cert(X509
*x
, X509
*cert
)
2368 char bufcert
[256], bufx
[256];
2371 get_fingerprint(cert
, EVP_md5(), bufcert
, sizeof(bufcert
));
2372 get_fingerprint(x
, EVP_md5(), bufx
, sizeof(bufx
));
2373 if(strcmp(bufx
, bufcert
) == 0)
2380 /* extract and save certificates from a PKCS7 package. The ctype variable
2381 * tells us if we want to extract it to a public/ or a ca/ directory. The
2382 * later makes sense only for recoverable errors (errors that can be fixed
2383 * by saving to the ca/ directory before we verify the signature).
2385 * 0 - no errors (in public/) no need to try again,
2386 * or validated self signed certificate (in ca/)
2387 * < 0 - certificate error is not recoverable, don't even think about it.
2390 int smime_extract_and_save_cert(PKCS7
*p7
)
2392 STACK_OF(X509
) *signers
;
2398 if((signers
= PKCS7_get0_signers(p7
, NULL
, 0)) == NULL
)
2401 for(i
= 0; i
< sk_X509_num(signers
); i
++){
2402 if((x
= sk_X509_value(signers
,i
)) == NULL
)
2405 if((email
= get_x509_subject_email(x
)) != NULL
){
2406 for(j
= 0; email
[j
] != NULL
; j
++){
2407 if((cert
= get_cert_for(email
[j
], Public
)) == NULL
2408 || same_cert(x
, cert
) == 0)
2409 save_cert_for(email
[j
], x
, Public
);
2411 fs_give((void **) &email
[i
]);
2413 fs_give((void **) email
);
2416 sk_X509_free(signers
);
2422 * Try to verify a signature.
2424 * p7 - the pkcs7 object to verify
2425 * in - the plain data to verify (NULL if not detached)
2426 * out - BIO to which to write the opaque data
2427 * silent - if non zero, do not print errors, only print success.
2430 do_signature_verify(PKCS7
*p7
, BIO
*in
, BIO
*out
, int silent
)
2432 STACK_OF(X509
) *otherCerts
= NULL
;
2438 if(!silent
) q_status_message(SM_ORDER
| SM_DING
, 2, 2,
2439 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2444 smime_extract_and_save_cert(p7
);
2446 result
= PKCS7_verify(p7
, otherCerts
, s_cert_store
, in
, out
, 0);
2449 q_status_message(SM_ORDER
, 1, 1, _("S/MIME signature verified ok"));
2452 err
= ERR_peek_error_line_data(NULL
, NULL
, &data
, NULL
);
2454 if(out
&& err
==ERR_PACK(ERR_LIB_PKCS7
,PKCS7_F_PKCS7_VERIFY
,PKCS7_R_CERTIFICATE_VERIFY_ERROR
)){
2456 /* Retry verification so we can get the plain text */
2457 /* Might be better to reimplement PKCS7_verify here? */
2459 PKCS7_verify(p7
, otherCerts
, s_cert_store
, in
, out
, PKCS7_NOVERIFY
);
2461 if (!silent
) q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2462 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
2470 free_smime_body_sparep(void **sparep
)
2472 if(sparep
&& *sparep
){
2473 PKCS7_free((PKCS7
*) (*sparep
));
2478 /* Big comment, explaining the mess that exists out there, and how we deal
2479 with it, and also how we solve the problems that are created this way.
2481 When Alpine sends a message, it constructs that message, computes the
2482 signature, but then it forgets the message it signed and reconstructs it
2483 again. Since it signs a message containing a notice about "mime aware
2484 tools", but it does not send that we do not include that in the part that
2485 is signed, and that takes care of much of the problems.
2487 Another problem is what is received from the servers. All servers tested
2488 seem to transmit the message that was signed intact and Alpine can check
2489 the signature correctly. That is not a problem. The problem arises when
2490 the message includes attachments. In this case different servers send
2491 different things, so it will be up to us to figure out what is the text
2492 that was actually signed. Confused? here is the story:
2494 When a message containing and attachment is sent by Alpine, UW-IMAP,
2495 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2496 that was sent by Alpine, but GMX.com, Exchange, and probably other servers
2497 add a trailing \r\n in the message, so when validating the signature,
2498 these messages will not validate. There are several things that can be
2501 1. Add a trailing \r\n to any message that contains attachments, sign that
2502 and send that. In this way, all messages will validate with all
2505 2. Compatibility mode: If a message has an attachment, contains a trailing
2506 \r\n and does not validate (sent by an earlier version of Alpine),
2507 remove the trailing \r\n and try to revalidate again.
2509 3. We do not add \r\n to validate a message that we sent, because that
2510 would only work in Alpine, and not in any other client. That would not
2511 be a good thing to do.
2515 Now we have to deal with encrypted and signed messages. The problem is that
2516 c-client makes all its pointers point to "on disk" content, but since we
2517 decrypted the data earlier, we have to make sure of two things. One is that we
2518 saved that data (so we do not have to decrypt it again) and second that we can
2521 In order to save the data we use create_local_cache, so that we do not
2522 have to redecrypt the message. Once this is saved, c-client functions will
2523 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2528 * Given a multipart body of type multipart/signed, attempt to verify it.
2529 * Returns non-zero if the body was changed.
2532 do_detached_signature_verify(BODY
*b
, long msgno
, char *section
)
2537 int result
, modified_the_body
= 0;
2538 unsigned long mimelen
, bodylen
;
2539 char newSec
[100], *mimetext
, *bodytext
;
2542 dprint((9, "do_detached_signature_verify(msgno=%ld type=%d subtype=%s section=%s)", msgno
, b
->type
, b
->subtype
? b
->subtype
: "NULL", (section
&& *section
) ? section
: (section
!= NULL
) ? "Top" : "NULL"));
2545 snprintf(newSec
, sizeof(newSec
), "%s%s1", section
? section
: "", (section
&& *section
) ? "." : "");
2547 mimetext
= mail_fetch_mime(ps_global
->mail_stream
, msgno
, (char*) newSec
, &mimelen
, 0);
2550 bodytext
= mail_fetch_body (ps_global
->mail_stream
, msgno
, (char*) newSec
, &bodylen
, 0);
2552 if (mimetext
== NULL
|| bodytext
== NULL
)
2553 return modified_the_body
;
2555 snprintf(newSec
, sizeof(newSec
), "%s%s2", section
? section
: "", (section
&& *section
) ? "." : "");
2557 if((p7
= get_pkcs7_from_part(msgno
, newSec
)) == NULL
2558 || (in
= BIO_new(BIO_s_mem())) == NULL
)
2559 return modified_the_body
;
2561 (void) BIO_reset(in
);
2562 BIO_write(in
, mimetext
, mimelen
);
2563 BIO_write(in
, bodytext
, bodylen
);
2565 /* Try compatibility with the past and check if this message
2566 * validates when we remove the last two characters. Silence
2567 * any failures first.
2569 if(((result
= do_signature_verify(p7
, in
, NULL
, 1)) == 0)
2571 && (strncmp(bodytext
+bodylen
-2,"\r\n", 2) == 0)){
2572 BUF_MEM
*biobuf
= NULL
;
2574 BIO_get_mem_ptr(in
, &biobuf
);
2576 BUF_MEM_grow(biobuf
, mimelen
+ bodylen
- 2);
2577 result
= do_signature_verify(p7
, in
, NULL
, 0);
2582 fs_give((void**) &b
->subtype
);
2584 b
->subtype
= cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE
);
2585 b
->encoding
= ENC8BIT
;
2588 fs_give ((void**) &b
->description
);
2590 what_we_did
= result
? _("This message was cryptographically signed.") :
2591 _("This message was cryptographically signed but the signature could not be verified.");
2593 b
->description
= cpystr(what_we_did
);
2599 /* p is signed plaintext */
2601 mail_free_body_part(&p
->next
); /* hide the pkcs7 from the viewer */
2603 modified_the_body
= 1;
2605 return modified_the_body
;
2610 find_certificate_matching_recip_info(PKCS7_RECIP_INFO
*ri
)
2612 PERSONAL_CERT
*x
= NULL
;
2614 if(ps_global
->smime
){
2615 for(x
= (PERSONAL_CERT
*) ps_global
->smime
->personal_certs
; x
; x
=x
->next
){
2620 if(!X509_NAME_cmp(ri
->issuer_and_serial
->issuer
,mine
->cert_info
->issuer
) &&
2621 !ASN1_INTEGER_cmp(ri
->issuer_and_serial
->serial
,mine
->cert_info
->serialNumber
)){
2631 static PERSONAL_CERT
*
2632 find_certificate_matching_pkcs7(PKCS7
*p7
)
2635 STACK_OF(PKCS7_RECIP_INFO
) *recips
;
2636 PERSONAL_CERT
*x
= NULL
;
2638 recips
= p7
->d
.enveloped
->recipientinfo
;
2640 for(i
=0; i
<sk_PKCS7_RECIP_INFO_num(recips
); i
++){
2641 PKCS7_RECIP_INFO
*ri
;
2643 ri
= sk_PKCS7_RECIP_INFO_value(recips
, i
);
2645 if((x
=find_certificate_matching_recip_info(ri
))!=0){
2653 /* decrypt an encrypted file.
2654 Args: fp - the path to the encrypted file.
2655 rv - a code that tells the caller what happened inside the function
2656 pcert - a personal certificate that was used to encrypt this file
2657 Returns the decoded text allocated in a char *, whose memory must be
2662 decrypt_file(char *fp
, int *rv
, PERSONAL_CERT
*pc
)
2665 char *text
, *tmp
, file_name
[MAILTMPLEN
];
2666 BIO
*in
= NULL
, *out
= NULL
;
2667 EVP_PKEY
*pkey
= NULL
;
2670 long unsigned int len
;
2675 if(pc
== NULL
|| (text
= read_file(fp
, 0)) == NULL
)
2678 tmp
= fs_get(strlen(text
) + (strlen(text
) << 6) + 1);
2679 for(j
= 0, i
= strlen("-----BEGIN PKCS7-----") + 1; text
[i
] != '\0'
2680 && text
[i
] != '-'; j
++, i
++)
2684 ret
= rfc822_base64(tmp
, strlen(tmp
), &len
);
2686 if((in
= BIO_new_mem_buf((char *)ret
, len
)) != NULL
){
2687 p7
= d2i_PKCS7_bio(in
, NULL
);
2691 if (text
) fs_give((void **)&text
);
2692 if (ret
) fs_give((void **)&ret
);
2694 if (rv
) *rv
= pc
->key
== NULL
? -1 : 1;
2696 out
= BIO_new(BIO_s_mem());
2697 (void) BIO_reset(out
);
2699 i
= PKCS7_decrypt(p7
, pc
->key
, pc
->cert
, out
, 0);
2702 q_status_message1(SM_ORDER
, 1, 1, _("Error decrypting: %s"),
2703 (char*) openssl_error_string());
2707 BIO_get_mem_data(out
, &tmp
);
2719 * Try to decode (decrypt or verify a signature) a PKCS7 body
2720 * Returns non-zero if something was changed.
2723 do_decoding(BODY
*b
, long msgno
, const char *section
)
2725 int modified_the_body
= 0;
2729 EVP_PKEY
*key
= NULL
;
2730 PERSONAL_CERT
*pcert
= NULL
;
2731 char *what_we_did
= "";
2734 dprint((9, "do_decoding(msgno=%ld type=%d subtype=%s section=%s)", msgno
, b
->type
, b
->subtype
? b
->subtype
: "NULL", (section
&& *section
) ? section
: (section
!= NULL
) ? "Top" : "NULL"));
2739 * Extract binary data from part to an in-memory store
2742 if(b
->sparep
){ /* already done */
2743 p7
= (PKCS7
*) b
->sparep
;
2747 p7
= get_pkcs7_from_part(msgno
, section
&& *section
? section
: "1");
2749 q_status_message1(SM_ORDER
, 2, 2, "Couldn't load PKCS7 object: %s",
2750 (char*) openssl_error_string());
2755 * Save the PKCS7 object for later dealings by the user interface.
2756 * It will be cleaned up when the body is garbage collected.
2761 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7
), PKCS7_type_is_enveloped(p7
)));
2763 if(PKCS7_type_is_signed(p7
)){
2766 out
= BIO_new(BIO_s_mem());
2767 (void) BIO_reset(out
);
2768 BIO_puts(out
, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
2770 sigok
= do_signature_verify(p7
, NULL
, out
, 0);
2772 what_we_did
= sigok
? _("This message was cryptographically signed.") :
2773 _("This message was cryptographically signed but the signature could not be verified.");
2775 /* make sure it's null terminated */
2776 BIO_write(out
, null
, 1);
2778 else if(!PKCS7_type_is_enveloped(p7
)){
2779 q_status_message(SM_ORDER
, 1, 1, "PKCS7 object not recognised.");
2782 else{ /* It *is* enveloped */
2785 what_we_did
= _("This message was encrypted.");
2787 /* now need to find a cert that can decrypt this */
2788 pcert
= find_certificate_matching_pkcs7(p7
);
2791 q_status_message(SM_ORDER
, 3, 3, _("Couldn't find the certificate needed to decrypt."));
2795 recip
= pcert
->cert
;
2797 if(!load_private_key(pcert
)
2799 && ps_global
->smime
->need_passphrase
2800 && !ps_global
->smime
->already_auto_asked
){
2801 /* Couldn't load key with blank password, ask user */
2802 ps_global
->smime
->already_auto_asked
= 1;
2803 if(pith_opt_smime_get_passphrase
){
2804 (*pith_opt_smime_get_passphrase
)();
2805 load_private_key(pcert
);
2813 out
= BIO_new(BIO_s_mem());
2814 (void) BIO_reset(out
);
2815 BIO_puts(out
, "MIME-Version: 1.0\r\n");
2817 decrypt_result
= PKCS7_decrypt(p7
, key
, recip
, out
, 0);
2819 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
2820 forget_private_keys();
2822 if(!decrypt_result
){
2823 q_status_message1(SM_ORDER
, 1, 1, _("Error decrypting: %s"),
2824 (char*) openssl_error_string());
2828 BIO_write(out
, null
, 1);
2832 * We've now produced a flattened MIME object in BIO out.
2833 * It needs to be turned back into a BODY.
2842 BUF_MEM
*bptr
= NULL
;
2844 BIO_get_mem_ptr(out
, &bptr
);
2848 /* look for start of body */
2849 bstart
= strstr(h
, "\r\n\r\n");
2852 q_status_message(SM_ORDER
, 3, 3, _("Encrypted data couldn't be parsed."));
2855 bstart
+= 4; /* skip over CRLF*2 */
2857 INIT(&s
, mail_string
, bstart
, strlen(bstart
));
2858 rfc822_parse_msg_full(&env
, &body
, h
, (bstart
-h
)-2, &s
, BADHOST
, 0, 0);
2859 mail_free_envelope(&env
); /* Don't care about this */
2861 body
->mime
.offset
= 0;
2862 body
->mime
.text
.size
= 0;
2864 * Now convert original body (application/pkcs7-mime)
2865 * to a multipart body with one sub-part (the decrypted body).
2866 * Note that the sub-part may also be multipart!
2869 b
->type
= TYPEMULTIPART
;
2871 fs_give((void**) &b
->subtype
);
2874 * This subtype is used in mailview.c to annotate the display of
2875 * encrypted or signed messages. We know for sure then that it's a PKCS7
2876 * part because the sparep field is set to the PKCS7 object (see above).
2878 b
->subtype
= cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE
);
2879 b
->encoding
= ENC8BIT
;
2882 fs_give((void**) &b
->description
);
2884 b
->description
= cpystr(what_we_did
);
2886 if(b
->disposition
.type
)
2887 fs_give((void **) &b
->disposition
.type
);
2889 if(b
->contents
.text
.data
)
2890 fs_give((void **) &b
->contents
.text
.data
);
2893 mail_free_body_parameter(&b
->parameter
);
2895 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
2896 b
->nested
.part
= fs_get(sizeof(PART
));
2897 b
->nested
.part
->body
= *body
;
2898 b
->nested
.part
->next
= NULL
;
2900 fs_give((void**) &body
);
2903 * IMPORTANT BIT: set the body->contents.text.data elements to contain the decrypted
2904 * data. Otherwise, it'll try to load it from the original data. Eek.
2906 create_local_cache(bstart
-b
->nested
.part
->body
.mime
.offset
, bstart
, &b
->nested
.part
->body
);
2908 modified_the_body
= 1;
2916 return modified_the_body
;
2921 * Recursively handle PKCS7 bodies in our message.
2923 * Returns non-zero if some fiddling was done.
2926 do_fiddle_smime_message(BODY
*b
, long msgno
, char *section
)
2928 int modified_the_body
= 0;
2933 dprint((9, "do_fiddle_smime_message(msgno=%ld type=%d subtype=%s section=%s)", msgno
, b
->type
, b
->subtype
? b
->subtype
: "NULL", (section
&& *section
) ? section
: (section
!= NULL
) ? "Top" : "NULL"));
2935 if(is_pkcs7_body(b
)){
2937 if(do_decoding(b
, msgno
, section
)){
2939 * b should now be a multipart message:
2940 * fiddle it too in case it's been multiply-encrypted!
2944 modified_the_body
= 1;
2948 if(b
->type
==TYPEMULTIPART
|| MIME_MSG(b
->type
, b
->subtype
)){
2954 if(MIME_MULT_SIGNED(b
->type
, b
->subtype
)){
2958 * Ahah. We have a multipart signed entity.
2961 * part 1 (signed thing)
2962 * part 2 (the pkcs7 signature)
2964 * We're going to convert that to
2966 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
2967 * part 1 (signed thing)
2968 * part 2 has been freed
2970 * We also extract the signature from part 2 and save it
2971 * in the multipart body->sparep, and we add a description
2972 * in the multipart body->description.
2975 * The results of a decrypted message will be similar. It
2978 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
2979 * part 1 (decrypted thing)
2982 modified_the_body
+= do_detached_signature_verify(b
, msgno
, section
);
2984 else if(MIME_MSG(b
->type
, b
->subtype
)){
2985 modified_the_body
+= do_fiddle_smime_message(b
->nested
.msg
->body
, msgno
, section
);
2989 for(p
=b
->nested
.part
,partNum
=1; p
; p
=p
->next
,partNum
++){
2991 /* Append part number to the section string */
2993 snprintf(newSec
, sizeof(newSec
), "%s%s%d", section
, *section
? "." : "", partNum
);
2995 modified_the_body
+= do_fiddle_smime_message(&p
->body
, msgno
, newSec
);
3000 return modified_the_body
;
3005 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3006 * Returns non-zero if something was changed.
3009 fiddle_smime_message(BODY
*b
, long msgno
)
3011 return do_fiddle_smime_message(b
, msgno
, "");
3015 /********************************************************************************/
3019 * Output a string in a distinctive style
3022 gf_puts_uline(char *txt
, gf_io_t pc
)
3024 pc(TAG_EMBED
); pc(TAG_BOLDON
);
3026 pc(TAG_EMBED
); pc(TAG_BOLDOFF
);
3031 get_chain_for_cert(X509
*cert
, int *error
)
3033 STACK_OF(X509
) *chain
= NULL
;
3034 X509_STORE_CTX
*ctx
;
3036 int rc
; /* return code */
3040 if((ctx
= X509_STORE_CTX_new()) != NULL
){
3041 X509_STORE_set_flags(s_cert_store
, 0);
3042 if(!X509_STORE_CTX_init(ctx
, s_cert_store
, cert
, NULL
))
3043 *error
= X509_STORE_CTX_get_error(ctx
);
3044 else if((chain
= sk_X509_new_null()) != NULL
){
3045 for(x
= cert
; ; x
= xtmp
){
3046 sk_X509_push(chain
, X509_dup(x
));
3047 rc
= X509_STORE_CTX_get1_issuer(&xtmp
, ctx
, x
);
3052 if(!X509_check_issued(xtmp
, xtmp
))
3056 if(*error
&& chain
!= NULL
)
3057 sk_X509_pop_free(chain
, X509_free
);
3058 X509_STORE_CTX_free(ctx
);
3065 * Sign a message. Called from call_mailer in send.c.
3067 * This takes the header for the outgoing message as well as a pointer
3068 * to the current body (which may be reallocated).
3071 sign_outgoing_message(METAENV
*header
, BODY
**bodyP
, int dont_detach
)
3073 STORE_S
*outs
= NULL
;
3074 BODY
*body
= *bodyP
;
3075 BODY
*newBody
= NULL
;
3078 PERSONAL_CERT
*pcert
;
3082 STACK_OF(X509
) *chain
;
3083 int result
= 0, error
;
3084 int flags
= dont_detach
? 0 : PKCS7_DETACHED
;
3086 dprint((9, "sign_outgoing_message()"));
3090 /* Look for a private key matching the sender address... */
3092 pcert
= match_personal_cert(header
->env
);
3095 q_status_message(SM_ORDER
, 3, 3, _("Couldn't find the certificate needed to sign."));
3099 if(!load_private_key(pcert
) && ps_global
->smime
&& ps_global
->smime
->need_passphrase
){
3100 /* Couldn't load key with blank password, try again */
3101 if(pith_opt_smime_get_passphrase
){
3102 (*pith_opt_smime_get_passphrase
)();
3103 load_private_key(pcert
);
3110 in
= body_to_bio(body
);
3112 chain
= get_chain_for_cert(pcert
->cert
, &error
);
3114 p7
= PKCS7_sign(pcert
->cert
, pcert
->key
, chain
, in
, flags
);
3116 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
3117 forget_private_keys();
3120 sk_X509_pop_free(chain
, X509_free
);
3123 q_status_message(SM_ORDER
, 1, 1, _("Error creating signed object."));
3128 q_status_message(SM_ORDER
, 1, 1, _("Not all certificates needed to verify signature included in signed message"));
3130 outs
= so_get(BioType
, NULL
, EDIT_ACCESS
);
3131 out
= bio_from_store(outs
);
3133 i2d_PKCS7_bio(out
, p7
);
3134 (void) BIO_flush(out
);
3136 so_seek(outs
, 0, SEEK_SET
);
3138 if((flags
&PKCS7_DETACHED
)==0){
3140 /* the simple case: the signed data is in the pkcs7 object */
3142 newBody
= mail_newbody();
3144 setup_pkcs7_body_for_signature(newBody
, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m");
3146 newBody
->contents
.text
.data
= (unsigned char *) outs
;
3155 * We have to create a new body as follows:
3157 * multipart/signed; blah blah blah
3158 * reference to existing body
3163 newBody
= mail_newbody();
3165 newBody
->type
= TYPEMULTIPART
;
3166 newBody
->subtype
= cpystr("signed");
3167 newBody
->encoding
= ENC7BIT
;
3169 set_parameter(&newBody
->parameter
, "protocol", "application/pkcs7-signature");
3170 set_parameter(&newBody
->parameter
, "micalg", "sha1");
3172 p1
= mail_newbody_part();
3173 p2
= mail_newbody_part();
3176 * This is nasty. We're just copying the body in here,
3177 * but since our newBody is freed at the end of call_mailer,
3178 * we mustn't let this body (the original one) be freed twice.
3180 p1
->body
= *body
; /* ARRGH. This is special cased at the end of call_mailer */
3184 setup_pkcs7_body_for_signature(&p2
->body
, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s");
3185 p2
->body
.contents
.text
.data
= (unsigned char *) outs
;
3187 newBody
->nested
.part
= p1
;
3199 dprint((9, "sign_outgoing_message returns %d", result
));
3205 new_smime_struct(void)
3207 SMIME_STUFF_S
*ret
= NULL
;
3209 ret
= (SMIME_STUFF_S
*) fs_get(sizeof(*ret
));
3210 memset((void *) ret
, 0, sizeof(*ret
));
3211 ret
->publictype
= Nada
;
3218 free_smime_struct(SMIME_STUFF_S
**smime
)
3220 if(smime
&& *smime
){
3221 if((*smime
)->passphrase_emailaddr
){
3223 for(i
= 0; (*smime
)->passphrase_emailaddr
[i
] != NULL
; i
++)
3224 fs_give((void **) &(*smime
)->passphrase_emailaddr
[i
]);
3225 fs_give((void **) (*smime
)->passphrase_emailaddr
);
3228 if((*smime
)->publicpath
)
3229 fs_give((void **) &(*smime
)->publicpath
);
3231 if((*smime
)->publiccertlist
)
3232 free_certlist(&(*smime
)->publiccertlist
);
3234 if((*smime
)->cacertlist
)
3235 free_certlist(&(*smime
)->cacertlist
);
3237 if((*smime
)->privatecertlist
)
3238 free_certlist(&(*smime
)->privatecertlist
);
3240 if((*smime
)->publiccontent
)
3241 fs_give((void **) &(*smime
)->publiccontent
);
3243 if((*smime
)->privatepath
)
3244 fs_give((void **) &(*smime
)->privatepath
);
3246 if((*smime
)->personal_certs
){
3249 pc
= (PERSONAL_CERT
*) (*smime
)->personal_certs
;
3250 free_personal_certs(&pc
);
3251 (*smime
)->personal_certs
= NULL
;
3255 if((*smime
)->pwdcert
){
3258 pc
= (PERSONAL_CERT
*) (*smime
)->pwdcert
;
3259 free_personal_certs(&pc
);
3260 (*smime
)->pwdcert
= NULL
;
3262 #endif /* PASSFILE */
3264 if((*smime
)->privatecontent
)
3265 fs_give((void **) &(*smime
)->privatecontent
);
3267 if((*smime
)->capath
)
3268 fs_give((void **) &(*smime
)->capath
);
3270 if((*smime
)->cacontent
)
3271 fs_give((void **) &(*smime
)->cacontent
);
3273 fs_give((void **) smime
);