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
, int type
);
57 static long rfc822_output_func(void *b
, char *string
);
58 static void setup_pkcs7_body_for_signature(BODY
*b
, char *description
,
59 char *type
, char *filename
, char *smime_type
);
60 static BIO
*body_to_bio(BODY
*body
);
61 static BIO
*bio_from_store(STORE_S
*store
);
62 static STORE_S
*get_part_contents(long msgno
, const char *section
);
63 static PKCS7
*get_pkcs7_from_part(long msgno
, const char *section
);
64 static int do_signature_verify(PKCS7
*p7
, BIO
*in
, BIO
*out
, int silent
);
65 int do_detached_signature_verify(BODY
*b
, long msgno
, char *section
);
66 static PERSONAL_CERT
*find_certificate_matching_pkcs7(PKCS7
*p7
);
67 static int do_decoding(BODY
*b
, long msgno
, const char *section
);
68 static void free_smime_struct(SMIME_STUFF_S
**smime
);
69 static void setup_storage_locations(void);
70 static int copy_container_to_dir(WhichCerts which
);
71 static int do_fiddle_smime_message(BODY
*b
, long msgno
, char *section
);
72 void setup_privatekey_storage(void);
73 int smime_path(char *rpath
, char *fpath
, size_t len
);
74 int smime_extract_and_save_cert(PKCS7
*p7
);
75 int same_cert(X509
*, X509
*);
76 CertList
* certlist_from_personal_certs(PERSONAL_CERT
*pc
);
78 void load_key_and_cert(char *pathkeydir
, char *pathcertdir
, char **keyfile
, char **certfile
, EVP_PKEY
**pkey
, X509
**pcert
);
80 STACK_OF(X509
) *get_chain_for_cert(X509
*cert
, int *error
);
81 EVP_PKEY
*load_pkey_with_prompt(char *fpath
, char *text
, char *prompt
);
83 int (*pith_opt_smime_get_passphrase
)(void);
84 int (*pith_smime_import_certificate
)(char *, char *, size_t);
85 int (*pith_smime_enter_password
)(char *prompt
, char *, size_t);
87 static X509_STORE
*s_cert_store
;
89 /* State management for randomness functions below */
90 static int seeded
= 0;
91 static int egdsocket
= 0;
93 typedef enum {P7Type
, CharType
, SizedText
} SpareType
;
95 typedef struct smime_sparep_t
{
101 create_smime_sparep(SpareType stype
, void *s
)
105 rv
= fs_get(sizeof(SMIME_SPARE_S
));
112 get_smime_sparep_type(void *s
)
114 return ((SMIME_SPARE_S
*)s
)->sptype
;
118 get_smime_sparep_data(void *s
)
120 return ((SMIME_SPARE_S
*)s
)->data
;
126 * load key from pathkeydir and cert from pathcertdir. It chooses the first
127 * key/certificate pair that matches. Delete pairs that you do not want used,
128 * if you do not want them selected. All parameters must be non-null.
129 * Memory freed by caller.
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
];
140 if(pathkeydir
== NULL
|| pathcertdir
== NULL
|| keyfile
== NULL
141 || pkey
== NULL
|| certfile
== NULL
|| pcert
== NULL
)
149 if((dirp
= opendir(pathkeydir
)) != NULL
){
150 while(b
== 0 && (d
=readdir(dirp
)) != NULL
){
153 if((ll
=strlen(d
->d_name
)) && ll
> 4){
154 if(!strcmp(d
->d_name
+ll
-4, ".key")){
155 strncpy(buf
, d
->d_name
, sizeof(buf
));
156 buf
[sizeof(buf
)-1] = '\0';
157 build_path(pathkey
, pathkeydir
, buf
, sizeof(pathkey
));
158 buf
[strlen(buf
)-4] = '\0';
159 snprintf(prompt
, sizeof(prompt
),
160 _("Enter password of key <%s> to unlock password file: "), buf
);
161 if((*pkey
= load_pkey_with_prompt(pathkey
, NULL
, prompt
)) != NULL
){
162 if(load_cert_for_key(pathcertdir
, *pkey
, certfile
, pcert
)){
164 *keyfile
= cpystr(buf
);
166 EVP_PKEY_free(*pkey
);
168 q_status_message1(SM_ORDER
, 0, 2,
169 _("Cannot find certificate that matches key <%s>. Continuing..."), buf
);
180 /* setup a key and certificate to encrypt and decrypt a password file.
181 * These files will be saved in the .alpine-smime/.pwd directory, but its
182 * location can be setup in the command line with the -pwdcertdir option.
183 * Here are the rules:
185 * Check if the .alpine-smime/.pwd (or -pwdcertdir directory) exists,
186 * if not create it. If we are successful, move to the next step
188 * - If the user has a key/cert pair, in the .alpine-smime/.pwd dir
189 * setup is successful;
190 * - if the user does not have a key/cert pair, look to see if
191 * ps_global->smime->personal_certs is already setup, if so, use it.
192 * - if ps_global->smime->personal_certs is not set up, see if we can
193 * find a certificate/cert pair in the default locations at compilation
194 * time. (~/.alpine-smime/private and ~/.alpine-smime/public
195 * - if none of this is successful, create a key/certificate pair
196 * (TODO: implement this)
197 * - in any other case, setup is not successful.
199 * If setup is successful, setup ps_global->pwdcert.
200 * If any of this fails, ps_global->pwdcert will be null.
201 * Ok, that should do it.
204 setup_pwdcert(void **pwdcert
)
207 int setup_dir
= 0; /* make it non zero if we know which dir to use */
209 char pathdir
[MAXPATH
+1], pathkey
[MAXPATH
+1], fpath
[MAXPATH
+1], pathcert
[MAXPATH
+1];
210 char fpath2
[MAXPATH
+1], prompt
[MAILTMPLEN
];
211 char *keyfile
, *certfile
, *text
;
212 EVP_PKEY
*pkey
= NULL
;
214 PERSONAL_CERT
*pc
, *pc2
= NULL
;
219 if(ps_global
->pwdcertdir
){
220 if(our_stat(ps_global
->pwdcertdir
, &sbuf
) == 0
221 && ((sbuf
.st_mode
& S_IFMT
) == S_IFDIR
)){
223 strncpy(pathdir
, ps_global
->pwdcertdir
, sizeof(pathdir
));
224 pathdir
[sizeof(pathdir
)-1] = '\0';
227 smime_path(DF_PASSWORD_DIR
, pathdir
, sizeof(pathdir
));
228 if(our_stat(pathdir
, &sbuf
) == 0){
229 if((sbuf
.st_mode
& S_IFMT
) == S_IFDIR
)
231 } else if(can_access(pathdir
, ACCESS_EXISTS
) != 0
232 && our_mkpath(pathdir
, 0700) == 0)
239 load_key_and_cert(pathdir
, pathdir
, &keyfile
, &certfile
, &pkey
, &pcert
);
241 if(certfile
&& keyfile
){
242 pc
= (PERSONAL_CERT
*) fs_get(sizeof(PERSONAL_CERT
));
243 memset((void *)pc
, 0, sizeof(PERSONAL_CERT
));
247 *pwdcert
= (void *) pc
;
248 fs_give((void **)&certfile
);
252 /* if the user gave a pwdcertdir and there is nothing there, do not
253 * continue. Let the user initialize on their own this directory.
255 if(ps_global
->pwdcertdir
!= NULL
)
258 /* look to see if there are any certificates lying around, first
259 * we try to load ps_global->smime to see if that has information
260 * we can use. If we are the process filling the smime structure
261 * we deinit at the end, since this might not do a full init.
263 if(ps_global
&& ps_global
->smime
&& !ps_global
->smime
->inited
){
268 /* at this point ps_global->smime->inited == 1 */
269 if(ps_global
->smime
&& ps_global
->smime
->personal_certs
!= NULL
){
270 pc
= (PERSONAL_CERT
*) ps_global
->smime
->personal_certs
;
271 if(ps_global
->smime
->privatetype
== Directory
){
272 build_path(pathkey
, ps_global
->smime
->privatepath
, pc
->name
, sizeof(pathkey
));
273 strncat(pathkey
, ".key", 4);
274 pathkey
[sizeof(pathkey
)-1] = '\0';
276 } else if (ps_global
->smime
->privatetype
== Container
){
277 if(pc
->keytext
== NULL
){ /* we should *never* be here, but just in case */
278 if(ps_global
->smime
->privatecontent
!= NULL
){
279 char tmp
[MAILTMPLEN
], *s
, *t
, c
;
280 snprintf(tmp
, sizeof(tmp
), "%s%s", EMAILADDRLEADER
, pc
->name
);
281 tmp
[sizeof(tmp
)-1] = '\0';
282 if((s
= strstr(ps_global
->smime
->privatecontent
, tmp
)) != NULL
){
283 if((t
= strstr(s
+strlen(tmp
), EMAILADDRLEADER
)) != NULL
){
286 pc
->keytext
= cpystr(s
+ strlen(tmp
) + 1); /* 1 = strlen("\n") */
290 pc
->keytext
= cpystr(s
+ strlen(tmp
) + 1); /* 1 = strlen("\n") */
294 if(pc
->keytext
!= NULL
) /* we should go straigth here */
296 } else if (ps_global
->smime
->privatetype
== Keychain
){
297 pathkey
[0] = '\0'; /* no apple key chain support yet */
300 if((pathkey
&& *pathkey
) || text
){
301 snprintf(prompt
, sizeof(prompt
),
302 _("Enter password of key <%s> to unlock password file: "), pc
->name
);
304 if((pkey
= load_pkey_with_prompt(pathkey
, text
, prompt
)) != NULL
){
305 pc2
= (PERSONAL_CERT
*) fs_get(sizeof(PERSONAL_CERT
));
306 memset((void *)pc2
, 0, sizeof(PERSONAL_CERT
));
307 pc2
->name
= cpystr(pc
->name
);
309 pc2
->cert
= X509_dup(pc
->cert
);
311 /* now copy the keys and certs, starting by the key... */
312 build_path(fpath
, pathdir
, pc
->name
, sizeof(fpath
));
313 strncat(fpath
, ".key", 4);
314 fpath
[sizeof(fpath
)-1] = '\0';
315 if(our_stat(fpath
, &sbuf
) == 0){ /* if fpath exists */
316 if((sbuf
.st_mode
& S_IFMT
) == S_IFREG
) /* and is a regular file */
317 setup_dir
++; /* we are done */
318 } else if(ps_global
->smime
->privatetype
== Directory
){
319 if(our_copy(fpath
, pathkey
) == 0)
321 } else if(ps_global
->smime
->privatetype
== Container
){
323 if((out
= BIO_new_file(fpath
, "w")) != NULL
){
324 if(BIO_puts(out
, pc
->keytext
) > 0)
328 } else if(ps_global
->smime
->privatetype
== Keychain
){
329 /* add support for Apple Mac OS X */
333 /* successful copy of key, now continue with certificate */
337 build_path(pathkey
, ps_global
->smime
->publicpath
, pc
->name
, sizeof(pathkey
));
338 strncat(pathkey
, ".crt", 4);
339 pathkey
[sizeof(pathkey
)-1] = '\0';
341 build_path(fpath
, pathdir
, pc
->name
, sizeof(fpath
));
342 strncat(fpath
, ".crt", 4);
343 fpath
[sizeof(fpath
)-1] = '\0';
345 if(our_stat(fpath
, &sbuf
) == 0){
346 if((sbuf
.st_mode
& S_IFMT
) == S_IFREG
)
349 else if(ps_global
->smime
->privatetype
== Directory
){
350 if(our_copy(fpath
, pathkey
) == 0)
352 } else if(ps_global
->smime
->privatetype
== Container
) {
354 if((out
= BIO_new_file(fpath
, "w")) != NULL
){
355 if(PEM_write_bio_X509(out
, pc
->cert
))
359 } else if (ps_global
->smime
->privatetype
== Keychain
) {
360 /* add support for Mac OS X */
365 *pwdcert
= (void *) pc2
;
369 free_personal_certs(&pc2
);
370 } /* if (pathkey...) */
371 } /* if(ps_global->smime->personal_certs) */
375 /* PATHCERTDIR(Private) must be null, so create a path */
376 set_current_val(&ps_global
->vars
[V_PRIVATEKEY_DIR
], TRUE
, TRUE
);
377 smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, pathkey
, sizeof(pathkey
));
379 /* PATHCERTDIR(Public) must be null, so create a path */
380 set_current_val(&ps_global
->vars
[V_PUBLICCERT_DIR
], TRUE
, TRUE
);
381 smime_path(ps_global
->VAR_PUBLICCERT_DIR
, pathcert
, sizeof(pathcert
));
383 /* BUG: this does not support local containers */
384 load_key_and_cert(pathkey
, pathcert
, &keyfile
, &certfile
, &pkey
, &pcert
);
386 if(certfile
&& keyfile
){
387 build_path(fpath
, pathdir
, keyfile
, sizeof(fpath
));
388 strncat(fpath
, ".key", 4);
389 fpath
[sizeof(fpath
)-1] = '\0';
391 build_path(fpath2
, pathkey
, keyfile
, sizeof(fpath
));
392 strncat(fpath2
, ".key", 4);
393 fpath2
[sizeof(fpath2
)-1] = '\0';
395 if(our_copy(fpath
, fpath2
) == 0)
401 build_path(fpath
, pathdir
, certfile
, sizeof(fpath
));
402 build_path(fpath2
, pathcert
, certfile
, sizeof(fpath2
));
404 if(our_copy(fpath
, fpath2
) == 0)
410 if(keyfile
&& certfile
){
411 pc
= (PERSONAL_CERT
*) fs_get(sizeof(PERSONAL_CERT
));
412 memset((void *)pc
, 0, sizeof(PERSONAL_CERT
));
416 *pwdcert
= (void *) pc
;
417 fs_give((void **)&certfile
);
421 /* TODO: create self signed certificate
422 q_status_message(SM_ORDER, 2, 2,
423 _("No key/certificate pair found for password file encryption support"));
429 #endif /* PASSFILE */
431 /* smime_expunge_cert.
432 * Return values: < 0 there was an error.
433 * >=0 the number of messages expunged
436 smime_expunge_cert(WhichCerts ctype
)
439 CertList
*cl
, *dummy
, *data
;
440 char *path
, buf
[MAXPATH
+1];
443 if(DATACERT(ctype
)== NULL
)
446 /* data cert is the way we unify certificate management across
447 * functions, but it is not where we really save the information in the
448 * case ctype is equal to Private. What we will do is to update the
449 * datacert, and in the case of ctype equal to Private use the updated
450 * certdata to update the personal_certs data.
453 path
= PATHCERTDIR(ctype
);
456 /* add a fake certificate at the beginning of the list */
457 dummy
= fs_get(sizeof(CertList
));
458 memset((void *)dummy
, 0, sizeof(CertList
));
459 dummy
->next
= DATACERT(ctype
);
461 for(cl
= dummy
, count
= 0; cl
&& cl
->next
;){
462 if(cl
->next
->data
.deleted
== 0){
467 removed
= 1; /* assume success */
468 if(SMHOLDERTYPE(ctype
) == Directory
){
469 build_path(buf
, path
, cl
->next
->name
, sizeof(buf
));
470 if(ctype
== Private
&& strlen(buf
) + strlen(EXTCERT(Private
)) < sizeof(buf
)){
471 strncat(buf
, EXTCERT(Private
), 4);
472 buf
[sizeof(buf
)-1] = '\0';
475 if(our_unlink(buf
) < 0){
476 q_status_message1(SM_ORDER
, 3, 3, _("Error removing certificate %s"), cl
->next
->name
);
481 else { /* SMHOLDERTYPE(ctype) == Container */
482 char *prefix
= ctype
== CACert
? CACERTSTORELEADER
: EMAILADDRLEADER
;
483 char tmp
[MAILTMPLEN
], *s
, *t
;
485 contents
= CONTENTCERTLIST(ctype
);
486 snprintf(tmp
, sizeof(tmp
), "%s%s", prefix
, cl
->next
->name
);
487 tmp
[sizeof(tmp
) - 1] = '\0';
488 if((s
= strstr(contents
, tmp
)) != NULL
){
489 if((t
= strstr(s
+strlen(tmp
), prefix
)) == NULL
)
492 memmove(s
, t
, strlen(t
)+1);
493 fs_resize((void **)&contents
, strlen(contents
)+1);
495 case Private
: ps_global
->smime
->privatecontent
= contents
; break;
496 case Public
: ps_global
->smime
->publiccontent
= contents
; break;
497 case CACert
: ps_global
->smime
->cacontent
= contents
; break;
506 count
++; /* count it! */
508 cl
->next
= data
->next
;
509 if(data
->name
) fs_give((void **)&data
->name
);
510 fs_give((void **)&data
);
514 q_status_message(SM_ORDER
, 3, 3, _("Error expunging certificate"));
517 case Private
: ps_global
->smime
->privatecertlist
= dummy
->next
; break;
518 case Public
: ps_global
->smime
->publiccertlist
= dummy
->next
; break;
519 case CACert
: ps_global
->smime
->cacertlist
= dummy
->next
; break;
522 fs_give((void **)&dummy
);
523 if(SMHOLDERTYPE(ctype
) == Container
){
524 if(copy_dir_to_container(ctype
, contents
) < 0)
528 q_status_message2(SM_ORDER
, 3, 3, _("Removed %s certificate%s"), comatose(count
), plural(count
));
531 q_status_message(SM_ORDER
, 3, 3, _("Error: No certificates were removed"));
536 mark_cert_deleted(WhichCerts ctype
, int num
, unsigned state
)
541 for(cl
= DATACERT(ctype
), i
= 0; cl
!= NULL
&& i
< num
; cl
= cl
->next
, i
++);
542 cl
->data
.deleted
= state
;
546 get_cert_deleted(WhichCerts ctype
, int num
)
551 for(cl
= DATACERT(ctype
), i
= 0; cl
!= NULL
&& i
< num
; cl
= cl
->next
, i
++);
552 return (cl
&& cl
->data
.deleted
) ? 1 : 0;
556 load_pkey_with_prompt(char *fpath
, char *text
, char *prompt
)
559 int rc
= 0; /* rc == 1, cancel, rc == 0 success */
560 char pass
[MAILTMPLEN
+1];
563 /* attempt to load with empty password */
564 in
= text
? BIO_new_mem_buf(text
, strlen(text
)) : BIO_new_file(fpath
, "r");
566 pkey
= PEM_read_bio_PrivateKey(in
, NULL
, NULL
, "");
568 if(pkey
!= NULL
) return pkey
;
571 if(pith_smime_enter_password
)
572 while(pkey
== NULL
&& rc
!= 1){
573 in
= text
? BIO_new_mem_buf(text
, strlen(text
)) : BIO_new_file(fpath
, "r");
576 rc
= (*pith_smime_enter_password
)(prompt
, (char *)pass
, sizeof(pass
));
577 } while (rc
!=0 && rc
!=1 && rc
>0);
579 pkey
= PEM_read_bio_PrivateKey(in
, NULL
, NULL
, (char *)pass
);
591 import_certificate(WhichCerts ctype
)
594 char filename
[MAXPATH
+1], full_filename
[MAXPATH
+1], buf
[MAXPATH
+1];
596 if(pith_smime_import_certificate
== NULL
){
597 q_status_message(SM_ORDER
, 0, 2,
598 _("import of certificates not implemented yet!"));
604 r
= (*pith_smime_import_certificate
)(filename
, full_filename
, sizeof(filename
) - 20);
606 ps_global
->mangled_screen
= 1;
610 else if (ctype
== Private
){
611 char prompt
[500], *s
, *t
;
612 EVP_PKEY
*key
= NULL
;
614 if(!ps_global
->smime
->privatecertlist
){
615 ps_global
->smime
->privatecertlist
= fs_get(sizeof(CertList
));
616 memset((void *)DATACERT(ctype
), 0, sizeof(CertList
));
619 for(s
= t
= filename
; (t
= strstr(s
, ".key")) != NULL
; s
= t
+ 1);
622 snprintf(prompt
, sizeof(prompt
), _("Enter passphrase for <%s>: "), filename
);
623 prompt
[sizeof(prompt
)-1] = '\0';
624 if((key
= load_pkey_with_prompt(full_filename
, NULL
, prompt
)) != NULL
){
625 if(SMHOLDERTYPE(ctype
) == Directory
){
626 build_path(buf
, PATHCERTDIR(ctype
), filename
, sizeof(buf
));
627 if(strcmp(buf
+ strlen(buf
) - 4, EXTCERT(ctype
)) != 0 && strlen(buf
) + 4 < sizeof(buf
)){
628 strncat(buf
, EXTCERT(ctype
), 4);
629 buf
[sizeof(buf
)-1] = '\0';
631 rc
= our_copy(buf
, full_filename
);
633 else /* if(SMHOLDERTYPE(ctype) == Container){ */
634 rc
= add_file_to_container(ctype
, full_filename
, NULL
);
636 q_status_message(SM_ORDER
, 1, 3, _("Private key saved"));
638 q_status_message(SM_ORDER
, 1, 3, _("Error saving private key"));
639 if(ps_global
->smime
->publiccertlist
)
640 ps_global
->smime
->publiccertlist
->data
.renew
= 1;
643 q_status_message(SM_ORDER
, 1, 3, _("Problem unlocking key (not a certificate and/or wrong password)"));
644 } else if (ctype
== CACert
){
648 if((ins
= BIO_new_file(full_filename
, "r")) != NULL
){
649 if((cert
= PEM_read_bio_X509(ins
, NULL
, NULL
, NULL
)) != NULL
){
650 if(SMHOLDERTYPE(ctype
) == Directory
){
651 build_path(buf
, PATHCERTDIR(ctype
), filename
, sizeof(buf
));
652 if(strcmp(buf
+ strlen(buf
) - 4, ".crt") != 0 && strlen(buf
) + 4 < sizeof(buf
)){
653 strncat(buf
, EXTCERT(ctype
), 4);
654 buf
[sizeof(buf
)-1] = '\0';
657 rc
= our_copy(buf
, full_filename
);
659 else /* if(SMHOLDERTYPE(ctype) == Container){ */
660 rc
= add_file_to_container(ctype
, full_filename
, NULL
);
662 q_status_message(SM_ORDER
, 1, 3, _("Certificate saved"));
664 q_status_message(SM_ORDER
, 1, 3, _("Error saving certificate"));
665 X509_free(cert
); /* not needed anymore */
668 q_status_message(SM_ORDER
, 1, 3, _("Error in certificate file (not a certificate?)"));
672 } else { /* ctype == Public. save certificate, but first validate that it is one */
676 if((ins
= BIO_new_file(full_filename
, "r")) != NULL
){
677 if((cert
= PEM_read_bio_X509(ins
, NULL
, NULL
, NULL
)) != NULL
){
678 if(SMHOLDERTYPE(ctype
) == Directory
){
679 char **email
= get_x509_subject_email(cert
);
682 for(i
= 0; email
[i
] != NULL
; i
++){
683 save_cert_for(email
[i
], cert
, Public
);
684 fs_give((void **)&email
[i
]);
686 fs_give((void **)email
);
688 else /* if(SMHOLDERTYPE(ctype) == Container){ */
689 add_file_to_container(ctype
, full_filename
, NULL
);
691 if(ps_global
->smime
->publiccertlist
)
692 ps_global
->smime
->publiccertlist
->data
.renew
= 1;
695 q_status_message(SM_ORDER
, 1, 3, _("Error in certificate file (not a certificate?)"));
699 if(DATACERT(ctype
)) RENEWCERT(DATACERT(ctype
)) = 1;
703 /* itype: information type to add: 0 - public, 1 - private.
704 * Memory freed by caller
707 print_private_key_information(char *email
, int itype
)
712 if(ps_global
->smime
== NULL
713 || ps_global
->smime
->personal_certs
== NULL
714 || (itype
!= 0 && itype
!= 1))
717 for(pc
= ps_global
->smime
->personal_certs
;
718 pc
!= NULL
&& strcmp(pc
->name
, email
) != 0; pc
= pc
->next
);
720 && !load_private_key(pc
)
722 && ps_global
->smime
->need_passphrase
){
723 if (*pith_opt_smime_get_passphrase
)
724 (*pith_opt_smime_get_passphrase
)();
725 load_private_key(pc
);
731 out
= BIO_new(BIO_s_mem());
732 if(itype
== 0) /* 0 means public */
733 EVP_PKEY_print_public(out
, pc
->key
, 0, NULL
);
734 else if (itype
== 1) /* 1 means private */
735 EVP_PKEY_print_private(out
, pc
->key
, 0, NULL
);
737 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
738 forget_private_keys();
744 * Forget any cached private keys
747 forget_private_keys(void)
749 PERSONAL_CERT
*pcert
;
753 dprint((9, "forget_private_keys()"));
754 if(ps_global
->smime
){
755 for(pcert
=(PERSONAL_CERT
*) ps_global
->smime
->personal_certs
;
760 EVP_PKEY_free(pcert
->key
);
765 ps_global
->smime
->entered_passphrase
= 0;
766 len
= sizeof(ps_global
->smime
->passphrase
);
767 p
= ps_global
->smime
->passphrase
;
774 /* modelled after signature_path in reply.c, but uses home dir instead of the
775 * directory where the .pinerc is located, since according to documentation,
776 * the .alpine-smime directories are subdirectories of the home directory
778 int smime_path(char *rpath
, char *fpath
, size_t len
)
782 size_t spl
= strlen(rpath
);
784 if(IS_REMOTE(rpath
)){
786 strncpy(fpath
, rpath
, len
-1);
789 else if(is_absolute_path(rpath
)){
790 strncpy(fpath
, rpath
, len
-1);
792 fnexpand(fpath
, len
);
794 else if(ps_global
->VAR_OPER_DIR
){
795 if(strlen(ps_global
->VAR_OPER_DIR
) + spl
< len
- 1)
796 build_path(fpath
, ps_global
->VAR_OPER_DIR
, rpath
, len
);
798 else if(ps_global
->home_dir
){
799 if(strlen(ps_global
->home_dir
) + spl
< len
- 1)
800 build_path(fpath
, ps_global
->home_dir
, rpath
, len
);
803 return fpath
&& *fpath
? 1 : 0;
809 * taken from openssl/apps/app_rand.c
812 app_RAND_load_file(const char *file
)
817 file
= RAND_file_name(buffer
, sizeof buffer
);
818 else if(RAND_egd(file
) > 0){
819 /* we try if the given filename is an EGD socket.
820 if it is, we don't write anything back to the file. */
825 if(file
== NULL
|| !RAND_load_file(file
, -1)){
826 if(RAND_status() == 0){
827 dprint((1, "unable to load 'random state'\n"));
828 dprint((1, "This means that the random number generator has not been seeded\n"));
829 dprint((1, "with much random data.\n"));
841 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
844 openssl_extra_randomness(void)
852 /* if system doesn't have /dev/urandom */
853 if(stat ("/dev/urandom", &sbuf
)){
855 tf
= temp_nam(NULL
, NULL
);
857 strncpy(tmp
, tf
, sizeof(tmp
));
858 tmp
[sizeof(tmp
)-1] = '\0';
859 fs_give((void **) &tf
);
862 if((fd
= open(tmp
, O_WRONLY
|O_CREAT
|O_EXCL
, 0600)) < 0)
863 i
= (unsigned long) tmp
;
865 unlink(tmp
); /* don't need the file */
866 fstat(fd
, &sbuf
); /* get information about the file */
867 i
= sbuf
.st_ino
; /* remember its inode */
868 close(fd
); /* or its descriptor */
870 /* not great but it'll have to do */
871 snprintf(tmp
+strlen(tmp
), sizeof(tmp
)-strlen(tmp
), "%.80s%lx%lx%lx",
873 (unsigned long) (time (0) ^ gethostid ()),
874 (unsigned long) getpid ());
875 RAND_seed(tmp
, strlen(tmp
));
881 /* taken from openssl/apps/app_rand.c */
883 app_RAND_write_file(const char *file
)
887 if(egdsocket
|| !seeded
)
889 * If we did not manage to read the seed file,
890 * we should not write a low-entropy seed file back --
891 * it would suppress a crucial warning the next time
897 file
= RAND_file_name(buffer
, sizeof buffer
);
899 if(file
== NULL
|| !RAND_write_file(file
)){
900 dprint((1, "unable to write 'random state'\n"));
908 certlist_from_personal_certs(PERSONAL_CERT
*pc
)
915 cl
= fs_get(sizeof(CertList
));
916 memset((void *)cl
, 0, sizeof(CertList
));
917 cl
->name
= cpystr(pc
->name
);
918 cl
->next
= certlist_from_personal_certs(pc
->next
);
925 renew_cert_data(CertList
**data
, WhichCerts ctype
)
928 if(ctype
== Private
){
930 PERSONAL_CERT
*pc
= (PERSONAL_CERT
*)ps_global
->smime
->personal_certs
;
933 free_personal_certs(&pc
);
934 setup_privatekey_storage();
935 *data
= certlist_from_personal_certs((PERSONAL_CERT
*)ps_global
->smime
->personal_certs
);
937 RENEWCERT(*data
) = 0;
938 ps_global
->smime
->privatecertlist
= *data
;
940 if(ps_global
->smime
->privatecertlist
)
941 RENEWCERT(ps_global
->smime
->privatecertlist
) = 0;
943 X509_LOOKUP
*lookup
= NULL
;
944 X509_STORE
*store
= NULL
;
946 if((store
= X509_STORE_new()) != NULL
){
947 if((lookup
= X509_STORE_add_lookup(store
, X509_LOOKUP_file())) == NULL
){
948 X509_STORE_free(store
);
952 if(SMHOLDERTYPE(ctype
) == Directory
)
953 add_certs_in_dir(lookup
, PATHCERTDIR(ctype
), EXTCERT(ctype
), data
);
954 else /* if(SMHOLDERTYPE(ctype) == Container) */
955 *data
= mem_to_certlist(CONTENTCERTLIST(ctype
), ctype
);
957 RENEWCERT(*data
) = 0;
959 ps_global
->smime
->publiccertlist
= *data
;
961 ps_global
->smime
->cacertlist
= *data
;
974 /* Installed as an atexit() handler to save the random data */
978 dprint((9, "smime_deinit()"));
979 app_RAND_write_file(NULL
);
980 free_smime_struct(&ps_global
->smime
);
983 /* we renew the store when it has changed */
984 void renew_store(void)
986 if(ps_global
->smime
->inited
){
987 if(s_cert_store
!= NULL
)
988 X509_STORE_free(s_cert_store
);
989 s_cert_store
= get_ca_store();
993 /* Initialise openssl stuff if needed */
997 if(F_OFF(F_DONT_DO_SMIME
, ps_global
) && !(ps_global
->smime
&& ps_global
->smime
->inited
)){
999 dprint((9, "smime_init()"));
1000 if(!ps_global
->smime
)
1001 ps_global
->smime
= new_smime_struct();
1003 setup_storage_locations();
1005 s_cert_store
= get_ca_store();
1007 OpenSSL_add_all_algorithms();
1008 ERR_load_crypto_strings();
1010 app_RAND_load_file(NULL
);
1011 openssl_extra_randomness();
1012 ps_global
->smime
->inited
= 1;
1019 /* validate a certificate. Return value : 0 for no error, -1 for error.
1020 * In the latter case, set the openssl smime error in *error.
1022 int smime_validate_cert(X509
*cert
, long *error
)
1024 X509_STORE_CTX
*csc
;
1028 if((csc
= X509_STORE_CTX_new()) != NULL
){
1029 X509_STORE_set_flags(s_cert_store
, 0);
1030 if(X509_STORE_CTX_init(csc
,s_cert_store
,cert
,NULL
)
1031 && X509_verify_cert(csc
) <= 0)
1032 *error
= X509_STORE_CTX_get_error(csc
);
1033 X509_STORE_CTX_free(csc
);
1035 return *error
? -1 : 0;
1039 get_personal_certs(char *path
)
1041 PERSONAL_CERT
*result
= NULL
;
1046 ps_global
->smime
->privatepath
= cpystr(path
);
1047 dirp
= opendir(path
);
1049 while((d
=readdir(dirp
)) != NULL
){
1053 if((ll
=strlen(d
->d_name
)) && ll
> 4 && !strcmp(d
->d_name
+ll
-4, ".key")){
1055 /* copy file name to temp buffer */
1056 strncpy(buf2
, d
->d_name
, sizeof(buf2
)-1);
1057 buf2
[sizeof(buf2
)-1] = '\0';
1058 /* chop off ".key" trailier */
1059 buf2
[strlen(buf2
)-4] = 0;
1060 /* Look for certificate */
1061 cert
= get_cert_for(buf2
, Public
);
1066 /* create a new PERSONAL_CERT, fill it in */
1068 pc
= (PERSONAL_CERT
*) fs_get(sizeof(*pc
));
1070 pc
->name
= cpystr(buf2
);
1072 /* Try to load the key with an empty password */
1073 pc
->key
= load_key(pc
, "");
1087 setup_privatekey_storage(void)
1089 char path
[MAXPATH
+1], *contents
;
1090 int privatekeycontainer
= 0;
1092 /* private keys in a container */
1093 if(ps_global
->VAR_PRIVATEKEY_CONTAINER
&& ps_global
->VAR_PRIVATEKEY_CONTAINER
[0]){
1095 privatekeycontainer
= 1;
1098 if(!smime_path(ps_global
->VAR_PRIVATEKEY_CONTAINER
, path
, MAXPATH
))
1099 privatekeycontainer
= 0;
1101 if(privatekeycontainer
&& !IS_REMOTE(path
)
1102 && ps_global
->VAR_OPER_DIR
1103 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1104 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1105 /* TRANSLATORS: First arg is the directory name, second is
1106 the file user wants to read but can't. */
1107 _("Can't read file outside %s: %s"),
1108 ps_global
->VAR_OPER_DIR
, path
);
1109 privatekeycontainer
= 0;
1112 if(privatekeycontainer
1113 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1114 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1116 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1117 privatekeycontainer
= 0;
1120 if(privatekeycontainer
&& path
[0]){
1121 ps_global
->smime
->privatetype
= Container
;
1122 ps_global
->smime
->privatepath
= cpystr(path
);
1125 ps_global
->smime
->privatecontent
= contents
;
1126 ps_global
->smime
->personal_certs
= mem_to_personal_certs(contents
);
1131 /* private keys in a directory of files */
1132 if(!privatekeycontainer
){
1133 ps_global
->smime
->privatetype
= Directory
;
1136 if(!(smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, path
, MAXPATH
)
1137 && !IS_REMOTE(path
)))
1138 ps_global
->smime
->privatetype
= Nada
;
1139 else if(can_access(path
, ACCESS_EXISTS
)){
1140 if(our_mkpath(path
, 0700)){
1141 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1142 ps_global
->smime
->privatetype
= Nada
;
1146 if(ps_global
->smime
->privatetype
== Directory
)
1147 ps_global
->smime
->personal_certs
= get_personal_certs(path
);
1154 setup_storage_locations(void)
1156 int publiccertcontainer
= 0, cacertcontainer
= 0;
1157 char path
[MAXPATH
+1], *contents
;
1159 if(!ps_global
->smime
)
1162 #ifdef APPLEKEYCHAIN
1163 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
1164 ps_global
->smime
->publictype
= Keychain
;
1167 #endif /* APPLEKEYCHAIN */
1168 /* Public certificates in a container */
1169 if(ps_global
->VAR_PUBLICCERT_CONTAINER
&& ps_global
->VAR_PUBLICCERT_CONTAINER
[0]){
1171 publiccertcontainer
= 1;
1174 if(!smime_path(ps_global
->VAR_PUBLICCERT_CONTAINER
, path
, MAXPATH
))
1175 publiccertcontainer
= 0;
1177 if(publiccertcontainer
&& !IS_REMOTE(path
)
1178 && ps_global
->VAR_OPER_DIR
1179 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1180 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1181 /* TRANSLATORS: First arg is the directory name, second is
1182 the file user wants to read but can't. */
1183 _("Can't read file outside %s: %s"),
1184 ps_global
->VAR_OPER_DIR
, path
);
1185 publiccertcontainer
= 0;
1188 if(publiccertcontainer
1189 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1190 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1192 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1193 publiccertcontainer
= 0;
1196 if(publiccertcontainer
&& path
[0]){
1197 ps_global
->smime
->publictype
= Container
;
1198 ps_global
->smime
->publicpath
= cpystr(path
);
1201 ps_global
->smime
->publiccontent
= contents
;
1202 ps_global
->smime
->publiccertlist
= mem_to_certlist(contents
, Public
);
1207 /* Public certificates in a directory of files */
1208 if(!publiccertcontainer
){
1209 ps_global
->smime
->publictype
= Directory
;
1212 if(!(smime_path(ps_global
->VAR_PUBLICCERT_DIR
, path
, MAXPATH
)
1213 && !IS_REMOTE(path
)))
1214 ps_global
->smime
->publictype
= Nada
;
1215 else if(can_access(path
, ACCESS_EXISTS
)){
1216 if(our_mkpath(path
, 0700)){
1217 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1218 ps_global
->smime
->publictype
= Nada
;
1222 if(ps_global
->smime
->publictype
== Directory
)
1223 ps_global
->smime
->publicpath
= cpystr(path
);
1226 #ifdef APPLEKEYCHAIN
1228 #endif /* APPLEKEYCHAIN */
1230 setup_privatekey_storage();
1232 /* extra cacerts in a container */
1233 if(ps_global
->VAR_CACERT_CONTAINER
&& ps_global
->VAR_CACERT_CONTAINER
[0]){
1235 cacertcontainer
= 1;
1238 if(!smime_path(ps_global
->VAR_CACERT_CONTAINER
, path
, MAXPATH
))
1239 cacertcontainer
= 0;
1241 if(cacertcontainer
&& !IS_REMOTE(path
)
1242 && ps_global
->VAR_OPER_DIR
1243 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1244 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1245 /* TRANSLATORS: First arg is the directory name, second is
1246 the file user wants to read but can't. */
1247 _("Can't read file outside %s: %s"),
1248 ps_global
->VAR_OPER_DIR
, path
);
1249 cacertcontainer
= 0;
1253 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1254 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1256 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1257 cacertcontainer
= 0;
1260 if(cacertcontainer
&& path
[0]){
1261 ps_global
->smime
->catype
= Container
;
1262 ps_global
->smime
->capath
= cpystr(path
);
1263 ps_global
->smime
->cacontent
= contents
;
1265 ps_global
->smime
->cacertlist
= mem_to_certlist(contents
, CACert
);
1269 if(!cacertcontainer
){
1270 ps_global
->smime
->catype
= Directory
;
1273 if(!(smime_path(ps_global
->VAR_CACERT_DIR
, path
, MAXPATH
)
1274 && !IS_REMOTE(path
)))
1275 ps_global
->smime
->catype
= Nada
;
1276 else if(can_access(path
, ACCESS_EXISTS
)){
1277 if(our_mkpath(path
, 0700)){
1278 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1279 ps_global
->smime
->catype
= Nada
;
1283 if(ps_global
->smime
->catype
== Directory
)
1284 ps_global
->smime
->capath
= cpystr(path
);
1290 copy_publiccert_dir_to_container(void)
1292 return(copy_dir_to_container(Public
, NULL
));
1297 copy_publiccert_container_to_dir(void)
1299 return(copy_container_to_dir(Public
));
1304 copy_privatecert_dir_to_container(void)
1306 return(copy_dir_to_container(Private
, NULL
));
1311 copy_privatecert_container_to_dir(void)
1313 return(copy_container_to_dir(Private
));
1318 copy_cacert_dir_to_container(void)
1320 return(copy_dir_to_container(CACert
, NULL
));
1325 copy_cacert_container_to_dir(void)
1327 return(copy_container_to_dir(CACert
));
1330 /* Add the contents of a file to a container. Do not check the content
1331 * of the file, just add it using the format for that container. The
1332 * caller must check the format, so that there is no data corruption
1334 * return value: 0 - success,
1338 add_file_to_container(WhichCerts ctype
, char *fpath
, char *altname
)
1340 char *sep
= (ctype
== Public
|| ctype
== Private
)
1341 ? EMAILADDRLEADER
: CACERTSTORELEADER
;
1342 char *content
= ctype
== Public
? ps_global
->smime
->publiccontent
1343 : (ctype
== Private
? ps_global
->smime
->privatecontent
1344 : ps_global
->smime
->cacontent
);
1350 int rv
= -1; /* assume error */
1352 if(our_stat(fpath
, &sbuf
) < 0
1353 || (in
= so_get(FileStar
, fpath
, READ_ACCESS
| READ_FROM_LOCALE
)) == NULL
)
1358 else if((name
= strrchr(fpath
, '/')) != NULL
){
1360 if((ll
= strlen(++name
)) > 4 && strucmp(name
+ ll
- 4, EXTCERT(ctype
)) == 0)
1361 name
[ll
-strlen(EXTCERT(ctype
))] = '\0';
1367 fs_resize((void **)&content
, strlen(content
) + strlen(sep
) + strlen(name
) + sbuf
.st_size
+ 3); /* 2 = \n + \n + \0*/
1369 content
+= strlen(content
);
1372 s
= content
= fs_get(strlen(sep
) + strlen(name
) + sbuf
.st_size
+ 1); /* 2 = \n + \0 */
1375 strncat(content
, sep
, strlen(sep
));
1376 strncat(content
, name
, strlen(name
));
1377 content
+= strlen(content
);
1380 while(so_readc(&c
, in
))
1381 *content
++ = (char) c
;
1385 case Private
: ps_global
->smime
->privatecontent
= s
; break;
1386 case Public
: ps_global
->smime
->publiccontent
= s
; break;
1387 case CACert
: ps_global
->smime
->cacontent
= s
; break;
1391 rv
= copy_dir_to_container(ctype
, s
);
1394 if(in
) so_give(&in
);
1401 * returns 0 on success, -1 on failure
1402 * contents is an argument which tells this function to write the value
1403 * of this variable instead of reading the contents of the directory.
1404 * If the var contents is not null use its value as the value of the
1408 copy_dir_to_container(WhichCerts which
, char *contents
)
1411 BIO
*bio_out
= NULL
, *bio_in
= NULL
;
1412 char srcpath
[MAXPATH
+1], dstpath
[MAXPATH
+1], emailaddr
[MAXPATH
], file
[MAXPATH
], line
[4096];
1413 char *tempfile
= NULL
;
1416 REMDATA_S
*rd
= NULL
;
1417 char *configdir
= NULL
;
1418 char *configpath
= NULL
;
1419 char *filesuffix
= NULL
;
1421 dprint((9, "copy_dir_to_container(%s)", which
==Public
? "Public" : which
==Private
? "Private" : which
==CACert
? "CACert" : "?"));
1427 emailaddr
[0] = '\0';
1429 if(which
== Public
){
1430 configdir
= ps_global
->VAR_PUBLICCERT_DIR
;
1431 configpath
= ps_global
->smime
->publicpath
;
1432 filesuffix
= ".crt";
1434 else if(which
== Private
){
1435 configdir
= ps_global
->VAR_PRIVATEKEY_DIR
;
1436 configpath
= ps_global
->smime
->privatepath
;
1437 filesuffix
= ".key";
1439 else if(which
== CACert
){
1440 configdir
= ps_global
->VAR_CACERT_DIR
;
1441 configpath
= ps_global
->smime
->capath
;
1442 filesuffix
= ".crt";
1445 if(!(configdir
&& configdir
[0])){
1446 q_status_message(SM_ORDER
, 3, 3, _("Directory not defined"));
1450 if(!(configpath
&& configpath
[0])){
1451 #ifdef APPLEKEYCHAIN
1452 if(which
== Public
&& F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
1453 q_status_message(SM_ORDER
, 3, 3, _("Turn off the Keychain feature above first"));
1456 #endif /* APPLEKEYCHAIN */
1457 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
1461 if(!(filesuffix
&& strlen(filesuffix
) == 4)){
1467 * If there is a legit directory to read from set up the
1468 * container file to write to.
1470 if(smime_path(configdir
, srcpath
, MAXPATH
) && !IS_REMOTE(srcpath
)){
1472 if(IS_REMOTE(configpath
)){
1473 rd
= rd_create_remote(RemImap
, configpath
, REMOTE_SMIME_SUBTYPE
,
1475 _("Can't access remote smime configuration."));
1479 (void) rd_read_metadata(rd
);
1481 if(rd
->access
== MaybeRorW
){
1482 if(rd
->read_status
== 'R')
1483 rd
->access
= ReadOnly
;
1485 rd
->access
= ReadWrite
;
1488 if(rd
->access
!= NoExists
){
1490 rd_check_remvalid(rd
, 1L);
1493 * If the cached info says it is readonly but
1494 * it looks like it's been fixed now, change it to readwrite.
1496 if(rd
->read_status
== 'R'){
1497 rd_check_readonly_access(rd
);
1498 if(rd
->read_status
== 'W'){
1499 rd
->access
= ReadWrite
;
1500 rd
->flags
|= REM_OUTOFDATE
;
1503 rd
->access
= ReadOnly
;
1507 if(rd
->flags
& REM_OUTOFDATE
){
1508 if(rd_update_local(rd
) != 0){
1510 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1511 rd_close_remdata(&rd
);
1518 if(rd
->access
!= ReadWrite
|| rd_remote_is_readonly(rd
)){
1519 rd_close_remdata(&rd
);
1523 rd
->flags
|= DO_REMTRIM
;
1525 strncpy(dstpath
, rd
->lf
, sizeof(dstpath
)-1);
1526 dstpath
[sizeof(dstpath
)-1] = '\0';
1529 strncpy(dstpath
, configpath
, sizeof(dstpath
)-1);
1530 dstpath
[sizeof(dstpath
)-1] = '\0';
1534 * dstpath is either the local Container file or the local cache file
1535 * for the remote Container file.
1537 tempfile
= tempfile_in_same_dir(dstpath
, "az", NULL
);
1541 * If there is a legit directory to read from and a tempfile
1542 * to write to we continue.
1544 if(tempfile
&& (bio_out
=BIO_new_file(tempfile
, "w")) != NULL
){
1546 if(contents
!= NULL
){
1547 if(BIO_puts(bio_out
, contents
) < 0)
1551 if((dirp
= opendir(srcpath
)) != NULL
){
1553 while((d
=readdir(dirp
)) && !ret
){
1556 if((ll
=strlen(d
->d_name
)) && ll
> 4 && !strcmp(d
->d_name
+ll
-4, filesuffix
)){
1558 /* copy file name to temp buffer */
1559 strncpy(emailaddr
, d
->d_name
, sizeof(emailaddr
)-1);
1560 emailaddr
[sizeof(emailaddr
)-1] = '\0';
1561 /* chop off suffix trailier */
1562 emailaddr
[strlen(emailaddr
)-4] = 0;
1565 * This is the separator between the contents of
1568 if(which
== CACert
){
1569 if(!((BIO_puts(bio_out
, CACERTSTORELEADER
) > 0)
1570 && (BIO_puts(bio_out
, emailaddr
) > 0)
1571 && (BIO_puts(bio_out
, "\n") > 0)))
1575 if(!((BIO_puts(bio_out
, EMAILADDRLEADER
) > 0)
1576 && (BIO_puts(bio_out
, emailaddr
) > 0)
1577 && (BIO_puts(bio_out
, "\n") > 0)))
1581 /* read then write contents of file */
1582 build_path(file
, srcpath
, d
->d_name
, sizeof(file
));
1583 if(!(bio_in
= BIO_new_file(file
, "r")))
1589 while(BIO_gets(bio_in
, line
, sizeof(line
)) > 0){
1590 if(strncmp("-----BEGIN", line
, strlen("-----BEGIN")) == 0)
1594 BIO_puts(bio_out
, line
);
1596 if(strncmp("-----END", line
, strlen("-----END")) == 0)
1612 if(rename_file(tempfile
, dstpath
) < 0){
1613 q_status_message2(SM_ORDER
, 3, 3,
1614 _("Can't rename %s to %s"), tempfile
, dstpath
);
1618 /* if the container is remote, copy it */
1619 if(!ret
&& IS_REMOTE(configpath
)){
1625 if((e
= rd_update_remote(rd
, datebuf
)) != 0){
1627 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1628 _("Error opening temporary smime file %s: %s"),
1629 rd
->lf
, error_description(errno
));
1631 "write_remote_smime: error opening temp file %s\n",
1632 rd
->lf
? rd
->lf
: "?"));
1635 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1636 _("Error copying to %s: %s"),
1637 rd
->rn
, error_description(errno
));
1639 "write_remote_smime: error copying from %s to %s\n",
1640 rd
->lf
? rd
->lf
: "?", rd
->rn
? rd
->rn
: "?"));
1643 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
1644 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1647 rd_update_metadata(rd
, datebuf
);
1648 rd
->read_status
= 'W';
1651 rd_close_remdata(&rd
);
1657 fs_give((void **) &tempfile
);
1664 * returns 0 on success, -1 on failure
1667 copy_container_to_dir(WhichCerts which
)
1669 char path
[MAXPATH
+1], file
[MAXPATH
+1], buf
[MAXPATH
+1];
1671 char *contents
= NULL
;
1672 char *leader
= NULL
;
1673 char *filesuffix
= NULL
;
1674 char *configdir
= NULL
;
1675 char *configpath
= NULL
;
1676 char *tempfile
= NULL
;
1677 char *p
, *q
, *line
, *name
, *certtext
, *save_p
;
1681 dprint((9, "copy_container_to_dir(%s)", which
==Public
? "Public" : which
==Private
? "Private" : which
==CACert
? "CACert" : "?"));
1686 if(which
== Public
){
1687 leader
= EMAILADDRLEADER
;
1688 contents
= ps_global
->smime
->publiccontent
;
1689 configdir
= ps_global
->VAR_PUBLICCERT_DIR
;
1690 configpath
= ps_global
->smime
->publicpath
;
1691 filesuffix
= ".crt";
1692 if(!(configpath
&& configpath
[0])){
1693 #ifdef APPLEKEYCHAIN
1694 if(which
== Public
&& F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
1695 q_status_message(SM_ORDER
, 3, 3, _("Turn off the Keychain feature above first"));
1698 #endif /* APPLEKEYCHAIN */
1699 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
1703 fs_give((void **) &ps_global
->smime
->publicpath
);
1706 if(!(smime_path(ps_global
->VAR_PUBLICCERT_DIR
, path
, MAXPATH
)
1707 && !IS_REMOTE(path
))){
1708 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
1712 if(can_access(path
, ACCESS_EXISTS
)){
1713 if(our_mkpath(path
, 0700)){
1714 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1719 ps_global
->smime
->publicpath
= cpystr(path
);
1720 configpath
= ps_global
->smime
->publicpath
;
1722 else if(which
== Private
){
1723 leader
= EMAILADDRLEADER
;
1724 contents
= ps_global
->smime
->privatecontent
;
1725 configdir
= ps_global
->VAR_PRIVATEKEY_DIR
;
1726 configpath
= ps_global
->smime
->privatepath
;
1727 filesuffix
= ".key";
1728 if(!(configpath
&& configpath
[0])){
1729 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
1733 fs_give((void **) &ps_global
->smime
->privatepath
);
1736 if(!(smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, path
, MAXPATH
)
1737 && !IS_REMOTE(path
))){
1738 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
1742 if(can_access(path
, ACCESS_EXISTS
)){
1743 if(our_mkpath(path
, 0700)){
1744 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1749 ps_global
->smime
->privatepath
= cpystr(path
);
1750 configpath
= ps_global
->smime
->privatepath
;
1752 else if(which
== CACert
){
1753 leader
= CACERTSTORELEADER
;
1754 contents
= ps_global
->smime
->cacontent
;
1755 configdir
= ps_global
->VAR_CACERT_DIR
;
1756 configpath
= ps_global
->smime
->capath
;
1757 filesuffix
= ".crt";
1758 if(!(configpath
&& configpath
[0])){
1759 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
1763 fs_give((void **) &ps_global
->smime
->capath
);
1766 if(!(smime_path(ps_global
->VAR_CACERT_DIR
, path
, MAXPATH
)
1767 && !IS_REMOTE(path
))){
1768 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
1772 if(can_access(path
, ACCESS_EXISTS
)){
1773 if(our_mkpath(path
, 0700)){
1774 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1779 ps_global
->smime
->capath
= cpystr(path
);
1780 configpath
= ps_global
->smime
->capath
;
1783 if(!(configdir
&& configdir
[0])){
1784 q_status_message(SM_ORDER
, 3, 3, _("Directory not defined"));
1788 if(!(configpath
&& configpath
[0])){
1789 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
1793 if(!(filesuffix
&& strlen(filesuffix
) == 4)){
1798 if(contents
&& *contents
){
1799 for(p
= contents
; *p
!= '\0';){
1802 while(*p
&& *p
!= '\n')
1811 if(strncmp(leader
, line
, strlen(leader
)) == 0){
1812 name
= line
+ strlen(leader
);
1814 if(strncmp("-----BEGIN", certtext
, strlen("-----BEGIN")) == 0){
1815 if((q
= strstr(certtext
, leader
)) != NULL
){
1818 else{ /* end of file */
1819 q
= certtext
+ strlen(certtext
);
1823 strncpy(buf
, name
, sizeof(buf
)-5);
1824 buf
[sizeof(buf
)-5] = '\0';
1825 strncat(buf
, filesuffix
, 5);
1826 build_path(file
, configpath
, buf
, sizeof(file
));
1828 in
= BIO_new_mem_buf(certtext
, q
-certtext
);
1830 tempfile
= tempfile_in_same_dir(file
, "az", NULL
);
1833 out
= BIO_new_file(tempfile
, "w");
1836 while((len
= BIO_read(in
, iobuf
, sizeof(iobuf
))) > 0)
1837 BIO_write(out
, iobuf
, len
);
1841 if(rename_file(tempfile
, file
) < 0){
1842 q_status_message2(SM_ORDER
, 3, 3,
1843 _("Can't rename %s to %s"),
1848 fs_give((void **) &tempfile
);
1865 #ifdef APPLEKEYCHAIN
1868 copy_publiccert_container_to_keychain(void)
1870 /* NOT IMPLEMNTED */
1875 copy_publiccert_keychain_to_container(void)
1877 /* NOT IMPLEMNTED */
1881 #endif /* APPLEKEYCHAIN */
1885 * Get a pointer to a string describing the most recent OpenSSL error.
1886 * It's statically allocated, so don't change or attempt to free it.
1889 openssl_error_string(void)
1892 const char *data
= NULL
;
1895 errn
= ERR_peek_error_line_data(NULL
, NULL
, &data
, NULL
);
1896 errs
= (char*) ERR_reason_error_string(errn
);
1903 return "unknown error";
1907 /* Return true if the body looks like a PKCS7 object */
1909 is_pkcs7_body(BODY
*body
)
1913 result
= body
->type
==TYPEAPPLICATION
&&
1915 (strucmp(body
->subtype
,"pkcs7-mime")==0 ||
1916 strucmp(body
->subtype
,"x-pkcs7-mime")==0 ||
1917 strucmp(body
->subtype
,"pkcs7-signature")==0 ||
1918 strucmp(body
->subtype
,"x-pkcs7-signature")==0);
1926 * Somewhat useful debug utility to dump the contents of a BIO to a file.
1927 * Note that a memory BIO will have its contents eliminated after they
1928 * are read so this will break the next step.
1931 dump_bio_to_file(BIO
*in
, char *filename
)
1937 out
= BIO_new_file(filename
, "w");
1940 if(BIO_method_type(in
) != BIO_TYPE_MEM
)
1943 while((len
= BIO_read(in
, iobuf
, sizeof(iobuf
))) > 0)
1944 BIO_write(out
, iobuf
, len
);
1955 * Recursively stash a pointer to the decrypted data in our
1956 * manufactured body.
1957 * parameters: type: call of type 1, save the base and header for multipart messages
1958 call of type 0, do not save the base and header for multipart messages
1961 create_local_cache(char *h
, char *base
, BODY
*b
, int type
)
1963 if(b
->type
==TYPEMULTIPART
){
1967 cpytxt(&b
->mime
.text
, h
+b
->mime
.offset
, b
->mime
.text
.size
);
1968 cpytxt(&b
->contents
.text
, base
+ b
->contents
.offset
, b
->size
.bytes
);
1969 } else if(type
== 0){
1971 * We don't really want to copy the real body contents. It shouldn't be
1972 * used, and in the case of a message with attachments, we'll be
1973 * duplicating the files multiple times.
1975 cpytxt(&b
->contents
.text
, "BODY UNAVAILABLE", 16);
1977 for(p
=b
->nested
.part
; p
; p
=p
->next
)
1978 create_local_cache(h
, base
, (BODY
*) p
, type
);
1982 cpytxt(&b
->mime
.text
, h
+b
->mime
.offset
, b
->mime
.text
.size
);
1983 cpytxt(&b
->contents
.text
, base
+ b
->contents
.offset
, b
->size
.bytes
);
1989 rfc822_output_func(void *b
, char *string
)
1991 BIO
*bio
= (BIO
*) b
;
1993 return(string
? *string
? (BIO_puts(bio
, string
) > 0 ? 1L : 0L)
1994 : (BIO_puts(bio
, string
) >= 0 ? 1L : 0L)
2000 * Attempt to load the private key for the given PERSONAL_CERT.
2001 * This sets the appropriate passphrase globals in order to
2002 * interact with the user correctly.
2005 load_private_key(PERSONAL_CERT
*pcert
)
2009 /* Try empty password by default */
2010 char *password
= "";
2013 && (ps_global
->smime
->need_passphrase
2014 || ps_global
->smime
->entered_passphrase
)){
2015 /* We've already been in here and discovered we need a different password */
2017 if(ps_global
->smime
->entered_passphrase
)
2018 password
= (char *) ps_global
->smime
->passphrase
; /* already entered */
2025 if(!(pcert
->key
= load_key(pcert
, password
))){
2026 long err
= ERR_get_error();
2028 /* Couldn't load key... */
2030 if(ps_global
->smime
&& ps_global
->smime
->entered_passphrase
){
2032 /* The user got the password wrong maybe? */
2034 if((ERR_GET_LIB(err
)==ERR_LIB_EVP
&& ERR_GET_REASON(err
)==EVP_R_BAD_DECRYPT
) ||
2035 (ERR_GET_LIB(err
)==ERR_LIB_PEM
&& ERR_GET_REASON(err
)==PEM_R_BAD_DECRYPT
))
2036 q_status_message(SM_ORDER
| SM_DING
, 4, 4, _("Incorrect passphrase"));
2038 q_status_message1(SM_ORDER
, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2040 /* This passphrase is no good; forget it */
2041 ps_global
->smime
->entered_passphrase
= 0;
2044 if(ps_global
->smime
){
2045 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2046 ps_global
->smime
->need_passphrase
= 1;
2047 if(ps_global
->smime
->passphrase_emailaddr
){
2049 for(i
= 0; ps_global
->smime
->passphrase_emailaddr
[i
] != NULL
; i
++)
2050 fs_give((void **)&ps_global
->smime
->passphrase_emailaddr
[i
]);
2051 fs_give((void **) ps_global
->smime
->passphrase_emailaddr
);
2054 ps_global
->smime
->passphrase_emailaddr
= get_x509_subject_email(pcert
->cert
);
2060 /* This key will be cached, so we won't be called again */
2061 if(ps_global
->smime
){
2062 ps_global
->smime
->entered_passphrase
= 0;
2063 ps_global
->smime
->need_passphrase
= 0;
2075 setup_pkcs7_body_for_signature(BODY
*b
, char *description
, char *type
, char *filename
, char *smime_type
)
2077 b
->type
= TYPEAPPLICATION
;
2078 b
->subtype
= cpystr(type
);
2079 b
->encoding
= ENCBINARY
;
2080 b
->description
= cpystr(description
);
2082 b
->disposition
.type
= cpystr("attachment");
2083 set_parameter(&b
->disposition
.parameter
, "filename", filename
);
2085 set_parameter(&b
->parameter
, "name", filename
);
2086 if(smime_type
&& *smime_type
)
2087 set_parameter(&b
->parameter
, "smime-type", smime_type
);
2092 * Look for a personal certificate matching the
2096 match_personal_cert_to_email(ADDRESS
*a
)
2098 PERSONAL_CERT
*pcert
= NULL
;
2103 if(!a
|| !a
->mailbox
|| !a
->host
)
2106 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2108 if(ps_global
->smime
){
2109 for(pcert
=(PERSONAL_CERT
*) ps_global
->smime
->personal_certs
;
2116 email
= get_x509_subject_email(pcert
->cert
);
2120 for(i
= 0; email
[i
] && strucmp(email
[i
], buf
) != 0; i
++);
2121 if(email
[i
] != NULL
) done
++;
2122 for(i
= 0; email
[i
] != NULL
; i
++)
2123 fs_give((void **)&email
[i
]);
2124 fs_give((void **)email
);
2137 * Look for a personal certificate matching the from
2138 * (or reply_to? in the given envelope)
2141 match_personal_cert(ENVELOPE
*env
)
2143 PERSONAL_CERT
*pcert
;
2145 pcert
= match_personal_cert_to_email(env
->reply_to
);
2147 pcert
= match_personal_cert_to_email(env
->from
);
2154 * Flatten the given body into its MIME representation.
2155 * Return the result in a BIO.
2158 body_to_bio(BODY
*body
)
2163 bio
= BIO_new(BIO_s_mem());
2167 pine_encode_body(body
); /* this attaches random boundary strings to multiparts */
2168 pine_write_body_header(body
, rfc822_output_func
, bio
);
2169 pine_rfc822_output_body(body
, rfc822_output_func
, bio
);
2172 * Now need to truncate by two characters since the above
2175 if((len
=BIO_ctrl_pending(bio
)) > 1){
2176 BUF_MEM
*biobuf
= NULL
;
2178 BIO_get_mem_ptr(bio
, &biobuf
);
2180 BUF_MEM_grow(biobuf
, len
-2); /* remove CRLF */
2189 bio_from_store(STORE_S
*store
)
2193 if(store
&& store
->src
== BioType
&& store
->txt
){
2194 ret
= (BIO
*) store
->txt
;
2201 * Encrypt file; given a path (char *) fp, replace the file
2202 * by an encrypted version of it. If (char *) text is not null, then
2203 * replace the text of (char *) fp by the encrypted version of (char *) text.
2204 * certpath is the FULL path to the file containing the certificate used for
2208 encrypt_file(char *fp
, char *text
, PERSONAL_CERT
*pc
)
2210 const EVP_CIPHER
*cipher
= NULL
;
2211 STACK_OF(X509
) *encerts
= NULL
;
2219 cipher
= EVP_aes_256_cbc();
2220 encerts
= sk_X509_new_null();
2222 sk_X509_push(encerts
, X509_dup(pc
->cert
));
2225 if((out
= BIO_new(BIO_s_mem())) == NULL
)
2227 (void) BIO_reset(out
);
2228 BIO_puts(out
, text
);
2231 if(!(out
= BIO_new_file(fp
, "rb")))
2234 BIO_read_filename(out
, fp
);
2237 if((p7
= PKCS7_encrypt(encerts
, out
, cipher
, 0)) == NULL
)
2239 BIO_set_close(out
, BIO_CLOSE
);
2241 if(!(out
= BIO_new_file(fp
, "w")))
2244 rv
= PEM_write_bio_PKCS7(out
, p7
);
2251 sk_X509_pop_free(encerts
, X509_free
);
2257 * Encrypt a message on the way out. Called from call_mailer in send.c
2258 * The body may be reallocated.
2261 encrypt_outgoing_message(METAENV
*header
, BODY
**bodyP
)
2266 const EVP_CIPHER
*cipher
= NULL
;
2267 STACK_OF(X509
) *encerts
= NULL
;
2268 STORE_S
*outs
= NULL
;
2271 BODY
*body
= *bodyP
;
2272 BODY
*newBody
= NULL
;
2277 dprint((9, "encrypt_outgoing_message()"));
2280 cipher
= EVP_aes_256_cbc();
2282 encerts
= sk_X509_new_null();
2284 /* Look for a certificate for each of the recipients */
2285 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
2286 if(pf
->type
== Address
&& pf
->rcptto
&& pf
->addr
&& *pf
->addr
){
2287 for(a
=*pf
->addr
; a
; a
=a
->next
){
2288 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2290 if((cert
= get_cert_for(buf
, Public
)) != NULL
)
2291 sk_X509_push(encerts
,cert
);
2293 q_status_message2(SM_ORDER
, 1, 1,
2294 _("Unable to find certificate for <%s@%s>"),
2295 a
->mailbox
, a
->host
);
2301 /* add the sender's certificate so that they can decrypt the message too */
2302 for(a
=header
->env
->from
; a
; a
= a
->next
){
2303 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2305 if((cert
= get_cert_for(buf
, Public
)) != NULL
2306 && sk_X509_find(encerts
, cert
) == -1)
2307 sk_X509_push(encerts
,cert
);
2310 in
= body_to_bio(body
);
2312 p7
= PKCS7_encrypt(encerts
, in
, cipher
, 0);
2314 outs
= so_get(BioType
, NULL
, EDIT_ACCESS
);
2315 out
= bio_from_store(outs
);
2317 i2d_PKCS7_bio(out
, p7
);
2318 (void) BIO_flush(out
);
2320 so_seek(outs
, 0, SEEK_SET
);
2322 newBody
= mail_newbody();
2324 newBody
->type
= TYPEAPPLICATION
;
2325 newBody
->subtype
= cpystr("pkcs7-mime");
2326 newBody
->encoding
= ENCBINARY
;
2328 newBody
->disposition
.type
= cpystr("attachment");
2329 set_parameter(&newBody
->disposition
.parameter
, "filename", "smime.p7m");
2331 newBody
->description
= cpystr("S/MIME Encrypted Message");
2332 set_parameter(&newBody
->parameter
, "smime-type", "enveloped-data");
2333 set_parameter(&newBody
->parameter
, "name", "smime.p7m");
2335 newBody
->contents
.text
.data
= (unsigned char *) outs
;
2345 sk_X509_pop_free(encerts
, X509_free
);
2347 dprint((9, "encrypt_outgoing_message returns %d", result
));
2353 Get (and decode) the body of the given section of msg
2356 get_part_contents(long msgno
, const char *section
)
2360 STORE_S
*store
= NULL
;
2363 store
= so_get(CharStar
, NULL
, EDIT_ACCESS
);
2365 gf_set_so_writec(&pc
,store
);
2367 err
= detach(ps_global
->mail_stream
, msgno
, (char*) section
, 0L, &len
, pc
, NULL
, 0L);
2369 gf_clear_so_writec(store
);
2371 so_seek(store
, 0, SEEK_SET
);
2382 get_pkcs7_from_part(long msgno
,const char *section
)
2384 STORE_S
*store
= NULL
;
2388 store
= get_part_contents(msgno
, section
);
2391 if(store
->src
== CharStar
){
2395 * We're reaching inside the STORE_S structure. We should
2396 * probably have a way to get the length, instead.
2398 len
= (int) (store
->eod
- store
->dp
);
2399 in
= BIO_new_mem_buf(store
->txt
, len
);
2401 else{ /* just copy it */
2404 in
= BIO_new(BIO_s_mem());
2405 (void) BIO_reset(in
);
2407 so_seek(store
, 0L, 0);
2408 while(so_readc(&c
, store
)){
2409 BIO_write(in
, &c
, 1);
2414 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2415 if((p7
=d2i_PKCS7_bio(in
,NULL
)) == NULL
){
2428 int same_cert(X509
*x
, X509
*cert
)
2430 char bufcert
[256], bufx
[256];
2433 get_fingerprint(cert
, EVP_md5(), bufcert
, sizeof(bufcert
));
2434 get_fingerprint(x
, EVP_md5(), bufx
, sizeof(bufx
));
2435 if(strcmp(bufx
, bufcert
) == 0)
2442 /* extract and save certificates from a PKCS7 package. The ctype variable
2443 * tells us if we want to extract it to a public/ or a ca/ directory. The
2444 * later makes sense only for recoverable errors (errors that can be fixed
2445 * by saving to the ca/ directory before we verify the signature).
2447 * 0 - no errors (in public/) no need to try again,
2448 * or validated self signed certificate (in ca/)
2449 * < 0 - certificate error is not recoverable, don't even think about it.
2452 int smime_extract_and_save_cert(PKCS7
*p7
)
2454 STACK_OF(X509
) *signers
;
2459 if((signers
= PKCS7_get0_signers(p7
, NULL
, 0)) == NULL
)
2462 for(i
= 0; i
< sk_X509_num(signers
); i
++){
2463 if((x
= sk_X509_value(signers
,i
)) == NULL
)
2466 if((email
= get_x509_subject_email(x
)) != NULL
){
2467 for(j
= 0; email
[j
] != NULL
; j
++){
2468 if((cert
= get_cert_for(email
[j
], Public
)) == NULL
2469 || same_cert(x
, cert
) == 0)
2470 save_cert_for(email
[j
], x
, Public
);
2472 fs_give((void **) &email
[i
]);
2474 fs_give((void **) email
);
2477 sk_X509_free(signers
);
2483 * Try to verify a signature.
2485 * p7 - the pkcs7 object to verify
2486 * in - the plain data to verify (NULL if not detached)
2487 * out - BIO to which to write the opaque data
2488 * silent - if non zero, do not print errors, only print success.
2491 do_signature_verify(PKCS7
*p7
, BIO
*in
, BIO
*out
, int silent
)
2493 STACK_OF(X509
) *otherCerts
= NULL
;
2499 if(!silent
) q_status_message(SM_ORDER
| SM_DING
, 2, 2,
2500 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2505 smime_extract_and_save_cert(p7
);
2507 result
= PKCS7_verify(p7
, otherCerts
, s_cert_store
, in
, out
, 0);
2510 q_status_message(SM_ORDER
, 1, 1, _("S/MIME signature verified ok"));
2513 err
= ERR_peek_error_line_data(NULL
, NULL
, &data
, NULL
);
2515 if(out
&& err
==ERR_PACK(ERR_LIB_PKCS7
,PKCS7_F_PKCS7_VERIFY
,PKCS7_R_CERTIFICATE_VERIFY_ERROR
)){
2517 /* Retry verification so we can get the plain text */
2518 /* Might be better to reimplement PKCS7_verify here? */
2520 PKCS7_verify(p7
, otherCerts
, s_cert_store
, in
, out
, PKCS7_NOVERIFY
);
2522 if (!silent
) q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2523 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
2531 free_smime_body_sparep(void **sparep
)
2535 if(sparep
&& *sparep
){
2536 switch(get_smime_sparep_type(*sparep
)){
2537 case P7Type
: PKCS7_free((PKCS7
*) get_smime_sparep_data(*sparep
));
2539 case CharType
: s
= (char *)get_smime_sparep_data(*sparep
);
2540 fs_give((void **) &s
);
2542 case SizedText
: st
= (SIZEDTEXT
*)get_smime_sparep_data(*sparep
);
2543 fs_give((void **) &st
->data
);
2544 fs_give((void **) &st
);
2548 ((SMIME_SPARE_S
*)(*sparep
))->data
= NULL
;
2553 /* return the mime header for this body part. Memory freed by caller */
2555 smime_fetch_mime(BODY
*b
, MAILSTREAM
*stream
, long msgno
, char * section
,
2556 unsigned long *mimelen
)
2561 if(b
->type
== TYPEMULTIPART
)
2562 snprintf(newSec
, sizeof(newSec
), "%s%s1", section
? section
: "", (section
&& *section
) ? "." : "");
2564 strncpy(newSec
, section
, sizeof(newSec
));
2565 newSec
[sizeof(newSec
)-1] = '\0';
2566 rv
= mail_fetch_mime(stream
, msgno
, newSec
, mimelen
, 0);
2567 return rv
? cpystr(rv
) : NULL
;
2572 smime_write_body_header(BODY
*b
, MAILSTREAM
*stream
, long msgno
, char *section
, soutr_t f
, void *s
)
2577 if(b
->nested
.part
== NULL
)
2580 if(b
->nested
.part
->body
.type
== TYPEMULTIPART
)
2581 snprintf(newSec
, sizeof(newSec
), "%s%s1", section
? section
: "", (section
&& *section
) ? "." : "");
2583 strncpy(newSec
, section
, sizeof(newSec
));
2584 newSec
[sizeof(newSec
)-1] = '\0';
2585 rv
= mail_fetch_mime(stream
, msgno
, newSec
, NULL
, 0);
2591 write_signed_body(BODY
*b
, MAILSTREAM
*stream
, long msgno
, char *section
, BIO
*in
)
2594 smime_write_body_header(b
, stream
, msgno
, section
, rfc822_output_func
, in
);
2601 /* Big comment, explaining the mess that exists out there, and how we deal
2602 with it, and also how we solve the problems that are created this way.
2604 When Alpine sends a message, it constructs that message, computes the
2605 signature, but then it forgets the message it signed and reconstructs it
2606 again. Since it signs a message containing a notice about "mime aware
2607 tools", but it does not send that we do not include that in the part
2608 that is signed, and that takes care of much of the problems.
2610 Another problem is what is received from the servers. All servers tested
2611 seem to transmit the message that was signed intact and Alpine can check
2612 the signature correctly. That is not a problem. The problem arises when
2613 the message includes attachments. In this case different servers send
2614 different things, so it will be up to us to figure out what is the text
2615 that was actually signed. Confused? here is the story:
2617 When a message containing and attachment is sent by Alpine, UW-IMAP,
2618 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2619 that was sent by Alpine, but GMX.com, Exchange, and probably other
2620 servers add a trailing \r\n in the message, so when validating the
2621 signature, these messages will not validate. There are several things
2624 1. Add a trailing \r\n to any message that contains attachments, sign that
2625 and send that. In this way, all messages will validate with all
2628 2. Compatibility mode: If a message has an attachment, contains a trailing
2629 \r\n and does not validate (sent by an earlier version of Alpine),
2630 remove the trailing \r\n and try to revalidate again.
2632 3. We do not add \r\n to validate a message that we sent, because that
2633 would only work in Alpine, and not in any other client. That would
2634 not be a good thing to do.
2638 Now we have to deal with encrypted and signed messages. The problem is
2639 that c-client makes all its pointers point to "on disk" content, but
2640 since we decrypted the data earlier, we have to make sure of two things.
2641 One is that we saved that data (so we do not have to decrypt it again)
2642 and second that we can use it.
2644 In order to save the data we use create_local_cache, so that we do not
2645 have to redecrypt the message. Once this is saved, c-client functions will
2646 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2650 When we are trying to verify messages with detached signatures, some
2651 imap servers send incorrect information in the mail_fetch_mime call. By
2652 incorrect I mean that this is not fetched directly from the message, but
2653 it is read from the message, processed, and then the processed part is
2654 sent to us, so this text might not agree with what is in the message,
2655 and so the validation of the signature might fail. However, the good
2656 news is that the message validates if saved to a local folder. This
2657 means that if normal validation does not work we can make it work by
2658 saving the message locally and validating that. This is implemented
2659 below, and causes delay in the display of the message. I am considering
2660 at this time not to do this automatically, but wait for the user to tell
2661 us to do it for them by means of a command available in the
2662 mail_view_screen. This might help in other situations, where a message
2663 is supposed to have an attachment, but it can not be seen in the
2664 processed text. Nevertheless, at this time, this is automatic, and is
2665 causing a delay in the processing of the message, but it is validating
2666 correctly all messages.
2670 * Given a multipart body of type multipart/signed, attempt to verify it.
2671 * Returns non-zero if the body was changed.
2674 do_detached_signature_verify(BODY
*b
, long msgno
, char *section
)
2679 int result
, modified_the_body
= 0;
2680 unsigned long mimelen
, bodylen
;
2681 char newSec
[100], *mimetext
, *bodytext
;
2685 dprint((9, "do_detached_signature_verify(msgno=%ld type=%d subtype=%s section=%s)", msgno
, b
->type
, b
->subtype
? b
->subtype
: "NULL", (section
&& *section
) ? section
: (section
!= NULL
) ? "Top" : "NULL"));
2689 /* if it was signed and then encrypted, use the decrypted text
2690 * to check the validity of the signature
2693 if(get_smime_sparep_type(b
->sparep
) == SizedText
){
2694 /* bodytext includes mimetext */
2695 st
= (SIZEDTEXT
*) get_smime_sparep_data(b
->sparep
);
2696 bodytext
= st
->data
;
2703 snprintf(newSec
, sizeof(newSec
), "%s%s1", section
? section
: "", (section
&& *section
) ? "." : "");
2704 mimetext
= mail_fetch_mime(ps_global
->mail_stream
, msgno
, (char*) newSec
, &mimelen
, 0);
2706 bodytext
= mail_fetch_body (ps_global
->mail_stream
, msgno
, (char*) newSec
, &bodylen
, 0);
2708 if(mimetext
== NULL
|| bodytext
== NULL
)
2709 return modified_the_body
;
2712 snprintf(newSec
, sizeof(newSec
), "%s%s2", section
? section
: "", (section
&& *section
) ? "." : "");
2714 if((p7
= get_pkcs7_from_part(msgno
, newSec
)) == NULL
2715 || (in
= BIO_new(BIO_s_mem())) == NULL
)
2716 return modified_the_body
;
2718 (void) BIO_reset(in
);
2719 if(mimetext
!= NULL
)
2720 BIO_write(in
, mimetext
, mimelen
);
2721 BIO_write(in
, bodytext
, bodylen
);
2723 /* Try compatibility with the past and check if this message
2724 * validates when we remove the last two characters. Silence
2725 * any failures first.
2727 if(((result
= do_signature_verify(p7
, in
, NULL
, 1)) == 0)
2729 && (strncmp(bodytext
+bodylen
-2,"\r\n", 2) == 0)){
2730 BUF_MEM
*biobuf
= NULL
;
2732 BIO_get_mem_ptr(in
, &biobuf
);
2734 BUF_MEM_grow(biobuf
, mimelen
+ bodylen
- 2);
2736 /* test one more time in case this is a remote connection and the
2737 * server massages the message to the point that it sends bogus
2738 * information. In this case, we fetch the message and we process
2741 if((result
= do_signature_verify(p7
, in
, NULL
, 1)) == 0
2742 && mimelen
> 0 /* do not do this for encrypted messages */
2743 && IS_REMOTE(ps_global
->mail_stream
->mailbox
)){
2745 unsigned long hlen
, tlen
;
2749 if((in
= BIO_new(BIO_s_mem())) != NULL
2750 && (fetch
= mail_fetch_header(ps_global
->mail_stream
, msgno
, NULL
,
2751 NULL
, &hlen
, FT_PEEK
)) != NULL
2752 && (msg_so
= so_get(CharStar
, NULL
, WRITE_ACCESS
)) != NULL
2753 && so_nputs(msg_so
, fetch
, (long) hlen
)
2754 && (fetch
= pine_mail_fetch_text(ps_global
->mail_stream
, msgno
, NULL
,
2755 &tlen
, FT_PEEK
)) != NULL
2756 && so_nputs(msg_so
, fetch
, tlen
)){
2758 char *h
= (char *) so_text(msg_so
);
2759 char *bstart
= strstr(h
, "\r\n\r\n");
2764 INIT(&bs
, mail_string
, bstart
, tlen
);
2765 rfc822_parse_msg_full(&env
, &body
, h
, bstart
-h
, &bs
, BADHOST
, 0, 0);
2766 mail_free_envelope(&env
);
2768 mail_free_body_part(&b
->nested
.part
);
2769 b
->nested
.part
= body
->nested
.part
;
2770 create_local_cache(bstart
, bstart
, &b
->nested
.part
->body
, 1);
2771 modified_the_body
= 1;
2773 snprintf(newSec
, sizeof(newSec
), "%s%s1", section
? section
: "", (section
&& *section
) ? "." : "");
2775 mimetext
= mail_fetch_mime(ps_global
->mail_stream
, msgno
, (char*) newSec
, &mimelen
, 0);
2778 bodytext
= mail_fetch_body (ps_global
->mail_stream
, msgno
, (char*) newSec
, &bodylen
, 0);
2780 if (mimetext
== NULL
|| bodytext
== NULL
)
2781 return modified_the_body
;
2783 snprintf(newSec
, sizeof(newSec
), "%s%s2", section
? section
: "", (section
&& *section
) ? "." : "");
2785 if((p7
= get_pkcs7_from_part(msgno
, newSec
)) == NULL
)
2786 return modified_the_body
;
2788 (void) BIO_reset(in
);
2789 BIO_write(in
, mimetext
, mimelen
);
2790 BIO_write(in
, bodytext
, bodylen
);
2793 if(((result
= do_signature_verify(p7
, in
, NULL
, 1)) == 0)
2795 && (strncmp(bodytext
+bodylen
-2,"\r\n", 2) == 0)){
2796 BUF_MEM
*biobuf
= NULL
;
2798 BIO_get_mem_ptr(in
, &biobuf
);
2800 BUF_MEM_grow(biobuf
, mimelen
+ bodylen
- 2);
2801 result
= do_signature_verify(p7
, in
, NULL
, 0);
2809 fs_give((void**) &b
->subtype
);
2811 b
->subtype
= cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE
);
2812 b
->encoding
= ENC8BIT
;
2815 fs_give ((void**) &b
->description
);
2817 what_we_did
= result
? _("This message was cryptographically signed.") :
2818 _("This message was cryptographically signed but the signature could not be verified.");
2820 b
->description
= cpystr(what_we_did
);
2822 b
->sparep
= create_smime_sparep(P7Type
, p7
);
2826 /* p is signed plaintext */
2828 mail_free_body_part(&p
->next
); /* hide the pkcs7 from the viewer */
2830 modified_the_body
= 1;
2832 return modified_the_body
;
2837 find_certificate_matching_recip_info(PKCS7_RECIP_INFO
*ri
)
2839 PERSONAL_CERT
*x
= NULL
;
2841 if(ps_global
->smime
){
2842 for(x
= (PERSONAL_CERT
*) ps_global
->smime
->personal_certs
; x
; x
=x
->next
){
2847 if(!X509_NAME_cmp(ri
->issuer_and_serial
->issuer
,mine
->cert_info
->issuer
) &&
2848 !ASN1_INTEGER_cmp(ri
->issuer_and_serial
->serial
,mine
->cert_info
->serialNumber
)){
2858 static PERSONAL_CERT
*
2859 find_certificate_matching_pkcs7(PKCS7
*p7
)
2862 STACK_OF(PKCS7_RECIP_INFO
) *recips
;
2863 PERSONAL_CERT
*x
= NULL
;
2865 recips
= p7
->d
.enveloped
->recipientinfo
;
2867 for(i
=0; i
<sk_PKCS7_RECIP_INFO_num(recips
); i
++){
2868 PKCS7_RECIP_INFO
*ri
;
2870 ri
= sk_PKCS7_RECIP_INFO_value(recips
, i
);
2872 if((x
=find_certificate_matching_recip_info(ri
))!=0){
2880 /* decrypt an encrypted file.
2881 Args: fp - the path to the encrypted file.
2882 rv - a code that tells the caller what happened inside the function
2883 pcert - a personal certificate that was used to encrypt this file
2884 Returns the decoded text allocated in a char *, whose memory must be
2889 decrypt_file(char *fp
, int *rv
, PERSONAL_CERT
*pc
)
2893 BIO
*in
= NULL
, *out
= NULL
;
2895 long unsigned int len
;
2898 if(pc
== NULL
|| (text
= read_file(fp
, 0)) == NULL
|| *text
== '\0')
2901 tmp
= fs_get(strlen(text
) + (strlen(text
) << 6) + 1);
2902 for(j
= 0, i
= strlen("-----BEGIN PKCS7-----") + 1; text
[i
] != '\0'
2903 && text
[i
] != '-'; j
++, i
++)
2907 ret
= rfc822_base64((unsigned char *)tmp
, strlen(tmp
), &len
);
2909 if((in
= BIO_new_mem_buf((char *)ret
, len
)) != NULL
){
2910 p7
= d2i_PKCS7_bio(in
, NULL
);
2914 if (text
) fs_give((void **)&text
);
2915 if (ret
) fs_give((void **)&ret
);
2917 if (rv
) *rv
= pc
->key
== NULL
? -1 : 1;
2919 out
= BIO_new(BIO_s_mem());
2920 (void) BIO_reset(out
);
2922 i
= PKCS7_decrypt(p7
, pc
->key
, pc
->cert
, out
, 0);
2925 q_status_message1(SM_ORDER
, 1, 1, _("Error decrypting: %s"),
2926 (char*) openssl_error_string());
2930 BIO_get_mem_data(out
, &tmp
);
2942 * Try to decode (decrypt or verify a signature) a PKCS7 body
2943 * Returns non-zero if something was changed.
2946 do_decoding(BODY
*b
, long msgno
, const char *section
)
2948 int modified_the_body
= 0;
2952 EVP_PKEY
*key
= NULL
;
2953 PERSONAL_CERT
*pcert
= NULL
;
2954 char *what_we_did
= "";
2957 dprint((9, "do_decoding(msgno=%ld type=%d subtype=%s section=%s)", msgno
, b
->type
, b
->subtype
? b
->subtype
: "NULL", (section
&& *section
) ? section
: (section
!= NULL
) ? "Top" : "NULL"));
2962 * Extract binary data from part to an in-memory store
2966 if(get_smime_sparep_type(b
->sparep
) == P7Type
)
2967 p7
= (PKCS7
*) get_smime_sparep_data(b
->sparep
);
2970 p7
= get_pkcs7_from_part(msgno
, section
&& *section
? section
: "1");
2972 q_status_message1(SM_ORDER
, 2, 2, "Couldn't load PKCS7 object: %s",
2973 (char*) openssl_error_string());
2978 * Save the PKCS7 object for later dealings by the user interface.
2979 * It will be cleaned up when the body is garbage collected.
2981 b
->sparep
= create_smime_sparep(P7Type
, p7
);
2984 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7
), PKCS7_type_is_enveloped(p7
)));
2986 if(PKCS7_type_is_signed(p7
)){
2989 out
= BIO_new(BIO_s_mem());
2990 (void) BIO_reset(out
);
2991 BIO_puts(out
, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
2993 sigok
= do_signature_verify(p7
, NULL
, out
, 0);
2995 what_we_did
= sigok
? _("This message was cryptographically signed.") :
2996 _("This message was cryptographically signed but the signature could not be verified.");
2998 /* make sure it's null terminated */
2999 BIO_write(out
, null
, 1);
3001 else if(!PKCS7_type_is_enveloped(p7
)){
3002 q_status_message(SM_ORDER
, 1, 1, "PKCS7 object not recognised.");
3005 else{ /* It *is* enveloped */
3008 what_we_did
= _("This message was encrypted.");
3010 /* now need to find a cert that can decrypt this */
3011 pcert
= find_certificate_matching_pkcs7(p7
);
3014 q_status_message(SM_ORDER
, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3018 recip
= pcert
->cert
;
3020 if(!load_private_key(pcert
)
3022 && ps_global
->smime
->need_passphrase
3023 && !ps_global
->smime
->already_auto_asked
){
3024 /* Couldn't load key with blank password, ask user */
3025 ps_global
->smime
->already_auto_asked
= 1;
3026 if(pith_opt_smime_get_passphrase
){
3027 (*pith_opt_smime_get_passphrase
)();
3028 load_private_key(pcert
);
3036 out
= BIO_new(BIO_s_mem());
3037 (void) BIO_reset(out
);
3038 BIO_puts(out
, "MIME-Version: 1.0\r\n");
3040 decrypt_result
= PKCS7_decrypt(p7
, key
, recip
, out
, 0);
3042 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
3043 forget_private_keys();
3045 if(!decrypt_result
){
3046 q_status_message1(SM_ORDER
, 1, 1, _("Error decrypting: %s"),
3047 (char*) openssl_error_string());
3050 BIO_write(out
, null
, 1);
3054 * We've now produced a flattened MIME object in BIO out.
3055 * It needs to be turned back into a BODY.
3064 BUF_MEM
*bptr
= NULL
;
3066 BIO_get_mem_ptr(out
, &bptr
);
3070 /* look for start of body */
3071 bstart
= strstr(h
, "\r\n\r\n");
3074 q_status_message(SM_ORDER
, 3, 3, _("Encrypted data couldn't be parsed."));
3078 bstart
+= 4; /* skip over CRLF*2 */
3080 INIT(&s
, mail_string
, bstart
, strlen(bstart
));
3081 rfc822_parse_msg_full(&env
, &body
, h
, bstart
-h
-2, &s
, BADHOST
, 0, 0);
3082 mail_free_envelope(&env
); /* Don't care about this */
3084 if(body
->type
== TYPEMULTIPART
3085 && !strucmp(body
->subtype
, "SIGNED")){
3086 char *cookie
= NULL
;
3088 for (param
= body
->parameter
; param
&& !cookie
; param
= param
->next
)
3089 if (!strucmp (param
->attribute
,"BOUNDARY")) cookie
= param
->value
;
3090 st
= fs_get(sizeof(SIZEDTEXT
));
3091 st
->data
= (void *) cpystr(bstart
+ strlen(cookie
)+4); /* 4 = strlen("--\r\n") */
3092 st
->size
= body
->nested
.part
->next
->body
.mime
.offset
- 2*(strlen(cookie
) + 4);
3093 body
->sparep
= create_smime_sparep(SizedText
, (void *)st
);
3095 body
->mime
.offset
= 0;
3096 body
->mime
.text
.size
= 0;
3099 * Now convert original body (application/pkcs7-mime)
3100 * to a multipart body with one sub-part (the decrypted body).
3101 * Note that the sub-part may also be multipart!
3104 b
->type
= TYPEMULTIPART
;
3106 fs_give((void**) &b
->subtype
);
3109 * This subtype is used in mailview.c to annotate the display of
3110 * encrypted or signed messages. We know for sure then that it's a PKCS7
3111 * part because the sparep field is set to the PKCS7 object (see above).
3113 b
->subtype
= cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE
);
3114 b
->encoding
= ENC8BIT
;
3117 fs_give((void**) &b
->description
);
3119 b
->description
= cpystr(what_we_did
);
3121 if(b
->disposition
.type
)
3122 fs_give((void **) &b
->disposition
.type
);
3124 if(b
->contents
.text
.data
)
3125 fs_give((void **) &b
->contents
.text
.data
);
3128 mail_free_body_parameter(&b
->parameter
);
3130 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3131 b
->nested
.part
= fs_get(sizeof(PART
));
3132 b
->nested
.part
->body
= *body
;
3133 b
->nested
.part
->next
= NULL
;
3135 fs_give((void**) &body
);
3138 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3139 * the decrypted data. Otherwise, it'll try to load it from the original
3142 create_local_cache(bstart
-b
->nested
.part
->body
.mime
.offset
, bstart
, &b
->nested
.part
->body
, 0);
3144 modified_the_body
= 1;
3152 return modified_the_body
;
3157 * Recursively handle PKCS7 bodies in our message.
3159 * Returns non-zero if some fiddling was done.
3162 do_fiddle_smime_message(BODY
*b
, long msgno
, char *section
)
3164 int modified_the_body
= 0;
3169 dprint((9, "do_fiddle_smime_message(msgno=%ld type=%d subtype=%s section=%s)", msgno
, b
->type
, b
->subtype
? b
->subtype
: "NULL", (section
&& *section
) ? section
: (section
!= NULL
) ? "Top" : "NULL"));
3171 if(is_pkcs7_body(b
)){
3173 if(do_decoding(b
, msgno
, section
)){
3175 * b should now be a multipart message:
3176 * fiddle it too in case it's been multiply-encrypted!
3180 modified_the_body
= 1;
3184 if(b
->type
==TYPEMULTIPART
|| MIME_MSG(b
->type
, b
->subtype
)){
3190 if(MIME_MULT_SIGNED(b
->type
, b
->subtype
)){
3194 * Ahah. We have a multipart signed entity.
3197 * part 1 (signed thing)
3198 * part 2 (the pkcs7 signature)
3200 * We're going to convert that to
3202 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3203 * part 1 (signed thing)
3204 * part 2 has been freed
3206 * We also extract the signature from part 2 and save it
3207 * in the multipart body->sparep, and we add a description
3208 * in the multipart body->description.
3211 * The results of a decrypted message will be similar. It
3214 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3215 * part 1 (decrypted thing)
3218 modified_the_body
+= do_detached_signature_verify(b
, msgno
, section
);
3220 else if(MIME_MSG(b
->type
, b
->subtype
)){
3221 modified_the_body
+= do_fiddle_smime_message(b
->nested
.msg
->body
, msgno
, section
);
3225 for(p
=b
->nested
.part
,partNum
=1; p
; p
=p
->next
,partNum
++){
3226 /* Append part number to the section string */
3228 snprintf(newSec
, sizeof(newSec
), "%s%s%d", section
, *section
? "." : "", partNum
);
3230 modified_the_body
+= do_fiddle_smime_message(&p
->body
, msgno
, newSec
);
3235 return modified_the_body
;
3240 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3241 * Returns non-zero if something was changed.
3244 fiddle_smime_message(BODY
*b
, long msgno
)
3246 return do_fiddle_smime_message(b
, msgno
, "");
3250 /********************************************************************************/
3254 * Output a string in a distinctive style
3257 gf_puts_uline(char *txt
, gf_io_t pc
)
3259 pc(TAG_EMBED
); pc(TAG_BOLDON
);
3261 pc(TAG_EMBED
); pc(TAG_BOLDOFF
);
3266 get_chain_for_cert(X509
*cert
, int *error
)
3268 STACK_OF(X509
) *chain
= NULL
;
3269 X509_STORE_CTX
*ctx
;
3271 int rc
; /* return code */
3276 if((ctx
= X509_STORE_CTX_new()) != NULL
){
3277 X509_STORE_set_flags(s_cert_store
, 0);
3278 if(!X509_STORE_CTX_init(ctx
, s_cert_store
, cert
, NULL
))
3279 *error
= X509_STORE_CTX_get_error(ctx
);
3280 else if((chain
= sk_X509_new_null()) != NULL
){
3281 for(x
= cert
; ; x
= xtmp
){
3283 sk_X509_push(chain
, X509_dup(x
));
3284 rc
= X509_STORE_CTX_get1_issuer(&xtmp
, ctx
, x
);
3289 if(!X509_check_issued(xtmp
, xtmp
))
3293 if((*error
&& chain
!= NULL
) || level
== 0){
3294 sk_X509_pop_free(chain
, X509_free
);
3297 X509_STORE_CTX_free(ctx
);
3304 * Sign a message. Called from call_mailer in send.c.
3306 * This takes the header for the outgoing message as well as a pointer
3307 * to the current body (which may be reallocated).
3310 sign_outgoing_message(METAENV
*header
, BODY
**bodyP
, int dont_detach
)
3312 STORE_S
*outs
= NULL
;
3313 BODY
*body
= *bodyP
;
3314 BODY
*newBody
= NULL
;
3317 PERSONAL_CERT
*pcert
;
3321 STACK_OF(X509
) *chain
;
3322 int result
= 0, error
;
3323 int flags
= dont_detach
? 0 : PKCS7_DETACHED
;
3325 dprint((9, "sign_outgoing_message()"));
3329 /* Look for a private key matching the sender address... */
3331 pcert
= match_personal_cert(header
->env
);
3334 q_status_message(SM_ORDER
, 3, 3, _("Couldn't find the certificate needed to sign."));
3338 if(!load_private_key(pcert
) && ps_global
->smime
&& ps_global
->smime
->need_passphrase
){
3339 /* Couldn't load key with blank password, try again */
3340 if(pith_opt_smime_get_passphrase
){
3341 (*pith_opt_smime_get_passphrase
)();
3342 load_private_key(pcert
);
3349 in
= body_to_bio(body
);
3351 chain
= get_chain_for_cert(pcert
->cert
, &error
);
3353 p7
= PKCS7_sign(pcert
->cert
, pcert
->key
, chain
, in
, flags
);
3355 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
3356 forget_private_keys();
3359 sk_X509_pop_free(chain
, X509_free
);
3362 q_status_message(SM_ORDER
, 1, 1, _("Error creating signed object."));
3367 q_status_message(SM_ORDER
, 1, 1, _("Not all certificates needed to verify signature included in signed message"));
3369 outs
= so_get(BioType
, NULL
, EDIT_ACCESS
);
3370 out
= bio_from_store(outs
);
3372 i2d_PKCS7_bio(out
, p7
);
3373 (void) BIO_flush(out
);
3375 so_seek(outs
, 0, SEEK_SET
);
3377 if((flags
&PKCS7_DETACHED
)==0){
3379 /* the simple case: the signed data is in the pkcs7 object */
3381 newBody
= mail_newbody();
3383 setup_pkcs7_body_for_signature(newBody
, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3385 newBody
->contents
.text
.data
= (unsigned char *) outs
;
3394 * We have to create a new body as follows:
3396 * multipart/signed; blah blah blah
3397 * reference to existing body
3402 newBody
= mail_newbody();
3404 newBody
->type
= TYPEMULTIPART
;
3405 newBody
->subtype
= cpystr("signed");
3406 newBody
->encoding
= ENC7BIT
;
3408 set_parameter(&newBody
->parameter
, "protocol", "application/pkcs7-signature");
3409 set_parameter(&newBody
->parameter
, "micalg", "sha1");
3411 p1
= mail_newbody_part();
3412 p2
= mail_newbody_part();
3415 * This is nasty. We're just copying the body in here,
3416 * but since our newBody is freed at the end of call_mailer,
3417 * we mustn't let this body (the original one) be freed twice.
3419 p1
->body
= *body
; /* ARRGH. This is special cased at the end of call_mailer */
3423 setup_pkcs7_body_for_signature(&p2
->body
, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL
);
3424 p2
->body
.contents
.text
.data
= (unsigned char *) outs
;
3426 newBody
->nested
.part
= p1
;
3438 dprint((9, "sign_outgoing_message returns %d", result
));
3444 new_smime_struct(void)
3446 SMIME_STUFF_S
*ret
= NULL
;
3448 ret
= (SMIME_STUFF_S
*) fs_get(sizeof(*ret
));
3449 memset((void *) ret
, 0, sizeof(*ret
));
3450 ret
->publictype
= Nada
;
3457 free_smime_struct(SMIME_STUFF_S
**smime
)
3459 if(smime
&& *smime
){
3460 if((*smime
)->passphrase_emailaddr
){
3462 for(i
= 0; (*smime
)->passphrase_emailaddr
[i
] != NULL
; i
++)
3463 fs_give((void **) &(*smime
)->passphrase_emailaddr
[i
]);
3464 fs_give((void **) (*smime
)->passphrase_emailaddr
);
3467 if((*smime
)->publicpath
)
3468 fs_give((void **) &(*smime
)->publicpath
);
3470 if((*smime
)->publiccertlist
)
3471 free_certlist(&(*smime
)->publiccertlist
);
3473 if((*smime
)->cacertlist
)
3474 free_certlist(&(*smime
)->cacertlist
);
3476 if((*smime
)->privatecertlist
)
3477 free_certlist(&(*smime
)->privatecertlist
);
3479 if((*smime
)->publiccontent
)
3480 fs_give((void **) &(*smime
)->publiccontent
);
3482 if((*smime
)->privatepath
)
3483 fs_give((void **) &(*smime
)->privatepath
);
3485 if((*smime
)->personal_certs
){
3488 pc
= (PERSONAL_CERT
*) (*smime
)->personal_certs
;
3489 free_personal_certs(&pc
);
3490 (*smime
)->personal_certs
= NULL
;
3493 if((*smime
)->privatecontent
)
3494 fs_give((void **) &(*smime
)->privatecontent
);
3496 if((*smime
)->capath
)
3497 fs_give((void **) &(*smime
)->capath
);
3499 if((*smime
)->cacontent
)
3500 fs_give((void **) &(*smime
)->cacontent
);
3502 fs_give((void **) smime
);