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
, int check_cert
);
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
1026 int smime_path(char *rpath
, char *fpath
, size_t len
)
1029 if(rpath
&& *rpath
){
1030 size_t spl
= strlen(rpath
);
1032 if(IS_REMOTE(rpath
)){
1034 strncpy(fpath
, rpath
, len
-1);
1035 fpath
[len
-1] = '\0';
1037 else if(is_absolute_path(rpath
)){
1038 strncpy(fpath
, rpath
, len
-1);
1039 fpath
[len
-1] = '\0';
1040 fnexpand(fpath
, len
);
1042 else if(ps_global
->VAR_OPER_DIR
){
1043 if(strlen(ps_global
->VAR_OPER_DIR
) + spl
< len
- 1)
1044 build_path(fpath
, ps_global
->VAR_OPER_DIR
, rpath
, len
);
1046 else if(ps_global
->home_dir
){
1047 if(strlen(ps_global
->home_dir
) + spl
< len
- 1)
1048 build_path(fpath
, ps_global
->home_dir
, rpath
, len
);
1051 return fpath
&& *fpath
? 1 : 0;
1057 * taken from openssl/apps/app_rand.c
1060 app_RAND_load_file(const char *file
)
1062 #define RANDBUFLEN 200
1063 char buffer
[RANDBUFLEN
];
1066 file
= RAND_file_name(buffer
, RANDBUFLEN
);
1068 if(file
== NULL
|| !RAND_load_file(file
, -1)){
1069 if(RAND_status() == 0){
1070 dprint((1, "unable to load 'random state'\n"));
1071 dprint((1, "This means that the random number generator has not been seeded\n"));
1072 dprint((1, "with much random data.\n"));
1084 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
1087 openssl_extra_randomness(void)
1095 /* if system doesn't have /dev/urandom */
1096 if(stat ("/dev/urandom", &sbuf
)){
1098 tf
= temp_nam(NULL
, NULL
);
1100 strncpy(tmp
, tf
, sizeof(tmp
));
1101 tmp
[sizeof(tmp
)-1] = '\0';
1102 fs_give((void **) &tf
);
1105 if((fd
= open(tmp
, O_WRONLY
|O_CREAT
|O_EXCL
, 0600)) < 0)
1106 i
= (unsigned long) tmp
;
1108 unlink(tmp
); /* don't need the file */
1109 fstat(fd
, &sbuf
); /* get information about the file */
1110 i
= sbuf
.st_ino
; /* remember its inode */
1111 close(fd
); /* or its descriptor */
1113 /* not great but it'll have to do */
1114 snprintf(tmp
+strlen(tmp
), sizeof(tmp
)-strlen(tmp
), "%.80s%lx%lx%lx",
1115 tcp_serverhost (),i
,
1116 (unsigned long) (time (0) ^ gethostid ()),
1117 (unsigned long) getpid ());
1118 RAND_seed(tmp
, strlen(tmp
));
1124 /* taken from openssl/apps/app_rand.c */
1126 app_RAND_write_file(const char *file
)
1132 * If we did not manage to read the seed file,
1133 * we should not write a low-entropy seed file back --
1134 * it would suppress a crucial warning the next time
1135 * we want to use it.
1140 file
= RAND_file_name(buffer
, sizeof buffer
);
1142 if(file
== NULL
|| !RAND_write_file(file
)){
1143 dprint((1, "unable to write 'random state'\n"));
1151 certlist_from_personal_certs(PERSONAL_CERT
*pc
)
1159 if((x
= get_cert_for(pc
->name
, Public
, 1)) != NULL
)
1160 cl
= smime_X509_to_cert_info(x
, pc
->name
);
1161 cl
->next
= certlist_from_personal_certs(pc
->next
);
1167 renew_cert_data(CertList
**data
, WhichCerts ctype
)
1170 if(ctype
== Private
){
1172 PERSONAL_CERT
*pc
= (PERSONAL_CERT
*)ps_global
->smime
->personal_certs
;
1174 free_certlist(data
);
1175 free_personal_certs(&pc
);
1176 setup_privatekey_storage();
1177 *data
= certlist_from_personal_certs((PERSONAL_CERT
*)ps_global
->smime
->personal_certs
);
1179 resort_certificates(data
, ctype
);
1180 RENEWCERT(*data
) = 0;
1182 ps_global
->smime
->privatecertlist
= *data
;
1184 if(ps_global
->smime
->privatecertlist
)
1185 RENEWCERT(ps_global
->smime
->privatecertlist
) = 0;
1187 X509_LOOKUP
*lookup
= NULL
;
1188 X509_STORE
*store
= NULL
;
1190 if((store
= X509_STORE_new()) != NULL
){
1191 if((lookup
= X509_STORE_add_lookup(store
, X509_LOOKUP_file())) == NULL
){
1192 X509_STORE_free(store
);
1195 free_certlist(data
);
1196 if(SMHOLDERTYPE(ctype
) == Directory
)
1197 add_certs_in_dir(lookup
, PATHCERTDIR(ctype
), EXTCERT(ctype
), data
);
1198 else /* if(SMHOLDERTYPE(ctype) == Container) */
1199 *data
= mem_to_certlist(CONTENTCERTLIST(ctype
), ctype
);
1201 resort_certificates(data
, ctype
);
1202 RENEWCERT(*data
) = 0;
1205 ps_global
->smime
->publiccertlist
= *data
;
1207 ps_global
->smime
->cacertlist
= *data
;
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 free_smime_struct(&ps_global
->smime
);
1230 /* we renew the store when it has changed */
1231 void renew_store(void)
1233 if(ps_global
->smime
->inited
){
1234 if(s_cert_store
!= NULL
)
1235 X509_STORE_free(s_cert_store
);
1236 s_cert_store
= get_ca_store();
1240 /* Initialise openssl stuff if needed */
1244 if(F_OFF(F_DONT_DO_SMIME
, ps_global
) && !(ps_global
->smime
&& ps_global
->smime
->inited
)){
1246 dprint((9, "smime_init()"));
1247 if(!ps_global
->smime
)
1248 ps_global
->smime
= new_smime_struct();
1250 setup_storage_locations();
1252 s_cert_store
= get_ca_store();
1253 setup_certs_backup_by_type(CACert
);
1255 #ifdef OPENSSL_1_1_0
1256 OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
|OPENSSL_INIT_ADD_ALL_DIGESTS
|OPENSSL_INIT_LOAD_CRYPTO_STRINGS
, NULL
);
1258 OpenSSL_add_all_algorithms();
1259 ERR_load_crypto_strings();
1260 #endif /* OPENSSL_1_1_0 */
1262 app_RAND_load_file(NULL
);
1263 openssl_extra_randomness();
1264 ps_global
->smime
->inited
= 1;
1271 /* validate a certificate. Return value : 0 for no error, -1 for error.
1272 * In the latter case, set the openssl smime error in *error.
1274 int smime_validate_cert(X509
*cert
, long *error
)
1276 X509_STORE_CTX
*csc
;
1280 if((s_cert_store
!= NULL
) && (csc
= X509_STORE_CTX_new()) != NULL
){
1281 X509_STORE_set_flags(s_cert_store
, 0);
1282 if(X509_STORE_CTX_init(csc
,s_cert_store
,cert
,NULL
)
1283 && X509_verify_cert(csc
) <= 0)
1284 *error
= X509_STORE_CTX_get_error(csc
);
1285 X509_STORE_CTX_free(csc
);
1287 return *error
? -1 : 0;
1291 get_personal_certs(char *path
)
1293 PERSONAL_CERT
*result
= NULL
;
1294 char buf2
[MAXPATH
], *fname
;
1300 #else /* _WINDOWS */
1301 struct _finddata_t dbuf
;
1302 char buf
[_MAX_PATH
+ 4];
1304 #endif /* _WINDOWS */
1306 ps_global
->smime
->privatepath
= cpystr(path
);
1309 dirp
= opendir(path
);
1311 while((d
=readdir(dirp
)) != NULL
){
1313 #else /* _WINDOWS */
1314 snprintf(buf
, sizeof(buf
), "%s%s*.*", path
, (path
[strlen(path
)-1] == '\\') ? "" : "\\");
1315 buf
[sizeof(buf
)-1] = '\0';
1316 if((findrv
= _findfirst(buf
, &dbuf
)) < 0)
1320 fname
= fname_to_utf8(dbuf
.name
);
1322 if((ll
=strlen(fname
)) && ll
> 4 && !strcmp(fname
+ll
-4, ".key")){
1324 /* copy file name to temp buffer */
1325 strncpy(buf2
, fname
, sizeof(buf2
)-1);
1326 buf2
[sizeof(buf2
)-1] = '\0';
1327 /* chop off ".key" trailier */
1328 buf2
[strlen(buf2
)-4] = '\0';
1329 /* Look for certificate */
1330 cert
= get_cert_for(buf2
, Public
, 1);
1335 /* create a new PERSONAL_CERT, fill it in */
1337 pc
= (PERSONAL_CERT
*) fs_get(sizeof(*pc
));
1339 pc
->name
= cpystr(buf2
);
1340 strncat(buf2
, EXTCERT(Public
), 4);
1341 pc
->cname
= cpystr(buf2
);
1343 /* Try to load the key with an empty password */
1344 pc
->key
= load_key(pc
, "", SM_NORMALCERT
);
1354 #else /* _WINDOWS */
1355 } while(_findnext(findrv
, &dbuf
) == 0);
1357 #endif /* !_WINDOWS */
1363 setup_privatekey_storage(void)
1365 char path
[MAXPATH
+1], *contents
;
1366 int privatekeycontainer
= 0;
1368 /* private keys in a container */
1369 if(ps_global
->VAR_PRIVATEKEY_CONTAINER
&& ps_global
->VAR_PRIVATEKEY_CONTAINER
[0]){
1371 privatekeycontainer
= 1;
1374 if(!smime_path(ps_global
->VAR_PRIVATEKEY_CONTAINER
, path
, MAXPATH
))
1375 privatekeycontainer
= 0;
1377 if(privatekeycontainer
&& !IS_REMOTE(path
)
1378 && ps_global
->VAR_OPER_DIR
1379 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1380 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1381 /* TRANSLATORS: First arg is the directory name, second is
1382 the file user wants to read but can't. */
1383 _("Can't read file outside %s: %s"),
1384 ps_global
->VAR_OPER_DIR
, path
);
1385 privatekeycontainer
= 0;
1388 if(privatekeycontainer
1389 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1390 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1392 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1393 privatekeycontainer
= 0;
1396 if(privatekeycontainer
&& path
[0]){
1397 ps_global
->smime
->privatetype
= Container
;
1398 ps_global
->smime
->privatepath
= cpystr(path
);
1401 ps_global
->smime
->privatecontent
= contents
;
1402 ps_global
->smime
->personal_certs
= mem_to_personal_certs(contents
);
1407 /* private keys in a directory of files */
1408 if(!privatekeycontainer
){
1409 ps_global
->smime
->privatetype
= Directory
;
1412 if(!(smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, path
, MAXPATH
)
1413 && !IS_REMOTE(path
)))
1414 ps_global
->smime
->privatetype
= Nada
;
1415 else if(can_access(path
, ACCESS_EXISTS
)){
1416 if(our_mkpath(path
, 0700)){
1417 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1418 ps_global
->smime
->privatetype
= Nada
;
1422 if(ps_global
->smime
->privatetype
== Directory
)
1423 ps_global
->smime
->personal_certs
= get_personal_certs(path
);
1425 setup_certs_backup_by_type(Private
);
1431 setup_storage_locations(void)
1433 int publiccertcontainer
= 0, cacertcontainer
= 0;
1434 char path
[MAXPATH
+1], *contents
;
1436 if(!ps_global
->smime
)
1439 #ifdef APPLEKEYCHAIN
1440 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
1441 ps_global
->smime
->publictype
= Keychain
;
1444 #endif /* APPLEKEYCHAIN */
1445 /* Public certificates in a container */
1446 if(ps_global
->VAR_PUBLICCERT_CONTAINER
&& ps_global
->VAR_PUBLICCERT_CONTAINER
[0]){
1448 publiccertcontainer
= 1;
1451 if(!smime_path(ps_global
->VAR_PUBLICCERT_CONTAINER
, path
, MAXPATH
))
1452 publiccertcontainer
= 0;
1454 if(publiccertcontainer
&& !IS_REMOTE(path
)
1455 && ps_global
->VAR_OPER_DIR
1456 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1457 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1458 /* TRANSLATORS: First arg is the directory name, second is
1459 the file user wants to read but can't. */
1460 _("Can't read file outside %s: %s"),
1461 ps_global
->VAR_OPER_DIR
, path
);
1462 publiccertcontainer
= 0;
1465 if(publiccertcontainer
1466 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1467 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1469 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1470 publiccertcontainer
= 0;
1473 if(publiccertcontainer
&& path
[0]){
1474 ps_global
->smime
->publictype
= Container
;
1475 ps_global
->smime
->publicpath
= cpystr(path
);
1478 ps_global
->smime
->publiccontent
= contents
;
1479 ps_global
->smime
->publiccertlist
= mem_to_certlist(contents
, Public
);
1484 /* Public certificates in a directory of files */
1485 if(!publiccertcontainer
){
1486 ps_global
->smime
->publictype
= Directory
;
1489 if(!(smime_path(ps_global
->VAR_PUBLICCERT_DIR
, path
, MAXPATH
)
1490 && !IS_REMOTE(path
)))
1491 ps_global
->smime
->publictype
= Nada
;
1492 else if(can_access(path
, ACCESS_EXISTS
)){
1493 if(our_mkpath(path
, 0700)){
1494 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1495 ps_global
->smime
->publictype
= Nada
;
1499 if(ps_global
->smime
->publictype
== Directory
)
1500 ps_global
->smime
->publicpath
= cpystr(path
);
1503 #ifdef APPLEKEYCHAIN
1505 #endif /* APPLEKEYCHAIN */
1507 setup_privatekey_storage();
1509 /* extra cacerts in a container */
1510 if(ps_global
->VAR_CACERT_CONTAINER
&& ps_global
->VAR_CACERT_CONTAINER
[0]){
1512 cacertcontainer
= 1;
1515 if(!smime_path(ps_global
->VAR_CACERT_CONTAINER
, path
, MAXPATH
))
1516 cacertcontainer
= 0;
1518 if(cacertcontainer
&& !IS_REMOTE(path
)
1519 && ps_global
->VAR_OPER_DIR
1520 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1521 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1522 /* TRANSLATORS: First arg is the directory name, second is
1523 the file user wants to read but can't. */
1524 _("Can't read file outside %s: %s"),
1525 ps_global
->VAR_OPER_DIR
, path
);
1526 cacertcontainer
= 0;
1530 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1531 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1533 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1534 cacertcontainer
= 0;
1537 if(cacertcontainer
&& path
[0]){
1538 ps_global
->smime
->catype
= Container
;
1539 ps_global
->smime
->capath
= cpystr(path
);
1540 ps_global
->smime
->cacontent
= contents
;
1542 ps_global
->smime
->cacertlist
= mem_to_certlist(contents
, CACert
);
1546 if(!cacertcontainer
){
1547 ps_global
->smime
->catype
= Directory
;
1550 if(!(smime_path(ps_global
->VAR_CACERT_DIR
, path
, MAXPATH
)
1551 && !IS_REMOTE(path
)))
1552 ps_global
->smime
->catype
= Nada
;
1553 else if(can_access(path
, ACCESS_EXISTS
)){
1554 if(our_mkpath(path
, 0700)){
1555 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1556 ps_global
->smime
->catype
= Nada
;
1560 if(ps_global
->smime
->catype
== Directory
)
1561 ps_global
->smime
->capath
= cpystr(path
);
1567 copy_publiccert_dir_to_container(void)
1569 return(copy_dir_to_container(Public
, NULL
));
1574 copy_publiccert_container_to_dir(void)
1576 return(copy_container_to_dir(Public
));
1581 copy_privatecert_dir_to_container(void)
1583 return(copy_dir_to_container(Private
, NULL
));
1588 copy_privatecert_container_to_dir(void)
1590 return(copy_container_to_dir(Private
));
1595 copy_cacert_dir_to_container(void)
1597 return(copy_dir_to_container(CACert
, NULL
));
1602 copy_cacert_container_to_dir(void)
1604 return(copy_container_to_dir(CACert
));
1607 /* Add the contents of a file to a container. Do not check the content
1608 * of the file, just add it using the format for that container. The
1609 * caller must check the format, so that there is no data corruption
1611 * return value: 0 - success,
1615 add_file_to_container(WhichCerts ctype
, char *fpath
, char *altname
)
1617 char *sep
= (ctype
== Public
|| ctype
== Private
)
1618 ? EMAILADDRLEADER
: CACERTSTORELEADER
;
1619 char *content
= ctype
== Public
? ps_global
->smime
->publiccontent
1620 : (ctype
== Private
? ps_global
->smime
->privatecontent
1621 : ps_global
->smime
->cacontent
);
1627 int rv
= -1; /* assume error */
1629 if(our_stat(fpath
, &sbuf
) < 0
1630 || (in
= so_get(FileStar
, fpath
, READ_ACCESS
| READ_FROM_LOCALE
)) == NULL
)
1635 else if((name
= strrchr(fpath
, '/')) != NULL
){
1637 if((ll
= strlen(++name
)) > 4 && strucmp(name
+ ll
- 4, EXTCERT(ctype
)) == 0)
1638 name
[ll
-strlen(EXTCERT(ctype
))] = '\0';
1644 fs_resize((void **)&content
, strlen(content
) + strlen(sep
) + strlen(name
) + sbuf
.st_size
+ 2*strlen(NEWLINE
) + 1); /* 1 = \0*/
1646 content
+= strlen(content
);
1649 s
= content
= fs_get(strlen(sep
) + strlen(name
) + sbuf
.st_size
+ strlen(NEWLINE
) + 1); /* 1 = \0 */
1652 strncat(content
, sep
, strlen(sep
));
1653 strncat(content
, name
, strlen(name
));
1654 content
+= strlen(content
);
1657 #endif /* _WINDOWS */
1660 while(so_readc(&c
, in
))
1661 *content
++ = (char) c
;
1665 case Private
: ps_global
->smime
->privatecontent
= s
; break;
1666 case Public
: ps_global
->smime
->publiccontent
= s
; break;
1667 case CACert
: ps_global
->smime
->cacontent
= s
; break;
1671 rv
= copy_dir_to_container(ctype
, s
);
1674 if(in
) so_give(&in
);
1681 * returns 0 on success, -1 on failure
1682 * contents is an argument which tells this function to write the value
1683 * of this variable instead of reading the contents of the directory.
1684 * If the var contents is not null use its value as the value of the
1688 copy_dir_to_container(WhichCerts which
, char *contents
)
1690 int ret
= 0, container
= 0;
1691 BIO
*bio_out
= NULL
, *bio_in
= NULL
;
1692 char srcpath
[MAXPATH
+1], dstpath
[MAXPATH
+1], emailaddr
[MAXPATH
], file
[MAXPATH
], line
[4096];
1693 char *tempfile
= NULL
, fpath
[MAXPATH
+1], *fname
;
1698 #else /* _WINDOWS */
1699 struct _finddata_t dbuf
;
1700 char buf
[_MAX_PATH
+ 4];
1702 #endif /* _WINDOWS */
1703 REMDATA_S
*rd
= NULL
;
1704 char *configdir
= NULL
;
1705 char *configpath
= NULL
;
1706 char *configcontainer
= NULL
;
1707 char *filesuffix
= NULL
;
1708 char *ret_dir
= NULL
;
1710 dprint((9, "copy_dir_to_container(%s)", which
==Public
? "Public" : which
==Private
? "Private" : which
==CACert
? "CACert" : "?"));
1716 emailaddr
[0] = '\0';
1718 if(which
== Public
){
1719 configdir
= ps_global
->VAR_PUBLICCERT_DIR
;
1720 configpath
= ps_global
->smime
->publicpath
;
1721 configcontainer
= cpystr(DF_PUBLIC_CONTAINER
);
1722 filesuffix
= ".crt";
1724 else if(which
== Private
){
1725 configdir
= ps_global
->VAR_PRIVATEKEY_DIR
;
1726 configpath
= ps_global
->smime
->privatepath
;
1727 configcontainer
= cpystr(DF_PRIVATE_CONTAINER
);
1728 filesuffix
= ".key";
1730 else if(which
== CACert
){
1731 configdir
= ps_global
->VAR_CACERT_DIR
;
1732 configpath
= ps_global
->smime
->capath
;
1733 configcontainer
= cpystr(DF_CA_CONTAINER
);
1734 filesuffix
= ".crt";
1736 container
= SMHOLDERTYPE(which
) == Container
;
1738 if(!(configdir
&& configdir
[0])){
1739 q_status_message(SM_ORDER
, 3, 3, _("Directory not defined"));
1743 if(!(configpath
&& configpath
[0])){
1744 #ifdef APPLEKEYCHAIN
1745 if(which
== Public
&& F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
1746 q_status_message(SM_ORDER
, 3, 3, _("Turn off the Keychain feature above first"));
1749 #endif /* APPLEKEYCHAIN */
1750 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
1754 if(!(filesuffix
&& strlen(filesuffix
) == 4)){
1760 * If there is a legit directory to read from set up the
1761 * container file to write to.
1763 if(smime_path(configdir
, srcpath
, MAXPATH
) && !IS_REMOTE(srcpath
)){
1765 if(IS_REMOTE(configpath
)){
1766 rd
= rd_create_remote(RemImap
, configpath
, REMOTE_SMIME_SUBTYPE
,
1768 _("Can't access remote smime configuration."));
1772 (void) rd_read_metadata(rd
);
1774 if(rd
->access
== MaybeRorW
){
1775 if(rd
->read_status
== 'R')
1776 rd
->access
= ReadOnly
;
1778 rd
->access
= ReadWrite
;
1781 if(rd
->access
!= NoExists
){
1783 rd_check_remvalid(rd
, 1L);
1786 * If the cached info says it is readonly but
1787 * it looks like it's been fixed now, change it to readwrite.
1789 if(rd
->read_status
== 'R'){
1790 rd_check_readonly_access(rd
);
1791 if(rd
->read_status
== 'W'){
1792 rd
->access
= ReadWrite
;
1793 rd
->flags
|= REM_OUTOFDATE
;
1796 rd
->access
= ReadOnly
;
1800 if(rd
->flags
& REM_OUTOFDATE
){
1801 if(rd_update_local(rd
) != 0){
1803 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1804 rd_close_remdata(&rd
);
1811 if(rd
->access
!= ReadWrite
|| rd_remote_is_readonly(rd
)){
1812 rd_close_remdata(&rd
);
1816 rd
->flags
|= DO_REMTRIM
;
1818 strncpy(dstpath
, rd
->lf
, sizeof(dstpath
)-1);
1819 dstpath
[sizeof(dstpath
)-1] = '\0';
1822 strncpy(dstpath
, configpath
, sizeof(dstpath
)-1);
1823 dstpath
[sizeof(dstpath
)-1] = '\0';
1827 * dstpath is either the local Container file or the local cache file
1828 * for the remote Container file.
1830 tempfile
= tempfile_in_same_dir(dstpath
, "az", &ret_dir
);
1834 * If there is a legit directory to read from and a tempfile
1835 * to write to we continue.
1837 if(tempfile
&& (bio_out
=BIO_new_file(tempfile
, "w")) != NULL
){
1839 if(contents
!= NULL
){
1840 if(BIO_puts(bio_out
, contents
) < 0)
1845 if((dirp
= opendir(srcpath
)) != NULL
){
1847 while((d
=readdir(dirp
)) && !ret
){
1849 #else /* _WINDOWS */
1850 snprintf(buf
, sizeof(buf
), "%s%s*.*", srcpath
, (srcpath
[strlen(srcpath
)-1] == '\\') ? "" : "\\");
1851 buf
[sizeof(buf
)-1] = '\0';
1852 if((findrv
= _findfirst(buf
, &dbuf
)) < 0)
1856 fname
= fname_to_utf8(dbuf
.name
);
1857 #endif /* ! _WINDOWS */
1858 if((ll
=strlen(fname
)) && ll
> 4 && !strcmp(fname
+ll
-4, filesuffix
)){
1860 /* copy file name to temp buffer */
1861 strncpy(emailaddr
, fname
, sizeof(emailaddr
)-1);
1862 emailaddr
[sizeof(emailaddr
)-1] = '\0';
1863 /* chop off suffix trailier */
1864 emailaddr
[strlen(emailaddr
)-4] = 0;
1867 * This is the separator between the contents of
1870 if(which
== CACert
){
1871 if(!((BIO_puts(bio_out
, CACERTSTORELEADER
) > 0)
1872 && (BIO_puts(bio_out
, emailaddr
) > 0)
1873 && (BIO_puts(bio_out
, NEWLINE
) > 0)))
1877 if(!((BIO_puts(bio_out
, EMAILADDRLEADER
) > 0)
1878 && (BIO_puts(bio_out
, emailaddr
) > 0)
1879 && (BIO_puts(bio_out
, NEWLINE
) > 0)))
1883 /* read then write contents of file */
1884 build_path(file
, srcpath
, fname
, sizeof(file
));
1885 if(!(bio_in
= BIO_new_file(file
, "r")))
1891 while(BIO_gets(bio_in
, line
, sizeof(line
)) > 0){
1892 if(strncmp("-----BEGIN", line
, strlen("-----BEGIN")) == 0)
1896 BIO_puts(bio_out
, line
);
1898 if(strncmp("-----END", line
, strlen("-----END")) == 0)
1909 #else /* _WINDOWS */
1910 } while (_findnext(findrv
, &dbuf
) == 0);
1912 #endif /* ! _WINDOWS */
1918 if(container
&& configpath
&& *configpath
){
1919 strncpy(fpath
, configpath
, sizeof(fpath
));
1920 fpath
[sizeof(fpath
) - 1] = '\0';
1923 if(strlen(dstpath
) + strlen(configcontainer
) - strlen(ret_dir
) + 1 < sizeof(dstpath
))
1924 snprintf(fpath
, sizeof(fpath
), "%s%c%s",
1925 dstpath
, tempfile
[strlen(ret_dir
)], configcontainer
);
1932 if(!IS_REMOTE(configpath
)){
1933 if(rename_file(tempfile
, fpath
) < 0){
1934 q_status_message2(SM_ORDER
, 3, 3,
1935 _("Can't rename %s to %s"), tempfile
, fpath
);
1937 } else q_status_message1(SM_ORDER
, 3, 3,
1938 _("saved container to %s"), fpath
);
1940 else { /* if the container is remote, copy it */
1944 if(rd
!= NULL
&& rename_file(tempfile
, rd
->lf
) < 0){
1945 q_status_message2(SM_ORDER
, 3, 3,
1946 _("Can't rename %s to %s"), tempfile
, rd
->lf
);
1952 if((e
= rd_update_remote(rd
, datebuf
)) != 0){
1954 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1955 _("Error opening temporary smime file %s: %s"),
1956 rd
->lf
, error_description(errno
));
1958 "write_remote_smime: error opening temp file %s\n",
1959 rd
->lf
? rd
->lf
: "?"));
1962 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1963 _("Error copying to %s: %s"),
1964 rd
->rn
, error_description(errno
));
1966 "write_remote_smime: error copying from %s to %s\n",
1967 rd
->lf
? rd
->lf
: "?", rd
->rn
? rd
->rn
: "?"));
1970 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
1971 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1974 rd_update_metadata(rd
, datebuf
);
1975 rd
->read_status
= 'W';
1978 rd_close_remdata(&rd
);
1985 fs_give((void **) &tempfile
);
1988 fs_give((void **) &ret_dir
);
1991 fs_give((void **) &configcontainer
);
1998 * returns 0 on success, -1 on failure
2001 copy_container_to_dir(WhichCerts which
)
2003 char path
[MAXPATH
+1], file
[MAXPATH
+1], buf
[MAXPATH
+1];
2005 char *contents
= NULL
;
2006 char *leader
= NULL
;
2007 char *filesuffix
= NULL
;
2008 char *configdir
= NULL
;
2009 char *configpath
= NULL
;
2010 char *tempfile
= NULL
;
2011 char *p
, *q
, *line
, *name
, *certtext
, *save_p
;
2015 dprint((9, "copy_container_to_dir(%s)", which
==Public
? "Public" : which
==Private
? "Private" : which
==CACert
? "CACert" : "?"));
2020 if(which
== Public
){
2021 leader
= EMAILADDRLEADER
;
2022 contents
= ps_global
->smime
->publiccontent
;
2023 configdir
= ps_global
->VAR_PUBLICCERT_DIR
;
2024 configpath
= ps_global
->smime
->publicpath
;
2025 filesuffix
= ".crt";
2026 if(!(configpath
&& configpath
[0])){
2027 #ifdef APPLEKEYCHAIN
2028 if(which
== Public
&& F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
2029 q_status_message(SM_ORDER
, 3, 3, _("Turn off the Keychain feature above first"));
2032 #endif /* APPLEKEYCHAIN */
2033 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
2037 fs_give((void **) &ps_global
->smime
->publicpath
);
2040 if(!(smime_path(ps_global
->VAR_PUBLICCERT_DIR
, path
, MAXPATH
)
2041 && !IS_REMOTE(path
))){
2042 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
2046 if(can_access(path
, ACCESS_EXISTS
)){
2047 if(our_mkpath(path
, 0700)){
2048 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
2053 ps_global
->smime
->publicpath
= cpystr(path
);
2054 configpath
= ps_global
->smime
->publicpath
;
2056 else if(which
== Private
){
2057 leader
= EMAILADDRLEADER
;
2058 contents
= ps_global
->smime
->privatecontent
;
2059 configdir
= ps_global
->VAR_PRIVATEKEY_DIR
;
2060 configpath
= ps_global
->smime
->privatepath
;
2061 filesuffix
= ".key";
2062 if(!(configpath
&& configpath
[0])){
2063 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
2067 fs_give((void **) &ps_global
->smime
->privatepath
);
2070 if(!(smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, path
, MAXPATH
)
2071 && !IS_REMOTE(path
))){
2072 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
2076 if(can_access(path
, ACCESS_EXISTS
)){
2077 if(our_mkpath(path
, 0700)){
2078 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
2083 ps_global
->smime
->privatepath
= cpystr(path
);
2084 configpath
= ps_global
->smime
->privatepath
;
2086 else if(which
== CACert
){
2087 leader
= CACERTSTORELEADER
;
2088 contents
= ps_global
->smime
->cacontent
;
2089 configdir
= ps_global
->VAR_CACERT_DIR
;
2090 configpath
= ps_global
->smime
->capath
;
2091 filesuffix
= ".crt";
2092 if(!(configpath
&& configpath
[0])){
2093 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
2097 fs_give((void **) &ps_global
->smime
->capath
);
2100 if(!(smime_path(ps_global
->VAR_CACERT_DIR
, path
, MAXPATH
)
2101 && !IS_REMOTE(path
))){
2102 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
2106 if(can_access(path
, ACCESS_EXISTS
)){
2107 if(our_mkpath(path
, 0700)){
2108 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
2113 ps_global
->smime
->capath
= cpystr(path
);
2114 configpath
= ps_global
->smime
->capath
;
2117 if(!(configdir
&& configdir
[0])){
2118 q_status_message(SM_ORDER
, 3, 3, _("Directory not defined"));
2122 if(!(configpath
&& configpath
[0])){
2123 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
2127 if(!(filesuffix
&& strlen(filesuffix
) == 4)){
2132 if(contents
&& *contents
){
2133 for(p
= contents
; *p
!= '\0';){
2136 while(*p
&& *p
!= '\n')
2145 if(strncmp(leader
, line
, strlen(leader
)) == 0){
2146 name
= line
+ strlen(leader
);
2148 if(strncmp("-----BEGIN", certtext
, strlen("-----BEGIN")) == 0){
2149 if((q
= strstr(certtext
, leader
)) != NULL
){
2152 else{ /* end of file */
2153 q
= certtext
+ strlen(certtext
);
2157 strncpy(buf
, name
, sizeof(buf
)-5);
2158 buf
[sizeof(buf
)-5] = '\0';
2159 strncat(buf
, filesuffix
, 5);
2160 build_path(file
, configpath
, buf
, sizeof(file
));
2162 in
= BIO_new_mem_buf(certtext
, q
-certtext
);
2164 tempfile
= tempfile_in_same_dir(file
, "az", NULL
);
2167 out
= BIO_new_file(tempfile
, "w");
2170 while((len
= BIO_read(in
, iobuf
, sizeof(iobuf
))) > 0)
2171 BIO_write(out
, iobuf
, len
);
2175 if(rename_file(tempfile
, file
) < 0){
2176 q_status_message2(SM_ORDER
, 3, 3,
2177 _("Can't rename %s to %s"),
2182 fs_give((void **) &tempfile
);
2199 #ifdef APPLEKEYCHAIN
2202 copy_publiccert_container_to_keychain(void)
2204 /* NOT IMPLEMNTED */
2209 copy_publiccert_keychain_to_container(void)
2211 /* NOT IMPLEMNTED */
2215 #endif /* APPLEKEYCHAIN */
2219 * Get a pointer to a string describing the most recent OpenSSL error.
2220 * It's statically allocated, so don't change or attempt to free it.
2223 openssl_error_string(void)
2226 const char *data
= NULL
;
2229 errn
= ERR_peek_error_line_data(NULL
, NULL
, &data
, NULL
);
2230 errs
= (char*) ERR_reason_error_string(errn
);
2237 return "unknown error";
2241 /* Return true if the body looks like a PKCS7 object */
2243 is_pkcs7_body(BODY
*body
)
2247 result
= body
->type
==TYPEAPPLICATION
&&
2249 (strucmp(body
->subtype
,"pkcs7-mime")==0 ||
2250 strucmp(body
->subtype
,"x-pkcs7-mime")==0 ||
2251 strucmp(body
->subtype
,"pkcs7-signature")==0 ||
2252 strucmp(body
->subtype
,"x-pkcs7-signature")==0);
2259 * Recursively stash a pointer to the decrypted data in our
2260 * manufactured body.
2261 * parameters: type: call of type 1, save the base and header for multipart messages
2262 call of type 0, do not save the base and header for multipart messages
2265 create_local_cache(char *h
, char *base
, BODY
*b
, int type
)
2267 if(b
->type
==TYPEMULTIPART
){
2271 cpytxt(&b
->mime
.text
, h
+b
->mime
.offset
, b
->mime
.text
.size
);
2272 cpytxt(&b
->contents
.text
, base
+ b
->contents
.offset
, b
->size
.bytes
);
2273 } else if(type
== 0){
2275 * We don't really want to copy the real body contents. It shouldn't be
2276 * used, and in the case of a message with attachments, we'll be
2277 * duplicating the files multiple times.
2279 cpytxt(&b
->contents
.text
, "BODY UNAVAILABLE", 16);
2281 for(p
=b
->nested
.part
; p
; p
=p
->next
)
2282 create_local_cache(h
, base
, (BODY
*) p
, type
);
2286 cpytxt(&b
->mime
.text
, h
+b
->mime
.offset
, b
->mime
.text
.size
);
2287 cpytxt(&b
->contents
.text
, base
+ b
->contents
.offset
, b
->size
.bytes
);
2293 rfc822_output_func(void *b
, char *string
)
2295 BIO
*bio
= (BIO
*) b
;
2297 return(string
? *string
? (BIO_puts(bio
, string
) > 0 ? 1L : 0L)
2298 : (BIO_puts(bio
, string
) >= 0 ? 1L : 0L)
2304 * Attempt to load the private key for the given PERSONAL_CERT.
2305 * This sets the appropriate passphrase globals in order to
2306 * interact with the user correctly.
2309 load_private_key(PERSONAL_CERT
*pcert
)
2313 /* Try empty password by default */
2314 char *password
= "";
2317 && (ps_global
->smime
->need_passphrase
2318 || ps_global
->smime
->entered_passphrase
)){
2319 /* We've already been in here and discovered we need a different password */
2321 if(ps_global
->smime
->entered_passphrase
)
2322 password
= (char *) ps_global
->smime
->passphrase
; /* already entered */
2329 if(!(pcert
->key
= load_key(pcert
, password
, SM_NORMALCERT
))){
2330 long err
= ERR_get_error();
2332 /* Couldn't load key... */
2334 if(ps_global
->smime
&& ps_global
->smime
->entered_passphrase
){
2336 /* The user got the password wrong maybe? */
2338 if((ERR_GET_LIB(err
)==ERR_LIB_EVP
&& ERR_GET_REASON(err
)==EVP_R_BAD_DECRYPT
) ||
2339 (ERR_GET_LIB(err
)==ERR_LIB_PEM
&& ERR_GET_REASON(err
)==PEM_R_BAD_DECRYPT
))
2340 q_status_message(SM_ORDER
| SM_DING
, 4, 4, _("Incorrect passphrase"));
2342 q_status_message1(SM_ORDER
, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2344 /* This passphrase is no good; forget it */
2345 ps_global
->smime
->entered_passphrase
= 0;
2348 if(ps_global
->smime
){
2349 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2350 ps_global
->smime
->need_passphrase
= 1;
2351 if(ps_global
->smime
->passphrase_emailaddr
){
2353 for(i
= 0; ps_global
->smime
->passphrase_emailaddr
[i
] != NULL
; i
++)
2354 fs_give((void **)&ps_global
->smime
->passphrase_emailaddr
[i
]);
2355 fs_give((void **) ps_global
->smime
->passphrase_emailaddr
);
2358 ps_global
->smime
->passphrase_emailaddr
= get_x509_subject_email(pcert
->cert
);
2364 /* This key will be cached, so we won't be called again */
2365 if(ps_global
->smime
){
2366 ps_global
->smime
->entered_passphrase
= 0;
2367 ps_global
->smime
->need_passphrase
= 0;
2379 setup_pkcs7_body_for_signature(BODY
*b
, char *description
, char *type
, char *filename
, char *smime_type
)
2381 b
->type
= TYPEAPPLICATION
;
2382 b
->subtype
= cpystr(type
);
2383 b
->encoding
= ENCBINARY
;
2384 b
->description
= cpystr(description
);
2386 b
->disposition
.type
= cpystr("attachment");
2387 set_parameter(&b
->disposition
.parameter
, "filename", filename
);
2389 set_parameter(&b
->parameter
, "name", filename
);
2390 if(smime_type
&& *smime_type
)
2391 set_parameter(&b
->parameter
, "smime-type", smime_type
);
2396 * Look for a personal certificate matching the
2400 match_personal_cert_to_email(ADDRESS
*a
)
2402 PERSONAL_CERT
*pcert
= NULL
;
2407 if(!a
|| !a
->mailbox
|| !a
->host
)
2410 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2412 if(ps_global
->smime
){
2413 for(pcert
=(PERSONAL_CERT
*) ps_global
->smime
->personal_certs
;
2420 email
= get_x509_subject_email(pcert
->cert
);
2424 for(i
= 0; email
[i
] && strucmp(email
[i
], buf
) != 0; i
++);
2425 if(email
[i
] != NULL
) done
++;
2426 for(i
= 0; email
[i
] != NULL
; i
++)
2427 fs_give((void **)&email
[i
]);
2428 fs_give((void **)email
);
2441 * Look for a personal certificate matching the from
2442 * (or reply_to? in the given envelope)
2445 match_personal_cert(ENVELOPE
*env
)
2447 PERSONAL_CERT
*pcert
;
2449 pcert
= match_personal_cert_to_email(env
->reply_to
);
2451 pcert
= match_personal_cert_to_email(env
->from
);
2458 * Flatten the given body into its MIME representation.
2459 * Return the result in a BIO.
2462 body_to_bio(BODY
*body
)
2467 bio
= BIO_new(BIO_s_mem());
2471 pine_encode_body(body
); /* this attaches random boundary strings to multiparts */
2472 pine_write_body_header(body
, rfc822_output_func
, bio
);
2473 pine_rfc822_output_body(body
, rfc822_output_func
, bio
);
2476 * Now need to truncate by two characters since the above
2479 if((len
=BIO_ctrl_pending(bio
)) > 1){
2480 BUF_MEM
*biobuf
= NULL
;
2482 BIO_get_mem_ptr(bio
, &biobuf
);
2484 BUF_MEM_grow(biobuf
, len
-2); /* remove CRLF */
2493 bio_from_store(STORE_S
*store
)
2497 if(store
&& store
->src
== BioType
&& store
->txt
){
2498 ret
= (BIO
*) store
->txt
;
2505 * Encrypt file; given a path (char *) fp, replace the file
2506 * by an encrypted version of it. If (char *) text is not null, then
2507 * replace the text of (char *) fp by the encrypted version of (char *) text.
2508 * certpath is the FULL path to the file containing the certificate used for
2510 * return value: 0 - failed to encrypt; 1 - success!
2513 encrypt_file(char *fp
, char *text
, PERSONAL_CERT
*pc
)
2515 const EVP_CIPHER
*cipher
= NULL
;
2516 STACK_OF(X509
) *encerts
= NULL
;
2524 cipher
= EVP_aes_256_cbc();
2525 encerts
= sk_X509_new_null();
2527 sk_X509_push(encerts
, X509_dup(pc
->cert
));
2530 if((out
= BIO_new(BIO_s_mem())) != NULL
){
2531 (void) BIO_reset(out
);
2532 BIO_puts(out
, text
);
2535 else if((out
= BIO_new_file(fp
, "rb")) != NULL
)
2536 BIO_read_filename(out
, fp
);
2538 if((p7
= PKCS7_encrypt(encerts
, out
, cipher
, 0)) != NULL
){
2539 BIO_set_close(out
, BIO_CLOSE
);
2541 if((out
= BIO_new_file(fp
, "w")) != NULL
){
2543 rv
= PEM_write_bio_PKCS7(out
, p7
);
2551 sk_X509_pop_free(encerts
, X509_free
);
2557 * Encrypt a message on the way out. Called from call_mailer in send.c
2558 * The body may be reallocated.
2561 encrypt_outgoing_message(METAENV
*header
, BODY
**bodyP
)
2566 const EVP_CIPHER
*cipher
= NULL
;
2567 STACK_OF(X509
) *encerts
= NULL
;
2568 STORE_S
*outs
= NULL
;
2571 BODY
*body
= *bodyP
;
2572 BODY
*newBody
= NULL
;
2577 dprint((9, "encrypt_outgoing_message()"));
2580 cipher
= EVP_aes_256_cbc();
2582 encerts
= sk_X509_new_null();
2584 /* Look for a certificate for each of the recipients */
2585 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
2586 if(pf
->type
== Address
&& pf
->rcptto
&& pf
->addr
&& *pf
->addr
){
2587 for(a
=*pf
->addr
; a
; a
=a
->next
){
2588 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2590 if((cert
= get_cert_for(buf
, Public
, 1)) != NULL
){
2591 sk_X509_push(encerts
,cert
);
2593 q_status_message2(SM_ORDER
, 1, 1,
2594 _("Unable to find certificate for <%s@%s>"),
2595 a
->mailbox
, a
->host
);
2601 /* add the sender's certificate so that they can decrypt the message too */
2602 for(a
=header
->env
->from
; a
; a
= a
->next
){
2603 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2605 if((cert
= get_cert_for(buf
, Public
, 1)) != NULL
2606 && sk_X509_find(encerts
, cert
) == -1)
2607 sk_X509_push(encerts
,cert
);
2610 in
= body_to_bio(body
);
2612 p7
= PKCS7_encrypt(encerts
, in
, cipher
, 0);
2614 outs
= so_get(BioType
, NULL
, EDIT_ACCESS
);
2615 out
= bio_from_store(outs
);
2617 i2d_PKCS7_bio(out
, p7
);
2618 (void) BIO_flush(out
);
2620 so_seek(outs
, 0, SEEK_SET
);
2622 newBody
= mail_newbody();
2624 newBody
->type
= TYPEAPPLICATION
;
2625 newBody
->subtype
= cpystr("pkcs7-mime");
2626 newBody
->encoding
= ENCBINARY
;
2628 newBody
->disposition
.type
= cpystr("attachment");
2629 set_parameter(&newBody
->disposition
.parameter
, "filename", "smime.p7m");
2631 newBody
->description
= cpystr("S/MIME Encrypted Message");
2632 set_parameter(&newBody
->parameter
, "smime-type", "enveloped-data");
2633 set_parameter(&newBody
->parameter
, "name", "smime.p7m");
2635 newBody
->contents
.text
.data
= (unsigned char *) outs
;
2645 sk_X509_pop_free(encerts
, X509_free
);
2647 dprint((9, "encrypt_outgoing_message returns %d", result
));
2653 Get (and decode) the body of the given section of msg
2656 get_part_contents(long msgno
, const char *section
)
2660 STORE_S
*store
= NULL
;
2663 store
= so_get(CharStar
, NULL
, EDIT_ACCESS
);
2665 gf_set_so_writec(&pc
,store
);
2667 err
= detach(ps_global
->mail_stream
, msgno
, (char*) section
, 0L, &len
, pc
, NULL
, 0L);
2669 gf_clear_so_writec(store
);
2671 so_seek(store
, 0, SEEK_SET
);
2682 get_pkcs7_from_part(long msgno
,const char *section
)
2684 STORE_S
*store
= NULL
;
2688 store
= get_part_contents(msgno
, section
);
2691 if(store
->src
== CharStar
){
2695 * We're reaching inside the STORE_S structure. We should
2696 * probably have a way to get the length, instead.
2698 len
= (int) (store
->eod
- store
->dp
);
2699 in
= BIO_new_mem_buf(store
->txt
, len
);
2701 else{ /* just copy it */
2704 in
= BIO_new(BIO_s_mem());
2705 (void) BIO_reset(in
);
2707 so_seek(store
, 0L, 0);
2708 while(so_readc(&c
, store
)){
2709 BIO_write(in
, &c
, 1);
2714 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2715 if((p7
=d2i_PKCS7_bio(in
,NULL
)) == NULL
){
2728 int same_cert(X509
*x
, X509
*cert
)
2730 char bufcert
[256], bufx
[256];
2733 get_fingerprint(cert
, EVP_md5(), bufcert
, sizeof(bufcert
), ":");
2734 get_fingerprint(x
, EVP_md5(), bufx
, sizeof(bufx
), ":");
2735 if(strcmp(bufx
, bufcert
) == 0)
2742 /* extract and save certificates from a PKCS7 package. The ctype variable
2743 * tells us if we want to extract it to a public/ or a ca/ directory. The
2744 * later makes sense only for recoverable errors (errors that can be fixed
2745 * by saving to the ca/ directory before we verify the signature).
2747 * 0 - no errors (in public/) no need to try again,
2748 * or validated self signed certificate (in ca/)
2749 * < 0 - certificate error is not recoverable, don't even think about it.
2752 int smime_extract_and_save_cert(PKCS7
*p7
, int check_cert
)
2754 STACK_OF(X509
) *signers
;
2760 if((signers
= PKCS7_get0_signers(p7
, NULL
, 0)) == NULL
)
2763 for(i
= 0; i
< sk_X509_num(signers
); i
++){
2764 if((x
= sk_X509_value(signers
,i
)) == NULL
)
2767 if((email
= get_x509_subject_email(x
)) != NULL
){
2768 for(j
= 0; email
[j
] != NULL
; j
++){
2769 if((cert
= get_cert_for(email
[j
], Public
, 1)) == NULL
2770 || same_cert(x
, cert
) == 0){
2772 || smime_validate_cert(x
, &error
) == 0
2773 || (*pith_smime_confirm_save
)(email
[j
]) == 1)
2774 save_cert_for(email
[j
], x
, Public
);
2778 fs_give((void **) &email
[j
]);
2780 fs_give((void **) email
);
2783 sk_X509_free(signers
);
2789 * Try to verify a signature.
2791 * p7 - the pkcs7 object to verify
2792 * in - the plain data to verify (NULL if not detached)
2793 * out - BIO to which to write the opaque data
2794 * silent - if non zero, do not print errors, only print success.
2797 do_signature_verify(PKCS7
*p7
, BIO
*in
, BIO
*out
, int silent
)
2799 STACK_OF(X509
) *otherCerts
= NULL
;
2806 if(!silent
) q_status_message(SM_ORDER
| SM_DING
, 2, 2,
2807 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2812 flags
= F_ON(F_USE_CERT_STORE_ONLY
, ps_global
) ? PKCS7_NOINTERN
: 0;
2814 if(ps_global
->smime
->publiccertlist
== NULL
){
2815 renew_cert_data(&ps_global
->smime
->publiccertlist
, Public
);
2816 for(cl
= ps_global
->smime
->publiccertlist
; cl
; cl
= cl
->next
){
2817 if(cl
->x509_cert
== NULL
){
2818 char *s
= strrchr(cl
->name
, '.');
2820 cl
->x509_cert
= get_cert_for(cl
->name
, Public
, 1);
2826 if(ps_global
->smime
->publiccertlist
){
2827 otherCerts
= sk_X509_new_null();
2828 for(cl
= ps_global
->smime
->publiccertlist
; cl
; cl
= cl
->next
)
2829 if(cl
->x509_cert
!= NULL
)
2830 sk_X509_push(otherCerts
, X509_dup(cl
->x509_cert
));
2833 result
= PKCS7_verify(p7
, otherCerts
, s_cert_store
, in
, out
, flags
);
2835 sk_X509_pop_free(otherCerts
, X509_free
);
2838 q_status_message(SM_ORDER
, 1, 1, _("S/MIME signature verified ok"));
2841 err
= ERR_peek_error_line_data(NULL
, NULL
, &data
, NULL
);
2843 if(out
&& err
==ERR_PACK(ERR_LIB_PKCS7
,PKCS7_F_PKCS7_VERIFY
,PKCS7_R_CERTIFICATE_VERIFY_ERROR
)){
2845 /* Retry verification so we can get the plain text */
2846 /* Might be better to reimplement PKCS7_verify here? */
2848 PKCS7_verify(p7
, otherCerts
, s_cert_store
, in
, out
, PKCS7_NOVERIFY
);
2850 if (!silent
) q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2851 _("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
2857 /* Big comment, explaining the mess that exists out there, and how we deal
2858 with it, and also how we solve the problems that are created this way.
2860 When Alpine sends a message, it constructs that message, computes the
2861 signature, but then it forgets the message it signed and reconstructs it
2862 again. Since it signs a message containing a notice about "mime aware
2863 tools", but it does not send that we do not include that in the part
2864 that is signed, and that takes care of much of the problems.
2866 Another problem is what is received from the servers. All servers tested
2867 seem to transmit the message that was signed intact and Alpine can check
2868 the signature correctly. That is not a problem. The problem arises when
2869 the message includes attachments. In this case different servers send
2870 different things, so it will be up to us to figure out what is the text
2871 that was actually signed. Confused? here is the story:
2873 When a message containing and attachment is sent by Alpine, UW-IMAP,
2874 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2875 that was sent by Alpine, but GMX.com, Exchange, and probably other
2876 servers add a trailing \r\n in the message, so when validating the
2877 signature, these messages will not validate. There are several things
2880 1. Add a trailing \r\n to any message that contains attachments, sign that
2881 and send that. In this way, all messages will validate with all
2884 2. Compatibility mode: If a message has an attachment, contains a trailing
2885 \r\n and does not validate (sent by an earlier version of Alpine),
2886 remove the trailing \r\n and try to revalidate again.
2888 3. We do not add \r\n to validate a message that we sent, because that
2889 would only work in Alpine, and not in any other client. That would
2890 not be a good thing to do.
2894 Now we have to deal with encrypted and signed messages. The problem is
2895 that c-client makes all its pointers point to "on disk" content, but
2896 since we decrypted the data earlier, we have to make sure of two things.
2897 One is that we saved that data (so we do not have to decrypt it again)
2898 and second that we can use it.
2900 In order to save the data we use create_local_cache, so that we do not
2901 have to redecrypt the message. Once this is saved, c-client functions will
2902 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2906 When we are trying to verify messages with detached signatures, some
2907 imap servers send incorrect information in the mail_fetch_mime call. By
2908 incorrect I mean that this is not fetched directly from the message, but
2909 it is read from the message, processed, and then the processed part is
2910 sent to us, so this text might not agree with what is in the message,
2911 and so the validation of the signature might fail. However, the good
2912 news is that the message validates if saved to a local folder. This
2913 means that if normal validation does not work we can make it work by
2914 saving the message locally and validating that. This is implemented
2915 below, and causes delay in the display of the message. I am considering
2916 at this time not to do this automatically, but wait for the user to tell
2917 us to do it for them by means of a command available in the
2918 mail_view_screen. This might help in other situations, where a message
2919 is supposed to have an attachment, but it can not be seen in the
2920 processed text. Nevertheless, at this time, this is automatic, and is
2921 causing a delay in the processing of the message, but it is validating
2922 correctly all messages.
2926 When the user sends a message as encrypted and signed, this code used to
2927 encrypt first, and then sign the pkcs7 body, but it turns out that some
2928 other clients can not handle these messages. While we could argue that the
2929 other clients need to improve, we will support reading messages in both
2930 ways, and will send messages using this technique; that is, signed first,
2931 encrypted second. It seems that all tested clients support this way, so it
2932 should be safe to do so.
2935 typedef struct smime_filter_s
{
2939 SMIME_FILTER_S sig_filter
[] = {
2940 {smime_remove_trailing_crlf
},
2941 {smime_remove_folding_space
}
2944 #define TOTAL_FILTERS (sizeof(sig_filter)/sizeof(sig_filter[0]))
2945 #define TOTAL_SIGFLTR (1 << TOTAL_FILTERS) /* not good, keep filters to a low number */
2948 smime_remove_trailing_crlf(char **mimetext
, unsigned long *mimelen
,
2949 char **bodytext
, unsigned long *bodylen
)
2951 if(*bodylen
> 2 && !strncmp(*bodytext
+*bodylen
-2, "\r\n", 2))
2956 smime_remove_folding_space(char **mimetext
, unsigned long *mimelen
,
2957 char **bodytext
, unsigned long *bodylen
)
2960 unsigned long mlen
= *mimelen
;
2963 for (s
= t
= *mimetext
; t
- *mimetext
< *mimelen
; ){
2964 if(*t
== '\r' && *(t
+1) == '\n' && (*(t
+2) == '\t' || *(t
+2) == ' ')){
2977 smime_validate_extra_test(char *mimetext
, unsigned long mimelen
, char *bodytext
, unsigned long bodylen
, PKCS7
*p7
, int nflag
)
2979 int result
, i
, j
, flag
;
2980 char *mtext
, *btext
;
2981 unsigned long mlen
, blen
;
2984 mtext
= mimelen
? fs_get(mimelen
+1) : NULL
;
2985 btext
= fs_get(bodylen
+1);
2988 flag
= 1; /* silence all failures */
2989 for(i
= 1; result
== 0 && i
< TOTAL_SIGFLTR
; i
++){
2990 if((in
= BIO_new(BIO_s_mem())) == NULL
)
2993 (void) BIO_reset(in
);
2995 if(i
+1 == TOTAL_SIGFLTR
)
2999 strncpy(mtext
, mimetext
, mlen
= mimelen
);
3000 strncpy(btext
, bodytext
, blen
= bodylen
);
3001 for(j
= 0; j
< TOTAL_FILTERS
; j
++)
3003 (sig_filter
[j
].filter
)(&mtext
, &mlen
, &btext
, &blen
);
3005 BIO_write(in
, mtext
, mlen
);
3006 BIO_write(in
, btext
, blen
);
3007 result
= do_signature_verify(p7
, in
, NULL
, flag
);
3010 if(mtext
) fs_give((void **)&mtext
);
3011 if(btext
) fs_give((void **)&btext
);
3016 * Given a multipart body of type multipart/signed, attempt to verify it.
3017 * Returns non-zero if the body was changed.
3020 do_detached_signature_verify(BODY
*b
, long msgno
, char *section
)
3025 int result
, modified_the_body
= 0;
3026 int flag
; /* 1 silent, 0 not silent */
3028 unsigned long mimelen
, bodylen
;
3029 char newSec
[100], *mimetext
, *bodytext
;
3033 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"));
3037 /* if it was signed and then encrypted, use the decrypted text
3038 * to check the validity of the signature
3041 if(get_body_sparep_type(b
->sparep
) == SizedText
){
3042 /* bodytext includes mimetext */
3043 st
= (SIZEDTEXT
*) get_body_sparep_data(b
->sparep
);
3044 bodytext
= (char *) st
->data
;
3051 snprintf(newSec
, sizeof(newSec
), "%s%s1", section
? section
: "", (section
&& *section
) ? "." : "");
3052 mimetext
= mail_fetch_mime(ps_global
->mail_stream
, msgno
, (char*) newSec
, &mimelen
, 0);
3054 bodytext
= mail_fetch_body (ps_global
->mail_stream
, msgno
, (char*) newSec
, &bodylen
, 0);
3056 if(mimetext
== NULL
|| bodytext
== NULL
)
3057 return modified_the_body
;
3060 snprintf(newSec
, sizeof(newSec
), "%s%s2", section
? section
: "", (section
&& *section
) ? "." : "");
3062 if((p7
= get_pkcs7_from_part(msgno
, newSec
)) == NULL
3063 || (in
= BIO_new(BIO_s_mem())) == NULL
)
3064 return modified_the_body
;
3066 (void) BIO_reset(in
);
3067 if(mimetext
!= NULL
)
3068 BIO_write(in
, mimetext
, mimelen
);
3069 BIO_write(in
, bodytext
, bodylen
);
3071 saved
= smime_extract_and_save_cert(p7
, F_ON(F_USE_CERT_STORE_ONLY
, ps_global
));
3072 if(saved
< 0 && F_ON(F_USE_CERT_STORE_ONLY
, ps_global
))
3073 return modified_the_body
;
3075 if((result
= do_signature_verify(p7
, in
, NULL
, 1)) == 0){
3076 flag
= (mimelen
== 0 || !IS_REMOTE(ps_global
->mail_stream
->mailbox
))
3078 result
= smime_validate_extra_test(mimetext
, mimelen
, bodytext
, bodylen
, p7
, flag
);
3080 return modified_the_body
;
3082 && mimelen
> 0 /* do not do this for encrypted messages */
3083 && IS_REMOTE(ps_global
->mail_stream
->mailbox
)){
3085 unsigned long hlen
, tlen
;
3089 if((in
= BIO_new(BIO_s_mem())) != NULL
3090 && (fetch
= mail_fetch_header(ps_global
->mail_stream
, msgno
, NULL
,
3091 NULL
, &hlen
, FT_PEEK
)) != NULL
3092 && (msg_so
= so_get(CharStar
, NULL
, WRITE_ACCESS
)) != NULL
3093 && so_nputs(msg_so
, fetch
, (long) hlen
)
3094 && (fetch
= pine_mail_fetch_text(ps_global
->mail_stream
, msgno
, NULL
,
3095 &tlen
, FT_PEEK
)) != NULL
3096 && so_nputs(msg_so
, fetch
, tlen
)){
3098 char *h
= (char *) so_text(msg_so
);
3099 char *bstart
= strstr(h
, "\r\n\r\n");
3104 INIT(&bs
, mail_string
, bstart
, tlen
);
3105 rfc822_parse_msg_full(&env
, &body
, h
, bstart
-h
-4, &bs
, BADHOST
, 0, 0);
3106 mail_free_envelope(&env
);
3108 mail_free_body_part(&b
->nested
.part
);
3109 tmpB
= mail_body_section(body
, (unsigned char *) section
);
3110 if(MIME_MSG(tmpB
->type
, tmpB
->subtype
))
3111 b
->nested
.part
= tmpB
->nested
.msg
->body
->nested
.part
;
3113 b
->nested
.part
= tmpB
->nested
.part
;
3114 create_local_cache(bstart
, bstart
, &b
->nested
.part
->body
, 1);
3115 modified_the_body
= 1;
3117 snprintf(newSec
, sizeof(newSec
), "%s%s1", section
? section
: "", (section
&& *section
) ? "." : "");
3119 mimetext
= mail_fetch_mime(ps_global
->mail_stream
, msgno
, (char*) newSec
, &mimelen
, 0);
3122 bodytext
= mail_fetch_body (ps_global
->mail_stream
, msgno
, (char*) newSec
, &bodylen
, 0);
3124 if (mimetext
== NULL
|| bodytext
== NULL
)
3125 return modified_the_body
;
3127 snprintf(newSec
, sizeof(newSec
), "%s%s2", section
? section
: "", (section
&& *section
) ? "." : "");
3129 if((p7
= get_pkcs7_from_part(msgno
, newSec
)) == NULL
)
3130 return modified_the_body
;
3132 (void) BIO_reset(in
);
3133 BIO_write(in
, mimetext
, mimelen
);
3134 BIO_write(in
, bodytext
, bodylen
);
3137 if((result
= do_signature_verify(p7
, in
, NULL
, 1)) == 0){
3138 result
= smime_validate_extra_test(mimetext
, mimelen
, bodytext
, bodylen
, p7
, 0);
3140 return modified_the_body
;
3148 fs_give((void**) &b
->subtype
);
3150 b
->subtype
= cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE
);
3151 b
->encoding
= ENC8BIT
;
3154 fs_give ((void**) &b
->description
);
3156 what_we_did
= result
? _("This message was cryptographically signed.") :
3157 _("This message was cryptographically signed but the signature could not be verified.");
3159 b
->description
= cpystr(what_we_did
);
3161 b
->sparep
= create_body_sparep(P7Type
, p7
);
3165 /* p is signed plaintext */
3167 mail_free_body_part(&p
->next
); /* hide the pkcs7 from the viewer */
3169 modified_the_body
= 1;
3171 return modified_the_body
;
3176 find_certificate_matching_recip_info(PKCS7_RECIP_INFO
*ri
)
3178 PERSONAL_CERT
*x
= NULL
;
3180 if(ps_global
->smime
){
3181 for(x
= (PERSONAL_CERT
*) ps_global
->smime
->personal_certs
; x
; x
=x
->next
){
3186 if(!X509_NAME_cmp(ri
->issuer_and_serial
->issuer
,X509_get_issuer_name(mine
)) &&
3187 !ASN1_INTEGER_cmp(ri
->issuer_and_serial
->serial
,X509_get_serialNumber(mine
))){
3197 static PERSONAL_CERT
*
3198 find_certificate_matching_pkcs7(PKCS7
*p7
)
3201 STACK_OF(PKCS7_RECIP_INFO
) *recips
;
3202 PERSONAL_CERT
*x
= NULL
;
3204 recips
= p7
->d
.enveloped
->recipientinfo
;
3206 for(i
=0; i
<sk_PKCS7_RECIP_INFO_num(recips
); i
++){
3207 PKCS7_RECIP_INFO
*ri
;
3209 ri
= sk_PKCS7_RECIP_INFO_value(recips
, i
);
3211 if((x
=find_certificate_matching_recip_info(ri
))!=0){
3219 /* decrypt an encrypted file.
3220 Args: fp - the path to the encrypted file.
3221 rv - a code that tells the caller what happened inside the function
3222 pcert - a personal certificate that was used to encrypt this file
3223 Returns the decoded text allocated in a char *, whose memory must be
3228 decrypt_file(char *fp
, int *rv
, PERSONAL_CERT
*pc
)
3232 BIO
*in
= NULL
, *out
= NULL
;
3234 long unsigned int len
;
3237 if(pc
== NULL
|| (text
= read_file(fp
, 0)) == NULL
|| *text
== '\0')
3240 tmp
= fs_get(strlen(text
) + (strlen(text
) << 6) + 1);
3241 for(j
= 0, i
= strlen("-----BEGIN PKCS7-----") + 1; text
[i
] != '\0'
3242 && text
[i
] != '-'; j
++, i
++)
3246 ret
= rfc822_base64((unsigned char *)tmp
, strlen(tmp
), &len
);
3248 if((in
= BIO_new_mem_buf((char *)ret
, len
)) != NULL
){
3249 p7
= d2i_PKCS7_bio(in
, NULL
);
3253 if (text
) fs_give((void **)&text
);
3254 if (ret
) fs_give((void **)&ret
);
3256 if (rv
) *rv
= pc
->key
== NULL
? -1 : 1;
3258 out
= BIO_new(BIO_s_mem());
3259 (void) BIO_reset(out
);
3261 if(PKCS7_decrypt(p7
, pc
->key
, pc
->cert
, out
, 0) != 0){
3262 BIO_get_mem_data(out
, &tmp
);
3266 q_status_message1(SM_ORDER
, 1, 1, _("Error decrypting: %s"),
3267 (char *) openssl_error_string());
3274 * Try to decode (decrypt or verify a signature) a PKCS7 body
3275 * Returns non-zero if something was changed.
3278 do_decoding(BODY
*b
, long msgno
, const char *section
)
3280 int modified_the_body
= 0;
3284 EVP_PKEY
*key
= NULL
;
3285 PERSONAL_CERT
*pcert
= NULL
;
3286 char *what_we_did
= "";
3289 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"));
3294 * Extract binary data from part to an in-memory store
3298 if(get_body_sparep_type(b
->sparep
) == P7Type
)
3299 p7
= (PKCS7
*) get_body_sparep_data(b
->sparep
);
3302 p7
= get_pkcs7_from_part(msgno
, section
&& *section
? section
: "1");
3304 q_status_message1(SM_ORDER
, 2, 2, "Couldn't load PKCS7 object: %s",
3305 (char*) openssl_error_string());
3310 * Save the PKCS7 object for later dealings by the user interface.
3311 * It will be cleaned up when the body is garbage collected.
3313 b
->sparep
= create_body_sparep(P7Type
, p7
);
3316 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7
), PKCS7_type_is_enveloped(p7
)));
3318 if(PKCS7_type_is_signed(p7
)){
3321 out
= BIO_new(BIO_s_mem());
3322 (void) BIO_reset(out
);
3323 BIO_puts(out
, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3325 sigok
= do_signature_verify(p7
, NULL
, out
, 0);
3327 what_we_did
= sigok
? _("This message was cryptographically signed.") :
3328 _("This message was cryptographically signed but the signature could not be verified.");
3330 /* make sure it's null terminated */
3331 BIO_write(out
, null
, 1);
3333 else if(!PKCS7_type_is_enveloped(p7
)){
3334 q_status_message(SM_ORDER
, 1, 1, "PKCS7 object not recognised.");
3337 else{ /* It *is* enveloped */
3340 what_we_did
= _("This message was encrypted.");
3342 /* now need to find a cert that can decrypt this */
3343 pcert
= find_certificate_matching_pkcs7(p7
);
3346 q_status_message(SM_ORDER
, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3350 recip
= pcert
->cert
;
3352 if(!load_private_key(pcert
)
3354 && ps_global
->smime
->need_passphrase
3355 && !ps_global
->smime
->already_auto_asked
){
3356 /* Couldn't load key with blank password, ask user */
3357 ps_global
->smime
->already_auto_asked
= 1;
3358 if(pith_opt_smime_get_passphrase
){
3359 (*pith_opt_smime_get_passphrase
)();
3360 load_private_key(pcert
);
3368 out
= BIO_new(BIO_s_mem());
3369 (void) BIO_reset(out
);
3370 BIO_puts(out
, "MIME-Version: 1.0\r\n");
3372 decrypt_result
= PKCS7_decrypt(p7
, key
, recip
, out
, 0);
3374 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
3375 forget_private_keys();
3377 if(!decrypt_result
){
3378 q_status_message1(SM_ORDER
, 1, 1, _("Error decrypting: %s"),
3379 (char*) openssl_error_string());
3382 BIO_write(out
, null
, 1);
3386 * We've now produced a flattened MIME object in BIO out.
3387 * It needs to be turned back into a BODY.
3396 BUF_MEM
*bptr
= NULL
;
3398 BIO_get_mem_ptr(out
, &bptr
);
3402 /* look for start of body */
3403 bstart
= strstr(h
, "\r\n\r\n");
3406 q_status_message(SM_ORDER
, 3, 3, _("Encrypted data couldn't be parsed."));
3410 bstart
+= 4; /* skip over CRLF*2 */
3412 INIT(&s
, mail_string
, bstart
, strlen(bstart
));
3413 rfc822_parse_msg_full(&env
, &body
, h
, bstart
-h
-2, &s
, BADHOST
, 0, 0);
3414 mail_free_envelope(&env
); /* Don't care about this */
3416 if(body
->type
== TYPEMULTIPART
3417 && !strucmp(body
->subtype
, "SIGNED")){
3418 char *cookie
= NULL
;
3420 for (param
= body
->parameter
; param
&& !cookie
; param
= param
->next
)
3421 if (!strucmp (param
->attribute
,"BOUNDARY")) cookie
= param
->value
;
3423 st
= fs_get(sizeof(SIZEDTEXT
));
3424 st
->data
= (void *) cpystr(bstart
+ strlen(cookie
)+4); /* 4 = strlen("--\r\n") */
3425 st
->size
= body
->nested
.part
->next
->body
.mime
.offset
- 2*(strlen(cookie
) + 4);
3426 body
->sparep
= create_body_sparep(SizedText
, (void *)st
);
3429 q_status_message(SM_ORDER
, 3, 3, _("Couldn't find cookie in attachment list."));
3431 body
->mime
.offset
= 0;
3432 body
->mime
.text
.size
= 0;
3435 * Now convert original body (application/pkcs7-mime)
3436 * to a multipart body with one sub-part (the decrypted body).
3437 * Note that the sub-part may also be multipart!
3440 b
->type
= TYPEMULTIPART
;
3442 fs_give((void**) &b
->subtype
);
3445 * This subtype is used in mailview.c to annotate the display of
3446 * encrypted or signed messages. We know for sure then that it's a PKCS7
3447 * part because the sparep field is set to the PKCS7 object (see above).
3449 b
->subtype
= cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE
);
3450 b
->encoding
= ENC8BIT
;
3453 fs_give((void**) &b
->description
);
3455 b
->description
= cpystr(what_we_did
);
3457 if(b
->disposition
.type
)
3458 fs_give((void **) &b
->disposition
.type
);
3460 if(b
->contents
.text
.data
)
3461 fs_give((void **) &b
->contents
.text
.data
);
3464 mail_free_body_parameter(&b
->parameter
);
3466 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3467 b
->nested
.part
= fs_get(sizeof(PART
));
3468 b
->nested
.part
->body
= *body
;
3469 b
->nested
.part
->next
= NULL
;
3471 fs_give((void**) &body
);
3474 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3475 * the decrypted data. Otherwise, it'll try to load it from the original
3478 create_local_cache(bstart
-b
->nested
.part
->body
.mime
.offset
, bstart
, &b
->nested
.part
->body
, 0);
3480 modified_the_body
= 1;
3488 return modified_the_body
;
3493 * Recursively handle PKCS7 bodies in our message.
3495 * Returns non-zero if some fiddling was done.
3498 do_fiddle_smime_message(BODY
*b
, long msgno
, char *section
)
3500 int modified_the_body
= 0;
3505 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"));
3507 if(is_pkcs7_body(b
)){
3509 if(do_decoding(b
, msgno
, section
)){
3511 * b should now be a multipart message:
3512 * fiddle it too in case it's been multiply-encrypted!
3516 modified_the_body
= 1;
3520 if(b
->type
==TYPEMULTIPART
|| MIME_MSG(b
->type
, b
->subtype
)){
3526 if(MIME_MULT_SIGNED(b
->type
, b
->subtype
)){
3530 * Ahah. We have a multipart signed entity.
3533 * part 1 (signed thing)
3534 * part 2 (the pkcs7 signature)
3536 * We're going to convert that to
3538 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3539 * part 1 (signed thing)
3540 * part 2 has been freed
3542 * We also extract the signature from part 2 and save it
3543 * in the multipart body->sparep, and we add a description
3544 * in the multipart body->description.
3547 * The results of a decrypted message will be similar. It
3550 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3551 * part 1 (decrypted thing)
3554 modified_the_body
+= do_detached_signature_verify(b
, msgno
, section
);
3556 else if(MIME_MSG(b
->type
, b
->subtype
)){
3557 modified_the_body
+= do_fiddle_smime_message(b
->nested
.msg
->body
, msgno
, section
);
3561 for(p
=b
->nested
.part
,partNum
=1; p
; p
=p
->next
,partNum
++){
3562 /* Append part number to the section string */
3564 snprintf(newSec
, sizeof(newSec
), "%s%s%d", section
, *section
? "." : "", partNum
);
3566 modified_the_body
+= do_fiddle_smime_message(&p
->body
, msgno
, newSec
);
3571 return modified_the_body
;
3576 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3577 * Returns non-zero if something was changed.
3580 fiddle_smime_message(BODY
*b
, long msgno
)
3582 return do_fiddle_smime_message(b
, msgno
, "");
3586 /********************************************************************************/
3590 * Output a string in a distinctive style
3593 gf_puts_uline(char *txt
, gf_io_t pc
)
3595 pc(TAG_EMBED
); pc(TAG_BOLDON
);
3597 pc(TAG_EMBED
); pc(TAG_BOLDOFF
);
3600 /* get_chain_for_cert: error and level are mandatory arguments */
3602 get_chain_for_cert(X509
*cert
, int *error
, int *level
)
3604 STACK_OF(X509
) *chain
= NULL
;
3605 X509_STORE_CTX
*ctx
;
3607 int rc
; /* return code */
3612 if((s_cert_store
!= NULL
) && (ctx
= X509_STORE_CTX_new()) != NULL
){
3613 X509_STORE_set_flags(s_cert_store
, 0);
3614 if(!X509_STORE_CTX_init(ctx
, s_cert_store
, cert
, NULL
))
3615 *error
= X509_STORE_CTX_get_error(ctx
);
3616 else if((chain
= sk_X509_new_null()) != NULL
){
3617 for(x
= cert
; ; x
= xtmp
){
3619 sk_X509_push(chain
, X509_dup(x
));
3620 rc
= X509_STORE_CTX_get1_issuer(&xtmp
, ctx
, x
);
3625 if(!X509_check_issued(xtmp
, xtmp
))
3629 X509_STORE_CTX_free(ctx
);
3636 * Sign a message. Called from call_mailer in send.c.
3638 * This takes the header for the outgoing message as well as a pointer
3639 * to the current body (which may be reallocated).
3640 * The last argument (BODY **bp) is an argument that tells Alpine
3641 * if the body has 8 bit. if *bp is not null we compute two signatures
3642 * one for the quoted-printable encoded message, and another for the
3643 * 8bit encoded message. We return the signature for the 8bit encoded
3644 * part in p2->body.mime.text.data.
3645 * The reason why we compute two signatures is so that we can decide
3646 * which one to use later, and we only do it in the case that *bp is
3647 * not null. If we did not do this, then we might not be able to sign
3648 * a message until we log in to the smtp server, so instead of doing
3649 * that, we get ready for any possible situation we might find.
3652 sign_outgoing_message(METAENV
*header
, BODY
**bodyP
, int dont_detach
, BODY
**bp
)
3654 STORE_S
*outs
= NULL
;
3655 STORE_S
*outs_2
= NULL
;
3656 BODY
*body
= *bodyP
;
3657 BODY
*newBody
= NULL
;
3660 PERSONAL_CERT
*pcert
;
3667 STACK_OF(X509
) *chain
;
3668 const EVP_MD
*md
= EVP_sha256(); /* use this digest instead of sha1 */
3669 int result
= 0, error
;
3670 int flags
= dont_detach
? 0 : PKCS7_DETACHED
;
3673 dprint((9, "sign_outgoing_message()"));
3677 /* Look for a private key matching the sender address... */
3679 pcert
= match_personal_cert(header
->env
);
3682 q_status_message(SM_ORDER
, 3, 3, _("Couldn't find the certificate needed to sign."));
3686 if(!load_private_key(pcert
) && ps_global
->smime
&& ps_global
->smime
->need_passphrase
){
3687 /* Couldn't load key with blank password, try again */
3688 if(pith_opt_smime_get_passphrase
){
3689 (*pith_opt_smime_get_passphrase
)();
3690 load_private_key(pcert
);
3697 if(((chain
= get_chain_for_cert(pcert
->cert
, &error
, &level
)) != NULL
&& error
)
3699 sk_X509_pop_free(chain
, X509_free
);
3704 q_status_message(SM_ORDER
, 1, 1,
3705 _("Not all certificates needed to verify signature included in signed message"));
3707 in
= body_to_bio(body
);
3709 flags
|= PKCS7_PARTIAL
;
3710 if((p7
= PKCS7_sign(NULL
, NULL
, chain
, in
, flags
)) != NULL
3711 && PKCS7_sign_add_signer(p7
, pcert
->cert
, pcert
->key
, md
, flags
))
3712 PKCS7_final(p7
, in
, flags
);
3715 int i
, save_encoding
;
3717 for(i
= 0; (i
<= ENCMAX
) && body_encodings
[i
]; i
++);
3719 if(i
> ENCMAX
){ /* no empty encoding slots! */
3723 save_encoding
= (*bp
)->encoding
;
3724 body_encodings
[(*bp
)->encoding
= i
] = body_encodings
[ENC8BIT
];
3726 in_2
= body_to_bio(body
);
3728 body_encodings
[i
] = NULL
;
3729 (*bp
)->encoding
= save_encoding
;
3734 if((p7_2
= PKCS7_sign(NULL
, NULL
, chain
, in_2
, flags
)) != NULL
3735 && PKCS7_sign_add_signer(p7_2
, pcert
->cert
, pcert
->key
, md
, flags
))
3736 PKCS7_final(p7_2
, in_2
, flags
);
3739 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
3740 forget_private_keys();
3743 sk_X509_pop_free(chain
, X509_free
);
3746 q_status_message(SM_ORDER
, 1, 1, _("Error creating signed object."));
3750 outs
= so_get(BioType
, NULL
, EDIT_ACCESS
);
3751 out
= bio_from_store(outs
);
3753 i2d_PKCS7_bio(out
, p7
);
3754 (void) BIO_flush(out
);
3756 so_seek(outs
, 0, SEEK_SET
);
3758 if(bp
&& *bp
&& p7_2
){
3759 outs_2
= so_get(BioType
, NULL
, EDIT_ACCESS
);
3760 out_2
= bio_from_store(outs_2
);
3762 i2d_PKCS7_bio(out_2
, p7_2
);
3763 (void) BIO_flush(out_2
);
3765 so_seek(outs_2
, 0, SEEK_SET
);
3768 if((flags
&PKCS7_DETACHED
)==0){
3770 /* the simple case: the signed data is in the pkcs7 object */
3772 newBody
= mail_newbody();
3774 setup_pkcs7_body_for_signature(newBody
, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3776 newBody
->contents
.text
.data
= (unsigned char *) outs
;
3785 * We have to create a new body as follows:
3787 * multipart/signed; blah blah blah
3788 * reference to existing body
3793 newBody
= mail_newbody();
3795 newBody
->type
= TYPEMULTIPART
;
3796 newBody
->subtype
= cpystr("signed");
3797 newBody
->encoding
= ENC7BIT
;
3799 set_parameter(&newBody
->parameter
, "protocol", "application/pkcs7-signature");
3800 set_parameter(&newBody
->parameter
, "micalg", "sha-256");
3802 p1
= mail_newbody_part();
3803 p2
= mail_newbody_part();
3806 * This is nasty. We're just copying the body in here,
3807 * but since our newBody is freed at the end of call_mailer,
3808 * we mustn't let this body (the original one) be freed twice.
3810 p1
->body
= *body
; /* ARRGH. This is special cased at the end of call_mailer */
3814 setup_pkcs7_body_for_signature(&p2
->body
, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL
);
3815 p2
->body
.mime
.text
.data
= (unsigned char *) outs_2
;
3816 p2
->body
.contents
.text
.data
= (unsigned char *) outs
;
3818 newBody
->nested
.part
= p1
;
3831 if(p7_2
) PKCS7_free(p7_2
);
3835 dprint((9, "sign_outgoing_message returns %d", result
));
3841 new_smime_struct(void)
3843 SMIME_STUFF_S
*ret
= NULL
;
3845 ret
= (SMIME_STUFF_S
*) fs_get(sizeof(*ret
));
3846 memset((void *) ret
, 0, sizeof(*ret
));
3847 ret
->publictype
= Nada
;
3854 free_smime_struct(SMIME_STUFF_S
**smime
)
3856 if(smime
&& *smime
){
3857 if((*smime
)->passphrase_emailaddr
){
3859 for(i
= 0; (*smime
)->passphrase_emailaddr
[i
] != NULL
; i
++)
3860 fs_give((void **) &(*smime
)->passphrase_emailaddr
[i
]);
3861 fs_give((void **) (*smime
)->passphrase_emailaddr
);
3864 if((*smime
)->publicpath
)
3865 fs_give((void **) &(*smime
)->publicpath
);
3867 if((*smime
)->publiccertlist
)
3868 free_certlist(&(*smime
)->publiccertlist
);
3870 if((*smime
)->backuppubliccertlist
)
3871 free_certlist(&(*smime
)->backuppubliccertlist
);
3873 if((*smime
)->cacertlist
)
3874 free_certlist(&(*smime
)->cacertlist
);
3876 if((*smime
)->backupcacertlist
)
3877 free_certlist(&(*smime
)->backupcacertlist
);
3879 if((*smime
)->privatecertlist
)
3880 free_certlist(&(*smime
)->privatecertlist
);
3882 if((*smime
)->backupprivatecertlist
)
3883 free_certlist(&(*smime
)->backupprivatecertlist
);
3885 if((*smime
)->publiccontent
)
3886 fs_give((void **) &(*smime
)->publiccontent
);
3888 if((*smime
)->privatepath
)
3889 fs_give((void **) &(*smime
)->privatepath
);
3891 if((*smime
)->personal_certs
){
3894 pc
= (PERSONAL_CERT
*) (*smime
)->personal_certs
;
3895 free_personal_certs(&pc
);
3896 (*smime
)->personal_certs
= NULL
;
3899 if((*smime
)->privatecontent
)
3900 fs_give((void **) &(*smime
)->privatecontent
);
3902 if((*smime
)->capath
)
3903 fs_give((void **) &(*smime
)->capath
);
3905 if((*smime
)->cacontent
)
3906 fs_give((void **) &(*smime
)->cacontent
);
3908 fs_give((void **) smime
);