1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: smime.c 1176 2008-09-29 21:16:42Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2013-2018 Eduardo Chappa
8 * Copyright 2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
20 * This is based on a contribution from Jonathan Paisley
23 * Author: paisleyj@dcs.gla.ac.uk
28 #include "../pith/headers.h"
32 #include "../pith/osdep/canaccess.h"
33 #include "../pith/helptext.h"
34 #include "../pith/store.h"
35 #include "../pith/status.h"
36 #include "../pith/detach.h"
37 #include "../pith/conf.h"
38 #include "../pith/smkeys.h"
39 #include "../pith/smime.h"
40 #include "../pith/mailpart.h"
41 #include "../pith/reply.h"
42 #include "../pith/tempfile.h"
43 #include "../pith/readfile.h"
44 #include "../pith/remote.h"
45 #include "../pith/body.h"
47 #include "../pith/imap.h"
50 #include <openssl/buffer.h>
51 #include <openssl/x509v3.h>
52 #include <openssl/evp.h>
54 /* internal prototypes */
55 static void forget_private_keys(void);
56 static int app_RAND_load_file(const char *file
);
57 static void openssl_extra_randomness(void);
58 static int app_RAND_write_file(const char *file
);
59 static const char *openssl_error_string(void);
60 static int load_private_key(PERSONAL_CERT
*pcert
);
61 static void create_local_cache(char *h
, char *base
, BODY
*b
, int type
);
62 static long rfc822_output_func(void *b
, char *string
);
63 static void setup_pkcs7_body_for_signature(BODY
*b
, char *description
,
64 char *type
, char *filename
, char *smime_type
);
65 static BIO
*body_to_bio(BODY
*body
);
66 static BIO
*bio_from_store(STORE_S
*store
);
67 static STORE_S
*get_part_contents(long msgno
, const char *section
);
68 static PKCS7
*get_pkcs7_from_part(long msgno
, const char *section
);
69 static int do_signature_verify(PKCS7
*p7
, BIO
*in
, BIO
*out
, int silent
);
70 static int do_detached_signature_verify(BODY
*b
, long msgno
, char *section
);
71 static PERSONAL_CERT
*find_certificate_matching_pkcs7(PKCS7
*p7
);
72 static int do_decoding(BODY
*b
, long msgno
, const char *section
);
73 static void free_smime_struct(SMIME_STUFF_S
**smime
);
74 static void setup_storage_locations(void);
75 static int copy_container_to_dir(WhichCerts which
);
76 static int do_fiddle_smime_message(BODY
*b
, long msgno
, char *section
);
77 void setup_privatekey_storage(void);
78 int smime_extract_and_save_cert(PKCS7
*p7
);
79 int same_cert(X509
*, X509
*);
81 int load_key_and_cert(char *pathkeydir
, char *pathcertdir
, char **keyfile
, char **certfile
, EVP_PKEY
**pkey
, X509
**pcert
);
83 EVP_PKEY
*load_pkey_with_prompt(char *fpath
, char *text
, char *prompt
, int *);
84 void smime_remove_trailing_crlf(char **mimetext
, unsigned long *mimelen
, char **bodytext
, unsigned long *bodylen
);
85 void smime_remove_folding_space(char **mimetext
, unsigned long *mimelen
, char **bodytext
, unsigned long *bodylen
);
86 int smime_validate_extra_test(char *mimetext
, unsigned long mimelen
, char *bodytext
, unsigned long bodylen
, PKCS7
*p7
, int nflag
);
88 int (*pith_opt_smime_get_passphrase
)(void);
89 int (*pith_smime_import_certificate
)(char *, char *, char *, size_t);
90 int (*pith_smime_enter_password
)(char *, char *, size_t);
91 int (*pith_smime_confirm_save
)(char *);
93 static X509_STORE
*s_cert_store
;
95 /* State management for randomness functions below */
96 static int seeded
= 0;
100 * This code does not work in Windows, because of the PASSFILE thing, so
101 * I did not try to fix it. If you think it does need to be applied to
102 * the Windows version of alpine, there are more changes that are needed
103 * than fixing this function in this module. E. Chappa 09/28/17.
105 * load key from pathkeydir and cert from pathcertdir. It chooses the first
106 * key/certificate pair that matches. Delete pairs that you do not want used,
107 * if you do not want them selected. All parameters must be non-null.
108 * Memory freed by caller.
110 * -1 : user cancelled load
111 * 0 : load was successful
112 * 1 : there was an error in the loading.
115 load_key_and_cert(char *pathkeydir
, char *pathcertdir
, char **keyfile
,
116 char **certfile
, EVP_PKEY
**pkey
, X509
**pcert
)
118 char buf
[MAXPATH
+1], pathkey
[MAXPATH
+1], prompt
[MAILTMPLEN
];
121 int b
= 0, ret
= 1; /* assume error */
123 if(pathkeydir
== NULL
|| pathcertdir
== NULL
|| keyfile
== NULL
124 || pkey
== NULL
|| certfile
== NULL
|| pcert
== NULL
)
132 if((dirp
= opendir(pathkeydir
)) != NULL
){
133 while(b
== 0 && (d
=readdir(dirp
)) != NULL
){
136 if((ll
=strlen(d
->d_name
)) && ll
> 4){
137 if(!strcmp(d
->d_name
+ll
-4, ".key")){
138 strncpy(buf
, d
->d_name
, sizeof(buf
));
139 buf
[sizeof(buf
)-1] = '\0';
140 build_path(pathkey
, pathkeydir
, buf
, sizeof(pathkey
));
141 buf
[strlen(buf
)-4] = '\0';
142 snprintf(prompt
, sizeof(prompt
),
143 _("Enter password of key <%s> to unlock password file: "), buf
);
144 if((*pkey
= load_pkey_with_prompt(pathkey
, NULL
, prompt
, &ret
)) != NULL
){
145 if(load_cert_for_key(pathcertdir
, *pkey
, certfile
, pcert
)){
147 *keyfile
= cpystr(buf
);
149 EVP_PKEY_free(*pkey
);
151 q_status_message1(SM_ORDER
, 0, 2,
152 _("Cannot find certificate that matches key <%s>. Continuing..."), buf
);
164 /* setup a key and certificate to encrypt and decrypt a password file.
165 * These files will be saved in the .alpine-smime/.pwd directory, but its
166 * location can be setup in the command line with the -pwdcertdir option.
167 * Here are the rules:
169 * Check if the .alpine-smime/.pwd (or -pwdcertdir directory) exists,
170 * if not create it. If we are successful, move to the next step
172 * - If the user has a key/cert pair, in the .alpine-smime/.pwd dir
173 * setup is successful;
174 * - if the user does not have a key/cert pair, look to see if
175 * ps_global->smime->personal_certs is already setup, if so, use it.
176 * - if ps_global->smime->personal_certs is not set up, see if we can
177 * find a certificate/cert pair in the default locations at compilation
178 * time. (~/.alpine-smime/private and ~/.alpine-smime/public
179 * - if none of this is successful, create a key/certificate pair
180 * (TODO: implement this)
181 * - in any other case, setup is not successful.
183 * If setup is successful, setup ps_global->pwdcert.
184 * If any of this fails, ps_global->pwdcert will be null.
185 * Ok, that should do it.
187 * return values: 0 - everything is normal
188 * 1 - User could not unlock key or no key in directory.
189 * 2 - User cancelled to create self signed certificate
190 * -1 - we do not know which directory to use
191 * -2 - "-pwdcertdir" was given by user, but directory does not exist
192 * -3 - "DF_PASSWORD_DIR" exists but it is not a directory!!??
193 * -4 - we tried to create DF_PASSWORD_DIR but failed.
194 * -5 - password directory exists, but it is empty
198 setup_pwdcert(void **pwdcert
)
202 int setup_dir
= 0; /* make it non zero if we know which dir to use */
204 char pathdir
[MAXPATH
+1], pathkey
[MAXPATH
+1], fpath
[MAXPATH
+1], pathcert
[MAXPATH
+1];
205 char fpath2
[MAXPATH
+1], prompt
[MAILTMPLEN
];
206 char *keyfile
, *certfile
, *text
;
207 EVP_PKEY
*pkey
= NULL
;
209 PERSONAL_CERT
*pc
, *pc2
= NULL
;
210 static int was_here
= 0;
212 if(pwdcert
== NULL
|| was_here
== 1)
216 if(ps_global
->pwdcertdir
){
217 if(our_stat(ps_global
->pwdcertdir
, &sbuf
) == 0
218 && ((sbuf
.st_mode
& S_IFMT
) == S_IFDIR
)){
220 strncpy(pathdir
, ps_global
->pwdcertdir
, sizeof(pathdir
));
221 pathdir
[sizeof(pathdir
)-1] = '\0';
225 smime_path(DF_PASSWORD_DIR
, pathdir
, sizeof(pathdir
));
226 if(our_stat(pathdir
, &sbuf
) == 0){
227 if((sbuf
.st_mode
& S_IFMT
) == S_IFDIR
)
230 } else if(can_access(pathdir
, ACCESS_EXISTS
) != 0
231 && our_mkpath(pathdir
, 0700) == 0)
241 if(load_key_and_cert(pathdir
, pathdir
, &keyfile
, &certfile
, &pkey
, &pcert
) < 0){
246 if(ps_global
->pwdcertdir
== NULL
){ /* save the result of pwdcertdir */
247 ps_global
->pwdcertdir
= cpystr(pathdir
);
248 /* if the user gave a pwdcertdir and there is nothing there, do not
249 * continue. Let the user initialize on their own this directory.
251 if(certfile
== NULL
|| keyfile
== NULL
){
257 if(certfile
&& keyfile
){
258 pc
= (PERSONAL_CERT
*) fs_get(sizeof(PERSONAL_CERT
));
259 memset((void *)pc
, 0, sizeof(PERSONAL_CERT
));
263 pc
->cname
= certfile
;
264 *pwdcert
= (void *) pc
;
269 /* look to see if there are any certificates lying around, first
270 * we try to load ps_global->smime to see if that has information
271 * we can use. If we are the process filling the smime structure
272 * we deinit at the end, since this might not do a full init.
274 if(ps_global
&& ps_global
->smime
&& !ps_global
->smime
->inited
){
279 /* at this point ps_global->smime->inited == 1 */
280 if(ps_global
->smime
&& ps_global
->smime
->personal_certs
!= NULL
){
281 pc
= (PERSONAL_CERT
*) ps_global
->smime
->personal_certs
;
282 if(ps_global
->smime
->privatetype
== Directory
){
283 build_path(pathkey
, ps_global
->smime
->privatepath
, pc
->name
, sizeof(pathkey
));
284 strncat(pathkey
, ".key", 4);
285 pathkey
[sizeof(pathkey
)-1] = '\0';
287 } else if (ps_global
->smime
->privatetype
== Container
){
288 if(pc
->keytext
== NULL
){ /* we should *never* be here, but just in case */
289 if(ps_global
->smime
->privatecontent
!= NULL
){
290 char tmp
[MAILTMPLEN
], *s
, *t
, c
;
291 snprintf(tmp
, sizeof(tmp
), "%s%s", EMAILADDRLEADER
, pc
->name
);
292 tmp
[sizeof(tmp
)-1] = '\0';
293 if((s
= strstr(ps_global
->smime
->privatecontent
, tmp
)) != NULL
){
294 if((t
= strstr(s
+strlen(tmp
), EMAILADDRLEADER
)) != NULL
){
297 pc
->keytext
= cpystr(s
+ strlen(tmp
) + strlen(NEWLINE
));
301 pc
->keytext
= cpystr(s
+ strlen(tmp
) + strlen(NEWLINE
));
305 if(pc
->keytext
!= NULL
) /* we should go straigth here */
307 } else if (ps_global
->smime
->privatetype
== Keychain
){
308 pathkey
[0] = '\0'; /* no apple key chain support yet */
311 if((pathkey
&& *pathkey
) || text
){
312 snprintf(prompt
, sizeof(prompt
),
313 _("Enter password of key <%s> to unlock password file: "), pc
->name
);
315 if((pkey
= load_pkey_with_prompt(pathkey
, text
, prompt
, NULL
)) != NULL
){
316 pc2
= (PERSONAL_CERT
*) fs_get(sizeof(PERSONAL_CERT
));
317 memset((void *)pc2
, 0, sizeof(PERSONAL_CERT
));
318 pc2
->name
= cpystr(pc
->name
);
320 pc2
->cert
= X509_dup(pc
->cert
);
322 /* now copy the keys and certs, starting by the key... */
323 build_path(fpath
, pathdir
, pc
->name
, sizeof(fpath
));
324 strncat(fpath
, ".key", 4);
325 fpath
[sizeof(fpath
)-1] = '\0';
326 if(our_stat(fpath
, &sbuf
) == 0){ /* if fpath exists */
327 if((sbuf
.st_mode
& S_IFMT
) == S_IFREG
) /* and is a regular file */
328 setup_dir
++; /* we are done */
329 } else if(ps_global
->smime
->privatetype
== Directory
){
330 if(our_copy(fpath
, pathkey
) == 0)
332 } else if(ps_global
->smime
->privatetype
== Container
){
334 if((out
= BIO_new_file(fpath
, "w")) != NULL
){
335 if(BIO_puts(out
, pc
->keytext
) > 0)
339 } else if(ps_global
->smime
->privatetype
== Keychain
){
340 /* add support for Apple Mac OS X */
344 /* successful copy of key, now continue with certificate */
348 build_path(pathkey
, ps_global
->smime
->publicpath
, pc
->name
, sizeof(pathkey
));
349 strncat(pathkey
, ".crt", 4);
350 pathkey
[sizeof(pathkey
)-1] = '\0';
352 build_path(fpath
, pathdir
, pc
->name
, sizeof(fpath
));
353 strncat(fpath
, ".crt", 4);
354 fpath
[sizeof(fpath
)-1] = '\0';
356 if(our_stat(fpath
, &sbuf
) == 0){
357 if((sbuf
.st_mode
& S_IFMT
) == S_IFREG
)
360 else if(ps_global
->smime
->privatetype
== Directory
){
361 if(our_copy(fpath
, pathkey
) == 0)
363 } else if(ps_global
->smime
->privatetype
== Container
) {
365 if((out
= BIO_new_file(fpath
, "w")) != NULL
){
366 if(PEM_write_bio_X509(out
, pc
->cert
))
370 } else if (ps_global
->smime
->privatetype
== Keychain
) {
371 /* add support for Mac OS X */
376 *pwdcert
= (void *) pc2
;
381 free_personal_certs(&pc2
);
382 } /* if (pathkey...) */
383 } /* if(ps_global->smime->personal_certs) */
387 /* PATHCERTDIR(Private) must be null, so create a path */
388 set_current_val(&ps_global
->vars
[V_PRIVATEKEY_DIR
], TRUE
, TRUE
);
389 smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, pathkey
, sizeof(pathkey
));
391 /* PATHCERTDIR(Public) must be null, so create a path */
392 set_current_val(&ps_global
->vars
[V_PUBLICCERT_DIR
], TRUE
, TRUE
);
393 smime_path(ps_global
->VAR_PUBLICCERT_DIR
, pathcert
, sizeof(pathcert
));
395 /* BUG: this does not support local containers */
396 load_key_and_cert(pathkey
, pathcert
, &keyfile
, &certfile
, &pkey
, &pcert
);
398 if(certfile
&& keyfile
){
399 build_path(fpath
, pathdir
, keyfile
, sizeof(fpath
));
400 strncat(fpath
, ".key", 4);
401 fpath
[sizeof(fpath
)-1] = '\0';
403 build_path(fpath2
, pathkey
, keyfile
, sizeof(fpath
));
404 strncat(fpath2
, ".key", 4);
405 fpath2
[sizeof(fpath2
)-1] = '\0';
407 if(our_copy(fpath
, fpath2
) == 0)
413 build_path(fpath
, pathdir
, certfile
, sizeof(fpath
));
414 build_path(fpath2
, pathcert
, certfile
, sizeof(fpath2
));
416 if(our_copy(fpath
, fpath2
) == 0)
422 if(keyfile
&& certfile
){
423 pc
= (PERSONAL_CERT
*) fs_get(sizeof(PERSONAL_CERT
));
424 memset((void *)pc
, 0, sizeof(PERSONAL_CERT
));
428 *pwdcert
= (void *) pc
;
429 fs_give((void **)&certfile
);
439 #endif /* PASSFILE */
441 /* smime_expunge_cert.
442 * Return values: < 0 there was an error.
443 * >=0 the number of messages expunged
446 smime_expunge_cert(WhichCerts ctype
)
449 CertList
*cl
, *dummy
, *data
;
450 char *path
, buf
[MAXPATH
+1];
453 if(DATACERT(ctype
)== NULL
)
456 /* data cert is the way we unify certificate management across
457 * functions, but it is not where we really save the information in the
458 * case ctype is equal to Private. What we will do is to update the
459 * datacert, and in the case of ctype equal to Private use the updated
460 * certdata to update the personal_certs data.
463 path
= PATHCERTDIR(ctype
);
466 /* add a fake certificate at the beginning of the list */
467 dummy
= fs_get(sizeof(CertList
));
468 memset((void *)dummy
, 0, sizeof(CertList
));
469 dummy
->next
= DATACERT(ctype
);
471 for(cl
= dummy
, count
= 0; cl
&& cl
->next
;){
472 if(cl
->next
->data
.deleted
== 0){
477 removed
= 1; /* assume success */
478 if(SMHOLDERTYPE(ctype
) == Directory
){
479 build_path(buf
, path
, cl
->next
->name
, sizeof(buf
));
480 if(ctype
== Private
&& strlen(buf
) + strlen(EXTCERT(Private
)) < sizeof(buf
)){
481 strncat(buf
, EXTCERT(Private
), 4);
482 buf
[sizeof(buf
)-1] = '\0';
485 if(our_unlink(buf
) < 0){
486 q_status_message1(SM_ORDER
, 3, 3, _("Error removing certificate %s"), cl
->next
->name
);
491 else if(SMHOLDERTYPE(ctype
) == Container
){
492 char *prefix
= ctype
== CACert
? CACERTSTORELEADER
: EMAILADDRLEADER
;
493 char tmp
[MAILTMPLEN
], *s
, *t
;
495 contents
= CONTENTCERTLIST(ctype
);
496 snprintf(tmp
, sizeof(tmp
), "%s%s", prefix
, cl
->next
->name
);
497 tmp
[sizeof(tmp
) - 1] = '\0';
498 if((s
= strstr(contents
, tmp
)) != NULL
){
499 if((t
= strstr(s
+strlen(tmp
), prefix
)) == NULL
)
502 memmove(s
, t
, strlen(t
)+1);
503 fs_resize((void **)&contents
, strlen(contents
)+1);
505 case Private
: ps_global
->smime
->privatecontent
= contents
; break;
506 case Public
: ps_global
->smime
->publiccontent
= contents
; break;
507 case CACert
: ps_global
->smime
->cacontent
= contents
; break;
513 } else { /* unhandled case */
517 count
++; /* count it! */
519 cl
->next
= data
->next
;
520 if(data
->name
) fs_give((void **)&data
->name
);
521 fs_give((void **)&data
);
525 q_status_message(SM_ORDER
, 3, 3, _("Error expunging certificate"));
528 case Private
: ps_global
->smime
->privatecertlist
= dummy
->next
; break;
529 case Public
: ps_global
->smime
->publiccertlist
= dummy
->next
; break;
530 case CACert
: ps_global
->smime
->cacertlist
= dummy
->next
; break;
533 fs_give((void **)&dummy
);
534 if(SMHOLDERTYPE(ctype
) == Container
){
535 if(copy_dir_to_container(ctype
, contents
) < 0)
539 q_status_message2(SM_ORDER
, 3, 3, _("Removed %s certificate%s"), comatose(count
), plural(count
));
542 q_status_message(SM_ORDER
, 3, 3, _("Error: No certificates were removed"));
547 mark_cert_deleted(WhichCerts ctype
, int num
, unsigned state
)
552 for(cl
= DATACERT(ctype
), i
= 0; cl
!= NULL
&& i
< num
; cl
= cl
->next
, i
++);
553 cl
->data
.deleted
= state
;
557 get_cert_deleted(WhichCerts ctype
, int num
)
562 for(cl
= DATACERT(ctype
), i
= 0; cl
!= NULL
&& i
< num
; cl
= cl
->next
, i
++);
563 return (cl
&& cl
->data
.deleted
) ? 1 : 0;
567 load_pkey_with_prompt(char *fpath
, char *text
, char *prompt
, int *ret
)
570 int rc
= 0; /* rc == 1, cancel, rc == 0 success */
571 char pass
[MAILTMPLEN
+1];
574 /* attempt to load with empty password */
575 in
= text
? BIO_new_mem_buf(text
, strlen(text
)) : BIO_new_file(fpath
, "r");
577 pkey
= PEM_read_bio_PrivateKey(in
, NULL
, NULL
, "");
578 if(pkey
!= NULL
) return pkey
;
581 if(pith_smime_enter_password
)
582 while(pkey
== NULL
&& rc
!= 1){
584 rc
= (*pith_smime_enter_password
)(prompt
, (char *)pass
, sizeof(pass
));
585 } while (rc
!=0 && rc
!=1 && rc
>0);
587 (void) BIO_reset(in
);
588 pkey
= PEM_read_bio_PrivateKey(in
, NULL
, NULL
, (char *)pass
);
593 if(ret
) *ret
= rc
== 1 ? -1 : pkey
!= NULL
? 0 : 1;
597 /* This is a tool for conf_screen, The return value must be zero when
598 * nothing changed, so if there is a failure in the import return 0
599 * and return 1 when we succeeded.\
600 * We call this function in two ways:
601 * either fname is null or not. If they fname is null, so is p_cert.
602 * if p_cert is not null, it is the PERSONAL_CERT structure of fname if this
603 * is available, otherwise we will fill it up here.
606 import_certificate(WhichCerts ctype
, PERSONAL_CERT
*p_cert
, char *fname
)
609 char filename
[MAXPATH
+1], full_filename
[MAXPATH
+1], buf
[MAXPATH
+1];
612 if(pith_smime_import_certificate
== NULL
613 || pith_smime_enter_password
== NULL
){
614 q_status_message(SM_ORDER
, 0, 2,
615 _("import of certificates not implemented yet!"));
620 what
= ctype
== Public
|| ctype
== CACert
? "certificate" : "key";
621 r
= (*pith_smime_import_certificate
)(filename
, full_filename
, what
, sizeof(filename
) - 20);
627 strncpy(full_filename
, fname
, sizeof(full_filename
));
628 if((s
= strrchr(full_filename
, '/')) != NULL
)
629 strncpy(filename
, s
+1, sizeof(filename
));
632 /* we are trying to import a new key for the password file. First we ask for the
633 * private key. Once this is loaded, we make a reasonable attempt to find the
634 * public key in the same directory as the key was loaded from. We do this by
635 * looking for a file with the correct public certificate name, then we look
636 * in the same private key, and if not, we ask the user for its location. If all
637 * of this works, we import the key and public to the password directory.
640 if(ctype
== Password
){
641 char PrivateKeyPath
[MAXPATH
+1], PublicCertPath
[MAXPATH
+1], s
[MAXPATH
+1];
642 char full_name_key
[MAXPATH
+1], full_name_cert
[MAXPATH
+1];
645 EVP_PKEY
*key
= p_cert
? p_cert
->key
: NULL
;
647 rc
= 1; /* assume success :) */
648 if(strlen(filename
) > 4){
649 strncpy(s
, filename
, sizeof(s
));
650 s
[sizeof(s
)-1] = '\0';
651 if(!strcmp(s
+ strlen(s
) - strlen(EXTCERT(Private
)), EXTCERT(Private
)))
652 s
[strlen(s
) - strlen(EXTCERT(Private
))] = '\0';
658 q_status_message(SM_ORDER
, 1, 3, _("Error in key name. Check file extension"));
662 snprintf(prompt
, sizeof(prompt
), _("Enter passphrase to unlock new key <%s>: "), filename
);
663 prompt
[sizeof(prompt
)-1] = '\0';
665 || (key
= load_pkey_with_prompt(full_filename
, NULL
, prompt
, NULL
)) != NULL
){
667 X509
*cert
= p_cert
? p_cert
->cert
: NULL
, *cert2
;
669 strncpy(full_name_key
, full_filename
, sizeof(full_filename
));
670 full_name_key
[sizeof(full_name_key
)-1] = '\0';
672 build_path(buf
, PATHCERTDIR(ctype
), s
, sizeof(buf
));
674 strncpy(PrivateKeyPath
, buf
, sizeof(PrivateKeyPath
));
675 PrivateKeyPath
[sizeof(PrivateKeyPath
)-1] = '\0';
676 if(strlen(PrivateKeyPath
) + 4 < sizeof(PrivateKeyPath
)){
677 strncat(PrivateKeyPath
, EXTCERT(Private
), 4);
678 PrivateKeyPath
[sizeof(PrivateKeyPath
)-1] = '\0';
681 /* remove .key extension and replace it with .crt extension */
682 strncpy(full_name_cert
, full_name_key
, sizeof(full_name_key
));
683 full_name_cert
[sizeof(full_name_cert
)-1] = '\0';
684 full_name_cert
[strlen(full_name_cert
) - strlen(EXTCERT(Private
))] = '\0';
685 strncat(full_name_cert
, EXTCERT(Public
), 4);
686 full_name_cert
[sizeof(full_name_cert
)-1] = '\0';
689 /* set up path to location where we will save public cert */
690 strncpy(PublicCertPath
, buf
, sizeof(PublicCertPath
));
691 PublicCertPath
[sizeof(PublicCertPath
)-1] = '\0';
692 if(strlen(PublicCertPath
) + 4 < sizeof(PublicCertPath
)){
693 strncat(PublicCertPath
, EXTCERT(Public
), 4);
694 PublicCertPath
[sizeof(PublicCertPath
)-1] = '\0';
696 /* attempt #1, use provided certificate,
697 * assumption is that full_name_cert is the file that this
698 * certificate derives from (which is obtained by substitution
699 * of .key extension in key by .crt extension)
701 if(cert
!= NULL
) /* attempt #1 */
702 use_this_file
= &full_name_cert
[0];
703 else if((ins
= BIO_new_file(full_name_cert
, "r")) != NULL
){
704 /* attempt #2 to guess public cert name, use .crt extension */
705 if((cert
= PEM_read_bio_X509(ins
, NULL
, NULL
, NULL
)) != NULL
){
706 use_this_file
= &full_name_cert
[0];
709 else{ /* attempt #3 to guess public cert name: use the original key */
710 if((ins
= BIO_new_file(full_name_key
, "r")) != NULL
){
711 if((cert
= PEM_read_bio_X509(ins
, NULL
, NULL
, NULL
)) != NULL
){
712 use_this_file
= &full_name_key
[0];
717 /* attempt #4, ask the user */
719 r
= (*pith_smime_import_certificate
)(filename
, use_this_file
, "certificate", sizeof(filename
) - 20);
721 if(ins
!= NULL
) BIO_free(ins
);
722 if(cert
!= NULL
) X509_free(cert
);
725 if((ins
= BIO_new_file(use_this_file
, "r")) != NULL
){
726 if((cert
= PEM_read_bio_X509(ins
, NULL
, NULL
, NULL
)) != NULL
)
729 q_status_message(SM_ORDER
, 1, 3, _("Error parsing certificate"));
732 q_status_message(SM_ORDER
, 1, 3, _("Error reading certificate"));
737 if(cert
!= NULL
){ /* check that certificate matches key */
738 if(!X509_check_private_key(cert
, key
)){
740 q_status_message(SM_ORDER
, 1, 3, _("Certificate does not match key"));
743 rc
= 1; /* Success! */
746 q_status_message(SM_ORDER
, 1, 3, _("Error in certificate file (not a certificate?)"));
748 if(rc
== 1){ /* if everything has been successful,
749 * copy the files to their final destination */
750 if(our_copy(PrivateKeyPath
, full_filename
) == 0){ /* <-- save the private key */
751 q_status_message(SM_ORDER
, 1, 3, _("Private key saved"));
752 if(our_copy(PublicCertPath
, use_this_file
) == 0){
753 char tmp
[MAILTMPLEN
];
756 if(!passfile_name(ps_global
->pinerc
, tmp
, sizeof(tmp
))
757 || !(fp
= our_fopen(tmp
, "rb"))){
758 q_status_message(SM_ORDER
, 1, 3, _("Error reading password file!"));
762 char tmp2
[MAILTMPLEN
];
765 PERSONAL_CERT
*pwdcert
, *pc
= p_cert
;
767 pwdcert
= (PERSONAL_CERT
*) ps_global
->pwdcert
;
769 setup_pwdcert((void **)&pwdcert
);
772 fgets(tmp2
, sizeof(tmp2
), fp
);
774 if(strcmp(tmp2
, "-----BEGIN PKCS7-----\n")){
775 if(encrypt_file((char *)tmp
, NULL
, pwdcert
))
782 text
= decrypt_file((char *)tmp
, NULL
, pwdcert
);
785 pc
= fs_get(sizeof(PERSONAL_CERT
));
786 memset((void *)pc
, 0, sizeof(PERSONAL_CERT
));
787 filename
[strlen(filename
)-strlen(EXTCERT(Private
))] = '\0';
788 pc
->name
= cpystr(filename
);
789 snprintf(buf
, sizeof(buf
), "%s%s", filename
, EXTCERT(Public
));
790 buf
[sizeof(buf
)-1] = '\0';
791 pc
->cname
= cpystr(buf
);
796 if(encrypt_file((char *)tmp
, text
, pc
)){ /* we did it! */
797 build_path(buf
, PATHCERTDIR(ctype
), pwdcert
->name
, sizeof(buf
));
798 strncat(buf
, EXTCERT(Private
), 4);
799 buf
[sizeof(buf
)-1] = '\0';
800 if(strcmp(PrivateKeyPath
, buf
)){
802 q_status_message(SM_ORDER
, 1, 3, _("Failed to remove old key"));
804 build_path(buf
, PATHCERTDIR(ctype
), pwdcert
->cname
, sizeof(buf
));
805 if(strcmp(PublicCertPath
, buf
)){
807 q_status_message(SM_ORDER
, 1, 3, _("Failed to remove old certificate"));
809 free_personal_certs((PERSONAL_CERT
**)&ps_global
->pwdcert
);
810 ps_global
->pwdcert
= pc
;
812 q_status_message(SM_ORDER
, 1, 3, _("Password file reencrypted"));
814 q_status_message(SM_ORDER
, 1, 3, _("Failed to reencrypt password file"));
818 q_status_message(SM_ORDER
, 1, 3, _("Error decrypting Password file"));
821 q_status_message(SM_ORDER
, 1, 3, _("Password file not encrypted and coulr not encrypt"));
827 q_status_message(SM_ORDER
, 1, 3, _("Error saving public certificate"));
828 if(our_unlink(PrivateKeyPath
) < 0)
829 q_status_message(SM_ORDER
, 1, 3, _("Error while cleaning private key"));
835 q_status_message(SM_ORDER
, 1, 3, _("Error saving private key"));
837 if(ins
!= NULL
) BIO_free(ins
);
838 if(rc
== 0 && cert
!= NULL
) X509_free(cert
);
842 q_status_message(SM_ORDER
, 1, 3, _("Error unlocking private key"));
847 #endif /* PASSFILE */
850 ps_global
->mangled_screen
= 1;
852 if (ctype
== Private
){
853 char prompt
[500], *s
, *t
;
854 EVP_PKEY
*key
= NULL
;
856 if(!ps_global
->smime
->privatecertlist
){
857 ps_global
->smime
->privatecertlist
= fs_get(sizeof(CertList
));
858 memset((void *)DATACERT(ctype
), 0, sizeof(CertList
));
861 for(s
= t
= filename
; (t
= strstr(s
, ".key")) != NULL
; s
= t
+ 1);
864 snprintf(prompt
, sizeof(prompt
), _("Enter passphrase for <%s>: "), filename
);
865 prompt
[sizeof(prompt
)-1] = '\0';
866 if((key
= load_pkey_with_prompt(full_filename
, NULL
, prompt
, NULL
)) != NULL
){
867 if(SMHOLDERTYPE(ctype
) == Directory
){
868 build_path(buf
, PATHCERTDIR(ctype
), filename
, sizeof(buf
));
869 if(strcmp(buf
+ strlen(buf
) - 4, EXTCERT(ctype
)) != 0 && strlen(buf
) + 4 < sizeof(buf
)){
870 strncat(buf
, EXTCERT(ctype
), 4);
871 buf
[sizeof(buf
)-1] = '\0';
873 rc
= our_copy(buf
, full_filename
);
875 else /* if(SMHOLDERTYPE(ctype) == Container){ */
876 rc
= add_file_to_container(ctype
, full_filename
, NULL
);
878 q_status_message(SM_ORDER
, 1, 3, _("Private key saved"));
880 q_status_message(SM_ORDER
, 1, 3, _("Error saving private key"));
881 if(ps_global
->smime
->publiccertlist
)
882 ps_global
->smime
->publiccertlist
->data
.renew
= 1;
885 q_status_message(SM_ORDER
, 1, 3, _("Problem unlocking key (not a certificate or wrong password)"));
886 } else if (ctype
== CACert
){
890 if((ins
= BIO_new_file(full_filename
, "r")) != NULL
){
891 if((cert
= PEM_read_bio_X509(ins
, NULL
, NULL
, NULL
)) != NULL
){
892 if(SMHOLDERTYPE(ctype
) == Directory
){
893 build_path(buf
, PATHCERTDIR(ctype
), filename
, sizeof(buf
));
894 if(strcmp(buf
+ strlen(buf
) - 4, ".crt") != 0 && strlen(buf
) + 4 < sizeof(buf
)){
895 strncat(buf
, EXTCERT(ctype
), 4);
896 buf
[sizeof(buf
)-1] = '\0';
899 rc
= our_copy(buf
, full_filename
);
901 else /* if(SMHOLDERTYPE(ctype) == Container){ */
902 rc
= add_file_to_container(ctype
, full_filename
, NULL
);
904 q_status_message(SM_ORDER
, 1, 3, _("Certificate saved"));
906 q_status_message(SM_ORDER
, 1, 3, _("Error saving certificate"));
907 X509_free(cert
); /* not needed anymore */
910 q_status_message(SM_ORDER
, 1, 3, _("Error in certificate file (not a certificate?)"));
914 } else { /* ctype == Public. save certificate, but first validate that it is one */
918 if((ins
= BIO_new_file(full_filename
, "r")) != NULL
){
919 if((cert
= PEM_read_bio_X509(ins
, NULL
, NULL
, NULL
)) != NULL
){
920 if(SMHOLDERTYPE(ctype
) == Directory
){
923 if((email
= get_x509_subject_email(cert
)) != NULL
){
925 for(i
= 0; email
[i
] != NULL
; i
++){
926 save_cert_for(email
[i
], cert
, Public
);
927 fs_give((void **)&email
[i
]);
929 fs_give((void **)email
);
931 if(strcmp(filename
+ strlen(filename
) - 4, ".crt") == 0)
932 filename
[strlen(filename
) - 4] = '\0';
933 save_cert_for(filename
, cert
, Public
);
935 else /* if(SMHOLDERTYPE(ctype) == Container){ */
936 add_file_to_container(ctype
, full_filename
, NULL
);
938 if(ps_global
->smime
->publiccertlist
)
939 ps_global
->smime
->publiccertlist
->data
.renew
= 1;
942 q_status_message(SM_ORDER
, 1, 3, _("Error in certificate file (not a certificate?)"));
946 if(DATACERT(ctype
)) RENEWCERT(DATACERT(ctype
)) = 1;
950 /* itype: information type to add: 0 - public, 1 - private.
951 * Memory freed by caller
954 print_private_key_information(char *email
, int itype
)
959 if(ps_global
->smime
== NULL
960 || ps_global
->smime
->personal_certs
== NULL
961 || (itype
!= 0 && itype
!= 1))
964 for(pc
= ps_global
->smime
->personal_certs
;
965 pc
!= NULL
&& strcmp(pc
->name
, email
) != 0; pc
= pc
->next
);
967 && !load_private_key(pc
)
969 && ps_global
->smime
->need_passphrase
){
970 if (*pith_opt_smime_get_passphrase
)
971 (*pith_opt_smime_get_passphrase
)();
972 load_private_key(pc
);
978 out
= BIO_new(BIO_s_mem());
979 if(itype
== 0) /* 0 means public */
980 EVP_PKEY_print_public(out
, pc
->key
, 0, NULL
);
981 else if (itype
== 1) /* 1 means private */
982 EVP_PKEY_print_private(out
, pc
->key
, 0, NULL
);
984 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
985 forget_private_keys();
991 * Forget any cached private keys
994 forget_private_keys(void)
996 PERSONAL_CERT
*pcert
;
1000 dprint((9, "forget_private_keys()"));
1001 if(ps_global
->smime
){
1002 ps_global
->smime
->already_auto_asked
= 0;
1003 for(pcert
=(PERSONAL_CERT
*) ps_global
->smime
->personal_certs
;
1008 EVP_PKEY_free(pcert
->key
);
1013 ps_global
->smime
->entered_passphrase
= 0;
1014 len
= sizeof(ps_global
->smime
->passphrase
);
1015 p
= ps_global
->smime
->passphrase
;
1022 /* modelled after signature_path in reply.c, but uses home dir instead of the
1023 * directory where the .pinerc is located, since according to documentation,
1024 * the .alpine-smime directories are subdirectories of the home directory
1027 smime_path(char *rpath
, char *fpath
, size_t len
)
1030 if(rpath
&& *rpath
){
1031 size_t spl
= strlen(rpath
);
1033 if(IS_REMOTE(rpath
)){
1035 strncpy(fpath
, rpath
, len
-1);
1036 fpath
[len
-1] = '\0';
1038 else if(is_absolute_path(rpath
)){
1039 strncpy(fpath
, rpath
, len
-1);
1040 fpath
[len
-1] = '\0';
1041 fnexpand(fpath
, len
);
1043 else if(ps_global
->VAR_OPER_DIR
){
1044 if(strlen(ps_global
->VAR_OPER_DIR
) + spl
< len
- 1)
1045 build_path(fpath
, ps_global
->VAR_OPER_DIR
, rpath
, len
);
1047 else if(ps_global
->home_dir
){
1048 if(strlen(ps_global
->home_dir
) + spl
< len
- 1)
1049 build_path(fpath
, ps_global
->home_dir
, rpath
, len
);
1052 return fpath
&& *fpath
? 1 : 0;
1058 * taken from openssl/apps/app_rand.c
1061 app_RAND_load_file(const char *file
)
1063 #define RANDBUFLEN 200
1064 char buffer
[RANDBUFLEN
];
1067 file
= RAND_file_name(buffer
, RANDBUFLEN
);
1069 if(file
== NULL
|| !RAND_load_file(file
, -1)){
1070 if(RAND_status() == 0){
1071 dprint((1, "unable to load 'random state'\n"));
1072 dprint((1, "This means that the random number generator has not been seeded\n"));
1073 dprint((1, "with much random data.\n"));
1085 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
1088 openssl_extra_randomness(void)
1096 /* if system doesn't have /dev/urandom */
1097 if(stat ("/dev/urandom", &sbuf
)){
1099 tf
= temp_nam(NULL
, NULL
);
1101 strncpy(tmp
, tf
, sizeof(tmp
));
1102 tmp
[sizeof(tmp
)-1] = '\0';
1103 fs_give((void **) &tf
);
1106 if((fd
= open(tmp
, O_WRONLY
|O_CREAT
|O_EXCL
, 0600)) < 0)
1107 i
= (unsigned long) tmp
;
1109 unlink(tmp
); /* don't need the file */
1110 fstat(fd
, &sbuf
); /* get information about the file */
1111 i
= sbuf
.st_ino
; /* remember its inode */
1112 close(fd
); /* or its descriptor */
1114 /* not great but it'll have to do */
1115 snprintf(tmp
+strlen(tmp
), sizeof(tmp
)-strlen(tmp
), "%.80s%lx%lx%lx",
1116 tcp_serverhost (),i
,
1117 (unsigned long) (time (0) ^ gethostid ()),
1118 (unsigned long) getpid ());
1119 RAND_seed(tmp
, strlen(tmp
));
1125 /* taken from openssl/apps/app_rand.c */
1127 app_RAND_write_file(const char *file
)
1133 * If we did not manage to read the seed file,
1134 * we should not write a low-entropy seed file back --
1135 * it would suppress a crucial warning the next time
1136 * we want to use it.
1141 file
= RAND_file_name(buffer
, sizeof buffer
);
1143 if(file
== NULL
|| !RAND_write_file(file
)){
1144 dprint((1, "unable to write 'random state'\n"));
1152 certlist_from_personal_certs(PERSONAL_CERT
*pc
)
1160 if((x
= get_cert_for(pc
->name
, Public
, 1)) != NULL
)
1161 cl
= smime_X509_to_cert_info(x
, pc
->name
);
1162 cl
->next
= certlist_from_personal_certs(pc
->next
);
1168 renew_cert_data(CertList
**data
, WhichCerts ctype
)
1171 if(ctype
== Private
){
1173 PERSONAL_CERT
*pc
= (PERSONAL_CERT
*)ps_global
->smime
->personal_certs
;
1175 free_certlist(data
);
1176 free_personal_certs(&pc
);
1177 setup_privatekey_storage();
1178 *data
= certlist_from_personal_certs((PERSONAL_CERT
*)ps_global
->smime
->personal_certs
);
1180 resort_certificates(data
, ctype
);
1181 RENEWCERT(*data
) = 0;
1183 ps_global
->smime
->privatecertlist
= *data
;
1185 if(ps_global
->smime
->privatecertlist
)
1186 RENEWCERT(ps_global
->smime
->privatecertlist
) = 0;
1188 X509_LOOKUP
*lookup
= NULL
;
1189 X509_STORE
*store
= NULL
;
1191 if((store
= X509_STORE_new()) != NULL
){
1192 if((lookup
= X509_STORE_add_lookup(store
, X509_LOOKUP_file())) != NULL
){
1193 free_certlist(data
);
1194 if(SMHOLDERTYPE(ctype
) == Directory
)
1195 add_certs_in_dir(lookup
, PATHCERTDIR(ctype
), EXTCERT(ctype
), data
);
1196 else /* if(SMHOLDERTYPE(ctype) == Container) */
1197 *data
= mem_to_certlist(CONTENTCERTLIST(ctype
), ctype
);
1199 resort_certificates(data
, ctype
);
1200 RENEWCERT(*data
) = 0;
1203 ps_global
->smime
->publiccertlist
= *data
;
1205 ps_global
->smime
->cacertlist
= *data
;
1207 X509_STORE_free(store
);
1211 setup_certs_backup_by_type(ctype
);
1221 /* Installed as an atexit() handler to save the random data */
1225 dprint((9, "smime_deinit()"));
1226 app_RAND_write_file(NULL
);
1227 if (s_cert_store
!= NULL
) X509_STORE_free(s_cert_store
);
1228 #ifdef ERR_free_strings
1230 #endif /* ERR_free_strings */
1233 #endif /* EVP_cleanup */
1234 free_smime_struct(&ps_global
->smime
);
1237 /* we renew the store when it has changed */
1241 if(ps_global
->smime
->inited
){
1242 if(s_cert_store
!= NULL
)
1243 X509_STORE_free(s_cert_store
);
1244 s_cert_store
= get_ca_store();
1248 /* Initialise openssl stuff if needed */
1252 if(F_OFF(F_DONT_DO_SMIME
, ps_global
) && !(ps_global
->smime
&& ps_global
->smime
->inited
)){
1254 dprint((9, "smime_init()"));
1255 if(!ps_global
->smime
)
1256 ps_global
->smime
= new_smime_struct();
1258 setup_storage_locations();
1260 s_cert_store
= get_ca_store();
1261 setup_certs_backup_by_type(CACert
);
1263 #ifdef OPENSSL_1_1_0
1264 OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
|OPENSSL_INIT_ADD_ALL_DIGESTS
|OPENSSL_INIT_LOAD_CRYPTO_STRINGS
, NULL
);
1266 OpenSSL_add_all_algorithms();
1267 ERR_load_crypto_strings();
1268 #endif /* OPENSSL_1_1_0 */
1270 app_RAND_load_file(NULL
);
1271 openssl_extra_randomness();
1272 ps_global
->smime
->inited
= 1;
1279 /* validate a certificate. Return value : 0 for no error, -1 for error.
1280 * In the latter case, set the openssl smime error in *error.
1283 smime_validate_cert(X509
*cert
, long *error
)
1285 X509_STORE_CTX
*csc
;
1289 if((s_cert_store
!= NULL
) && (csc
= X509_STORE_CTX_new()) != NULL
){
1290 X509_STORE_set_flags(s_cert_store
, 0);
1291 if(X509_STORE_CTX_init(csc
,s_cert_store
,cert
,NULL
)
1292 && X509_verify_cert(csc
) <= 0)
1293 *error
= X509_STORE_CTX_get_error(csc
);
1294 X509_STORE_CTX_free(csc
);
1296 return *error
? -1 : 0;
1300 get_personal_certs(char *path
)
1302 PERSONAL_CERT
*result
= NULL
;
1303 char buf2
[MAXPATH
], *fname
;
1309 #else /* _WINDOWS */
1310 struct _finddata_t dbuf
;
1311 char buf
[_MAX_PATH
+ 4];
1313 #endif /* _WINDOWS */
1315 ps_global
->smime
->privatepath
= cpystr(path
);
1318 dirp
= opendir(path
);
1320 while((d
=readdir(dirp
)) != NULL
){
1322 #else /* _WINDOWS */
1323 snprintf(buf
, sizeof(buf
), "%s%s*.*", path
, (path
[strlen(path
)-1] == '\\') ? "" : "\\");
1324 buf
[sizeof(buf
)-1] = '\0';
1325 if((findrv
= _findfirst(buf
, &dbuf
)) < 0)
1329 fname
= fname_to_utf8(dbuf
.name
);
1331 if((ll
=strlen(fname
)) && ll
> 4 && !strcmp(fname
+ll
-4, ".key")){
1333 /* copy file name to temp buffer */
1334 strncpy(buf2
, fname
, sizeof(buf2
)-1);
1335 buf2
[sizeof(buf2
)-1] = '\0';
1336 /* chop off ".key" trailier */
1337 buf2
[strlen(buf2
)-4] = '\0';
1338 /* Look for certificate */
1339 cert
= get_cert_for(buf2
, Public
, 1);
1344 /* create a new PERSONAL_CERT, fill it in */
1346 pc
= (PERSONAL_CERT
*) fs_get(sizeof(*pc
));
1348 pc
->name
= cpystr(buf2
);
1349 strncat(buf2
, EXTCERT(Public
), 4);
1350 pc
->cname
= cpystr(buf2
);
1352 /* Try to load the key with an empty password */
1353 pc
->key
= load_key(pc
, "", SM_NORMALCERT
);
1363 #else /* _WINDOWS */
1364 } while(_findnext(findrv
, &dbuf
) == 0);
1366 #endif /* !_WINDOWS */
1372 setup_privatekey_storage(void)
1374 char path
[MAXPATH
+1], *contents
;
1375 int privatekeycontainer
= 0;
1377 /* private keys in a container */
1378 if(ps_global
->VAR_PRIVATEKEY_CONTAINER
&& ps_global
->VAR_PRIVATEKEY_CONTAINER
[0]){
1380 privatekeycontainer
= 1;
1383 if(!smime_path(ps_global
->VAR_PRIVATEKEY_CONTAINER
, path
, MAXPATH
))
1384 privatekeycontainer
= 0;
1386 if(privatekeycontainer
&& !IS_REMOTE(path
)
1387 && ps_global
->VAR_OPER_DIR
1388 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1389 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1390 /* TRANSLATORS: First arg is the directory name, second is
1391 the file user wants to read but can't. */
1392 _("Can't read file outside %s: %s"),
1393 ps_global
->VAR_OPER_DIR
, path
);
1394 privatekeycontainer
= 0;
1397 if(privatekeycontainer
1398 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1399 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1401 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1402 privatekeycontainer
= 0;
1405 if(privatekeycontainer
&& path
[0]){
1406 ps_global
->smime
->privatetype
= Container
;
1407 ps_global
->smime
->privatepath
= cpystr(path
);
1410 ps_global
->smime
->privatecontent
= contents
;
1411 ps_global
->smime
->personal_certs
= mem_to_personal_certs(contents
);
1416 /* private keys in a directory of files */
1417 if(!privatekeycontainer
){
1418 ps_global
->smime
->privatetype
= Directory
;
1421 if(!(smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, path
, MAXPATH
)
1422 && !IS_REMOTE(path
)))
1423 ps_global
->smime
->privatetype
= Nada
;
1424 else if(can_access(path
, ACCESS_EXISTS
)){
1425 if(our_mkpath(path
, 0700)){
1426 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1427 ps_global
->smime
->privatetype
= Nada
;
1431 if(ps_global
->smime
->privatetype
== Directory
)
1432 ps_global
->smime
->personal_certs
= get_personal_certs(path
);
1434 setup_certs_backup_by_type(Private
);
1438 setup_storage_locations(void)
1440 int publiccertcontainer
= 0, cacertcontainer
= 0;
1441 char path
[MAXPATH
+1], *contents
;
1443 if(!ps_global
->smime
)
1446 #ifdef APPLEKEYCHAIN
1447 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
1448 ps_global
->smime
->publictype
= Keychain
;
1451 #endif /* APPLEKEYCHAIN */
1452 /* Public certificates in a container */
1453 if(ps_global
->VAR_PUBLICCERT_CONTAINER
&& ps_global
->VAR_PUBLICCERT_CONTAINER
[0]){
1455 publiccertcontainer
= 1;
1458 if(!smime_path(ps_global
->VAR_PUBLICCERT_CONTAINER
, path
, MAXPATH
))
1459 publiccertcontainer
= 0;
1461 if(publiccertcontainer
&& !IS_REMOTE(path
)
1462 && ps_global
->VAR_OPER_DIR
1463 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1464 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1465 /* TRANSLATORS: First arg is the directory name, second is
1466 the file user wants to read but can't. */
1467 _("Can't read file outside %s: %s"),
1468 ps_global
->VAR_OPER_DIR
, path
);
1469 publiccertcontainer
= 0;
1472 if(publiccertcontainer
1473 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1474 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1476 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1477 publiccertcontainer
= 0;
1480 if(publiccertcontainer
&& path
[0]){
1481 ps_global
->smime
->publictype
= Container
;
1482 ps_global
->smime
->publicpath
= cpystr(path
);
1485 ps_global
->smime
->publiccontent
= contents
;
1486 ps_global
->smime
->publiccertlist
= mem_to_certlist(contents
, Public
);
1491 /* Public certificates in a directory of files */
1492 if(!publiccertcontainer
){
1493 ps_global
->smime
->publictype
= Directory
;
1496 if(!(smime_path(ps_global
->VAR_PUBLICCERT_DIR
, path
, MAXPATH
)
1497 && !IS_REMOTE(path
)))
1498 ps_global
->smime
->publictype
= Nada
;
1499 else if(can_access(path
, ACCESS_EXISTS
)){
1500 if(our_mkpath(path
, 0700)){
1501 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1502 ps_global
->smime
->publictype
= Nada
;
1506 if(ps_global
->smime
->publictype
== Directory
)
1507 ps_global
->smime
->publicpath
= cpystr(path
);
1510 #ifdef APPLEKEYCHAIN
1512 #endif /* APPLEKEYCHAIN */
1514 setup_privatekey_storage();
1516 /* extra cacerts in a container */
1517 if(ps_global
->VAR_CACERT_CONTAINER
&& ps_global
->VAR_CACERT_CONTAINER
[0]){
1519 cacertcontainer
= 1;
1522 if(!smime_path(ps_global
->VAR_CACERT_CONTAINER
, path
, MAXPATH
))
1523 cacertcontainer
= 0;
1525 if(cacertcontainer
&& !IS_REMOTE(path
)
1526 && ps_global
->VAR_OPER_DIR
1527 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1528 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1529 /* TRANSLATORS: First arg is the directory name, second is
1530 the file user wants to read but can't. */
1531 _("Can't read file outside %s: %s"),
1532 ps_global
->VAR_OPER_DIR
, path
);
1533 cacertcontainer
= 0;
1537 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1538 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1540 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1541 cacertcontainer
= 0;
1544 if(cacertcontainer
&& path
[0]){
1545 ps_global
->smime
->catype
= Container
;
1546 ps_global
->smime
->capath
= cpystr(path
);
1547 ps_global
->smime
->cacontent
= contents
;
1549 ps_global
->smime
->cacertlist
= mem_to_certlist(contents
, CACert
);
1553 if(!cacertcontainer
){
1554 ps_global
->smime
->catype
= Directory
;
1557 if(!(smime_path(ps_global
->VAR_CACERT_DIR
, path
, MAXPATH
)
1558 && !IS_REMOTE(path
)))
1559 ps_global
->smime
->catype
= Nada
;
1560 else if(can_access(path
, ACCESS_EXISTS
)){
1561 if(our_mkpath(path
, 0700)){
1562 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1563 ps_global
->smime
->catype
= Nada
;
1567 if(ps_global
->smime
->catype
== Directory
)
1568 ps_global
->smime
->capath
= cpystr(path
);
1574 copy_publiccert_dir_to_container(void)
1576 return(copy_dir_to_container(Public
, NULL
));
1581 copy_publiccert_container_to_dir(void)
1583 return(copy_container_to_dir(Public
));
1588 copy_privatecert_dir_to_container(void)
1590 return(copy_dir_to_container(Private
, NULL
));
1595 copy_privatecert_container_to_dir(void)
1597 return(copy_container_to_dir(Private
));
1602 copy_cacert_dir_to_container(void)
1604 return(copy_dir_to_container(CACert
, NULL
));
1609 copy_cacert_container_to_dir(void)
1611 return(copy_container_to_dir(CACert
));
1614 /* Add the contents of a file to a container. Do not check the content
1615 * of the file, just add it using the format for that container. The
1616 * caller must check the format, so that there is no data corruption
1618 * return value: 0 - success,
1622 add_file_to_container(WhichCerts ctype
, char *fpath
, char *altname
)
1624 char *sep
= (ctype
== Public
|| ctype
== Private
)
1625 ? EMAILADDRLEADER
: CACERTSTORELEADER
;
1626 char *content
= ctype
== Public
? ps_global
->smime
->publiccontent
1627 : (ctype
== Private
? ps_global
->smime
->privatecontent
1628 : ps_global
->smime
->cacontent
);
1634 int rv
= -1; /* assume error */
1636 if(our_stat(fpath
, &sbuf
) < 0
1637 || (in
= so_get(FileStar
, fpath
, READ_ACCESS
| READ_FROM_LOCALE
)) == NULL
)
1642 else if((name
= strrchr(fpath
, '/')) != NULL
){
1644 if((ll
= strlen(++name
)) > 4 && strucmp(name
+ ll
- 4, EXTCERT(ctype
)) == 0)
1645 name
[ll
-strlen(EXTCERT(ctype
))] = '\0';
1651 fs_resize((void **)&content
, strlen(content
) + strlen(sep
) + strlen(name
) + sbuf
.st_size
+ 2*strlen(NEWLINE
) + 1); /* 1 = \0*/
1653 content
+= strlen(content
);
1656 s
= content
= fs_get(strlen(sep
) + strlen(name
) + sbuf
.st_size
+ strlen(NEWLINE
) + 1); /* 1 = \0 */
1659 strncat(content
, sep
, strlen(sep
));
1660 strncat(content
, name
, strlen(name
));
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
) - strlen(ret_dir
) + 1 < sizeof(dstpath
))
1931 snprintf(fpath
, sizeof(fpath
), "%s%c%s",
1932 dstpath
, tempfile
[strlen(ret_dir
)], configcontainer
);
1939 if(!IS_REMOTE(configpath
)){
1940 if(rename_file(tempfile
, fpath
) < 0){
1941 q_status_message2(SM_ORDER
, 3, 3,
1942 _("Can't rename %s to %s"), tempfile
, fpath
);
1944 } else q_status_message1(SM_ORDER
, 3, 3,
1945 _("saved container to %s"), fpath
);
1947 else { /* if the container is remote, copy it */
1951 if(rd
!= NULL
&& rename_file(tempfile
, rd
->lf
) < 0){
1952 q_status_message2(SM_ORDER
, 3, 3,
1953 _("Can't rename %s to %s"), tempfile
, rd
->lf
);
1959 if((e
= rd_update_remote(rd
, datebuf
)) != 0){
1961 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1962 _("Error opening temporary smime file %s: %s"),
1963 rd
->lf
, error_description(errno
));
1965 "write_remote_smime: error opening temp file %s\n",
1966 rd
->lf
? rd
->lf
: "?"));
1969 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1970 _("Error copying to %s: %s"),
1971 rd
->rn
, error_description(errno
));
1973 "write_remote_smime: error copying from %s to %s\n",
1974 rd
->lf
? rd
->lf
: "?", rd
->rn
? rd
->rn
: "?"));
1977 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
1978 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1981 rd_update_metadata(rd
, datebuf
);
1982 rd
->read_status
= 'W';
1985 rd_close_remdata(&rd
);
1992 fs_give((void **) &tempfile
);
1995 fs_give((void **) &ret_dir
);
1998 fs_give((void **) &configcontainer
);
2005 * returns 0 on success, -1 on failure
2008 copy_container_to_dir(WhichCerts which
)
2010 char path
[MAXPATH
+1], file
[MAXPATH
+1], buf
[MAXPATH
+1];
2012 char *contents
= NULL
;
2013 char *leader
= NULL
;
2014 char *filesuffix
= NULL
;
2015 char *configdir
= NULL
;
2016 char *configpath
= NULL
;
2017 char *tempfile
= NULL
;
2018 char *p
, *q
, *line
, *name
, *certtext
, *save_p
;
2022 dprint((9, "copy_container_to_dir(%s)", which
==Public
? "Public" : which
==Private
? "Private" : which
==CACert
? "CACert" : "?"));
2027 if(which
== Public
){
2028 leader
= EMAILADDRLEADER
;
2029 contents
= ps_global
->smime
->publiccontent
;
2030 configdir
= ps_global
->VAR_PUBLICCERT_DIR
;
2031 configpath
= ps_global
->smime
->publicpath
;
2032 filesuffix
= ".crt";
2033 if(!(configpath
&& configpath
[0])){
2034 #ifdef APPLEKEYCHAIN
2035 if(which
== Public
&& F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
2036 q_status_message(SM_ORDER
, 3, 3, _("Turn off the Keychain feature above first"));
2039 #endif /* APPLEKEYCHAIN */
2040 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
2044 fs_give((void **) &ps_global
->smime
->publicpath
);
2047 if(!(smime_path(ps_global
->VAR_PUBLICCERT_DIR
, path
, MAXPATH
)
2048 && !IS_REMOTE(path
))){
2049 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
2053 if(can_access(path
, ACCESS_EXISTS
)){
2054 if(our_mkpath(path
, 0700)){
2055 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
2060 ps_global
->smime
->publicpath
= cpystr(path
);
2061 configpath
= ps_global
->smime
->publicpath
;
2063 else if(which
== Private
){
2064 leader
= EMAILADDRLEADER
;
2065 contents
= ps_global
->smime
->privatecontent
;
2066 configdir
= ps_global
->VAR_PRIVATEKEY_DIR
;
2067 configpath
= ps_global
->smime
->privatepath
;
2068 filesuffix
= ".key";
2069 if(!(configpath
&& configpath
[0])){
2070 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
2074 fs_give((void **) &ps_global
->smime
->privatepath
);
2077 if(!(smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, path
, MAXPATH
)
2078 && !IS_REMOTE(path
))){
2079 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
2083 if(can_access(path
, ACCESS_EXISTS
)){
2084 if(our_mkpath(path
, 0700)){
2085 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
2090 ps_global
->smime
->privatepath
= cpystr(path
);
2091 configpath
= ps_global
->smime
->privatepath
;
2093 else if(which
== CACert
){
2094 leader
= CACERTSTORELEADER
;
2095 contents
= ps_global
->smime
->cacontent
;
2096 configdir
= ps_global
->VAR_CACERT_DIR
;
2097 configpath
= ps_global
->smime
->capath
;
2098 filesuffix
= ".crt";
2099 if(!(configpath
&& configpath
[0])){
2100 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
2104 fs_give((void **) &ps_global
->smime
->capath
);
2107 if(!(smime_path(ps_global
->VAR_CACERT_DIR
, path
, MAXPATH
)
2108 && !IS_REMOTE(path
))){
2109 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
2113 if(can_access(path
, ACCESS_EXISTS
)){
2114 if(our_mkpath(path
, 0700)){
2115 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
2120 ps_global
->smime
->capath
= cpystr(path
);
2121 configpath
= ps_global
->smime
->capath
;
2124 if(!(configdir
&& configdir
[0])){
2125 q_status_message(SM_ORDER
, 3, 3, _("Directory not defined"));
2129 if(!(configpath
&& configpath
[0])){
2130 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
2134 if(!(filesuffix
&& strlen(filesuffix
) == 4)){
2139 if(contents
&& *contents
){
2140 for(p
= contents
; *p
!= '\0';){
2143 while(*p
&& *p
!= '\n')
2152 if(strncmp(leader
, line
, strlen(leader
)) == 0){
2153 name
= line
+ strlen(leader
);
2155 if(strncmp("-----BEGIN", certtext
, strlen("-----BEGIN")) == 0){
2156 if((q
= strstr(certtext
, leader
)) != NULL
){
2159 else{ /* end of file */
2160 q
= certtext
+ strlen(certtext
);
2164 strncpy(buf
, name
, sizeof(buf
)-5);
2165 buf
[sizeof(buf
)-5] = '\0';
2166 strncat(buf
, filesuffix
, 5);
2167 build_path(file
, configpath
, buf
, sizeof(file
));
2169 in
= BIO_new_mem_buf(certtext
, q
-certtext
);
2171 tempfile
= tempfile_in_same_dir(file
, "az", NULL
);
2174 out
= BIO_new_file(tempfile
, "w");
2177 while((len
= BIO_read(in
, iobuf
, sizeof(iobuf
))) > 0)
2178 BIO_write(out
, iobuf
, len
);
2182 if(rename_file(tempfile
, file
) < 0){
2183 q_status_message2(SM_ORDER
, 3, 3,
2184 _("Can't rename %s to %s"),
2189 fs_give((void **) &tempfile
);
2206 #ifdef APPLEKEYCHAIN
2209 copy_publiccert_container_to_keychain(void)
2211 /* NOT IMPLEMNTED */
2216 copy_publiccert_keychain_to_container(void)
2218 /* NOT IMPLEMNTED */
2222 #endif /* APPLEKEYCHAIN */
2226 * Get a pointer to a string describing the most recent OpenSSL error.
2227 * It's statically allocated, so don't change or attempt to free it.
2230 openssl_error_string(void)
2233 const char *data
= NULL
;
2236 errn
= ERR_peek_error_line_data(NULL
, NULL
, &data
, NULL
);
2237 errs
= (char*) ERR_reason_error_string(errn
);
2244 return "unknown error";
2248 /* Return true if the body looks like a PKCS7 object */
2250 is_pkcs7_body(BODY
*body
)
2254 result
= body
->type
==TYPEAPPLICATION
&&
2256 (strucmp(body
->subtype
,"pkcs7-mime")==0 ||
2257 strucmp(body
->subtype
,"x-pkcs7-mime")==0 ||
2258 strucmp(body
->subtype
,"pkcs7-signature")==0 ||
2259 strucmp(body
->subtype
,"x-pkcs7-signature")==0);
2266 * Recursively stash a pointer to the decrypted data in our
2267 * manufactured body.
2268 * parameters: type: call of type 1, save the base and header for multipart messages
2269 call of type 0, do not save the base and header for multipart messages
2272 create_local_cache(char *h
, char *base
, BODY
*b
, int type
)
2274 if(b
->type
==TYPEMULTIPART
){
2278 cpytxt(&b
->mime
.text
, h
+b
->mime
.offset
, b
->mime
.text
.size
);
2279 cpytxt(&b
->contents
.text
, base
+ b
->contents
.offset
, b
->size
.bytes
);
2280 } else if(type
== 0){
2282 * We don't really want to copy the real body contents. It shouldn't be
2283 * used, and in the case of a message with attachments, we'll be
2284 * duplicating the files multiple times.
2286 cpytxt(&b
->contents
.text
, "BODY UNAVAILABLE", 16);
2288 for(p
=b
->nested
.part
; p
; p
=p
->next
)
2289 create_local_cache(h
, base
, (BODY
*) p
, type
);
2293 cpytxt(&b
->mime
.text
, h
+b
->mime
.offset
, b
->mime
.text
.size
);
2294 cpytxt(&b
->contents
.text
, base
+ b
->contents
.offset
, b
->size
.bytes
);
2300 rfc822_output_func(void *b
, char *string
)
2302 BIO
*bio
= (BIO
*) b
;
2304 return(string
? *string
? (BIO_puts(bio
, string
) > 0 ? 1L : 0L)
2305 : (BIO_puts(bio
, string
) >= 0 ? 1L : 0L)
2311 * Attempt to load the private key for the given PERSONAL_CERT.
2312 * This sets the appropriate passphrase globals in order to
2313 * interact with the user correctly.
2316 load_private_key(PERSONAL_CERT
*pcert
)
2320 /* Try empty password by default */
2321 char *password
= "";
2324 && (ps_global
->smime
->need_passphrase
2325 || ps_global
->smime
->entered_passphrase
)){
2326 /* We've already been in here and discovered we need a different password */
2328 if(ps_global
->smime
->entered_passphrase
)
2329 password
= (char *) ps_global
->smime
->passphrase
; /* already entered */
2336 if(!(pcert
->key
= load_key(pcert
, password
, SM_NORMALCERT
))){
2337 long err
= ERR_get_error();
2339 /* Couldn't load key... */
2341 if(ps_global
->smime
&& ps_global
->smime
->entered_passphrase
){
2343 /* The user got the password wrong maybe? */
2345 if((ERR_GET_LIB(err
)==ERR_LIB_EVP
&& ERR_GET_REASON(err
)==EVP_R_BAD_DECRYPT
) ||
2346 (ERR_GET_LIB(err
)==ERR_LIB_PEM
&& ERR_GET_REASON(err
)==PEM_R_BAD_DECRYPT
))
2347 q_status_message(SM_ORDER
| SM_DING
, 4, 4, _("Incorrect passphrase"));
2349 q_status_message1(SM_ORDER
, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2351 /* This passphrase is no good; forget it */
2352 ps_global
->smime
->entered_passphrase
= 0;
2355 if(ps_global
->smime
){
2356 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2357 ps_global
->smime
->need_passphrase
= 1;
2358 if(ps_global
->smime
->passphrase_emailaddr
){
2360 for(i
= 0; ps_global
->smime
->passphrase_emailaddr
[i
] != NULL
; i
++)
2361 fs_give((void **)&ps_global
->smime
->passphrase_emailaddr
[i
]);
2362 fs_give((void **) ps_global
->smime
->passphrase_emailaddr
);
2365 ps_global
->smime
->passphrase_emailaddr
= get_x509_subject_email(pcert
->cert
);
2371 /* This key will be cached, so we won't be called again */
2372 if(ps_global
->smime
){
2373 ps_global
->smime
->entered_passphrase
= 0;
2374 ps_global
->smime
->need_passphrase
= 0;
2386 setup_pkcs7_body_for_signature(BODY
*b
, char *description
, char *type
, char *filename
, char *smime_type
)
2388 b
->type
= TYPEAPPLICATION
;
2389 b
->subtype
= cpystr(type
);
2390 b
->encoding
= ENCBINARY
;
2391 b
->description
= cpystr(description
);
2393 b
->disposition
.type
= cpystr("attachment");
2394 set_parameter(&b
->disposition
.parameter
, "filename", filename
);
2396 set_parameter(&b
->parameter
, "name", filename
);
2397 if(smime_type
&& *smime_type
)
2398 set_parameter(&b
->parameter
, "smime-type", smime_type
);
2403 * Look for a personal certificate matching the
2407 match_personal_cert_to_email(ADDRESS
*a
)
2409 PERSONAL_CERT
*pcert
= NULL
;
2414 if(!a
|| !a
->mailbox
|| !a
->host
)
2417 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2419 if(ps_global
->smime
){
2420 for(pcert
=(PERSONAL_CERT
*) ps_global
->smime
->personal_certs
;
2427 email
= get_x509_subject_email(pcert
->cert
);
2431 for(i
= 0; email
[i
] && strucmp(email
[i
], buf
) != 0; i
++);
2432 if(email
[i
] != NULL
) done
++;
2433 for(i
= 0; email
[i
] != NULL
; i
++)
2434 fs_give((void **)&email
[i
]);
2435 fs_give((void **)email
);
2448 * Look for a personal certificate matching the from
2449 * (or reply_to? in the given envelope)
2452 match_personal_cert(ENVELOPE
*env
)
2454 PERSONAL_CERT
*pcert
;
2456 pcert
= match_personal_cert_to_email(env
->reply_to
);
2458 pcert
= match_personal_cert_to_email(env
->from
);
2465 * Flatten the given body into its MIME representation.
2466 * Return the result in a BIO.
2469 body_to_bio(BODY
*body
)
2474 bio
= BIO_new(BIO_s_mem());
2478 pine_encode_body(body
); /* this attaches random boundary strings to multiparts */
2479 pine_write_body_header(body
, rfc822_output_func
, bio
);
2480 pine_rfc822_output_body(body
, rfc822_output_func
, bio
);
2483 * Now need to truncate by two characters since the above
2486 if((len
=BIO_ctrl_pending(bio
)) > 1){
2487 BUF_MEM
*biobuf
= NULL
;
2489 BIO_get_mem_ptr(bio
, &biobuf
);
2491 BUF_MEM_grow(biobuf
, len
-2); /* remove CRLF */
2500 bio_from_store(STORE_S
*store
)
2504 if(store
&& store
->src
== BioType
&& store
->txt
){
2505 ret
= (BIO
*) store
->txt
;
2512 * Encrypt file; given a path (char *) fp, replace the file
2513 * by an encrypted version of it. If (char *) text is not null, then
2514 * replace the text of (char *) fp by the encrypted version of (char *) text.
2515 * certpath is the FULL path to the file containing the certificate used for
2517 * return value: 0 - failed to encrypt; 1 - success!
2520 encrypt_file(char *fp
, char *text
, PERSONAL_CERT
*pc
)
2522 const EVP_CIPHER
*cipher
= NULL
;
2523 STACK_OF(X509
) *encerts
= NULL
;
2531 cipher
= EVP_aes_256_cbc();
2532 encerts
= sk_X509_new_null();
2534 sk_X509_push(encerts
, X509_dup(pc
->cert
));
2537 if((out
= BIO_new(BIO_s_mem())) != NULL
){
2538 (void) BIO_reset(out
);
2539 BIO_puts(out
, text
);
2542 else if((out
= BIO_new_file(fp
, "rb")) != NULL
)
2543 BIO_read_filename(out
, fp
);
2545 if((p7
= PKCS7_encrypt(encerts
, out
, cipher
, 0)) != NULL
){
2546 BIO_set_close(out
, BIO_CLOSE
);
2548 if((out
= BIO_new_file(fp
, "w")) != NULL
){
2550 rv
= PEM_write_bio_PKCS7(out
, p7
);
2558 sk_X509_pop_free(encerts
, X509_free
);
2564 * Encrypt a message on the way out. Called from call_mailer in send.c
2565 * The body may be reallocated.
2568 encrypt_outgoing_message(METAENV
*header
, BODY
**bodyP
)
2573 const EVP_CIPHER
*cipher
= NULL
;
2574 STACK_OF(X509
) *encerts
= NULL
;
2575 STORE_S
*outs
= NULL
;
2578 BODY
*body
= *bodyP
;
2579 BODY
*newBody
= NULL
;
2584 dprint((9, "encrypt_outgoing_message()"));
2587 cipher
= EVP_aes_256_cbc();
2589 encerts
= sk_X509_new_null();
2591 /* Look for a certificate for each of the recipients */
2592 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
2593 if(pf
->type
== Address
&& pf
->rcptto
&& pf
->addr
&& *pf
->addr
){
2594 for(a
=*pf
->addr
; a
; a
=a
->next
){
2595 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2597 if((cert
= get_cert_for(buf
, Public
, 1)) != NULL
){
2598 sk_X509_push(encerts
,cert
);
2600 q_status_message2(SM_ORDER
, 1, 1,
2601 _("Unable to find certificate for <%s@%s>"),
2602 a
->mailbox
, a
->host
);
2608 /* add the sender's certificate so that they can decrypt the message too */
2609 for(a
=header
->env
->from
; a
; a
= a
->next
){
2610 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2612 if((cert
= get_cert_for(buf
, Public
, 1)) != NULL
2613 && sk_X509_find(encerts
, cert
) == -1)
2614 sk_X509_push(encerts
,cert
);
2617 in
= body_to_bio(body
);
2619 p7
= PKCS7_encrypt(encerts
, in
, cipher
, 0);
2621 outs
= so_get(BioType
, NULL
, EDIT_ACCESS
);
2622 out
= bio_from_store(outs
);
2624 i2d_PKCS7_bio(out
, p7
);
2625 (void) BIO_flush(out
);
2627 so_seek(outs
, 0, SEEK_SET
);
2629 newBody
= mail_newbody();
2631 newBody
->type
= TYPEAPPLICATION
;
2632 newBody
->subtype
= cpystr("pkcs7-mime");
2633 newBody
->encoding
= ENCBINARY
;
2635 newBody
->disposition
.type
= cpystr("attachment");
2636 set_parameter(&newBody
->disposition
.parameter
, "filename", "smime.p7m");
2638 newBody
->description
= cpystr("S/MIME Encrypted Message");
2639 set_parameter(&newBody
->parameter
, "smime-type", "enveloped-data");
2640 set_parameter(&newBody
->parameter
, "name", "smime.p7m");
2642 newBody
->contents
.text
.data
= (unsigned char *) outs
;
2652 sk_X509_pop_free(encerts
, X509_free
);
2654 dprint((9, "encrypt_outgoing_message returns %d", result
));
2660 Get (and decode) the body of the given section of msg
2663 get_part_contents(long msgno
, const char *section
)
2667 STORE_S
*store
= NULL
;
2670 store
= so_get(CharStar
, NULL
, EDIT_ACCESS
);
2672 gf_set_so_writec(&pc
,store
);
2674 err
= detach(ps_global
->mail_stream
, msgno
, (char*) section
, 0L, &len
, pc
, NULL
, 0L);
2676 gf_clear_so_writec(store
);
2678 so_seek(store
, 0, SEEK_SET
);
2689 get_pkcs7_from_part(long msgno
,const char *section
)
2691 STORE_S
*store
= NULL
;
2695 store
= get_part_contents(msgno
, section
);
2698 if(store
->src
== CharStar
){
2702 * We're reaching inside the STORE_S structure. We should
2703 * probably have a way to get the length, instead.
2705 len
= (int) (store
->eod
- store
->dp
);
2706 in
= BIO_new_mem_buf(store
->txt
, len
);
2708 else{ /* just copy it */
2711 in
= BIO_new(BIO_s_mem());
2712 (void) BIO_reset(in
);
2714 so_seek(store
, 0L, 0);
2715 while(so_readc(&c
, store
)){
2716 BIO_write(in
, &c
, 1);
2721 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2722 if((p7
=d2i_PKCS7_bio(in
,NULL
)) == NULL
){
2736 same_cert(X509
*x
, X509
*cert
)
2738 char bufcert
[256], bufx
[256];
2741 get_fingerprint(cert
, EVP_md5(), bufcert
, sizeof(bufcert
), ":");
2742 get_fingerprint(x
, EVP_md5(), bufx
, sizeof(bufx
), ":");
2743 if(strcmp(bufx
, bufcert
) == 0)
2750 /* extract and save certificates from a PKCS7 package.
2752 * 0 - no errors. Either the certificate was in public/
2753 * or we could save it there.
2754 * < 0 - the certificate was not in public/ and the user
2755 * did not save it there.
2759 smime_extract_and_save_cert(PKCS7
*p7
)
2761 STACK_OF(X509
) *signers
;
2764 int i
, j
, rv
, already_saved
;
2767 /* any signers for this message? */
2768 if((signers
= PKCS7_get0_signers(p7
, NULL
, 0)) == NULL
)
2772 for(i
= 0; i
< sk_X509_num(signers
); i
++){
2773 if((x
= sk_X509_value(signers
,i
)) == NULL
)
2776 if((email
= get_x509_subject_email(x
)) != NULL
){
2777 for(j
= 0; email
[j
] != NULL
; j
++){
2779 /* check if we have the certificate for this address */
2780 cert
= get_cert_for(email
[j
], Public
, 1);
2781 /* if we have one, check if it is the one packaged */
2783 already_saved
= same_cert(x
, cert
);
2787 /* if not saved, try to save it */
2788 if(already_saved
== 0
2789 && (*pith_smime_confirm_save
)(email
[j
]) == 1)
2790 save_cert_for(email
[j
], x
, Public
);
2792 /* check if it got saved */
2793 cert
= get_cert_for(email
[j
], Public
, 1);
2794 /* if saved, all is good */
2797 else /* else, we do not have this certificate saved */
2800 fs_give((void **) &email
[j
]);
2802 fs_give((void **) email
);
2805 sk_X509_free(signers
);
2811 * Try to verify a signature.
2813 * p7 - the pkcs7 object to verify
2814 * in - the plain data to verify (NULL if not detached)
2815 * out - BIO to which to write the opaque data
2816 * silent - if non zero, do not print errors, only print success.
2819 do_signature_verify(PKCS7
*p7
, BIO
*in
, BIO
*out
, int silent
)
2821 STACK_OF(X509
) *otherCerts
= NULL
;
2828 if(!silent
) q_status_message(SM_ORDER
| SM_DING
, 2, 2,
2829 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2834 flags
= F_ON(F_USE_CERT_STORE_ONLY
, ps_global
) ? PKCS7_NOINTERN
: 0;
2836 if(ps_global
->smime
->publiccertlist
== NULL
){
2837 renew_cert_data(&ps_global
->smime
->publiccertlist
, Public
);
2838 for(cl
= ps_global
->smime
->publiccertlist
; cl
; cl
= cl
->next
){
2839 if(cl
->x509_cert
== NULL
){
2840 char *s
= strrchr(cl
->name
, '.');
2842 cl
->x509_cert
= get_cert_for(cl
->name
, Public
, 1);
2848 if(ps_global
->smime
->publiccertlist
){
2849 otherCerts
= sk_X509_new_null();
2850 for(cl
= ps_global
->smime
->publiccertlist
; cl
; cl
= cl
->next
)
2851 if(cl
->x509_cert
!= NULL
)
2852 sk_X509_push(otherCerts
, X509_dup(cl
->x509_cert
));
2855 result
= PKCS7_verify(p7
, otherCerts
, s_cert_store
, in
, out
, flags
);
2858 q_status_message(SM_ORDER
, 1, 1, _("S/MIME signature verified ok"));
2861 err
= ERR_peek_error_line_data(NULL
, NULL
, &data
, NULL
);
2863 if(out
&& err
==ERR_PACK(ERR_LIB_PKCS7
,PKCS7_F_PKCS7_VERIFY
,PKCS7_R_CERTIFICATE_VERIFY_ERROR
)){
2866 * verification failed due to an error in verifying a certificate.
2867 * Just write the "out" BIO, and leave. Of course let the user
2868 * know about this. Make two more attempts to get the data out. The
2869 * last one should succeed. In any case, let the user know why it
2872 if(PKCS7_verify(p7
, otherCerts
, s_cert_store
, in
, out
, PKCS7_NOVERIFY
) == 0)
2873 PKCS7_verify(p7
, otherCerts
, s_cert_store
, in
, out
, PKCS7_NOVERIFY
|PKCS7_NOSIGS
);
2875 if (!silent
) q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2876 _("Couldn't verify S/MIME signature: %s"), (char *) openssl_error_string());
2879 sk_X509_pop_free(otherCerts
, X509_free
);
2884 /* Big comment, explaining the mess that exists out there, and how we deal
2885 with it, and also how we solve the problems that are created this way.
2887 When Alpine sends a message, it constructs that message, computes the
2888 signature, but then it forgets the message it signed and reconstructs it
2889 again. Since it signs a message containing a notice about "mime aware
2890 tools", but it does not send that we do not include that in the part
2891 that is signed, and that takes care of much of the problems.
2893 Another problem is what is received from the servers. All servers tested
2894 seem to transmit the message that was signed intact and Alpine can check
2895 the signature correctly. That is not a problem. The problem arises when
2896 the message includes attachments. In this case different servers send
2897 different things, so it will be up to us to figure out what is the text
2898 that was actually signed. Confused? here is the story:
2900 When a message containing and attachment is sent by Alpine, UW-IMAP,
2901 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2902 that was sent by Alpine, but GMX.com, Exchange, and probably other
2903 servers add a trailing \r\n in the message, so when validating the
2904 signature, these messages will not validate. There are several things
2907 1. Add a trailing \r\n to any message that contains attachments, sign that
2908 and send that. In this way, all messages will validate with all
2911 2. Compatibility mode: If a message has an attachment, contains a trailing
2912 \r\n and does not validate (sent by an earlier version of Alpine),
2913 remove the trailing \r\n and try to revalidate again.
2915 3. We do not add \r\n to validate a message that we sent, because that
2916 would only work in Alpine, and not in any other client. That would
2917 not be a good thing to do.
2921 Now we have to deal with encrypted and signed messages. The problem is
2922 that c-client makes all its pointers point to "on disk" content, but
2923 since we decrypted the data earlier, we have to make sure of two things.
2924 One is that we saved that data (so we do not have to decrypt it again)
2925 and second that we can use it.
2927 In order to save the data we use create_local_cache, so that we do not
2928 have to redecrypt the message. Once this is saved, c-client functions will
2929 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2933 When we are trying to verify messages with detached signatures, some
2934 imap servers send incorrect information in the mail_fetch_mime call. By
2935 incorrect I mean that this is not fetched directly from the message, but
2936 it is read from the message, processed, and then the processed part is
2937 sent to us, so this text might not agree with what is in the message,
2938 and so the validation of the signature might fail. However, the good
2939 news is that the message validates if saved to a local folder. This
2940 means that if normal validation does not work we can make it work by
2941 saving the message locally and validating that. This is implemented
2942 below, and causes delay in the display of the message. I am considering
2943 at this time not to do this automatically, but wait for the user to tell
2944 us to do it for them by means of a command available in the
2945 mail_view_screen. This might help in other situations, where a message
2946 is supposed to have an attachment, but it can not be seen in the
2947 processed text. Nevertheless, at this time, this is automatic, and is
2948 causing a delay in the processing of the message, but it is validating
2949 correctly all messages.
2953 When the user sends a message as encrypted and signed, this code used to
2954 encrypt first, and then sign the pkcs7 body, but it turns out that some
2955 other clients can not handle these messages. While we could argue that the
2956 other clients need to improve, we will support reading messages in both
2957 ways, and will send messages using this technique; that is, signed first,
2958 encrypted second. It seems that all tested clients support this way, so it
2959 should be safe to do so.
2962 typedef struct smime_filter_s
{
2966 SMIME_FILTER_S sig_filter
[] = {
2967 {smime_remove_trailing_crlf
},
2968 {smime_remove_folding_space
}
2971 #define TOTAL_FILTERS (sizeof(sig_filter)/sizeof(sig_filter[0]))
2972 #define TOTAL_SIGFLTR (1 << TOTAL_FILTERS) /* not good, keep filters to a low number */
2975 smime_remove_trailing_crlf(char **mimetext
, unsigned long *mimelen
,
2976 char **bodytext
, unsigned long *bodylen
)
2978 if(*bodylen
> 2 && !strncmp(*bodytext
+*bodylen
-2, "\r\n", 2))
2983 smime_remove_folding_space(char **mimetext
, unsigned long *mimelen
,
2984 char **bodytext
, unsigned long *bodylen
)
2987 unsigned long mlen
= *mimelen
;
2990 for (s
= t
= *mimetext
; t
- *mimetext
< *mimelen
; ){
2991 if(*t
== '\r' && *(t
+1) == '\n' && (*(t
+2) == '\t' || *(t
+2) == ' ')){
3004 smime_validate_extra_test(char *mimetext
, unsigned long mimelen
, char *bodytext
, unsigned long bodylen
, PKCS7
*p7
, int nflag
)
3006 int result
, i
, j
, flag
;
3007 char *mtext
, *btext
;
3008 unsigned long mlen
, blen
;
3011 mtext
= mimelen
? fs_get(mimelen
+1) : NULL
;
3012 btext
= fs_get(bodylen
+1);
3015 flag
= 1; /* silence all failures */
3016 for(i
= 1; result
== 0 && i
< TOTAL_SIGFLTR
; i
++){
3017 if((in
= BIO_new(BIO_s_mem())) == NULL
)
3020 (void) BIO_reset(in
);
3022 if(i
+1 == TOTAL_SIGFLTR
)
3026 strncpy(mtext
, mimetext
, mlen
= mimelen
);
3027 strncpy(btext
, bodytext
, blen
= bodylen
);
3028 for(j
= 0; j
< TOTAL_FILTERS
; j
++)
3030 (sig_filter
[j
].filter
)(&mtext
, &mlen
, &btext
, &blen
);
3032 BIO_write(in
, mtext
, mlen
);
3033 BIO_write(in
, btext
, blen
);
3034 result
= do_signature_verify(p7
, in
, NULL
, flag
);
3037 if(mtext
) fs_give((void **)&mtext
);
3038 if(btext
) fs_give((void **)&btext
);
3043 * Given a multipart body of type multipart/signed, attempt to verify it.
3044 * Returns non-zero if the body was changed.
3047 do_detached_signature_verify(BODY
*b
, long msgno
, char *section
)
3052 int result
, modified_the_body
= 0;
3053 int flag
; /* 1 silent, 0 not silent */
3054 unsigned long mimelen
, bodylen
;
3055 char newSec
[100], *mimetext
, *bodytext
;
3059 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"));
3063 /* if it was signed and then encrypted, use the decrypted text
3064 * to check the validity of the signature
3067 if(get_body_sparep_type(b
->sparep
) == SizedText
){
3068 /* bodytext includes mimetext */
3069 st
= (SIZEDTEXT
*) get_body_sparep_data(b
->sparep
);
3070 bodytext
= (char *) st
->data
;
3077 snprintf(newSec
, sizeof(newSec
), "%s%s1", section
? section
: "", (section
&& *section
) ? "." : "");
3078 mimetext
= mail_fetch_mime(ps_global
->mail_stream
, msgno
, (char*) newSec
, &mimelen
, 0);
3080 bodytext
= mail_fetch_body (ps_global
->mail_stream
, msgno
, (char*) newSec
, &bodylen
, 0);
3082 if(mimetext
== NULL
|| bodytext
== NULL
)
3083 return modified_the_body
;
3086 snprintf(newSec
, sizeof(newSec
), "%s%s2", section
? section
: "", (section
&& *section
) ? "." : "");
3088 if((p7
= get_pkcs7_from_part(msgno
, newSec
)) == NULL
3089 || (in
= BIO_new(BIO_s_mem())) == NULL
)
3090 return modified_the_body
;
3092 (void) BIO_reset(in
);
3093 if(mimetext
!= NULL
)
3094 BIO_write(in
, mimetext
, mimelen
);
3095 BIO_write(in
, bodytext
, bodylen
);
3097 smime_extract_and_save_cert(p7
);
3099 if((result
= do_signature_verify(p7
, in
, NULL
, 1)) == 0){
3100 flag
= (mimelen
== 0 || !IS_REMOTE(ps_global
->mail_stream
->mailbox
))
3102 result
= smime_validate_extra_test(mimetext
, mimelen
, bodytext
, bodylen
, p7
, flag
);
3104 return modified_the_body
;
3106 && mimelen
> 0 /* do not do this for encrypted messages */
3107 && IS_REMOTE(ps_global
->mail_stream
->mailbox
)){
3109 unsigned long hlen
, tlen
;
3113 if((in
= BIO_new(BIO_s_mem())) != NULL
3114 && (fetch
= mail_fetch_header(ps_global
->mail_stream
, msgno
, NULL
,
3115 NULL
, &hlen
, FT_PEEK
)) != NULL
3116 && (msg_so
= so_get(CharStar
, NULL
, WRITE_ACCESS
)) != NULL
3117 && so_nputs(msg_so
, fetch
, (long) hlen
)
3118 && (fetch
= pine_mail_fetch_text(ps_global
->mail_stream
, msgno
, NULL
,
3119 &tlen
, FT_PEEK
)) != NULL
3120 && so_nputs(msg_so
, fetch
, tlen
)){
3122 char *h
= (char *) so_text(msg_so
);
3123 char *bstart
= strstr(h
, "\r\n\r\n");
3128 INIT(&bs
, mail_string
, bstart
, tlen
);
3129 rfc822_parse_msg_full(&env
, &body
, h
, bstart
-h
-4, &bs
, BADHOST
, 0, 0);
3130 mail_free_envelope(&env
);
3132 mail_free_body_part(&b
->nested
.part
);
3133 tmpB
= mail_body_section(body
, (unsigned char *) section
);
3134 if(MIME_MSG(tmpB
->type
, tmpB
->subtype
))
3135 b
->nested
.part
= tmpB
->nested
.msg
->body
->nested
.part
;
3137 b
->nested
.part
= tmpB
->nested
.part
;
3138 create_local_cache(bstart
, bstart
, &b
->nested
.part
->body
, 1);
3139 modified_the_body
= 1;
3141 snprintf(newSec
, sizeof(newSec
), "%s%s1", section
? section
: "", (section
&& *section
) ? "." : "");
3143 mimetext
= mail_fetch_mime(ps_global
->mail_stream
, msgno
, (char*) newSec
, &mimelen
, 0);
3146 bodytext
= mail_fetch_body (ps_global
->mail_stream
, msgno
, (char*) newSec
, &bodylen
, 0);
3148 if (mimetext
== NULL
|| bodytext
== NULL
)
3149 return modified_the_body
;
3151 snprintf(newSec
, sizeof(newSec
), "%s%s2", section
? section
: "", (section
&& *section
) ? "." : "");
3153 if((p7
= get_pkcs7_from_part(msgno
, newSec
)) == NULL
)
3154 return modified_the_body
;
3156 (void) BIO_reset(in
);
3157 BIO_write(in
, mimetext
, mimelen
);
3158 BIO_write(in
, bodytext
, bodylen
);
3161 if((result
= do_signature_verify(p7
, in
, NULL
, 1)) == 0){
3162 result
= smime_validate_extra_test(mimetext
, mimelen
, bodytext
, bodylen
, p7
, 0);
3164 return modified_the_body
;
3172 fs_give((void**) &b
->subtype
);
3174 b
->subtype
= cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE
);
3175 b
->encoding
= ENC8BIT
;
3178 fs_give ((void**) &b
->description
);
3180 what_we_did
= result
? _("This message was cryptographically signed.") :
3181 _("This message was cryptographically signed but the signature could not be verified.");
3183 b
->description
= cpystr(what_we_did
);
3185 b
->sparep
= create_body_sparep(P7Type
, p7
);
3189 /* p is signed plaintext */
3191 mail_free_body_part(&p
->next
); /* hide the pkcs7 from the viewer */
3193 modified_the_body
= 1;
3195 return modified_the_body
;
3200 find_certificate_matching_recip_info(PKCS7_RECIP_INFO
*ri
)
3202 PERSONAL_CERT
*x
= NULL
;
3204 if(ps_global
->smime
){
3205 for(x
= (PERSONAL_CERT
*) ps_global
->smime
->personal_certs
; x
; x
=x
->next
){
3210 if(!X509_NAME_cmp(ri
->issuer_and_serial
->issuer
,X509_get_issuer_name(mine
)) &&
3211 !ASN1_INTEGER_cmp(ri
->issuer_and_serial
->serial
,X509_get_serialNumber(mine
))){
3221 static PERSONAL_CERT
*
3222 find_certificate_matching_pkcs7(PKCS7
*p7
)
3225 STACK_OF(PKCS7_RECIP_INFO
) *recips
;
3226 PERSONAL_CERT
*x
= NULL
;
3228 recips
= p7
->d
.enveloped
->recipientinfo
;
3230 for(i
=0; i
<sk_PKCS7_RECIP_INFO_num(recips
); i
++){
3231 PKCS7_RECIP_INFO
*ri
;
3233 ri
= sk_PKCS7_RECIP_INFO_value(recips
, i
);
3235 if((x
=find_certificate_matching_recip_info(ri
))!=0){
3243 /* decrypt an encrypted file.
3244 Args: fp - the path to the encrypted file.
3245 rv - a code that tells the caller what happened inside the function
3246 pcert - a personal certificate that was used to encrypt this file
3247 Returns the decoded text allocated in a char *, whose memory must be
3252 decrypt_file(char *fp
, int *rv
, PERSONAL_CERT
*pc
)
3256 BIO
*in
= NULL
, *out
= NULL
;
3258 long unsigned int len
;
3261 if(pc
== NULL
|| (text
= read_file(fp
, 0)) == NULL
|| *text
== '\0')
3264 tmp
= strchr(text
+ strlen("-----BEGIN PKCS7-----") + strlen(NEWLINE
), '-');
3265 if(tmp
!= NULL
) *tmp
= '\0';
3266 tmp
= text
+ strlen("-----BEGIN PKCS7-----") + strlen(NEWLINE
);
3268 ret
= rfc822_base64((unsigned char *)tmp
, strlen(tmp
), &len
);
3270 if((in
= BIO_new_mem_buf((char *)ret
, len
)) != NULL
){
3271 p7
= d2i_PKCS7_bio(in
, NULL
);
3275 if (text
) fs_give((void **)&text
);
3276 if (ret
) fs_give((void **)&ret
);
3278 if (rv
) *rv
= pc
->key
== NULL
? -1 : 1;
3280 out
= BIO_new(BIO_s_mem());
3281 (void) BIO_reset(out
);
3283 if(PKCS7_decrypt(p7
, pc
->key
, pc
->cert
, out
, 0) != 0){
3284 len
= BIO_get_mem_data(out
, &tmp
);
3285 text
= fs_get((len
+1)*sizeof(char));
3286 strncpy(text
, tmp
, len
);
3290 q_status_message1(SM_ORDER
, 1, 1, _("Error decrypting: %s"),
3291 (char *) openssl_error_string());
3298 * Try to decode (decrypt or verify a signature) a PKCS7 body
3299 * Returns non-zero if something was changed.
3302 do_decoding(BODY
*b
, long msgno
, const char *section
)
3304 int modified_the_body
= 0;
3308 EVP_PKEY
*key
= NULL
;
3309 PERSONAL_CERT
*pcert
= NULL
;
3310 char *what_we_did
= "";
3313 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"));
3318 * Extract binary data from part to an in-memory store
3322 if(get_body_sparep_type(b
->sparep
) == P7Type
)
3323 p7
= (PKCS7
*) get_body_sparep_data(b
->sparep
);
3326 p7
= get_pkcs7_from_part(msgno
, section
&& *section
? section
: "1");
3328 q_status_message1(SM_ORDER
, 2, 2, "Couldn't load PKCS7 object: %s",
3329 (char*) openssl_error_string());
3334 * Save the PKCS7 object for later dealings by the user interface.
3335 * It will be cleaned up when the body is garbage collected.
3337 b
->sparep
= create_body_sparep(P7Type
, p7
);
3340 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7
), PKCS7_type_is_enveloped(p7
)));
3342 if(PKCS7_type_is_signed(p7
)){
3345 out
= BIO_new(BIO_s_mem());
3346 (void) BIO_reset(out
);
3347 BIO_puts(out
, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3349 sigok
= do_signature_verify(p7
, NULL
, out
, 0);
3351 what_we_did
= sigok
? _("This message was cryptographically signed.") :
3352 _("This message was cryptographically signed but the signature could not be verified.");
3354 /* make sure it's null terminated */
3355 BIO_write(out
, null
, 1);
3357 else if(!PKCS7_type_is_enveloped(p7
)){
3358 q_status_message(SM_ORDER
, 1, 1, "PKCS7 object not recognised.");
3361 else{ /* It *is* enveloped */
3364 what_we_did
= _("This message was encrypted.");
3366 /* now need to find a cert that can decrypt this */
3367 pcert
= find_certificate_matching_pkcs7(p7
);
3370 q_status_message(SM_ORDER
, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3374 recip
= pcert
->cert
;
3376 if(!load_private_key(pcert
)
3378 && ps_global
->smime
->need_passphrase
3379 && !ps_global
->smime
->already_auto_asked
){
3380 /* Couldn't load key with blank password, ask user */
3381 ps_global
->smime
->already_auto_asked
= 1;
3382 if(pith_opt_smime_get_passphrase
){
3383 (*pith_opt_smime_get_passphrase
)();
3384 load_private_key(pcert
);
3392 out
= BIO_new(BIO_s_mem());
3393 (void) BIO_reset(out
);
3394 BIO_puts(out
, "MIME-Version: 1.0\r\n");
3396 decrypt_result
= PKCS7_decrypt(p7
, key
, recip
, out
, 0);
3398 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
3399 forget_private_keys();
3401 if(!decrypt_result
){
3402 q_status_message1(SM_ORDER
, 1, 1, _("Error decrypting: %s"),
3403 (char*) openssl_error_string());
3406 BIO_write(out
, null
, 1);
3410 * We've now produced a flattened MIME object in BIO out.
3411 * It needs to be turned back into a BODY.
3420 BUF_MEM
*bptr
= NULL
;
3422 BIO_get_mem_ptr(out
, &bptr
);
3426 /* look for start of body */
3427 bstart
= strstr(h
, "\r\n\r\n");
3430 q_status_message(SM_ORDER
, 3, 3, _("Encrypted data couldn't be parsed."));
3434 bstart
+= 4; /* skip over CRLF*2 */
3436 INIT(&s
, mail_string
, bstart
, strlen(bstart
));
3437 rfc822_parse_msg_full(&env
, &body
, h
, bstart
-h
-2, &s
, BADHOST
, 0, 0);
3438 mail_free_envelope(&env
); /* Don't care about this */
3440 if(body
->type
== TYPEMULTIPART
3441 && !strucmp(body
->subtype
, "SIGNED")){
3442 char *cookie
= NULL
;
3444 for (param
= body
->parameter
; param
&& !cookie
; param
= param
->next
)
3445 if (!strucmp (param
->attribute
,"BOUNDARY")) cookie
= param
->value
;
3447 st
= fs_get(sizeof(SIZEDTEXT
));
3448 st
->data
= (void *) cpystr(bstart
+ strlen(cookie
)+4); /* 4 = strlen("--\r\n") */
3449 st
->size
= body
->nested
.part
->next
->body
.mime
.offset
- 2*(strlen(cookie
) + 4);
3450 body
->sparep
= create_body_sparep(SizedText
, (void *)st
);
3453 q_status_message(SM_ORDER
, 3, 3, _("Couldn't find cookie in attachment list."));
3455 body
->mime
.offset
= 0;
3456 body
->mime
.text
.size
= 0;
3459 * Now convert original body (application/pkcs7-mime)
3460 * to a multipart body with one sub-part (the decrypted body).
3461 * Note that the sub-part may also be multipart!
3464 b
->type
= TYPEMULTIPART
;
3466 fs_give((void**) &b
->subtype
);
3469 * This subtype is used in mailview.c to annotate the display of
3470 * encrypted or signed messages. We know for sure then that it's a PKCS7
3471 * part because the sparep field is set to the PKCS7 object (see above).
3473 b
->subtype
= cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE
);
3474 b
->encoding
= ENC8BIT
;
3477 fs_give((void**) &b
->description
);
3479 b
->description
= cpystr(what_we_did
);
3481 if(b
->disposition
.type
)
3482 fs_give((void **) &b
->disposition
.type
);
3484 if(b
->contents
.text
.data
)
3485 fs_give((void **) &b
->contents
.text
.data
);
3488 mail_free_body_parameter(&b
->parameter
);
3490 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3491 b
->nested
.part
= fs_get(sizeof(PART
));
3492 b
->nested
.part
->body
= *body
;
3493 b
->nested
.part
->next
= NULL
;
3495 fs_give((void**) &body
);
3498 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3499 * the decrypted data. Otherwise, it'll try to load it from the original
3502 create_local_cache(bstart
-b
->nested
.part
->body
.mime
.offset
, bstart
, &b
->nested
.part
->body
, 0);
3504 modified_the_body
= 1;
3512 return modified_the_body
;
3517 * Recursively handle PKCS7 bodies in our message.
3519 * Returns non-zero if some fiddling was done.
3522 do_fiddle_smime_message(BODY
*b
, long msgno
, char *section
)
3524 int modified_the_body
= 0;
3529 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"));
3531 if(is_pkcs7_body(b
)){
3533 if(do_decoding(b
, msgno
, section
)){
3535 * b should now be a multipart message:
3536 * fiddle it too in case it's been multiply-encrypted!
3540 modified_the_body
= 1;
3544 if(b
->type
==TYPEMULTIPART
|| MIME_MSG(b
->type
, b
->subtype
)){
3550 if(MIME_MULT_SIGNED(b
->type
, b
->subtype
)){
3554 * Ahah. We have a multipart signed entity.
3557 * part 1 (signed thing)
3558 * part 2 (the pkcs7 signature)
3560 * We're going to convert that to
3562 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3563 * part 1 (signed thing)
3564 * part 2 has been freed
3566 * We also extract the signature from part 2 and save it
3567 * in the multipart body->sparep, and we add a description
3568 * in the multipart body->description.
3571 * The results of a decrypted message will be similar. It
3574 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3575 * part 1 (decrypted thing)
3578 modified_the_body
+= do_detached_signature_verify(b
, msgno
, section
);
3580 else if(MIME_MSG(b
->type
, b
->subtype
)){
3581 modified_the_body
+= do_fiddle_smime_message(b
->nested
.msg
->body
, msgno
, section
);
3585 for(p
=b
->nested
.part
,partNum
=1; p
; p
=p
->next
,partNum
++){
3586 /* Append part number to the section string */
3588 snprintf(newSec
, sizeof(newSec
), "%s%s%d", section
, *section
? "." : "", partNum
);
3590 modified_the_body
+= do_fiddle_smime_message(&p
->body
, msgno
, newSec
);
3595 return modified_the_body
;
3600 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3601 * Returns non-zero if something was changed.
3604 fiddle_smime_message(BODY
*b
, long msgno
)
3606 return do_fiddle_smime_message(b
, msgno
, "");
3610 /********************************************************************************/
3614 * Output a string in a distinctive style
3617 gf_puts_uline(char *txt
, gf_io_t pc
)
3619 pc(TAG_EMBED
); pc(TAG_BOLDON
);
3621 pc(TAG_EMBED
); pc(TAG_BOLDOFF
);
3624 /* get_chain_for_cert: error and level are mandatory arguments */
3626 get_chain_for_cert(X509
*cert
, int *error
, int *level
)
3628 STACK_OF(X509
) *chain
= NULL
;
3629 X509_STORE_CTX
*ctx
;
3631 int rc
; /* return code */
3636 if((s_cert_store
!= NULL
) && (ctx
= X509_STORE_CTX_new()) != NULL
){
3637 X509_STORE_set_flags(s_cert_store
, 0);
3638 if(!X509_STORE_CTX_init(ctx
, s_cert_store
, cert
, NULL
))
3639 *error
= X509_STORE_CTX_get_error(ctx
);
3640 else if((chain
= sk_X509_new_null()) != NULL
){
3641 for(x
= cert
; ; x
= xtmp
){
3643 sk_X509_push(chain
, X509_dup(x
));
3644 rc
= X509_STORE_CTX_get1_issuer(&xtmp
, ctx
, x
);
3649 if(!X509_check_issued(xtmp
, xtmp
))
3653 X509_STORE_CTX_free(ctx
);
3660 * Sign a message. Called from call_mailer in send.c.
3662 * This takes the header for the outgoing message as well as a pointer
3663 * to the current body (which may be reallocated).
3664 * The last argument (BODY **bp) is an argument that tells Alpine
3665 * if the body has 8 bit. if *bp is not null we compute two signatures
3666 * one for the quoted-printable encoded message, and another for the
3667 * 8bit encoded message. We return the signature for the 8bit encoded
3668 * part in p2->body.mime.text.data.
3669 * The reason why we compute two signatures is so that we can decide
3670 * which one to use later, and we only do it in the case that *bp is
3671 * not null. If we did not do this, then we might not be able to sign
3672 * a message until we log in to the smtp server, so instead of doing
3673 * that, we get ready for any possible situation we might find.
3676 sign_outgoing_message(METAENV
*header
, BODY
**bodyP
, int dont_detach
, BODY
**bp
)
3678 STORE_S
*outs
= NULL
;
3679 STORE_S
*outs_2
= NULL
;
3680 BODY
*body
= *bodyP
;
3681 BODY
*newBody
= NULL
;
3684 PERSONAL_CERT
*pcert
;
3691 STACK_OF(X509
) *chain
;
3692 const EVP_MD
*md
= EVP_sha256(); /* use this digest instead of sha1 */
3693 int result
= 0, error
;
3694 int flags
= dont_detach
? 0 : PKCS7_DETACHED
;
3697 dprint((9, "sign_outgoing_message()"));
3701 /* Look for a private key matching the sender address... */
3703 pcert
= match_personal_cert(header
->env
);
3706 q_status_message(SM_ORDER
, 3, 3, _("Couldn't find the certificate needed to sign."));
3710 if(!load_private_key(pcert
) && ps_global
->smime
&& ps_global
->smime
->need_passphrase
){
3711 /* Couldn't load key with blank password, try again */
3712 if(pith_opt_smime_get_passphrase
){
3713 (*pith_opt_smime_get_passphrase
)();
3714 load_private_key(pcert
);
3721 if(((chain
= get_chain_for_cert(pcert
->cert
, &error
, &level
)) != NULL
&& error
)
3723 sk_X509_pop_free(chain
, X509_free
);
3728 q_status_message(SM_ORDER
, 1, 1,
3729 _("Not all certificates needed to verify signature included in signed message"));
3731 in
= body_to_bio(body
);
3733 flags
|= PKCS7_PARTIAL
;
3734 if((p7
= PKCS7_sign(NULL
, NULL
, chain
, in
, flags
)) != NULL
3735 && PKCS7_sign_add_signer(p7
, pcert
->cert
, pcert
->key
, md
, flags
))
3736 PKCS7_final(p7
, in
, flags
);
3739 int i
, save_encoding
;
3741 for(i
= 0; (i
<= ENCMAX
) && body_encodings
[i
]; i
++);
3743 if(i
> ENCMAX
){ /* no empty encoding slots! */
3747 save_encoding
= (*bp
)->encoding
;
3748 body_encodings
[(*bp
)->encoding
= i
] = body_encodings
[ENC8BIT
];
3750 in_2
= body_to_bio(body
);
3752 body_encodings
[i
] = NULL
;
3753 (*bp
)->encoding
= save_encoding
;
3758 if((p7_2
= PKCS7_sign(NULL
, NULL
, chain
, in_2
, flags
)) != NULL
3759 && PKCS7_sign_add_signer(p7_2
, pcert
->cert
, pcert
->key
, md
, flags
))
3760 PKCS7_final(p7_2
, in_2
, flags
);
3763 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
3764 forget_private_keys();
3767 sk_X509_pop_free(chain
, X509_free
);
3770 q_status_message(SM_ORDER
, 1, 1, _("Error creating signed object."));
3774 outs
= so_get(BioType
, NULL
, EDIT_ACCESS
);
3775 out
= bio_from_store(outs
);
3777 i2d_PKCS7_bio(out
, p7
);
3778 (void) BIO_flush(out
);
3780 so_seek(outs
, 0, SEEK_SET
);
3782 if(bp
&& *bp
&& p7_2
){
3783 outs_2
= so_get(BioType
, NULL
, EDIT_ACCESS
);
3784 out_2
= bio_from_store(outs_2
);
3786 i2d_PKCS7_bio(out_2
, p7_2
);
3787 (void) BIO_flush(out_2
);
3789 so_seek(outs_2
, 0, SEEK_SET
);
3792 if((flags
&PKCS7_DETACHED
)==0){
3794 /* the simple case: the signed data is in the pkcs7 object */
3796 newBody
= mail_newbody();
3798 setup_pkcs7_body_for_signature(newBody
, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3800 newBody
->contents
.text
.data
= (unsigned char *) outs
;
3809 * We have to create a new body as follows:
3811 * multipart/signed; blah blah blah
3812 * reference to existing body
3817 newBody
= mail_newbody();
3819 newBody
->type
= TYPEMULTIPART
;
3820 newBody
->subtype
= cpystr("signed");
3821 newBody
->encoding
= ENC7BIT
;
3823 set_parameter(&newBody
->parameter
, "protocol", "application/pkcs7-signature");
3824 set_parameter(&newBody
->parameter
, "micalg", "sha-256");
3826 p1
= mail_newbody_part();
3827 p2
= mail_newbody_part();
3830 * This is nasty. We're just copying the body in here,
3831 * but since our newBody is freed at the end of call_mailer,
3832 * we mustn't let this body (the original one) be freed twice.
3834 p1
->body
= *body
; /* ARRGH. This is special cased at the end of call_mailer */
3838 setup_pkcs7_body_for_signature(&p2
->body
, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL
);
3839 p2
->body
.mime
.text
.data
= (unsigned char *) outs_2
;
3840 p2
->body
.contents
.text
.data
= (unsigned char *) outs
;
3842 newBody
->nested
.part
= p1
;
3855 if(p7_2
) PKCS7_free(p7_2
);
3859 dprint((9, "sign_outgoing_message returns %d", result
));
3865 new_smime_struct(void)
3867 SMIME_STUFF_S
*ret
= NULL
;
3869 ret
= (SMIME_STUFF_S
*) fs_get(sizeof(*ret
));
3870 memset((void *) ret
, 0, sizeof(*ret
));
3871 ret
->publictype
= Nada
;
3878 free_smime_struct(SMIME_STUFF_S
**smime
)
3880 if(smime
&& *smime
){
3881 if((*smime
)->passphrase_emailaddr
){
3883 for(i
= 0; (*smime
)->passphrase_emailaddr
[i
] != NULL
; i
++)
3884 fs_give((void **) &(*smime
)->passphrase_emailaddr
[i
]);
3885 fs_give((void **) (*smime
)->passphrase_emailaddr
);
3888 if((*smime
)->publicpath
)
3889 fs_give((void **) &(*smime
)->publicpath
);
3891 if((*smime
)->publiccertlist
)
3892 free_certlist(&(*smime
)->publiccertlist
);
3894 if((*smime
)->backuppubliccertlist
)
3895 free_certlist(&(*smime
)->backuppubliccertlist
);
3897 if((*smime
)->cacertlist
)
3898 free_certlist(&(*smime
)->cacertlist
);
3900 if((*smime
)->backupcacertlist
)
3901 free_certlist(&(*smime
)->backupcacertlist
);
3903 if((*smime
)->privatecertlist
)
3904 free_certlist(&(*smime
)->privatecertlist
);
3906 if((*smime
)->backupprivatecertlist
)
3907 free_certlist(&(*smime
)->backupprivatecertlist
);
3909 if((*smime
)->publiccontent
)
3910 fs_give((void **) &(*smime
)->publiccontent
);
3912 if((*smime
)->privatepath
)
3913 fs_give((void **) &(*smime
)->privatepath
);
3915 if((*smime
)->personal_certs
){
3918 pc
= (PERSONAL_CERT
*) (*smime
)->personal_certs
;
3919 free_personal_certs(&pc
);
3920 (*smime
)->personal_certs
= NULL
;
3923 if((*smime
)->privatecontent
)
3924 fs_give((void **) &(*smime
)->privatecontent
);
3926 if((*smime
)->capath
)
3927 fs_give((void **) &(*smime
)->capath
);
3929 if((*smime
)->cacontent
)
3930 fs_give((void **) &(*smime
)->cacontent
);
3932 fs_give((void **) smime
);