2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
16 * This is based on a contribution from Jonathan Paisley
19 * Author: paisleyj@dcs.gla.ac.uk
24 #include "../pith/headers.h"
28 #include "../pith/osdep/canaccess.h"
29 #include "../pith/helptext.h"
30 #include "../pith/store.h"
31 #include "../pith/status.h"
32 #include "../pith/detach.h"
33 #include "../pith/conf.h"
34 #include "../pith/smkeys.h"
35 #include "../pith/smime.h"
36 #include "../pith/mailpart.h"
37 #include "../pith/reply.h"
38 #include "../pith/tempfile.h"
39 #include "../pith/readfile.h"
40 #include "../pith/remote.h"
41 #include "../pith/body.h"
43 #include "../pith/imap.h"
46 #include <openssl/buffer.h>
47 #include <openssl/x509v3.h>
48 #include <openssl/evp.h>
50 /* internal prototypes */
51 static void forget_private_keys(void);
52 static int app_RAND_load_file(const char *file
);
53 static void openssl_extra_randomness(void);
54 static int app_RAND_write_file(const char *file
);
55 static const char *openssl_error_string(void);
56 static int load_private_key(PERSONAL_CERT
*pcert
);
57 static void create_local_cache(char *h
, char *base
, BODY
*b
, int type
);
58 static long rfc822_output_func(void *b
, char *string
);
59 static void setup_pkcs7_body_for_signature(BODY
*b
, char *description
,
60 char *type
, char *filename
, char *smime_type
);
61 static BIO
*body_to_bio(BODY
*body
);
62 static BIO
*bio_from_store(STORE_S
*store
);
63 static STORE_S
*get_part_contents(long msgno
, const char *section
);
64 static PKCS7
*get_pkcs7_from_part(long msgno
, const char *section
);
65 static int do_signature_verify(PKCS7
*p7
, BIO
*in
, BIO
*out
, int silent
);
66 static int do_detached_signature_verify(BODY
*b
, long msgno
, char *section
);
67 static PERSONAL_CERT
*find_certificate_matching_pkcs7(PKCS7
*p7
);
68 static int do_decoding(BODY
*b
, long msgno
, const char *section
);
69 static void free_smime_struct(SMIME_STUFF_S
**smime
);
70 static void setup_storage_locations(void);
71 static int copy_container_to_dir(WhichCerts which
);
72 static int do_fiddle_smime_message(BODY
*b
, long msgno
, char *section
);
73 void setup_privatekey_storage(void);
74 int smime_extract_and_save_cert(PKCS7
*p7
);
75 int same_cert(X509
*, X509
*);
77 int load_key_and_cert(char *pathkeydir
, char *pathcertdir
, char **keyfile
, char **certfile
, EVP_PKEY
**pkey
, X509
**pcert
);
79 EVP_PKEY
*load_pkey_with_prompt(char *fpath
, char *text
, char *prompt
, int *);
80 void smime_remove_trailing_crlf(char **mimetext
, unsigned long *mimelen
, char **bodytext
, unsigned long *bodylen
);
81 void smime_remove_folding_space(char **mimetext
, unsigned long *mimelen
, char **bodytext
, unsigned long *bodylen
);
82 int smime_validate_extra_test(char *mimetext
, unsigned long mimelen
, char *bodytext
, unsigned long bodylen
, PKCS7
*p7
, int nflag
);
84 int (*pith_opt_smime_get_passphrase
)(void);
85 int (*pith_smime_import_certificate
)(char *, char *, char *, size_t);
86 int (*pith_smime_enter_password
)(char *, char *, size_t);
87 int (*pith_smime_confirm_save
)(char *);
89 static X509_STORE
*s_cert_store
;
91 /* State management for randomness functions below */
92 static int seeded
= 0;
96 * This code does not work in Windows, because of the PASSFILE thing, so
97 * I did not try to fix it. If you think it does need to be applied to
98 * the Windows version of alpine, there are more changes that are needed
99 * than fixing this function in this module. E. Chappa 09/28/17.
101 * load key from pathkeydir and cert from pathcertdir. It chooses the first
102 * key/certificate pair that matches. Delete pairs that you do not want used,
103 * if you do not want them selected. All parameters must be non-null.
104 * Memory freed by caller.
106 * -1 : user cancelled load
107 * 0 : load was successful
108 * 1 : there was an error in the loading.
111 load_key_and_cert(char *pathkeydir
, char *pathcertdir
, char **keyfile
,
112 char **certfile
, EVP_PKEY
**pkey
, X509
**pcert
)
114 char buf
[MAXPATH
+1], pathkey
[MAXPATH
+1], prompt
[MAILTMPLEN
];
117 int b
= 0, ret
= 1; /* assume error */
119 if(pathkeydir
== NULL
|| pathcertdir
== NULL
|| keyfile
== NULL
120 || pkey
== NULL
|| certfile
== NULL
|| pcert
== NULL
)
128 if((dirp
= opendir(pathkeydir
)) != NULL
){
129 while(b
== 0 && (d
=readdir(dirp
)) != NULL
){
132 if((ll
=strlen(d
->d_name
)) && ll
> 4){
133 if(!strcmp(d
->d_name
+ll
-4, ".key")){
134 strncpy(buf
, d
->d_name
, sizeof(buf
));
135 buf
[sizeof(buf
)-1] = '\0';
136 build_path(pathkey
, pathkeydir
, buf
, sizeof(pathkey
));
137 buf
[strlen(buf
)-4] = '\0';
138 snprintf(prompt
, sizeof(prompt
),
139 _("Enter password of key <%s> to unlock password file: "), buf
);
140 if((*pkey
= load_pkey_with_prompt(pathkey
, NULL
, prompt
, &ret
)) != NULL
){
141 if(load_cert_for_key(pathcertdir
, *pkey
, certfile
, pcert
)){
143 *keyfile
= cpystr(buf
);
145 EVP_PKEY_free(*pkey
);
147 q_status_message1(SM_ORDER
, 0, 2,
148 _("Cannot find certificate that matches key <%s>. Continuing..."), buf
);
160 /* setup a key and certificate to encrypt and decrypt a password file.
161 * These files will be saved in the .alpine-smime/.pwd directory, but its
162 * location can be setup in the command line with the -pwdcertdir option.
163 * Here are the rules:
165 * Check if the .alpine-smime/.pwd (or -pwdcertdir directory) exists,
166 * if not create it. If we are successful, move to the next step
168 * - If the user has a key/cert pair, in the .alpine-smime/.pwd dir
169 * setup is successful;
170 * - if the user does not have a key/cert pair, look to see if
171 * ps_global->smime->personal_certs is already setup, if so, use it.
172 * - if ps_global->smime->personal_certs is not set up, see if we can
173 * find a certificate/cert pair in the default locations at compilation
174 * time. (~/.alpine-smime/private and ~/.alpine-smime/public
175 * - if none of this is successful, create a key/certificate pair
176 * (TODO: implement this)
177 * - in any other case, setup is not successful.
179 * If setup is successful, setup ps_global->pwdcert.
180 * If any of this fails, ps_global->pwdcert will be null.
181 * Ok, that should do it.
183 * return values: 0 - everything is normal
184 * 1 - User could not unlock key or no key in directory.
185 * 2 - User cancelled to create self signed certificate
186 * -1 - we do not know which directory to use
187 * -2 - "-pwdcertdir" was given by user, but directory does not exist
188 * -3 - "DF_PASSWORD_DIR" exists but it is not a directory!!??
189 * -4 - we tried to create DF_PASSWORD_DIR but failed.
190 * -5 - password directory exists, but it is empty
194 setup_pwdcert(void **pwdcert
)
198 int setup_dir
= 0; /* make it non zero if we know which dir to use */
200 char pathdir
[MAXPATH
+1], pathkey
[MAXPATH
+1], fpath
[MAXPATH
+1], pathcert
[MAXPATH
+1];
201 char fpath2
[MAXPATH
+1], prompt
[MAILTMPLEN
];
202 char *keyfile
, *certfile
, *text
= NULL
;
203 EVP_PKEY
*pkey
= NULL
;
205 PERSONAL_CERT
*pc
, *pc2
= NULL
;
206 static int was_here
= 0;
208 if(pwdcert
== NULL
|| was_here
== 1)
212 if(ps_global
->pwdcertdir
){
213 if(our_stat(ps_global
->pwdcertdir
, &sbuf
) == 0
214 && ((sbuf
.st_mode
& S_IFMT
) == S_IFDIR
)){
216 strncpy(pathdir
, ps_global
->pwdcertdir
, sizeof(pathdir
));
217 pathdir
[sizeof(pathdir
)-1] = '\0';
221 smime_path(DF_PASSWORD_DIR
, pathdir
, sizeof(pathdir
));
222 if(our_stat(pathdir
, &sbuf
) == 0){
223 if((sbuf
.st_mode
& S_IFMT
) == S_IFDIR
)
226 } else if(can_access(pathdir
, ACCESS_EXISTS
) != 0
227 && our_mkpath(pathdir
, 0700) == 0)
237 if(load_key_and_cert(pathdir
, pathdir
, &keyfile
, &certfile
, &pkey
, &pcert
) < 0){
242 if(ps_global
->pwdcertdir
== NULL
){ /* save the result of pwdcertdir */
243 ps_global
->pwdcertdir
= cpystr(pathdir
);
244 /* if the user gave a pwdcertdir and there is nothing there, do not
245 * continue. Let the user initialize on their own this directory.
247 if(certfile
== NULL
|| keyfile
== NULL
){
253 if(certfile
&& keyfile
){
254 pc
= (PERSONAL_CERT
*) fs_get(sizeof(PERSONAL_CERT
));
255 memset((void *)pc
, 0, sizeof(PERSONAL_CERT
));
259 pc
->cname
= certfile
;
260 *pwdcert
= (void *) pc
;
265 /* look to see if there are any certificates lying around, first
266 * we try to load ps_global->smime to see if that has information
267 * we can use. If we are the process filling the smime structure
268 * we deinit at the end, since this might not do a full init.
270 if(ps_global
&& ps_global
->smime
&& !ps_global
->smime
->inited
){
275 /* at this point ps_global->smime->inited == 1 */
276 if(ps_global
->smime
&& ps_global
->smime
->personal_certs
!= NULL
){
277 pc
= (PERSONAL_CERT
*) ps_global
->smime
->personal_certs
;
278 if(ps_global
->smime
->privatetype
== Directory
){
279 build_path(pathkey
, ps_global
->smime
->privatepath
, pc
->name
, sizeof(pathkey
));
280 strncat(pathkey
, ".key", 5);
281 pathkey
[sizeof(pathkey
)-1] = '\0';
283 } else if (ps_global
->smime
->privatetype
== Container
){
284 if(pc
->keytext
== NULL
){ /* we should *never* be here, but just in case */
285 if(ps_global
->smime
->privatecontent
!= NULL
){
286 char tmp
[MAILTMPLEN
], *s
, *t
, c
;
287 snprintf(tmp
, sizeof(tmp
), "%s%s", EMAILADDRLEADER
, pc
->name
);
288 tmp
[sizeof(tmp
)-1] = '\0';
289 if((s
= strstr(ps_global
->smime
->privatecontent
, tmp
)) != NULL
){
290 if((t
= strstr(s
+strlen(tmp
), EMAILADDRLEADER
)) != NULL
){
293 pc
->keytext
= cpystr(s
+ strlen(tmp
) + strlen(NEWLINE
));
297 pc
->keytext
= cpystr(s
+ strlen(tmp
) + strlen(NEWLINE
));
301 if(pc
->keytext
!= NULL
) /* we should go straight here */
303 } else if (ps_global
->smime
->privatetype
== Keychain
){
304 pathkey
[0] = '\0'; /* no apple key chain support yet */
307 if((pathkey
&& *pathkey
) || text
){
308 snprintf(prompt
, sizeof(prompt
),
309 _("Enter password of key <%s> to unlock password file: "), pc
->name
);
311 if((pkey
= load_pkey_with_prompt(pathkey
, text
, prompt
, NULL
)) != NULL
){
312 pc2
= (PERSONAL_CERT
*) fs_get(sizeof(PERSONAL_CERT
));
313 memset((void *)pc2
, 0, sizeof(PERSONAL_CERT
));
314 pc2
->name
= cpystr(pc
->name
);
316 pc2
->cert
= X509_dup(pc
->cert
);
318 /* now copy the keys and certs, starting by the key... */
319 build_path(fpath
, pathdir
, pc
->name
, sizeof(fpath
));
320 strncat(fpath
, ".key", 5);
321 fpath
[sizeof(fpath
)-1] = '\0';
322 if(our_stat(fpath
, &sbuf
) == 0){ /* if fpath exists */
323 if((sbuf
.st_mode
& S_IFMT
) == S_IFREG
) /* and is a regular file */
324 setup_dir
++; /* we are done */
325 } else if(ps_global
->smime
->privatetype
== Directory
){
326 if(our_copy(fpath
, pathkey
) == 0)
328 } else if(ps_global
->smime
->privatetype
== Container
){
330 if((out
= BIO_new_file(fpath
, "w")) != NULL
){
331 if(BIO_puts(out
, pc
->keytext
) > 0)
335 } else if(ps_global
->smime
->privatetype
== Keychain
){
336 /* add support for Apple Mac OS X */
340 /* successful copy of key, now continue with certificate */
344 build_path(pathkey
, ps_global
->smime
->publicpath
, pc
->name
, sizeof(pathkey
));
345 strncat(pathkey
, ".crt", 5);
346 pathkey
[sizeof(pathkey
)-1] = '\0';
348 build_path(fpath
, pathdir
, pc
->name
, sizeof(fpath
));
349 strncat(fpath
, ".crt", 5);
350 fpath
[sizeof(fpath
)-1] = '\0';
352 if(our_stat(fpath
, &sbuf
) == 0){
353 if((sbuf
.st_mode
& S_IFMT
) == S_IFREG
)
356 else if(ps_global
->smime
->privatetype
== Directory
){
357 if(our_copy(fpath
, pathkey
) == 0)
359 } else if(ps_global
->smime
->privatetype
== Container
) {
361 if((out
= BIO_new_file(fpath
, "w")) != NULL
){
362 if(PEM_write_bio_X509(out
, pc
->cert
))
366 } else if (ps_global
->smime
->privatetype
== Keychain
) {
367 /* add support for Mac OS X */
372 *pwdcert
= (void *) pc2
;
377 free_personal_certs(&pc2
);
378 } /* if (pathkey...) */
379 } /* if(ps_global->smime->personal_certs) */
383 /* PATHCERTDIR(Private) must be null, so create a path */
384 set_current_val(&ps_global
->vars
[V_PRIVATEKEY_DIR
], TRUE
, TRUE
);
385 smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, pathkey
, sizeof(pathkey
));
387 /* PATHCERTDIR(Public) must be null, so create a path */
388 set_current_val(&ps_global
->vars
[V_PUBLICCERT_DIR
], TRUE
, TRUE
);
389 smime_path(ps_global
->VAR_PUBLICCERT_DIR
, pathcert
, sizeof(pathcert
));
391 /* BUG: this does not support local containers */
392 load_key_and_cert(pathkey
, pathcert
, &keyfile
, &certfile
, &pkey
, &pcert
);
394 if(certfile
&& keyfile
){
395 build_path(fpath
, pathdir
, keyfile
, sizeof(fpath
));
396 strncat(fpath
, ".key", 5);
397 fpath
[sizeof(fpath
)-1] = '\0';
399 build_path(fpath2
, pathkey
, keyfile
, sizeof(fpath
));
400 strncat(fpath2
, ".key", 5);
401 fpath2
[sizeof(fpath2
)-1] = '\0';
403 if(our_copy(fpath
, fpath2
) == 0)
409 build_path(fpath
, pathdir
, certfile
, sizeof(fpath
));
410 build_path(fpath2
, pathcert
, certfile
, sizeof(fpath2
));
412 if(our_copy(fpath
, fpath2
) == 0)
418 if(keyfile
&& certfile
){
419 pc
= (PERSONAL_CERT
*) fs_get(sizeof(PERSONAL_CERT
));
420 memset((void *)pc
, 0, sizeof(PERSONAL_CERT
));
424 *pwdcert
= (void *) pc
;
425 fs_give((void **)&certfile
);
435 #endif /* PASSFILE */
437 /* smime_expunge_cert.
438 * Return values: < 0 there was an error.
439 * >=0 the number of messages expunged
442 smime_expunge_cert(WhichCerts ctype
)
444 int count
= 0, removed
;
445 CertList
*cl
, *dummy
, *data
;
446 char *path
, buf
[MAXPATH
+1];
449 if(DATACERT(ctype
)== NULL
)
452 /* data cert is the way we unify certificate management across
453 * functions, but it is not where we really save the information in the
454 * case ctype is equal to Private. What we will do is to update the
455 * datacert, and in the case of ctype equal to Private use the updated
456 * certdata to update the personal_certs data.
459 path
= PATHCERTDIR(ctype
);
462 /* add a fake certificate at the beginning of the list */
463 dummy
= fs_get(sizeof(CertList
));
464 memset((void *)dummy
, 0, sizeof(CertList
));
465 dummy
->next
= DATACERT(ctype
);
467 for(cl
= dummy
, count
= 0; cl
&& cl
->next
;){
468 if(cl
->next
->data
.deleted
== 0){
473 removed
= 1; /* assume success */
474 if(SMHOLDERTYPE(ctype
) == Directory
){
475 build_path(buf
, path
, cl
->next
->name
, sizeof(buf
));
476 if(ctype
== Private
&& strlen(buf
) + strlen(EXTCERT(Private
)) < sizeof(buf
)){
477 strncat(buf
, EXTCERT(Private
), 5);
478 buf
[sizeof(buf
)-1] = '\0';
481 if(our_unlink(buf
) < 0){
482 q_status_message1(SM_ORDER
, 3, 3, _("Error removing certificate %s"), cl
->next
->name
);
487 else if(SMHOLDERTYPE(ctype
) == Container
){
488 char *prefix
= ctype
== CACert
? CACERTSTORELEADER
: EMAILADDRLEADER
;
489 char tmp
[MAILTMPLEN
], *s
, *t
;
491 contents
= CONTENTCERTLIST(ctype
);
492 snprintf(tmp
, sizeof(tmp
), "%s%s", prefix
, cl
->next
->name
);
493 tmp
[sizeof(tmp
) - 1] = '\0';
494 if((s
= strstr(contents
, tmp
)) != NULL
){
495 if((t
= strstr(s
+strlen(tmp
), prefix
)) == NULL
)
498 memmove(s
, t
, strlen(t
)+1);
499 fs_resize((void **)&contents
, strlen(contents
)+1);
501 case Private
: ps_global
->smime
->privatecontent
= contents
; break;
502 case Public
: ps_global
->smime
->publiccontent
= contents
; break;
503 case CACert
: ps_global
->smime
->cacontent
= contents
; break;
509 } else { /* unhandled case */
513 count
++; /* count it! */
515 cl
->next
= data
->next
;
516 if(data
->name
) fs_give((void **)&data
->name
);
517 fs_give((void **)&data
);
521 q_status_message(SM_ORDER
, 3, 3, _("Error expunging certificate"));
524 case Private
: ps_global
->smime
->privatecertlist
= dummy
->next
; break;
525 case Public
: ps_global
->smime
->publiccertlist
= dummy
->next
; break;
526 case CACert
: ps_global
->smime
->cacertlist
= dummy
->next
; break;
529 fs_give((void **)&dummy
);
530 if(SMHOLDERTYPE(ctype
) == Container
){
531 if(copy_dir_to_container(ctype
, contents
) < 0)
535 q_status_message2(SM_ORDER
, 3, 3, _("Removed %s certificate%s"), comatose(count
), plural(count
));
538 q_status_message(SM_ORDER
, 3, 3, _("Error: No certificates were removed"));
543 mark_cert_deleted(WhichCerts ctype
, int num
, unsigned state
)
548 for(cl
= DATACERT(ctype
), i
= 0; cl
!= NULL
&& i
< num
; cl
= cl
->next
, i
++);
549 cl
->data
.deleted
= state
;
553 get_cert_deleted(WhichCerts ctype
, int num
)
558 for(cl
= DATACERT(ctype
), i
= 0; cl
!= NULL
&& i
< num
; cl
= cl
->next
, i
++);
559 return (cl
&& cl
->data
.deleted
) ? 1 : 0;
563 load_pkey_with_prompt(char *fpath
, char *text
, char *prompt
, int *ret
)
566 int rc
= 0; /* rc == 1, cancel, rc == 0 success */
567 char pass
[MAILTMPLEN
+1];
570 /* attempt to load with empty password */
571 in
= text
? BIO_new_mem_buf(text
, strlen(text
)) : BIO_new_file(fpath
, "r");
573 pkey
= PEM_read_bio_PrivateKey(in
, NULL
, NULL
, "");
574 if(pkey
!= NULL
) return pkey
;
577 if(pith_smime_enter_password
)
578 while(pkey
== NULL
&& rc
!= 1){
580 rc
= (*pith_smime_enter_password
)(prompt
, (char *)pass
, sizeof(pass
));
581 } while (rc
!=0 && rc
!=1 && rc
>0);
583 (void) BIO_reset(in
);
584 pkey
= PEM_read_bio_PrivateKey(in
, NULL
, NULL
, (char *)pass
);
589 if(ret
) *ret
= rc
== 1 ? -1 : pkey
!= NULL
? 0 : 1;
593 /* This is a tool for conf_screen, The return value must be zero when
594 * nothing changed, so if there is a failure in the import return 0
595 * and return 1 when we succeeded.\
596 * We call this function in two ways:
597 * either fname is null or not. If they fname is null, so is p_cert.
598 * if p_cert is not null, it is the PERSONAL_CERT structure of fname if this
599 * is available, otherwise we will fill it up here.
602 import_certificate(WhichCerts ctype
, PERSONAL_CERT
*p_cert
, char *fname
)
605 char filename
[MAXPATH
+1], full_filename
[MAXPATH
+1], buf
[MAXPATH
+1];
608 if(pith_smime_import_certificate
== NULL
609 || pith_smime_enter_password
== NULL
){
610 q_status_message(SM_ORDER
, 0, 2,
611 _("import of certificates not implemented yet!"));
616 what
= ctype
== Public
|| ctype
== CACert
? "certificate" : "key";
617 r
= (*pith_smime_import_certificate
)(filename
, full_filename
, what
, sizeof(filename
) - 20);
623 strncpy(full_filename
, fname
, sizeof(full_filename
));
624 if((s
= strrchr(full_filename
, '/')) != NULL
)
625 strncpy(filename
, s
+1, sizeof(filename
));
628 /* we are trying to import a new key for the password file. First we ask for the
629 * private key. Once this is loaded, we make a reasonable attempt to find the
630 * public key in the same directory as the key was loaded from. We do this by
631 * looking for a file with the correct public certificate name, then we look
632 * in the same private key, and if not, we ask the user for its location. If all
633 * of this works, we import the key and public to the password directory.
636 if(ctype
== Password
){
637 char PrivateKeyPath
[MAXPATH
+1], PublicCertPath
[MAXPATH
+1], s
[MAXPATH
+1];
638 char full_name_key
[MAXPATH
+1], full_name_cert
[MAXPATH
+1];
639 char *use_this_file
= NULL
;
641 EVP_PKEY
*key
= p_cert
? p_cert
->key
: NULL
;
643 rc
= 1; /* assume success :) */
644 if(strlen(filename
) > 4){
645 strncpy(s
, filename
, sizeof(s
));
646 s
[sizeof(s
)-1] = '\0';
647 if(!strcmp(s
+ strlen(s
) - strlen(EXTCERT(Private
)), EXTCERT(Private
)))
648 s
[strlen(s
) - strlen(EXTCERT(Private
))] = '\0';
654 q_status_message(SM_ORDER
, 1, 3, _("Error in key name. Check file extension"));
658 snprintf(prompt
, sizeof(prompt
), _("Enter passphrase to unlock new key <%s>: "), filename
);
659 prompt
[sizeof(prompt
)-1] = '\0';
661 || (key
= load_pkey_with_prompt(full_filename
, NULL
, prompt
, NULL
)) != NULL
){
663 X509
*cert
= p_cert
? p_cert
->cert
: NULL
;
665 strncpy(full_name_key
, full_filename
, sizeof(full_filename
));
666 full_name_key
[sizeof(full_name_key
)-1] = '\0';
668 build_path(buf
, PATHCERTDIR(ctype
), s
, sizeof(buf
));
670 strncpy(PrivateKeyPath
, buf
, sizeof(PrivateKeyPath
));
671 PrivateKeyPath
[sizeof(PrivateKeyPath
)-1] = '\0';
672 if(strlen(PrivateKeyPath
) + 4 < sizeof(PrivateKeyPath
)){
673 strncat(PrivateKeyPath
, EXTCERT(Private
), 5);
674 PrivateKeyPath
[sizeof(PrivateKeyPath
)-1] = '\0';
677 /* remove .key extension and replace it with .crt extension */
678 strncpy(full_name_cert
, full_name_key
, sizeof(full_name_key
));
679 full_name_cert
[sizeof(full_name_cert
)-1] = '\0';
680 full_name_cert
[strlen(full_name_cert
) - strlen(EXTCERT(Private
))] = '\0';
681 strncat(full_name_cert
, EXTCERT(Public
), 5);
682 full_name_cert
[sizeof(full_name_cert
)-1] = '\0';
685 /* set up path to location where we will save public cert */
686 strncpy(PublicCertPath
, buf
, sizeof(PublicCertPath
));
687 PublicCertPath
[sizeof(PublicCertPath
)-1] = '\0';
688 if(strlen(PublicCertPath
) + 4 < sizeof(PublicCertPath
)){
689 strncat(PublicCertPath
, EXTCERT(Public
), 5);
690 PublicCertPath
[sizeof(PublicCertPath
)-1] = '\0';
692 /* attempt #1, use provided certificate,
693 * assumption is that full_name_cert is the file that this
694 * certificate derives from (which is obtained by substitution
695 * of .key extension in key by .crt extension)
697 if(cert
!= NULL
) /* attempt #1 */
698 use_this_file
= &full_name_cert
[0];
699 else if((ins
= BIO_new_file(full_name_cert
, "r")) != NULL
){
700 /* attempt #2 to guess public cert name, use .crt extension */
701 if((cert
= PEM_read_bio_X509(ins
, NULL
, NULL
, NULL
)) != NULL
){
702 use_this_file
= &full_name_cert
[0];
705 else{ /* attempt #3 to guess public cert name: use the original key */
706 if((ins
= BIO_new_file(full_name_key
, "r")) != NULL
){
707 if((cert
= PEM_read_bio_X509(ins
, NULL
, NULL
, NULL
)) != NULL
){
708 use_this_file
= &full_name_key
[0];
713 /* attempt #4, ask the user */
715 r
= (*pith_smime_import_certificate
)(filename
, use_this_file
, "certificate", sizeof(filename
) - 20);
717 if(ins
!= NULL
) BIO_free(ins
);
718 if(cert
!= NULL
) X509_free(cert
);
721 if((ins
= BIO_new_file(use_this_file
, "r")) != NULL
){
722 if((cert
= PEM_read_bio_X509(ins
, NULL
, NULL
, NULL
)) != NULL
)
725 q_status_message(SM_ORDER
, 1, 3, _("Error parsing certificate"));
728 q_status_message(SM_ORDER
, 1, 3, _("Error reading certificate"));
733 if(cert
!= NULL
){ /* check that certificate matches key */
734 if(!X509_check_private_key(cert
, key
)){
736 q_status_message(SM_ORDER
, 1, 3, _("Certificate does not match key"));
739 rc
= 1; /* Success! */
742 q_status_message(SM_ORDER
, 1, 3, _("Error in certificate file (not a certificate?)"));
744 if(rc
== 1){ /* if everything has been successful,
745 * copy the files to their final destination */
746 if(our_copy(PrivateKeyPath
, full_filename
) == 0){ /* <-- save the private key */
747 q_status_message(SM_ORDER
, 1, 3, _("Private key saved"));
748 if(our_copy(PublicCertPath
, use_this_file
) == 0){
749 char tmp
[MAILTMPLEN
];
752 if(!passfile_name(ps_global
->pinerc
, tmp
, sizeof(tmp
))
753 || !(fp
= our_fopen(tmp
, "rb"))){
754 q_status_message(SM_ORDER
, 1, 3, _("Error reading password file!"));
758 char tmp2
[MAILTMPLEN
];
761 PERSONAL_CERT
*pwdcert
, *pc
= p_cert
;
763 pwdcert
= (PERSONAL_CERT
*) ps_global
->pwdcert
;
765 setup_pwdcert((void **)&pwdcert
);
768 fgets(tmp2
, sizeof(tmp2
), fp
);
770 if(strcmp(tmp2
, "-----BEGIN PKCS7-----\n")){
771 if(encrypt_file((char *)tmp
, NULL
, pwdcert
))
778 text
= decrypt_file((char *)tmp
, NULL
, pwdcert
);
781 filename
[strlen(filename
)-strlen(EXTCERT(Private
))] = '\0';
782 if(strlen(filename
) + strlen(EXTCERT(Public
)) < MAXPATH
){
783 pc
= fs_get(sizeof(PERSONAL_CERT
));
784 memset((void *)pc
, 0, sizeof(PERSONAL_CERT
));
785 pc
->name
= cpystr(filename
);
786 pc
->cname
= fs_get(strlen(filename
) + strlen(EXTCERT(Public
)) + 1);
787 sprintf(pc
->cname
, "%s%s", filename
, EXTCERT(Public
));
793 if(encrypt_file((char *)tmp
, text
, pc
)){ /* we did it! */
794 build_path(buf
, PATHCERTDIR(ctype
), pwdcert
->name
, sizeof(buf
));
795 strncat(buf
, EXTCERT(Private
), 5);
796 buf
[sizeof(buf
)-1] = '\0';
797 if(strcmp(PrivateKeyPath
, buf
)){
799 q_status_message(SM_ORDER
, 1, 3, _("Failed to remove old key"));
801 build_path(buf
, PATHCERTDIR(ctype
), pwdcert
->cname
, sizeof(buf
));
802 if(strcmp(PublicCertPath
, buf
)){
804 q_status_message(SM_ORDER
, 1, 3, _("Failed to remove old certificate"));
806 free_personal_certs((PERSONAL_CERT
**)&ps_global
->pwdcert
);
807 ps_global
->pwdcert
= pc
;
809 q_status_message(SM_ORDER
, 1, 3, _("Password file reencrypted"));
811 q_status_message(SM_ORDER
, 1, 3, _("Failed to reencrypt password file"));
815 q_status_message(SM_ORDER
, 1, 3, _("Error decrypting Password file"));
818 q_status_message(SM_ORDER
, 1, 3, _("Password file not encrypted and could not encrypt"));
824 q_status_message(SM_ORDER
, 1, 3, _("Error saving public certificate"));
825 if(our_unlink(PrivateKeyPath
) < 0)
826 q_status_message(SM_ORDER
, 1, 3, _("Error while cleaning private key"));
832 q_status_message(SM_ORDER
, 1, 3, _("Error saving private key"));
834 if(ins
!= NULL
) BIO_free(ins
);
835 if(rc
== 0 && cert
!= NULL
) X509_free(cert
);
839 q_status_message(SM_ORDER
, 1, 3, _("Error unlocking private key"));
844 #endif /* PASSFILE */
847 ps_global
->mangled_screen
= 1;
849 if (ctype
== Private
){
850 char prompt
[500], *s
, *t
;
851 EVP_PKEY
*key
= NULL
;
853 if(!ps_global
->smime
->privatecertlist
){
854 ps_global
->smime
->privatecertlist
= fs_get(sizeof(CertList
));
855 memset((void *) ps_global
->smime
->privatecertlist
, 0, sizeof(CertList
));
858 for(s
= t
= filename
; (t
= strstr(s
, ".key")) != NULL
; s
= t
+ 1);
861 snprintf(prompt
, sizeof(prompt
), _("Enter passphrase for <%s>: "), filename
);
862 prompt
[sizeof(prompt
)-1] = '\0';
863 if((key
= load_pkey_with_prompt(full_filename
, NULL
, prompt
, NULL
)) != NULL
){
864 if(SMHOLDERTYPE(ctype
) == Directory
){
865 build_path(buf
, PATHCERTDIR(ctype
), filename
, sizeof(buf
));
866 if(strcmp(buf
+ strlen(buf
) - 4, EXTCERT(ctype
)) != 0 && strlen(buf
) + 4 < sizeof(buf
)){
867 strncat(buf
, EXTCERT(ctype
), 5);
868 buf
[sizeof(buf
)-1] = '\0';
870 rc
= our_copy(buf
, full_filename
);
872 else /* if(SMHOLDERTYPE(ctype) == Container){ */
873 rc
= add_file_to_container(ctype
, full_filename
, NULL
);
875 q_status_message(SM_ORDER
, 1, 3, _("Private key saved"));
877 q_status_message(SM_ORDER
, 1, 3, _("Error saving private key"));
878 if(ps_global
->smime
->publiccertlist
)
879 ps_global
->smime
->publiccertlist
->data
.renew
= 1;
882 q_status_message(SM_ORDER
, 1, 3, _("Problem unlocking key (not a certificate or wrong password)"));
883 } else if (ctype
== CACert
){
887 if((ins
= BIO_new_file(full_filename
, "r")) != NULL
){
888 if((cert
= PEM_read_bio_X509(ins
, NULL
, NULL
, NULL
)) != NULL
){
889 if(SMHOLDERTYPE(ctype
) == Directory
){
890 build_path(buf
, PATHCERTDIR(ctype
), filename
, sizeof(buf
));
891 if(strcmp(buf
+ strlen(buf
) - 4, ".crt") != 0 && strlen(buf
) + 4 < sizeof(buf
)){
892 strncat(buf
, EXTCERT(ctype
), 5);
893 buf
[sizeof(buf
)-1] = '\0';
896 rc
= our_copy(buf
, full_filename
);
898 else /* if(SMHOLDERTYPE(ctype) == Container){ */
899 rc
= add_file_to_container(ctype
, full_filename
, NULL
);
901 q_status_message(SM_ORDER
, 1, 3, _("Certificate saved"));
903 q_status_message(SM_ORDER
, 1, 3, _("Error saving certificate"));
904 X509_free(cert
); /* not needed anymore */
907 q_status_message(SM_ORDER
, 1, 3, _("Error in certificate file (not a certificate?)"));
911 } else { /* ctype == Public. save certificate, but first validate that it is one */
915 if((ins
= BIO_new_file(full_filename
, "r")) != NULL
){
916 if((cert
= PEM_read_bio_X509(ins
, NULL
, NULL
, NULL
)) != NULL
){
917 if(SMHOLDERTYPE(ctype
) == Directory
){
920 if((email
= get_x509_subject_email(cert
)) != NULL
){
922 for(i
= 0; email
[i
] != NULL
; i
++){
923 save_cert_for(email
[i
], cert
, Public
);
924 fs_give((void **)&email
[i
]);
926 fs_give((void **)email
);
928 if(strcmp(filename
+ strlen(filename
) - 4, ".crt") == 0)
929 filename
[strlen(filename
) - 4] = '\0';
930 save_cert_for(filename
, cert
, Public
);
932 else /* if(SMHOLDERTYPE(ctype) == Container){ */
933 add_file_to_container(ctype
, full_filename
, NULL
);
935 if(ps_global
->smime
->publiccertlist
)
936 ps_global
->smime
->publiccertlist
->data
.renew
= 1;
939 q_status_message(SM_ORDER
, 1, 3, _("Error in certificate file (not a certificate?)"));
943 if(DATACERT(ctype
)) RENEWCERT(DATACERT(ctype
)) = 1;
947 /* itype: information type to add: 0 - public, 1 - private.
948 * Memory freed by caller
951 print_private_key_information(char *email
, int itype
)
956 if(ps_global
->smime
== NULL
957 || ps_global
->smime
->personal_certs
== NULL
958 || (itype
!= 0 && itype
!= 1))
961 for(pc
= ps_global
->smime
->personal_certs
;
962 pc
!= NULL
&& strcmp(pc
->name
, email
) != 0; pc
= pc
->next
);
964 && !load_private_key(pc
)
966 && ps_global
->smime
->need_passphrase
){
967 if (pith_opt_smime_get_passphrase
)
968 (*pith_opt_smime_get_passphrase
)();
969 load_private_key(pc
);
975 out
= BIO_new(BIO_s_mem());
976 if(itype
== 0) /* 0 means public */
977 EVP_PKEY_print_public(out
, pc
->key
, 0, NULL
);
978 else if (itype
== 1) /* 1 means private */
979 EVP_PKEY_print_private(out
, pc
->key
, 0, NULL
);
981 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
982 forget_private_keys();
988 * Forget any cached private keys
991 forget_private_keys(void)
993 PERSONAL_CERT
*pcert
;
997 dprint((9, "forget_private_keys()"));
998 if(ps_global
->smime
){
999 ps_global
->smime
->already_auto_asked
= 0;
1000 for(pcert
=(PERSONAL_CERT
*) ps_global
->smime
->personal_certs
;
1005 EVP_PKEY_free(pcert
->key
);
1010 ps_global
->smime
->entered_passphrase
= 0;
1011 len
= sizeof(ps_global
->smime
->passphrase
);
1012 p
= ps_global
->smime
->passphrase
;
1019 /* modelled after signature_path in reply.c, but uses home dir instead of the
1020 * directory where the .pinerc is located, since according to documentation,
1021 * the .alpine-smime directories are subdirectories of the home directory
1024 smime_path(char *rpath
, char *fpath
, size_t len
)
1027 if(rpath
&& *rpath
){
1028 size_t spl
= strlen(rpath
);
1030 if(IS_REMOTE(rpath
)){
1032 strncpy(fpath
, rpath
, len
-1);
1033 fpath
[len
-1] = '\0';
1035 else if(is_absolute_path(rpath
)){
1036 strncpy(fpath
, rpath
, len
-1);
1037 fpath
[len
-1] = '\0';
1038 fnexpand(fpath
, len
);
1040 else if(ps_global
->VAR_OPER_DIR
){
1041 if(strlen(ps_global
->VAR_OPER_DIR
) + spl
< len
- 1)
1042 build_path(fpath
, ps_global
->VAR_OPER_DIR
, rpath
, len
);
1044 else if(ps_global
->home_dir
){
1045 if(strlen(ps_global
->home_dir
) + spl
< len
- 1)
1046 build_path(fpath
, ps_global
->home_dir
, rpath
, len
);
1049 return fpath
&& *fpath
? 1 : 0;
1055 * taken from openssl/apps/app_rand.c
1058 app_RAND_load_file(const char *file
)
1060 #define RANDBUFLEN 200
1061 char buffer
[RANDBUFLEN
];
1064 file
= RAND_file_name(buffer
, RANDBUFLEN
);
1066 if(file
== NULL
|| !RAND_load_file(file
, -1)){
1067 if(RAND_status() == 0){
1068 dprint((1, "unable to load 'random state'\n"));
1069 dprint((1, "This means that the random number generator has not been seeded\n"));
1070 dprint((1, "with much random data.\n"));
1082 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
1085 openssl_extra_randomness(void)
1093 /* if system doesn't have /dev/urandom */
1094 if(stat ("/dev/urandom", &sbuf
)){
1096 tf
= temp_nam(NULL
, NULL
);
1098 strncpy(tmp
, tf
, sizeof(tmp
));
1099 tmp
[sizeof(tmp
)-1] = '\0';
1100 fs_give((void **) &tf
);
1103 if((fd
= open(tmp
, O_WRONLY
|O_CREAT
|O_EXCL
, 0600)) < 0)
1104 i
= (unsigned long) tmp
;
1106 unlink(tmp
); /* don't need the file */
1107 fstat(fd
, &sbuf
); /* get information about the file */
1108 i
= sbuf
.st_ino
; /* remember its inode */
1109 close(fd
); /* or its descriptor */
1111 /* not great but it'll have to do */
1112 snprintf(tmp
+strlen(tmp
), sizeof(tmp
)-strlen(tmp
), "%.80s%lx%lx%lx",
1113 tcp_serverhost (),i
,
1114 (unsigned long) (time (0) ^ gethostid ()),
1115 (unsigned long) getpid ());
1116 RAND_seed(tmp
, strlen(tmp
));
1122 /* taken from openssl/apps/app_rand.c */
1124 app_RAND_write_file(const char *file
)
1130 * If we did not manage to read the seed file,
1131 * we should not write a low-entropy seed file back --
1132 * it would suppress a crucial warning the next time
1133 * we want to use it.
1138 file
= RAND_file_name(buffer
, sizeof buffer
);
1140 if(file
== NULL
|| !RAND_write_file(file
)){
1141 dprint((1, "unable to write 'random state'\n"));
1149 certlist_from_personal_certs(PERSONAL_CERT
*pc
)
1151 CertList
*cl
= NULL
;
1157 if((x
= get_cert_for(pc
->name
, Public
, 1)) != NULL
){
1158 cl
= smime_X509_to_cert_info(x
, pc
->name
);
1159 cl
->next
= certlist_from_personal_certs(pc
->next
);
1166 renew_cert_data(CertList
**data
, WhichCerts ctype
)
1169 if(ctype
== Private
){
1171 PERSONAL_CERT
*pc
= (PERSONAL_CERT
*)ps_global
->smime
->personal_certs
;
1173 free_certlist(data
);
1174 free_personal_certs(&pc
);
1175 setup_privatekey_storage();
1176 *data
= certlist_from_personal_certs((PERSONAL_CERT
*)ps_global
->smime
->personal_certs
);
1178 resort_certificates(data
, ctype
);
1179 RENEWCERT(*data
) = 0;
1181 ps_global
->smime
->privatecertlist
= *data
;
1183 if(ps_global
->smime
->privatecertlist
)
1184 RENEWCERT(ps_global
->smime
->privatecertlist
) = 0;
1186 X509_LOOKUP
*lookup
= NULL
;
1187 X509_STORE
*store
= NULL
;
1189 if((store
= X509_STORE_new()) != NULL
){
1190 if((lookup
= X509_STORE_add_lookup(store
, X509_LOOKUP_file())) != NULL
){
1191 free_certlist(data
);
1192 if(SMHOLDERTYPE(ctype
) == Directory
)
1193 add_certs_in_dir(lookup
, PATHCERTDIR(ctype
), EXTCERT(ctype
), data
);
1194 else /* if(SMHOLDERTYPE(ctype) == Container) */
1195 *data
= mem_to_certlist(CONTENTCERTLIST(ctype
), ctype
);
1197 resort_certificates(data
, ctype
);
1198 RENEWCERT(*data
) = 0;
1201 ps_global
->smime
->publiccertlist
= *data
;
1203 ps_global
->smime
->cacertlist
= *data
;
1205 free_x509_store(&store
);
1208 setup_certs_backup_by_type(ctype
);
1218 /* Installed as an atexit() handler to save the random data */
1222 dprint((9, "smime_deinit()"));
1223 app_RAND_write_file(NULL
);
1224 if (s_cert_store
!= NULL
) free_x509_store(&s_cert_store
);
1225 #ifdef ERR_free_strings
1227 #endif /* ERR_free_strings */
1230 #endif /* EVP_cleanup */
1231 free_smime_struct(&ps_global
->smime
);
1234 /* we renew the store when it has changed */
1238 if(ps_global
->smime
->inited
){
1239 if(s_cert_store
!= NULL
)
1240 free_x509_store(&s_cert_store
);
1241 s_cert_store
= get_ca_store();
1245 /* Initialise openssl stuff if needed */
1249 if(F_OFF(F_DONT_DO_SMIME
, ps_global
) && !(ps_global
->smime
&& ps_global
->smime
->inited
)){
1251 dprint((9, "smime_init()"));
1252 if(!ps_global
->smime
)
1253 ps_global
->smime
= new_smime_struct();
1255 setup_storage_locations();
1257 s_cert_store
= get_ca_store();
1258 setup_certs_backup_by_type(CACert
);
1260 #ifdef OPENSSL_1_1_0
1261 OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
|OPENSSL_INIT_ADD_ALL_DIGESTS
|OPENSSL_INIT_LOAD_CRYPTO_STRINGS
, NULL
);
1263 OpenSSL_add_all_algorithms();
1264 ERR_load_crypto_strings();
1265 #endif /* OPENSSL_1_1_0 */
1267 app_RAND_load_file(NULL
);
1268 openssl_extra_randomness();
1269 ps_global
->smime
->inited
= 1;
1276 /* validate a certificate. Return value : 0 for no error, -1 for error.
1277 * In the latter case, set the openssl smime error in *error.
1280 smime_validate_cert(X509
*cert
, long *error
)
1282 X509_STORE_CTX
*csc
;
1286 if((s_cert_store
!= NULL
) && (csc
= X509_STORE_CTX_new()) != NULL
){
1287 X509_STORE_set_flags(s_cert_store
, 0);
1288 if(X509_STORE_CTX_init(csc
,s_cert_store
,cert
,NULL
)
1289 && X509_verify_cert(csc
) <= 0)
1290 *error
= X509_STORE_CTX_get_error(csc
);
1291 X509_STORE_CTX_free(csc
);
1293 return *error
? -1 : 0;
1297 get_personal_certs(char *path
)
1299 PERSONAL_CERT
*result
= NULL
;
1300 char buf2
[MAXPATH
], *fname
;
1306 #else /* _WINDOWS */
1307 struct _finddata_t dbuf
;
1308 char buf
[_MAX_PATH
+ 4];
1310 #endif /* _WINDOWS */
1312 ps_global
->smime
->privatepath
= cpystr(path
);
1315 dirp
= opendir(path
);
1317 while((d
=readdir(dirp
)) != NULL
){
1319 #else /* _WINDOWS */
1320 snprintf(buf
, sizeof(buf
), "%s%s*.*", path
, (path
[strlen(path
)-1] == '\\') ? "" : "\\");
1321 buf
[sizeof(buf
)-1] = '\0';
1322 if((findrv
= _findfirst(buf
, &dbuf
)) < 0)
1326 fname
= fname_to_utf8(dbuf
.name
);
1328 if((ll
=strlen(fname
)) && ll
> 4 && !strcmp(fname
+ll
-4, ".key")){
1330 /* copy file name to temp buffer */
1331 strncpy(buf2
, fname
, sizeof(buf2
)-1);
1332 buf2
[sizeof(buf2
)-1] = '\0';
1333 /* chop off ".key" trailier */
1334 buf2
[strlen(buf2
)-4] = '\0';
1335 /* Look for certificate */
1336 cert
= get_cert_for(buf2
, Public
, 1);
1341 /* create a new PERSONAL_CERT, fill it in */
1343 pc
= (PERSONAL_CERT
*) fs_get(sizeof(*pc
));
1345 pc
->name
= cpystr(buf2
);
1346 strncat(buf2
, EXTCERT(Public
), 5);
1347 pc
->cname
= cpystr(buf2
);
1349 /* Try to load the key with an empty password */
1350 pc
->key
= load_key(pc
, "", SM_NORMALCERT
);
1360 #else /* _WINDOWS */
1361 } while(_findnext(findrv
, &dbuf
) == 0);
1363 #endif /* !_WINDOWS */
1369 setup_privatekey_storage(void)
1371 char path
[MAXPATH
+1], *contents
;
1372 int privatekeycontainer
= 0;
1374 /* private keys in a container */
1375 if(ps_global
->VAR_PRIVATEKEY_CONTAINER
&& ps_global
->VAR_PRIVATEKEY_CONTAINER
[0]){
1377 privatekeycontainer
= 1;
1380 if(!smime_path(ps_global
->VAR_PRIVATEKEY_CONTAINER
, path
, MAXPATH
))
1381 privatekeycontainer
= 0;
1383 if(privatekeycontainer
&& !IS_REMOTE(path
)
1384 && ps_global
->VAR_OPER_DIR
1385 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1386 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1387 /* TRANSLATORS: First arg is the directory name, second is
1388 the file user wants to read but can't. */
1389 _("Can't read file outside %s: %s"),
1390 ps_global
->VAR_OPER_DIR
, path
);
1391 privatekeycontainer
= 0;
1394 if(privatekeycontainer
1395 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1396 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1398 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1399 privatekeycontainer
= 0;
1402 if(privatekeycontainer
&& path
[0]){
1403 ps_global
->smime
->privatetype
= Container
;
1404 ps_global
->smime
->privatepath
= cpystr(path
);
1407 ps_global
->smime
->privatecontent
= contents
;
1408 ps_global
->smime
->personal_certs
= mem_to_personal_certs(contents
);
1413 /* private keys in a directory of files */
1414 if(!privatekeycontainer
){
1415 ps_global
->smime
->privatetype
= Directory
;
1418 if(!(smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, path
, MAXPATH
)
1419 && !IS_REMOTE(path
)))
1420 ps_global
->smime
->privatetype
= Nada
;
1421 else if(can_access(path
, ACCESS_EXISTS
)){
1422 if(our_mkpath(path
, 0700)){
1423 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1424 ps_global
->smime
->privatetype
= Nada
;
1428 if(ps_global
->smime
->privatetype
== Directory
)
1429 ps_global
->smime
->personal_certs
= get_personal_certs(path
);
1431 setup_certs_backup_by_type(Private
);
1435 setup_storage_locations(void)
1437 int publiccertcontainer
= 0, cacertcontainer
= 0;
1438 char path
[MAXPATH
+1], *contents
;
1440 if(!ps_global
->smime
)
1443 #ifdef APPLEKEYCHAIN
1444 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
1445 ps_global
->smime
->publictype
= Keychain
;
1448 #endif /* APPLEKEYCHAIN */
1449 /* Public certificates in a container */
1450 if(ps_global
->VAR_PUBLICCERT_CONTAINER
&& ps_global
->VAR_PUBLICCERT_CONTAINER
[0]){
1452 publiccertcontainer
= 1;
1455 if(!smime_path(ps_global
->VAR_PUBLICCERT_CONTAINER
, path
, MAXPATH
))
1456 publiccertcontainer
= 0;
1458 if(publiccertcontainer
&& !IS_REMOTE(path
)
1459 && ps_global
->VAR_OPER_DIR
1460 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1461 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1462 /* TRANSLATORS: First arg is the directory name, second is
1463 the file user wants to read but can't. */
1464 _("Can't read file outside %s: %s"),
1465 ps_global
->VAR_OPER_DIR
, path
);
1466 publiccertcontainer
= 0;
1469 if(publiccertcontainer
1470 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1471 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1473 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1474 publiccertcontainer
= 0;
1477 if(publiccertcontainer
&& path
[0]){
1478 ps_global
->smime
->publictype
= Container
;
1479 ps_global
->smime
->publicpath
= cpystr(path
);
1482 ps_global
->smime
->publiccontent
= contents
;
1483 ps_global
->smime
->publiccertlist
= mem_to_certlist(contents
, Public
);
1488 /* Public certificates in a directory of files */
1489 if(!publiccertcontainer
){
1490 ps_global
->smime
->publictype
= Directory
;
1493 if(!(smime_path(ps_global
->VAR_PUBLICCERT_DIR
, path
, MAXPATH
)
1494 && !IS_REMOTE(path
)))
1495 ps_global
->smime
->publictype
= Nada
;
1496 else if(can_access(path
, ACCESS_EXISTS
)){
1497 if(our_mkpath(path
, 0700)){
1498 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1499 ps_global
->smime
->publictype
= Nada
;
1503 if(ps_global
->smime
->publictype
== Directory
)
1504 ps_global
->smime
->publicpath
= cpystr(path
);
1507 #ifdef APPLEKEYCHAIN
1509 #endif /* APPLEKEYCHAIN */
1511 setup_privatekey_storage();
1513 /* extra cacerts in a container */
1514 if(ps_global
->VAR_CACERT_CONTAINER
&& ps_global
->VAR_CACERT_CONTAINER
[0]){
1516 cacertcontainer
= 1;
1519 if(!smime_path(ps_global
->VAR_CACERT_CONTAINER
, path
, MAXPATH
))
1520 cacertcontainer
= 0;
1522 if(cacertcontainer
&& !IS_REMOTE(path
)
1523 && ps_global
->VAR_OPER_DIR
1524 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1525 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1526 /* TRANSLATORS: First arg is the directory name, second is
1527 the file user wants to read but can't. */
1528 _("Can't read file outside %s: %s"),
1529 ps_global
->VAR_OPER_DIR
, path
);
1530 cacertcontainer
= 0;
1534 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1535 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1537 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1538 cacertcontainer
= 0;
1541 if(cacertcontainer
&& path
[0]){
1542 ps_global
->smime
->catype
= Container
;
1543 ps_global
->smime
->capath
= cpystr(path
);
1544 ps_global
->smime
->cacontent
= contents
;
1546 ps_global
->smime
->cacertlist
= mem_to_certlist(contents
, CACert
);
1550 if(!cacertcontainer
){
1551 ps_global
->smime
->catype
= Directory
;
1554 if(!(smime_path(ps_global
->VAR_CACERT_DIR
, path
, MAXPATH
)
1555 && !IS_REMOTE(path
)))
1556 ps_global
->smime
->catype
= Nada
;
1557 else if(can_access(path
, ACCESS_EXISTS
)){
1558 if(our_mkpath(path
, 0700)){
1559 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1560 ps_global
->smime
->catype
= Nada
;
1564 if(ps_global
->smime
->catype
== Directory
)
1565 ps_global
->smime
->capath
= cpystr(path
);
1571 copy_publiccert_dir_to_container(void)
1573 return(copy_dir_to_container(Public
, NULL
));
1578 copy_publiccert_container_to_dir(void)
1580 return(copy_container_to_dir(Public
));
1585 copy_privatecert_dir_to_container(void)
1587 return(copy_dir_to_container(Private
, NULL
));
1592 copy_privatecert_container_to_dir(void)
1594 return(copy_container_to_dir(Private
));
1599 copy_cacert_dir_to_container(void)
1601 return(copy_dir_to_container(CACert
, NULL
));
1606 copy_cacert_container_to_dir(void)
1608 return(copy_container_to_dir(CACert
));
1611 /* Add the contents of a file to a container. Do not check the content
1612 * of the file, just add it using the format for that container. The
1613 * caller must check the format, so that there is no data corruption
1615 * return value: 0 - success,
1619 add_file_to_container(WhichCerts ctype
, char *fpath
, char *altname
)
1621 char *sep
= (ctype
== Public
|| ctype
== Private
)
1622 ? EMAILADDRLEADER
: CACERTSTORELEADER
;
1623 char *content
= ctype
== Public
? ps_global
->smime
->publiccontent
1624 : (ctype
== Private
? ps_global
->smime
->privatecontent
1625 : ps_global
->smime
->cacontent
);
1631 int rv
= -1; /* assume error */
1632 size_t clen
; /* content buffer size */
1634 if(our_stat(fpath
, &sbuf
) < 0
1635 || (in
= so_get(FileStar
, fpath
, READ_ACCESS
| READ_FROM_LOCALE
)) == NULL
)
1640 else if((name
= strrchr(fpath
, '/')) != NULL
){
1642 if((ll
= strlen(++name
)) > 4 && strucmp(name
+ ll
- 4, EXTCERT(ctype
)) == 0)
1643 name
[ll
-strlen(EXTCERT(ctype
))] = '\0';
1649 clen
= strlen(content
) + strlen(sep
) + strlen(name
) + sbuf
.st_size
+ 2*strlen(NEWLINE
) + 1;
1650 fs_resize((void **)&content
, clen
);
1652 content
+= strlen(content
);
1655 clen
= strlen(sep
) + strlen(name
) + sbuf
.st_size
+ strlen(NEWLINE
) + 1;
1656 s
= content
= fs_get(clen
);
1659 strncat(content
, sep
, clen
- strlen(content
));
1660 strncat(content
, name
, clen
- strlen(content
));
1661 content
+= strlen(content
);
1664 #endif /* _WINDOWS */
1667 while(so_readc(&c
, in
))
1668 *content
++ = (char) c
;
1672 case Private
: ps_global
->smime
->privatecontent
= s
; break;
1673 case Public
: ps_global
->smime
->publiccontent
= s
; break;
1674 case CACert
: ps_global
->smime
->cacontent
= s
; break;
1678 rv
= copy_dir_to_container(ctype
, s
);
1681 if(in
) so_give(&in
);
1688 * returns 0 on success, -1 on failure
1689 * contents is an argument which tells this function to write the value
1690 * of this variable instead of reading the contents of the directory.
1691 * If the var contents is not null use its value as the value of the
1695 copy_dir_to_container(WhichCerts which
, char *contents
)
1697 int ret
= 0, container
= 0;
1698 BIO
*bio_out
= NULL
, *bio_in
= NULL
;
1699 char srcpath
[MAXPATH
+1], dstpath
[MAXPATH
+1], emailaddr
[MAXPATH
], file
[MAXPATH
], line
[4096];
1700 char *tempfile
= NULL
, fpath
[MAXPATH
+1], *fname
;
1705 #else /* _WINDOWS */
1706 struct _finddata_t dbuf
;
1707 char buf
[_MAX_PATH
+ 4];
1709 #endif /* _WINDOWS */
1710 REMDATA_S
*rd
= NULL
;
1711 char *configdir
= NULL
;
1712 char *configpath
= NULL
;
1713 char *configcontainer
= NULL
;
1714 char *filesuffix
= NULL
;
1715 char *ret_dir
= NULL
;
1717 dprint((9, "copy_dir_to_container(%s)", which
==Public
? "Public" : which
==Private
? "Private" : which
==CACert
? "CACert" : "?"));
1723 emailaddr
[0] = '\0';
1725 if(which
== Public
){
1726 configdir
= ps_global
->VAR_PUBLICCERT_DIR
;
1727 configpath
= ps_global
->smime
->publicpath
;
1728 configcontainer
= cpystr(DF_PUBLIC_CONTAINER
);
1729 filesuffix
= ".crt";
1731 else if(which
== Private
){
1732 configdir
= ps_global
->VAR_PRIVATEKEY_DIR
;
1733 configpath
= ps_global
->smime
->privatepath
;
1734 configcontainer
= cpystr(DF_PRIVATE_CONTAINER
);
1735 filesuffix
= ".key";
1737 else if(which
== CACert
){
1738 configdir
= ps_global
->VAR_CACERT_DIR
;
1739 configpath
= ps_global
->smime
->capath
;
1740 configcontainer
= cpystr(DF_CA_CONTAINER
);
1741 filesuffix
= ".crt";
1743 container
= SMHOLDERTYPE(which
) == Container
;
1745 if(!(configdir
&& configdir
[0])){
1746 q_status_message(SM_ORDER
, 3, 3, _("Directory not defined"));
1750 if(!(configpath
&& configpath
[0])){
1751 #ifdef APPLEKEYCHAIN
1752 if(which
== Public
&& F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
1753 q_status_message(SM_ORDER
, 3, 3, _("Turn off the Keychain feature above first"));
1756 #endif /* APPLEKEYCHAIN */
1757 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
1761 if(!(filesuffix
&& strlen(filesuffix
) == 4)){
1767 * If there is a legit directory to read from set up the
1768 * container file to write to.
1770 if(smime_path(configdir
, srcpath
, MAXPATH
) && !IS_REMOTE(srcpath
)){
1772 if(IS_REMOTE(configpath
)){
1773 rd
= rd_create_remote(RemImap
, configpath
, REMOTE_SMIME_SUBTYPE
,
1775 _("Can't access remote smime configuration."));
1779 (void) rd_read_metadata(rd
);
1781 if(rd
->access
== MaybeRorW
){
1782 if(rd
->read_status
== 'R')
1783 rd
->access
= ReadOnly
;
1785 rd
->access
= ReadWrite
;
1788 if(rd
->access
!= NoExists
){
1790 rd_check_remvalid(rd
, 1L);
1793 * If the cached info says it is readonly but
1794 * it looks like it's been fixed now, change it to readwrite.
1796 if(rd
->read_status
== 'R'){
1797 rd_check_readonly_access(rd
);
1798 if(rd
->read_status
== 'W'){
1799 rd
->access
= ReadWrite
;
1800 rd
->flags
|= REM_OUTOFDATE
;
1803 rd
->access
= ReadOnly
;
1807 if(rd
->flags
& REM_OUTOFDATE
){
1808 if(rd_update_local(rd
) != 0){
1810 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1811 rd_close_remdata(&rd
);
1818 if(rd
->access
!= ReadWrite
|| rd_remote_is_readonly(rd
)){
1819 rd_close_remdata(&rd
);
1823 rd
->flags
|= DO_REMTRIM
;
1825 strncpy(dstpath
, rd
->lf
, sizeof(dstpath
)-1);
1826 dstpath
[sizeof(dstpath
)-1] = '\0';
1829 strncpy(dstpath
, configpath
, sizeof(dstpath
)-1);
1830 dstpath
[sizeof(dstpath
)-1] = '\0';
1834 * dstpath is either the local Container file or the local cache file
1835 * for the remote Container file.
1837 tempfile
= tempfile_in_same_dir(dstpath
, "az", &ret_dir
);
1841 * If there is a legit directory to read from and a tempfile
1842 * to write to we continue.
1844 if(tempfile
&& (bio_out
=BIO_new_file(tempfile
, "w")) != NULL
){
1846 if(contents
!= NULL
){
1847 if(BIO_puts(bio_out
, contents
) < 0)
1852 if((dirp
= opendir(srcpath
)) != NULL
){
1854 while((d
=readdir(dirp
)) && !ret
){
1856 #else /* _WINDOWS */
1857 snprintf(buf
, sizeof(buf
), "%s%s*.*", srcpath
, (srcpath
[strlen(srcpath
)-1] == '\\') ? "" : "\\");
1858 buf
[sizeof(buf
)-1] = '\0';
1859 if((findrv
= _findfirst(buf
, &dbuf
)) < 0)
1863 fname
= fname_to_utf8(dbuf
.name
);
1864 #endif /* ! _WINDOWS */
1865 if((ll
=strlen(fname
)) && ll
> 4 && !strcmp(fname
+ll
-4, filesuffix
)){
1867 /* copy file name to temp buffer */
1868 strncpy(emailaddr
, fname
, sizeof(emailaddr
)-1);
1869 emailaddr
[sizeof(emailaddr
)-1] = '\0';
1870 /* chop off suffix trailier */
1871 emailaddr
[strlen(emailaddr
)-4] = 0;
1874 * This is the separator between the contents of
1877 if(which
== CACert
){
1878 if(!((BIO_puts(bio_out
, CACERTSTORELEADER
) > 0)
1879 && (BIO_puts(bio_out
, emailaddr
) > 0)
1880 && (BIO_puts(bio_out
, NEWLINE
) > 0)))
1884 if(!((BIO_puts(bio_out
, EMAILADDRLEADER
) > 0)
1885 && (BIO_puts(bio_out
, emailaddr
) > 0)
1886 && (BIO_puts(bio_out
, NEWLINE
) > 0)))
1890 /* read then write contents of file */
1891 build_path(file
, srcpath
, fname
, sizeof(file
));
1892 if(!(bio_in
= BIO_new_file(file
, "r")))
1898 while(BIO_gets(bio_in
, line
, sizeof(line
)) > 0){
1899 if(strncmp("-----BEGIN", line
, strlen("-----BEGIN")) == 0)
1903 BIO_puts(bio_out
, line
);
1905 if(strncmp("-----END", line
, strlen("-----END")) == 0)
1916 #else /* _WINDOWS */
1917 } while (_findnext(findrv
, &dbuf
) == 0);
1919 #endif /* ! _WINDOWS */
1925 if(container
&& configpath
&& *configpath
){
1926 strncpy(fpath
, configpath
, sizeof(fpath
));
1927 fpath
[sizeof(fpath
) - 1] = '\0';
1930 if(strlen(dstpath
) + strlen(configcontainer
) + 2 < sizeof(dstpath
))
1931 snprintf(fpath
, sizeof(fpath
), "%.*s%c%.*s",
1932 (int) strlen(dstpath
), dstpath
,
1933 tempfile
[strlen(ret_dir
)],
1934 (int) (sizeof(fpath
) - strlen(dstpath
) - 1), configcontainer
);
1941 if(!IS_REMOTE(configpath
)){
1942 if(rename_file(tempfile
, fpath
) < 0){
1943 q_status_message2(SM_ORDER
, 3, 3,
1944 _("Can't rename %s to %s"), tempfile
, fpath
);
1946 } else q_status_message1(SM_ORDER
, 3, 3,
1947 _("saved container to %s"), fpath
);
1949 else { /* if the container is remote, copy it */
1953 if(rd
!= NULL
&& rename_file(tempfile
, rd
->lf
) < 0){
1954 q_status_message2(SM_ORDER
, 3, 3,
1955 _("Can't rename %s to %s"), tempfile
, rd
->lf
);
1961 if((e
= rd_update_remote(rd
, datebuf
)) != 0){
1963 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1964 _("Error opening temporary smime file %s: %s"),
1965 rd
->lf
, error_description(errno
));
1967 "write_remote_smime: error opening temp file %s\n",
1968 rd
->lf
? rd
->lf
: "?"));
1971 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1972 _("Error copying to %s: %s"),
1973 rd
->rn
, error_description(errno
));
1975 "write_remote_smime: error copying from %s to %s\n",
1976 rd
->lf
? rd
->lf
: "?", rd
->rn
? rd
->rn
: "?"));
1979 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
1980 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1983 rd_update_metadata(rd
, datebuf
);
1984 rd
->read_status
= 'W';
1987 rd_close_remdata(&rd
);
1994 fs_give((void **) &tempfile
);
1997 fs_give((void **) &ret_dir
);
2000 fs_give((void **) &configcontainer
);
2007 * returns 0 on success, -1 on failure
2010 copy_container_to_dir(WhichCerts which
)
2012 char path
[MAXPATH
+1], file
[MAXPATH
+1], buf
[MAXPATH
+1];
2014 char *contents
= NULL
;
2015 char *leader
= NULL
;
2016 char *filesuffix
= NULL
;
2017 char *configdir
= NULL
;
2018 char *configpath
= NULL
;
2019 char *tempfile
= NULL
;
2020 char *p
, *q
, *line
, *name
, *certtext
, *save_p
;
2024 dprint((9, "copy_container_to_dir(%s)", which
==Public
? "Public" : which
==Private
? "Private" : which
==CACert
? "CACert" : "?"));
2029 if(which
== Public
){
2030 leader
= EMAILADDRLEADER
;
2031 contents
= ps_global
->smime
->publiccontent
;
2032 configdir
= ps_global
->VAR_PUBLICCERT_DIR
;
2033 configpath
= ps_global
->smime
->publicpath
;
2034 filesuffix
= ".crt";
2035 if(!(configpath
&& configpath
[0])){
2036 #ifdef APPLEKEYCHAIN
2037 if(which
== Public
&& F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
2038 q_status_message(SM_ORDER
, 3, 3, _("Turn off the Keychain feature above first"));
2041 #endif /* APPLEKEYCHAIN */
2042 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
2046 fs_give((void **) &ps_global
->smime
->publicpath
);
2049 if(!(smime_path(ps_global
->VAR_PUBLICCERT_DIR
, path
, MAXPATH
)
2050 && !IS_REMOTE(path
))){
2051 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
2055 if(can_access(path
, ACCESS_EXISTS
)){
2056 if(our_mkpath(path
, 0700)){
2057 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
2062 ps_global
->smime
->publicpath
= cpystr(path
);
2063 configpath
= ps_global
->smime
->publicpath
;
2065 else if(which
== Private
){
2066 leader
= EMAILADDRLEADER
;
2067 contents
= ps_global
->smime
->privatecontent
;
2068 configdir
= ps_global
->VAR_PRIVATEKEY_DIR
;
2069 configpath
= ps_global
->smime
->privatepath
;
2070 filesuffix
= ".key";
2071 if(!(configpath
&& configpath
[0])){
2072 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
2076 fs_give((void **) &ps_global
->smime
->privatepath
);
2079 if(!(smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, path
, MAXPATH
)
2080 && !IS_REMOTE(path
))){
2081 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
2085 if(can_access(path
, ACCESS_EXISTS
)){
2086 if(our_mkpath(path
, 0700)){
2087 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
2092 ps_global
->smime
->privatepath
= cpystr(path
);
2093 configpath
= ps_global
->smime
->privatepath
;
2095 else if(which
== CACert
){
2096 leader
= CACERTSTORELEADER
;
2097 contents
= ps_global
->smime
->cacontent
;
2098 configdir
= ps_global
->VAR_CACERT_DIR
;
2099 configpath
= ps_global
->smime
->capath
;
2100 filesuffix
= ".crt";
2101 if(!(configpath
&& configpath
[0])){
2102 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
2106 fs_give((void **) &ps_global
->smime
->capath
);
2109 if(!(smime_path(ps_global
->VAR_CACERT_DIR
, path
, MAXPATH
)
2110 && !IS_REMOTE(path
))){
2111 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
2115 if(can_access(path
, ACCESS_EXISTS
)){
2116 if(our_mkpath(path
, 0700)){
2117 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
2122 ps_global
->smime
->capath
= cpystr(path
);
2123 configpath
= ps_global
->smime
->capath
;
2126 if(!(configdir
&& configdir
[0])){
2127 q_status_message(SM_ORDER
, 3, 3, _("Directory not defined"));
2131 if(!(configpath
&& configpath
[0])){
2132 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
2136 if(!(filesuffix
&& strlen(filesuffix
) == 4)){
2141 if(contents
&& *contents
){
2142 for(p
= contents
; *p
!= '\0';){
2145 while(*p
&& *p
!= '\n')
2154 if(strncmp(leader
, line
, strlen(leader
)) == 0){
2155 name
= line
+ strlen(leader
);
2157 if(strncmp("-----BEGIN", certtext
, strlen("-----BEGIN")) == 0){
2158 if((q
= strstr(certtext
, leader
)) != NULL
){
2161 else{ /* end of file */
2162 q
= certtext
+ strlen(certtext
);
2166 strncpy(buf
, name
, sizeof(buf
)-5);
2167 buf
[sizeof(buf
)-5] = '\0';
2168 strncat(buf
, filesuffix
, 5);
2169 build_path(file
, configpath
, buf
, sizeof(file
));
2171 in
= BIO_new_mem_buf(certtext
, q
-certtext
);
2173 tempfile
= tempfile_in_same_dir(file
, "az", NULL
);
2176 out
= BIO_new_file(tempfile
, "w");
2179 while((len
= BIO_read(in
, iobuf
, sizeof(iobuf
))) > 0)
2180 BIO_write(out
, iobuf
, len
);
2184 if(rename_file(tempfile
, file
) < 0){
2185 q_status_message2(SM_ORDER
, 3, 3,
2186 _("Can't rename %s to %s"),
2191 fs_give((void **) &tempfile
);
2208 #ifdef APPLEKEYCHAIN
2211 copy_publiccert_container_to_keychain(void)
2213 /* NOT IMPLEMNTED */
2218 copy_publiccert_keychain_to_container(void)
2220 /* NOT IMPLEMNTED */
2224 #endif /* APPLEKEYCHAIN */
2228 * Get a pointer to a string describing the most recent OpenSSL error.
2229 * It's statically allocated, so don't change or attempt to free it.
2232 openssl_error_string(void)
2235 const char *data
= NULL
;
2238 errn
= ERR_peek_error_line_data(NULL
, NULL
, &data
, NULL
);
2239 errs
= (char*) ERR_reason_error_string(errn
);
2246 return "unknown error";
2250 /* Return true if the body looks like a PKCS7 object */
2252 is_pkcs7_body(BODY
*body
)
2256 result
= body
->type
==TYPEAPPLICATION
&&
2258 (strucmp(body
->subtype
,"pkcs7-mime")==0 ||
2259 strucmp(body
->subtype
,"x-pkcs7-mime")==0 ||
2260 strucmp(body
->subtype
,"pkcs7-signature")==0 ||
2261 strucmp(body
->subtype
,"x-pkcs7-signature")==0);
2268 * Recursively stash a pointer to the decrypted data in our
2269 * manufactured body.
2270 * parameters: type: call of type 1, save the base and header for multipart messages
2271 call of type 0, do not save the base and header for multipart messages
2274 create_local_cache(char *h
, char *base
, BODY
*b
, int type
)
2276 if(b
->type
==TYPEMULTIPART
){
2280 cpytxt(&b
->mime
.text
, h
+b
->mime
.offset
, b
->mime
.text
.size
);
2281 cpytxt(&b
->contents
.text
, base
+ b
->contents
.offset
, b
->size
.bytes
);
2282 } else if(type
== 0){
2284 * We don't really want to copy the real body contents. It shouldn't be
2285 * used, and in the case of a message with attachments, we'll be
2286 * duplicating the files multiple times.
2288 cpytxt(&b
->contents
.text
, "BODY UNAVAILABLE", 16);
2290 for(p
=b
->nested
.part
; p
; p
=p
->next
)
2291 create_local_cache(h
, base
, (BODY
*) p
, type
);
2295 cpytxt(&b
->mime
.text
, h
+b
->mime
.offset
, b
->mime
.text
.size
);
2296 cpytxt(&b
->contents
.text
, base
+ b
->contents
.offset
, b
->size
.bytes
);
2302 rfc822_output_func(void *b
, char *string
)
2304 BIO
*bio
= (BIO
*) b
;
2306 return(string
? *string
? (BIO_puts(bio
, string
) > 0 ? 1L : 0L)
2307 : (BIO_puts(bio
, string
) >= 0 ? 1L : 0L)
2313 * Attempt to load the private key for the given PERSONAL_CERT.
2314 * This sets the appropriate passphrase globals in order to
2315 * interact with the user correctly.
2318 load_private_key(PERSONAL_CERT
*pcert
)
2322 /* Try empty password by default */
2323 char *password
= "";
2326 && (ps_global
->smime
->need_passphrase
2327 || ps_global
->smime
->entered_passphrase
)){
2328 /* We've already been in here and discovered we need a different password */
2330 if(ps_global
->smime
->entered_passphrase
)
2331 password
= (char *) ps_global
->smime
->passphrase
; /* already entered */
2338 if(!(pcert
->key
= load_key(pcert
, password
, SM_NORMALCERT
))){
2339 long err
= ERR_get_error();
2341 /* Couldn't load key... */
2343 if(ps_global
->smime
&& ps_global
->smime
->entered_passphrase
){
2345 /* The user got the password wrong maybe? */
2347 if((ERR_GET_LIB(err
)==ERR_LIB_EVP
&& ERR_GET_REASON(err
)==EVP_R_BAD_DECRYPT
) ||
2348 (ERR_GET_LIB(err
)==ERR_LIB_PEM
&& ERR_GET_REASON(err
)==PEM_R_BAD_DECRYPT
))
2349 q_status_message(SM_ORDER
| SM_DING
, 4, 4, _("Incorrect passphrase"));
2351 q_status_message1(SM_ORDER
, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2353 /* This passphrase is no good; forget it */
2354 ps_global
->smime
->entered_passphrase
= 0;
2357 if(ps_global
->smime
){
2358 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2359 ps_global
->smime
->need_passphrase
= 1;
2360 if(ps_global
->smime
->passphrase_emailaddr
){
2362 for(i
= 0; ps_global
->smime
->passphrase_emailaddr
[i
] != NULL
; i
++)
2363 fs_give((void **)&ps_global
->smime
->passphrase_emailaddr
[i
]);
2364 fs_give((void **) ps_global
->smime
->passphrase_emailaddr
);
2367 ps_global
->smime
->passphrase_emailaddr
= get_x509_subject_email(pcert
->cert
);
2373 /* This key will be cached, so we won't be called again */
2374 if(ps_global
->smime
){
2375 ps_global
->smime
->entered_passphrase
= 0;
2376 ps_global
->smime
->need_passphrase
= 0;
2388 setup_pkcs7_body_for_signature(BODY
*b
, char *description
, char *type
, char *filename
, char *smime_type
)
2390 b
->type
= TYPEAPPLICATION
;
2391 b
->subtype
= cpystr(type
);
2392 b
->encoding
= ENCBINARY
;
2393 b
->description
= cpystr(description
);
2395 b
->disposition
.type
= cpystr("attachment");
2396 set_parameter(&b
->disposition
.parameter
, "filename", filename
);
2398 set_parameter(&b
->parameter
, "name", filename
);
2399 if(smime_type
&& *smime_type
)
2400 set_parameter(&b
->parameter
, "smime-type", smime_type
);
2405 * Look for a personal certificate matching the
2409 match_personal_cert_to_email(ADDRESS
*a
)
2411 PERSONAL_CERT
*pcert
= NULL
;
2416 if(!a
|| !a
->mailbox
|| !a
->host
)
2419 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2421 if(ps_global
->smime
){
2422 for(pcert
=(PERSONAL_CERT
*) ps_global
->smime
->personal_certs
;
2429 email
= get_x509_subject_email(pcert
->cert
);
2433 for(i
= 0; email
[i
] && strucmp(email
[i
], buf
) != 0; i
++);
2434 if(email
[i
] != NULL
) done
++;
2435 for(i
= 0; email
[i
] != NULL
; i
++)
2436 fs_give((void **)&email
[i
]);
2437 fs_give((void **)email
);
2450 * Look for a personal certificate matching the from
2451 * (or reply_to? in the given envelope)
2454 match_personal_cert(ENVELOPE
*env
)
2456 PERSONAL_CERT
*pcert
;
2458 pcert
= match_personal_cert_to_email(env
->reply_to
);
2460 pcert
= match_personal_cert_to_email(env
->from
);
2467 * Flatten the given body into its MIME representation.
2468 * Return the result in a BIO.
2471 body_to_bio(BODY
*body
)
2476 bio
= BIO_new(BIO_s_mem());
2480 pine_encode_body(body
); /* this attaches random boundary strings to multiparts */
2481 pine_write_body_header(body
, rfc822_output_func
, bio
);
2482 pine_rfc822_output_body(body
, rfc822_output_func
, bio
);
2485 * Now need to truncate by two characters since the above
2488 if((len
=BIO_ctrl_pending(bio
)) > 1){
2489 BUF_MEM
*biobuf
= NULL
;
2491 /* this code used to truncate without closing the bio, and
2492 then resetting the memory, causing non validation in
2493 signatures. Fix contributed by Bernd Edlinger.
2495 BIO_get_mem_ptr(bio
, &biobuf
);
2496 BIO_set_close(bio
, BIO_NOCLOSE
);
2497 BUF_MEM_grow(biobuf
, len
-2); /* remove CRLF */
2498 BIO_set_mem_buf(bio
, biobuf
, BIO_CLOSE
);
2506 bio_from_store(STORE_S
*store
)
2510 if(store
&& store
->src
== BioType
&& store
->txt
){
2511 ret
= (BIO
*) store
->txt
;
2518 * Encrypt file; given a path (char *) fp, replace the file
2519 * by an encrypted version of it. If (char *) text is not null, then
2520 * replace the text of (char *) fp by the encrypted version of (char *) text.
2521 * certpath is the FULL path to the file containing the certificate used for
2523 * return value: 0 - failed to encrypt; 1 - success!
2526 encrypt_file(char *fp
, char *text
, PERSONAL_CERT
*pc
)
2528 const EVP_CIPHER
*cipher
= NULL
;
2529 STACK_OF(X509
) *encerts
= NULL
;
2537 cipher
= EVP_aes_256_cbc();
2538 encerts
= sk_X509_new_null();
2540 sk_X509_push(encerts
, X509_dup(pc
->cert
));
2543 if((out
= BIO_new(BIO_s_mem())) != NULL
){
2544 (void) BIO_reset(out
);
2545 BIO_puts(out
, text
);
2548 else if((out
= BIO_new_file(fp
, "rb")) != NULL
)
2549 BIO_read_filename(out
, fp
);
2551 if((p7
= PKCS7_encrypt(encerts
, out
, cipher
, 0)) != NULL
){
2552 BIO_set_close(out
, BIO_CLOSE
);
2554 if((out
= BIO_new_file(fp
, "w")) != NULL
){
2556 rv
= PEM_write_bio_PKCS7(out
, p7
);
2564 sk_X509_pop_free(encerts
, X509_free
);
2570 * Encrypt a message on the way out. Called from call_mailer in send.c
2571 * The body may be reallocated.
2574 encrypt_outgoing_message(METAENV
*header
, BODY
**bodyP
)
2579 const EVP_CIPHER
*cipher
= NULL
;
2580 STACK_OF(X509
) *encerts
= NULL
;
2581 STORE_S
*outs
= NULL
;
2584 BODY
*body
= *bodyP
;
2585 BODY
*newBody
= NULL
;
2590 dprint((9, "encrypt_outgoing_message()"));
2593 cipher
= EVP_aes_256_cbc();
2595 encerts
= sk_X509_new_null();
2597 /* Look for a certificate for each of the recipients */
2598 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
2599 if(pf
->type
== Address
&& pf
->rcptto
&& pf
->addr
&& *pf
->addr
){
2600 for(a
=*pf
->addr
; a
; a
=a
->next
){
2601 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2603 if((cert
= get_cert_for(buf
, Public
, 1)) != NULL
){
2604 sk_X509_push(encerts
,cert
);
2606 q_status_message2(SM_ORDER
, 1, 1,
2607 _("Unable to find certificate for <%s@%s>"),
2608 a
->mailbox
, a
->host
);
2614 /* add the sender's certificate so that they can decrypt the message too */
2615 for(a
=header
->env
->from
; a
; a
= a
->next
){
2616 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2618 if((cert
= get_cert_for(buf
, Public
, 1)) != NULL
2619 && sk_X509_find(encerts
, cert
) == -1)
2620 sk_X509_push(encerts
,cert
);
2623 in
= body_to_bio(body
);
2625 p7
= PKCS7_encrypt(encerts
, in
, cipher
, 0);
2627 outs
= so_get(BioType
, NULL
, EDIT_ACCESS
);
2628 out
= bio_from_store(outs
);
2630 i2d_PKCS7_bio(out
, p7
);
2631 (void) BIO_flush(out
);
2633 so_seek(outs
, 0, SEEK_SET
);
2635 newBody
= mail_newbody();
2637 newBody
->type
= TYPEAPPLICATION
;
2638 newBody
->subtype
= cpystr("pkcs7-mime");
2639 newBody
->encoding
= ENCBINARY
;
2641 newBody
->disposition
.type
= cpystr("attachment");
2642 set_parameter(&newBody
->disposition
.parameter
, "filename", "smime.p7m");
2644 newBody
->description
= cpystr("S/MIME Encrypted Message");
2645 set_parameter(&newBody
->parameter
, "smime-type", "enveloped-data");
2646 set_parameter(&newBody
->parameter
, "name", "smime.p7m");
2648 newBody
->contents
.text
.data
= (unsigned char *) outs
;
2658 sk_X509_pop_free(encerts
, X509_free
);
2660 dprint((9, "encrypt_outgoing_message returns %d", result
));
2666 Get (and decode) the body of the given section of msg
2669 get_part_contents(long msgno
, const char *section
)
2673 STORE_S
*store
= NULL
;
2676 store
= so_get(CharStar
, NULL
, EDIT_ACCESS
);
2678 gf_set_so_writec(&pc
,store
);
2680 err
= detach(ps_global
->mail_stream
, msgno
, (char*) section
, 0L, &len
, pc
, NULL
, 0L);
2682 gf_clear_so_writec(store
);
2684 so_seek(store
, 0, SEEK_SET
);
2695 get_pkcs7_from_part(long msgno
,const char *section
)
2697 STORE_S
*store
= NULL
;
2701 store
= get_part_contents(msgno
, section
);
2704 if(store
->src
== CharStar
){
2708 * We're reaching inside the STORE_S structure. We should
2709 * probably have a way to get the length, instead.
2711 len
= (int) (store
->eod
- store
->dp
);
2712 in
= BIO_new_mem_buf(store
->txt
, len
);
2714 else{ /* just copy it */
2717 in
= BIO_new(BIO_s_mem());
2718 (void) BIO_reset(in
);
2720 so_seek(store
, 0L, 0);
2721 while(so_readc(&c
, store
)){
2722 BIO_write(in
, &c
, 1);
2727 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2728 if((p7
=d2i_PKCS7_bio(in
,NULL
)) == NULL
){
2742 same_cert(X509
*x
, X509
*cert
)
2744 char bufcert
[256], bufx
[256];
2747 get_fingerprint(cert
, EVP_md5(), bufcert
, sizeof(bufcert
), ":");
2748 get_fingerprint(x
, EVP_md5(), bufx
, sizeof(bufx
), ":");
2749 if(strcmp(bufx
, bufcert
) == 0)
2756 /* extract and save certificates from a PKCS7 package.
2758 * 0 - no errors. Either the certificate was in public/
2759 * or we could save it there.
2760 * < 0 - the certificate was not in public/ and the user
2761 * did not save it there.
2765 smime_extract_and_save_cert(PKCS7
*p7
)
2767 STACK_OF(X509
) *signers
;
2770 int i
, j
, rv
, already_saved
;
2772 /* any signers for this message? */
2773 if((signers
= PKCS7_get0_signers(p7
, NULL
, 0)) == NULL
)
2777 for(i
= 0; i
< sk_X509_num(signers
); i
++){
2778 if((x
= sk_X509_value(signers
,i
)) == NULL
)
2781 if((email
= get_x509_subject_email(x
)) != NULL
){
2782 for(j
= 0; email
[j
] != NULL
; j
++){
2784 /* check if we have the certificate for this address */
2785 cert
= get_cert_for(email
[j
], Public
, 1);
2786 /* if we have one, check if it is the one packaged */
2788 already_saved
= same_cert(x
, cert
);
2792 /* if not saved, try to save it */
2793 if(already_saved
== 0
2794 && (*pith_smime_confirm_save
)(email
[j
]) == 1){
2795 save_cert_for(email
[j
], x
, Public
);
2796 if(ps_global
->smime
->publiccertlist
) /* renew store */
2797 free_certlist(&ps_global
->smime
->publiccertlist
);
2800 /* check if it got saved */
2801 cert
= get_cert_for(email
[j
], Public
, 1);
2802 /* if saved, all is good */
2805 else /* else, we do not have this certificate saved */
2808 fs_give((void **) &email
[j
]);
2810 fs_give((void **) email
);
2813 sk_X509_free(signers
);
2819 * Try to verify a signature.
2821 * p7 - the pkcs7 object to verify
2822 * in - the plain data to verify (NULL if not detached)
2823 * out - BIO to which to write the opaque data
2824 * silent - if non zero, do not print errors, only print success.
2827 do_signature_verify(PKCS7
*p7
, BIO
*in
, BIO
*out
, int silent
)
2829 STACK_OF(X509
) *otherCerts
= NULL
;
2836 if(!silent
) q_status_message(SM_ORDER
| SM_DING
, 2, 2,
2837 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2842 flags
= F_ON(F_USE_CERT_STORE_ONLY
, ps_global
) ? PKCS7_NOINTERN
: 0;
2844 if(ps_global
->smime
->publiccertlist
== NULL
){
2845 renew_cert_data(&ps_global
->smime
->publiccertlist
, Public
);
2846 for(cl
= ps_global
->smime
->publiccertlist
; cl
; cl
= cl
->next
){
2847 if(cl
->x509_cert
== NULL
){
2848 char *s
= strrchr(cl
->name
, '.');
2850 cl
->x509_cert
= get_cert_for(cl
->name
, Public
, 1);
2856 if(ps_global
->smime
->publiccertlist
){
2857 otherCerts
= sk_X509_new_null();
2858 for(cl
= ps_global
->smime
->publiccertlist
; cl
; cl
= cl
->next
)
2859 if(cl
->x509_cert
!= NULL
)
2860 sk_X509_push(otherCerts
, X509_dup(cl
->x509_cert
));
2863 result
= PKCS7_verify(p7
, otherCerts
, s_cert_store
, in
, out
, flags
);
2866 q_status_message(SM_ORDER
, 1, 1, _("S/MIME signature verified ok"));
2869 err
= ERR_peek_error_line_data(NULL
, NULL
, &data
, NULL
);
2871 if(out
&& err
==ERR_PACK(ERR_LIB_PKCS7
,PKCS7_F_PKCS7_VERIFY
,PKCS7_R_CERTIFICATE_VERIFY_ERROR
)){
2874 * verification failed due to an error in verifying a certificate.
2875 * Just write the "out" BIO, and leave. Of course let the user
2876 * know about this. Make two more attempts to get the data out. The
2877 * last one should succeed. In any case, let the user know why it
2880 if(PKCS7_verify(p7
, otherCerts
, s_cert_store
, in
, out
, PKCS7_NOVERIFY
) == 0)
2881 PKCS7_verify(p7
, otherCerts
, s_cert_store
, in
, out
, PKCS7_NOVERIFY
|PKCS7_NOSIGS
);
2883 if (!silent
) q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2884 _("Couldn't verify S/MIME signature: %s"), (char *) openssl_error_string());
2887 sk_X509_pop_free(otherCerts
, X509_free
);
2892 /* Big comment, explaining the mess that exists out there, and how we deal
2893 with it, and also how we solve the problems that are created this way.
2895 When Alpine sends a message, it constructs that message, computes the
2896 signature, but then it forgets the message it signed and reconstructs it
2897 again. Since it signs a message containing a notice about "mime aware
2898 tools", but it does not send that we do not include that in the part
2899 that is signed, and that takes care of much of the problems.
2901 Another problem is what is received from the servers. All servers tested
2902 seem to transmit the message that was signed intact and Alpine can check
2903 the signature correctly. That is not a problem. The problem arises when
2904 the message includes attachments. In this case different servers send
2905 different things, so it will be up to us to figure out what is the text
2906 that was actually signed. Confused? here is the story:
2908 When a message containing and attachment is sent by Alpine, UW-IMAP,
2909 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2910 that was sent by Alpine, but GMX.com, Exchange, and probably other
2911 servers add a trailing \r\n in the message, so when validating the
2912 signature, these messages will not validate. There are several things
2915 1. Add a trailing \r\n to any message that contains attachments, sign that
2916 and send that. In this way, all messages will validate with all
2919 2. Compatibility mode: If a message has an attachment, contains a trailing
2920 \r\n and does not validate (sent by an earlier version of Alpine),
2921 remove the trailing \r\n and try to revalidate again.
2923 3. We do not add \r\n to validate a message that we sent, because that
2924 would only work in Alpine, and not in any other client. That would
2925 not be a good thing to do.
2929 Now we have to deal with encrypted and signed messages. The problem is
2930 that c-client makes all its pointers point to "on disk" content, but
2931 since we decrypted the data earlier, we have to make sure of two things.
2932 One is that we saved that data (so we do not have to decrypt it again)
2933 and second that we can use it.
2935 In order to save the data we use create_local_cache, so that we do not
2936 have to redecrypt the message. Once this is saved, c-client functions will
2937 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2941 When we are trying to verify messages with detached signatures, some
2942 imap servers send incorrect information in the mail_fetch_mime call. By
2943 incorrect I mean that this is not fetched directly from the message, but
2944 it is read from the message, processed, and then the processed part is
2945 sent to us, so this text might not agree with what is in the message,
2946 and so the validation of the signature might fail. However, the good
2947 news is that the message validates if saved to a local folder. This
2948 means that if normal validation does not work we can make it work by
2949 saving the message locally and validating that. This is implemented
2950 below, and causes delay in the display of the message. I am considering
2951 at this time not to do this automatically, but wait for the user to tell
2952 us to do it for them by means of a command available in the
2953 mail_view_screen. This might help in other situations, where a message
2954 is supposed to have an attachment, but it can not be seen in the
2955 processed text. Nevertheless, at this time, this is automatic, and is
2956 causing a delay in the processing of the message, but it is validating
2957 correctly all messages.
2961 When the user sends a message as encrypted and signed, this code used to
2962 encrypt first, and then sign the pkcs7 body, but it turns out that some
2963 other clients can not handle these messages. While we could argue that the
2964 other clients need to improve, we will support reading messages in both
2965 ways, and will send messages using this technique; that is, signed first,
2966 encrypted second. It seems that all tested clients support this way, so it
2967 should be safe to do so.
2970 typedef struct smime_filter_s
{
2974 SMIME_FILTER_S sig_filter
[] = {
2975 {smime_remove_trailing_crlf
},
2976 {smime_remove_folding_space
}
2979 #define TOTAL_FILTERS (sizeof(sig_filter)/sizeof(sig_filter[0]))
2980 #define TOTAL_SIGFLTR (1 << TOTAL_FILTERS) /* not good, keep filters to a low number */
2983 smime_remove_trailing_crlf(char **mimetext
, unsigned long *mimelen
,
2984 char **bodytext
, unsigned long *bodylen
)
2986 if(*bodylen
> 2 && !strncmp(*bodytext
+*bodylen
-2, "\r\n", 2))
2991 smime_remove_folding_space(char **mimetext
, unsigned long *mimelen
,
2992 char **bodytext
, unsigned long *bodylen
)
2995 unsigned long mlen
= *mimelen
;
2998 for (s
= t
= *mimetext
; t
- *mimetext
< *mimelen
; ){
2999 if(*t
== '\r' && *(t
+1) == '\n' && (*(t
+2) == '\t' || *(t
+2) == ' ')){
3012 smime_validate_extra_test(char *mimetext
, unsigned long mimelen
, char *bodytext
, unsigned long bodylen
, PKCS7
*p7
, int nflag
)
3014 int result
, i
, j
, flag
;
3015 char *mtext
, *btext
;
3016 unsigned long mlen
, blen
;
3019 mtext
= mimelen
? fs_get(mimelen
+1) : NULL
;
3020 btext
= fs_get(bodylen
+1);
3023 flag
= 1; /* silence all failures */
3024 for(i
= 1; result
== 0 && i
< TOTAL_SIGFLTR
; i
++){
3025 if((in
= BIO_new(BIO_s_mem())) == NULL
)
3028 (void) BIO_reset(in
);
3030 if(i
+1 == TOTAL_SIGFLTR
)
3034 strncpy(mtext
, mimetext
, mlen
= mimelen
);
3035 strncpy(btext
, bodytext
, blen
= bodylen
);
3036 for(j
= 0; j
< TOTAL_FILTERS
; j
++)
3038 (sig_filter
[j
].filter
)(&mtext
, &mlen
, &btext
, &blen
);
3040 BIO_write(in
, mtext
, mlen
);
3041 BIO_write(in
, btext
, blen
);
3042 result
= do_signature_verify(p7
, in
, NULL
, flag
);
3045 if(mtext
) fs_give((void **)&mtext
);
3046 if(btext
) fs_give((void **)&btext
);
3051 * Given a multipart body of type multipart/signed, attempt to verify it.
3052 * Returns non-zero if the body was changed.
3055 do_detached_signature_verify(BODY
*b
, long msgno
, char *section
)
3060 int result
, modified_the_body
= 0;
3061 int flag
; /* 1 silent, 0 not silent */
3062 unsigned long mimelen
, bodylen
;
3063 char newSec
[100], *mimetext
, *bodytext
;
3067 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"));
3070 mimetext
= bodytext
= NULL
;
3072 /* if it was signed and then encrypted, use the decrypted text
3073 * to check the validity of the signature
3076 if(get_body_sparep_type(b
->sparep
) == SizedText
){
3077 /* bodytext includes mimetext */
3078 st
= (SIZEDTEXT
*) get_body_sparep_data(b
->sparep
);
3079 bodytext
= (char *) st
->data
;
3086 snprintf(newSec
, sizeof(newSec
), "%s%s1", section
? section
: "", (section
&& *section
) ? "." : "");
3087 mimetext
= mail_fetch_mime(ps_global
->mail_stream
, msgno
, (char*) newSec
, &mimelen
, 0);
3089 bodytext
= mail_fetch_body (ps_global
->mail_stream
, msgno
, (char*) newSec
, &bodylen
, 0);
3091 if(mimetext
== NULL
|| bodytext
== NULL
)
3092 return modified_the_body
;
3095 snprintf(newSec
, sizeof(newSec
), "%s%s2", section
? section
: "", (section
&& *section
) ? "." : "");
3097 if((p7
= get_pkcs7_from_part(msgno
, newSec
)) == NULL
3098 || (in
= BIO_new(BIO_s_mem())) == NULL
)
3099 return modified_the_body
;
3101 (void) BIO_reset(in
);
3102 if(mimetext
!= NULL
)
3103 BIO_write(in
, mimetext
, mimelen
);
3104 BIO_write(in
, bodytext
, bodylen
);
3106 smime_extract_and_save_cert(p7
);
3108 if((result
= do_signature_verify(p7
, in
, NULL
, 1)) == 0){
3109 flag
= (mimelen
== 0 || !IS_REMOTE(ps_global
->mail_stream
->mailbox
))
3111 result
= smime_validate_extra_test(mimetext
, mimelen
, bodytext
, bodylen
, p7
, flag
);
3113 return modified_the_body
;
3115 && mimelen
> 0 /* do not do this for encrypted messages */
3116 && IS_REMOTE(ps_global
->mail_stream
->mailbox
)){
3118 unsigned long hlen
, tlen
;
3122 if((in
= BIO_new(BIO_s_mem())) != NULL
3123 && (fetch
= mail_fetch_header(ps_global
->mail_stream
, msgno
, NULL
,
3124 NULL
, &hlen
, FT_PEEK
)) != NULL
3125 && (msg_so
= so_get(CharStar
, NULL
, WRITE_ACCESS
)) != NULL
3126 && so_nputs(msg_so
, fetch
, (long) hlen
)
3127 && (fetch
= pine_mail_fetch_text(ps_global
->mail_stream
, msgno
, NULL
,
3128 &tlen
, FT_PEEK
)) != NULL
3129 && so_nputs(msg_so
, fetch
, tlen
)){
3131 char *h
= (char *) so_text(msg_so
);
3132 char *bstart
= strstr(h
, "\r\n\r\n");
3137 INIT(&bs
, mail_string
, bstart
, tlen
);
3138 rfc822_parse_msg_full(&env
, &body
, h
, bstart
-h
-4, &bs
, BADHOST
, 0, 0);
3139 mail_free_envelope(&env
);
3141 mail_free_body_part(&b
->nested
.part
);
3142 tmpB
= mail_body_section(body
, (unsigned char *) section
);
3143 if(MIME_MSG(tmpB
->type
, tmpB
->subtype
))
3144 b
->nested
.part
= tmpB
->nested
.msg
->body
->nested
.part
;
3146 b
->nested
.part
= tmpB
->nested
.part
;
3147 create_local_cache(bstart
, bstart
, &b
->nested
.part
->body
, 1);
3148 modified_the_body
= 1;
3150 snprintf(newSec
, sizeof(newSec
), "%s%s1", section
? section
: "", (section
&& *section
) ? "." : "");
3152 mimetext
= mail_fetch_mime(ps_global
->mail_stream
, msgno
, (char*) newSec
, &mimelen
, 0);
3155 bodytext
= mail_fetch_body (ps_global
->mail_stream
, msgno
, (char*) newSec
, &bodylen
, 0);
3157 if (mimetext
== NULL
|| bodytext
== NULL
)
3158 return modified_the_body
;
3160 snprintf(newSec
, sizeof(newSec
), "%s%s2", section
? section
: "", (section
&& *section
) ? "." : "");
3162 if((p7
= get_pkcs7_from_part(msgno
, newSec
)) == NULL
)
3163 return modified_the_body
;
3165 (void) BIO_reset(in
);
3166 BIO_write(in
, mimetext
, mimelen
);
3167 BIO_write(in
, bodytext
, bodylen
);
3170 if((result
= do_signature_verify(p7
, in
, NULL
, 1)) == 0){
3171 result
= smime_validate_extra_test(mimetext
, mimelen
, bodytext
, bodylen
, p7
, 0);
3173 return modified_the_body
;
3181 fs_give((void**) &b
->subtype
);
3183 b
->subtype
= cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE
);
3184 b
->encoding
= ENC8BIT
;
3187 fs_give ((void**) &b
->description
);
3189 what_we_did
= result
? _("This message was cryptographically signed.") :
3190 _("This message was cryptographically signed but the signature could not be verified.");
3192 b
->description
= cpystr(what_we_did
);
3194 b
->sparep
= create_body_sparep(P7Type
, p7
);
3198 /* p is signed plaintext */
3200 mail_free_body_part(&p
->next
); /* hide the pkcs7 from the viewer */
3202 modified_the_body
= 1;
3204 return modified_the_body
;
3209 find_certificate_matching_recip_info(PKCS7_RECIP_INFO
*ri
)
3211 PERSONAL_CERT
*x
= NULL
;
3213 if(ps_global
->smime
){
3214 for(x
= (PERSONAL_CERT
*) ps_global
->smime
->personal_certs
; x
; x
=x
->next
){
3219 if(!X509_NAME_cmp(ri
->issuer_and_serial
->issuer
,X509_get_issuer_name(mine
)) &&
3220 !ASN1_INTEGER_cmp(ri
->issuer_and_serial
->serial
,X509_get_serialNumber(mine
))){
3230 static PERSONAL_CERT
*
3231 find_certificate_matching_pkcs7(PKCS7
*p7
)
3234 STACK_OF(PKCS7_RECIP_INFO
) *recips
;
3235 PERSONAL_CERT
*x
= NULL
;
3237 recips
= p7
->d
.enveloped
->recipientinfo
;
3239 for(i
=0; i
<sk_PKCS7_RECIP_INFO_num(recips
); i
++){
3240 PKCS7_RECIP_INFO
*ri
;
3242 ri
= sk_PKCS7_RECIP_INFO_value(recips
, i
);
3244 if((x
=find_certificate_matching_recip_info(ri
))!=0){
3252 /* decrypt an encrypted file.
3253 Args: fp - the path to the encrypted file.
3254 rv - a code that tells the caller what happened inside the function
3255 pcert - a personal certificate that was used to encrypt this file
3256 Returns the decoded text allocated in a char *, whose memory must be
3261 decrypt_file(char *fp
, int *rv
, PERSONAL_CERT
*pc
)
3265 BIO
*in
= NULL
, *out
= NULL
;
3266 long unsigned int len
;
3269 if(pc
== NULL
|| (text
= read_file(fp
, 0)) == NULL
|| *text
== '\0')
3272 tmp
= strchr(text
+ strlen("-----BEGIN PKCS7-----") + strlen(NEWLINE
), '-');
3273 if(tmp
!= NULL
) *tmp
= '\0';
3274 tmp
= text
+ strlen("-----BEGIN PKCS7-----") + strlen(NEWLINE
);
3276 ret
= rfc822_base64((unsigned char *)tmp
, strlen(tmp
), &len
);
3278 if((in
= BIO_new_mem_buf((char *)ret
, len
)) != NULL
){
3279 p7
= d2i_PKCS7_bio(in
, NULL
);
3283 if (text
) fs_give((void **)&text
);
3284 if (ret
) fs_give((void **)&ret
);
3286 if (rv
) *rv
= pc
->key
== NULL
? -1 : 1;
3288 out
= BIO_new(BIO_s_mem());
3289 (void) BIO_reset(out
);
3291 if(PKCS7_decrypt(p7
, pc
->key
, pc
->cert
, out
, 0) != 0){
3292 len
= BIO_get_mem_data(out
, &tmp
);
3293 text
= fs_get((len
+1)*sizeof(char));
3294 strncpy(text
, tmp
, len
);
3298 q_status_message1(SM_ORDER
, 1, 1, _("Error decrypting: %s"),
3299 (char *) openssl_error_string());
3306 * Try to decode (decrypt or verify a signature) a PKCS7 body
3307 * Returns non-zero if something was changed.
3310 do_decoding(BODY
*b
, long msgno
, const char *section
)
3312 int modified_the_body
= 0;
3316 EVP_PKEY
*key
= NULL
;
3317 PERSONAL_CERT
*pcert
= NULL
;
3318 char *what_we_did
= "";
3321 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"));
3326 * Extract binary data from part to an in-memory store
3330 if(get_body_sparep_type(b
->sparep
) == P7Type
)
3331 p7
= (PKCS7
*) get_body_sparep_data(b
->sparep
);
3334 p7
= get_pkcs7_from_part(msgno
, section
&& *section
? section
: "1");
3336 q_status_message1(SM_ORDER
, 2, 2, "Couldn't load PKCS7 object: %s",
3337 (char*) openssl_error_string());
3342 * Save the PKCS7 object for later dealings by the user interface.
3343 * It will be cleaned up when the body is garbage collected.
3345 b
->sparep
= create_body_sparep(P7Type
, p7
);
3348 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7
), PKCS7_type_is_enveloped(p7
)));
3350 if(PKCS7_type_is_signed(p7
)){
3353 out
= BIO_new(BIO_s_mem());
3354 (void) BIO_reset(out
);
3355 BIO_puts(out
, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3357 sigok
= do_signature_verify(p7
, NULL
, out
, 0);
3359 what_we_did
= sigok
? _("This message was cryptographically signed.") :
3360 _("This message was cryptographically signed but the signature could not be verified.");
3362 /* make sure it's null terminated */
3363 BIO_write(out
, null
, 1);
3365 else if(!PKCS7_type_is_enveloped(p7
)){
3366 q_status_message(SM_ORDER
, 1, 1, "PKCS7 object not recognised.");
3369 else{ /* It *is* enveloped */
3372 what_we_did
= _("This message was encrypted.");
3374 /* now need to find a cert that can decrypt this */
3375 pcert
= find_certificate_matching_pkcs7(p7
);
3378 q_status_message(SM_ORDER
, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3382 recip
= pcert
->cert
;
3384 if(!load_private_key(pcert
)
3386 && ps_global
->smime
->need_passphrase
3387 && !ps_global
->smime
->already_auto_asked
){
3388 /* Couldn't load key with blank password, ask user */
3389 ps_global
->smime
->already_auto_asked
= 1;
3390 if(pith_opt_smime_get_passphrase
){
3391 (*pith_opt_smime_get_passphrase
)();
3392 load_private_key(pcert
);
3400 out
= BIO_new(BIO_s_mem());
3401 (void) BIO_reset(out
);
3402 BIO_puts(out
, "MIME-Version: 1.0\r\n");
3404 decrypt_result
= PKCS7_decrypt(p7
, key
, recip
, out
, 0);
3406 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
3407 forget_private_keys();
3409 if(!decrypt_result
){
3410 q_status_message1(SM_ORDER
, 1, 1, _("Error decrypting: %s"),
3411 (char*) openssl_error_string());
3414 BIO_write(out
, null
, 1);
3418 * We've now produced a flattened MIME object in BIO out.
3419 * It needs to be turned back into a BODY.
3428 BUF_MEM
*bptr
= NULL
;
3431 BIO_get_mem_ptr(out
, &bptr
);
3435 /* look for start of body */
3436 bstart
= strstr(h
, "\r\n\r\n");
3440 * Some clients do not canonicalize before encrypting, so
3441 * look for "\n\n" instead.
3443 bstart
= strstr(h
, "\n\n");
3447 for(lines
= 0, bstart
= h
; (bstart
= strchr(bstart
, '\n')) != NULL
;
3449 h
= t
= fs_get(strlen(bptr
->data
) + lines
+ 1);
3451 for(s
= bptr
->data
; *s
!= '\0'; s
++)
3452 if(*s
== '\n' && *(s
-1) != '\r'){
3459 bstart
= strstr(h
, "\r\n\r\n");
3464 q_status_message(SM_ORDER
, 3, 3, _("Encrypted data couldn't be parsed."));
3468 bstart
+= 4; /* skip over CRLF*2 */
3470 INIT(&s
, mail_string
, bstart
, strlen(bstart
));
3471 rfc822_parse_msg_full(&env
, &body
, h
, bstart
-h
-2, &s
, BADHOST
, 0, 0);
3472 mail_free_envelope(&env
); /* Don't care about this */
3474 if(body
->type
== TYPEMULTIPART
3475 && !strucmp(body
->subtype
, "SIGNED")){
3476 char *cookie
= NULL
;
3478 for (param
= body
->parameter
; param
&& !cookie
; param
= param
->next
)
3479 if (!strucmp (param
->attribute
,"BOUNDARY")) cookie
= param
->value
;
3481 st
= fs_get(sizeof(SIZEDTEXT
));
3482 st
->data
= (void *) cpystr(bstart
+ strlen(cookie
)+4); /* 4 = strlen("--\r\n") */
3483 st
->size
= body
->nested
.part
->next
->body
.mime
.offset
- 2*(strlen(cookie
) + 4);
3484 body
->sparep
= create_body_sparep(SizedText
, (void *)st
);
3487 q_status_message(SM_ORDER
, 3, 3, _("Couldn't find cookie in attachment list."));
3489 body
->mime
.offset
= 0;
3490 body
->mime
.text
.size
= 0;
3493 * Now convert original body (application/pkcs7-mime)
3494 * to a multipart body with one sub-part (the decrypted body).
3495 * Note that the sub-part may also be multipart!
3498 b
->type
= TYPEMULTIPART
;
3500 fs_give((void **) &b
->subtype
);
3503 * This subtype is used in mailview.c to annotate the display of
3504 * encrypted or signed messages. We know for sure then that it's a PKCS7
3505 * part because the sparep field is set to the PKCS7 object (see above).
3507 b
->subtype
= cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE
);
3508 b
->encoding
= ENC8BIT
;
3511 fs_give((void **) &b
->description
);
3513 b
->description
= cpystr(what_we_did
);
3515 if(b
->disposition
.type
)
3516 fs_give((void **) &b
->disposition
.type
);
3518 if(b
->contents
.text
.data
)
3519 fs_give((void **) &b
->contents
.text
.data
);
3522 mail_free_body_parameter(&b
->parameter
);
3524 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3525 b
->nested
.part
= fs_get(sizeof(PART
));
3526 b
->nested
.part
->body
= *body
;
3527 b
->nested
.part
->next
= NULL
;
3529 fs_give((void**) &body
);
3532 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3533 * the decrypted data. Otherwise, it'll try to load it from the original
3536 create_local_cache(bstart
-b
->nested
.part
->body
.mime
.offset
, bstart
, &b
->nested
.part
->body
, 0);
3538 modified_the_body
= 1;
3541 fs_give((void **) &h
);
3548 return modified_the_body
;
3553 * Recursively handle PKCS7 bodies in our message.
3555 * Returns non-zero if some fiddling was done.
3558 do_fiddle_smime_message(BODY
*b
, long msgno
, char *section
)
3560 int modified_the_body
= 0;
3565 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"));
3567 if(is_pkcs7_body(b
)){
3569 if(do_decoding(b
, msgno
, section
)){
3571 * b should now be a multipart message:
3572 * fiddle it too in case it's been multiply-encrypted!
3576 modified_the_body
= 1;
3580 if(b
->type
==TYPEMULTIPART
|| MIME_MSG(b
->type
, b
->subtype
)){
3586 if(MIME_MULT_SIGNED(b
->type
, b
->subtype
)){
3590 * Ahah. We have a multipart signed entity.
3593 * part 1 (signed thing)
3594 * part 2 (the pkcs7 signature)
3596 * We're going to convert that to
3598 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3599 * part 1 (signed thing)
3600 * part 2 has been freed
3602 * We also extract the signature from part 2 and save it
3603 * in the multipart body->sparep, and we add a description
3604 * in the multipart body->description.
3607 * The results of a decrypted message will be similar. It
3610 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3611 * part 1 (decrypted thing)
3614 modified_the_body
+= do_detached_signature_verify(b
, msgno
, section
);
3616 else if(MIME_MSG(b
->type
, b
->subtype
)){
3617 modified_the_body
+= do_fiddle_smime_message(b
->nested
.msg
->body
, msgno
, section
);
3621 for(p
=b
->nested
.part
,partNum
=1; p
; p
=p
->next
,partNum
++){
3622 /* Append part number to the section string */
3624 snprintf(newSec
, sizeof(newSec
), "%s%s%d", section
, *section
? "." : "", partNum
);
3626 modified_the_body
+= do_fiddle_smime_message(&p
->body
, msgno
, newSec
);
3631 return modified_the_body
;
3636 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3637 * Returns non-zero if something was changed.
3640 fiddle_smime_message(BODY
*b
, long msgno
)
3642 return do_fiddle_smime_message(b
, msgno
, "");
3646 /********************************************************************************/
3650 * Output a string in a distinctive style
3653 gf_puts_uline(char *txt
, gf_io_t pc
)
3655 pc(TAG_EMBED
); pc(TAG_BOLDON
);
3657 pc(TAG_EMBED
); pc(TAG_BOLDOFF
);
3660 /* get_chain_for_cert: error and level are mandatory arguments */
3662 get_chain_for_cert(X509
*cert
, int *error
, int *level
)
3664 STACK_OF(X509
) *chain
= NULL
;
3665 X509_STORE_CTX
*ctx
;
3667 int rc
; /* return code */
3672 if((s_cert_store
!= NULL
) && (ctx
= X509_STORE_CTX_new()) != NULL
){
3673 X509_STORE_set_flags(s_cert_store
, 0);
3674 if(!X509_STORE_CTX_init(ctx
, s_cert_store
, cert
, NULL
))
3675 *error
= X509_STORE_CTX_get_error(ctx
);
3676 else if((chain
= sk_X509_new_null()) != NULL
){
3677 for(x
= cert
; ; x
= xtmp
){
3679 sk_X509_push(chain
, X509_dup(x
));
3680 rc
= X509_STORE_CTX_get1_issuer(&xtmp
, ctx
, x
);
3685 if(!X509_check_issued(xtmp
, xtmp
))
3689 X509_STORE_CTX_free(ctx
);
3696 * Sign a message. Called from call_mailer in send.c.
3698 * This takes the header for the outgoing message as well as a pointer
3699 * to the current body (which may be reallocated).
3700 * The last argument (BODY **bp) is an argument that tells Alpine
3701 * if the body has 8 bit. if *bp is not null we compute two signatures
3702 * one for the quoted-printable encoded message, and another for the
3703 * 8bit encoded message. We return the signature for the 8bit encoded
3704 * part in p2->body.mime.text.data.
3705 * The reason why we compute two signatures is so that we can decide
3706 * which one to use later, and we only do it in the case that *bp is
3707 * not null. If we did not do this, then we might not be able to sign
3708 * a message until we log in to the smtp server, so instead of doing
3709 * that, we get ready for any possible situation we might find.
3712 sign_outgoing_message(METAENV
*header
, BODY
**bodyP
, int dont_detach
, BODY
**bp
)
3714 STORE_S
*outs
= NULL
;
3715 STORE_S
*outs_2
= NULL
;
3716 BODY
*body
= *bodyP
;
3717 BODY
*newBody
= NULL
;
3720 PERSONAL_CERT
*pcert
;
3727 STACK_OF(X509
) *chain
;
3728 const EVP_MD
*md
= EVP_sha256(); /* use this digest instead of sha1 */
3729 int result
= 0, error
;
3730 int flags
= dont_detach
? 0 : PKCS7_DETACHED
;
3733 dprint((9, "sign_outgoing_message()"));
3737 /* Look for a private key matching the sender address... */
3739 pcert
= match_personal_cert(header
->env
);
3742 q_status_message(SM_ORDER
, 3, 3, _("Couldn't find the certificate needed to sign."));
3746 if(!load_private_key(pcert
) && ps_global
->smime
&& ps_global
->smime
->need_passphrase
){
3747 /* Couldn't load key with blank password, try again */
3748 if(pith_opt_smime_get_passphrase
){
3749 (*pith_opt_smime_get_passphrase
)();
3750 load_private_key(pcert
);
3757 if(((chain
= get_chain_for_cert(pcert
->cert
, &error
, &level
)) != NULL
&& error
)
3759 sk_X509_pop_free(chain
, X509_free
);
3764 q_status_message(SM_ORDER
, 1, 1,
3765 _("Not all certificates needed to verify signature included in signed message"));
3767 in
= body_to_bio(body
);
3769 flags
|= PKCS7_PARTIAL
;
3770 if((p7
= PKCS7_sign(NULL
, NULL
, chain
, in
, flags
)) != NULL
3771 && PKCS7_sign_add_signer(p7
, pcert
->cert
, pcert
->key
, md
, flags
))
3772 PKCS7_final(p7
, in
, flags
);
3775 int i
, save_encoding
;
3777 for(i
= 0; (i
<= ENCMAX
) && body_encodings
[i
]; i
++);
3779 if(i
> ENCMAX
){ /* no empty encoding slots! */
3783 save_encoding
= (*bp
)->encoding
;
3784 body_encodings
[(*bp
)->encoding
= i
] = body_encodings
[ENC8BIT
];
3786 in_2
= body_to_bio(body
);
3788 body_encodings
[i
] = NULL
;
3789 (*bp
)->encoding
= save_encoding
;
3794 if((p7_2
= PKCS7_sign(NULL
, NULL
, chain
, in_2
, flags
)) != NULL
3795 && PKCS7_sign_add_signer(p7_2
, pcert
->cert
, pcert
->key
, md
, flags
))
3796 PKCS7_final(p7_2
, in_2
, flags
);
3799 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
3800 forget_private_keys();
3803 sk_X509_pop_free(chain
, X509_free
);
3806 q_status_message(SM_ORDER
, 1, 1, _("Error creating signed object."));
3810 outs
= so_get(BioType
, NULL
, EDIT_ACCESS
);
3811 out
= bio_from_store(outs
);
3813 i2d_PKCS7_bio(out
, p7
);
3814 (void) BIO_flush(out
);
3816 so_seek(outs
, 0, SEEK_SET
);
3818 if(bp
&& *bp
&& p7_2
){
3819 outs_2
= so_get(BioType
, NULL
, EDIT_ACCESS
);
3820 out_2
= bio_from_store(outs_2
);
3822 i2d_PKCS7_bio(out_2
, p7_2
);
3823 (void) BIO_flush(out_2
);
3825 so_seek(outs_2
, 0, SEEK_SET
);
3828 if((flags
&PKCS7_DETACHED
)==0){
3830 /* the simple case: the signed data is in the pkcs7 object */
3832 newBody
= mail_newbody();
3834 setup_pkcs7_body_for_signature(newBody
, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3836 newBody
->contents
.text
.data
= (unsigned char *) outs
;
3845 * We have to create a new body as follows:
3847 * multipart/signed; blah blah blah
3848 * reference to existing body
3853 newBody
= mail_newbody();
3855 newBody
->type
= TYPEMULTIPART
;
3856 newBody
->subtype
= cpystr("signed");
3857 newBody
->encoding
= ENC7BIT
;
3859 set_parameter(&newBody
->parameter
, "protocol", "application/pkcs7-signature");
3860 set_parameter(&newBody
->parameter
, "micalg", "sha-256");
3862 p1
= mail_newbody_part();
3863 p2
= mail_newbody_part();
3866 * This is nasty. We're just copying the body in here,
3867 * but since our newBody is freed at the end of call_mailer,
3868 * we mustn't let this body (the original one) be freed twice.
3870 p1
->body
= *body
; /* ARRGH. This is special cased at the end of call_mailer */
3874 setup_pkcs7_body_for_signature(&p2
->body
, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL
);
3875 p2
->body
.mime
.text
.data
= (unsigned char *) outs_2
;
3876 p2
->body
.contents
.text
.data
= (unsigned char *) outs
;
3878 newBody
->nested
.part
= p1
;
3891 if(p7_2
) PKCS7_free(p7_2
);
3895 dprint((9, "sign_outgoing_message returns %d", result
));
3901 new_smime_struct(void)
3903 SMIME_STUFF_S
*ret
= NULL
;
3905 ret
= (SMIME_STUFF_S
*) fs_get(sizeof(*ret
));
3906 memset((void *) ret
, 0, sizeof(*ret
));
3907 ret
->publictype
= Nada
;
3914 free_smime_struct(SMIME_STUFF_S
**smime
)
3916 if(smime
&& *smime
){
3917 if((*smime
)->passphrase_emailaddr
){
3919 for(i
= 0; (*smime
)->passphrase_emailaddr
[i
] != NULL
; i
++)
3920 fs_give((void **) &(*smime
)->passphrase_emailaddr
[i
]);
3921 fs_give((void **) (*smime
)->passphrase_emailaddr
);
3924 if((*smime
)->publicpath
)
3925 fs_give((void **) &(*smime
)->publicpath
);
3927 if((*smime
)->publiccertlist
)
3928 free_certlist(&(*smime
)->publiccertlist
);
3930 if((*smime
)->backuppubliccertlist
)
3931 free_certlist(&(*smime
)->backuppubliccertlist
);
3933 if((*smime
)->cacertlist
)
3934 free_certlist(&(*smime
)->cacertlist
);
3936 if((*smime
)->backupcacertlist
)
3937 free_certlist(&(*smime
)->backupcacertlist
);
3939 if((*smime
)->privatecertlist
)
3940 free_certlist(&(*smime
)->privatecertlist
);
3942 if((*smime
)->backupprivatecertlist
)
3943 free_certlist(&(*smime
)->backupprivatecertlist
);
3945 if((*smime
)->publiccontent
)
3946 fs_give((void **) &(*smime
)->publiccontent
);
3948 if((*smime
)->privatepath
)
3949 fs_give((void **) &(*smime
)->privatepath
);
3951 if((*smime
)->personal_certs
){
3954 pc
= (PERSONAL_CERT
*) (*smime
)->personal_certs
;
3955 free_personal_certs(&pc
);
3956 (*smime
)->personal_certs
= NULL
;
3959 if((*smime
)->privatecontent
)
3960 fs_give((void **) &(*smime
)->privatecontent
);
3962 if((*smime
)->capath
)
3963 fs_give((void **) &(*smime
)->capath
);
3965 if((*smime
)->cacontent
)
3966 fs_give((void **) &(*smime
)->cacontent
);
3968 fs_give((void **) smime
);