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-2020 Eduardo Chappa
8 * Copyright 2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
20 * This is based on a contribution from Jonathan Paisley
23 * Author: paisleyj@dcs.gla.ac.uk
28 #include "../pith/headers.h"
32 #include "../pith/osdep/canaccess.h"
33 #include "../pith/helptext.h"
34 #include "../pith/store.h"
35 #include "../pith/status.h"
36 #include "../pith/detach.h"
37 #include "../pith/conf.h"
38 #include "../pith/smkeys.h"
39 #include "../pith/smime.h"
40 #include "../pith/mailpart.h"
41 #include "../pith/reply.h"
42 #include "../pith/tempfile.h"
43 #include "../pith/readfile.h"
44 #include "../pith/remote.h"
45 #include "../pith/body.h"
47 #include "../pith/imap.h"
50 #include <openssl/buffer.h>
51 #include <openssl/x509v3.h>
52 #include <openssl/evp.h>
54 /* internal prototypes */
55 static void forget_private_keys(void);
56 static int app_RAND_load_file(const char *file
);
57 static void openssl_extra_randomness(void);
58 static int app_RAND_write_file(const char *file
);
59 static const char *openssl_error_string(void);
60 static int load_private_key(PERSONAL_CERT
*pcert
);
61 static void create_local_cache(char *h
, char *base
, BODY
*b
, int type
);
62 static long rfc822_output_func(void *b
, char *string
);
63 static void setup_pkcs7_body_for_signature(BODY
*b
, char *description
,
64 char *type
, char *filename
, char *smime_type
);
65 static BIO
*body_to_bio(BODY
*body
);
66 static BIO
*bio_from_store(STORE_S
*store
);
67 static STORE_S
*get_part_contents(long msgno
, const char *section
);
68 static PKCS7
*get_pkcs7_from_part(long msgno
, const char *section
);
69 static int do_signature_verify(PKCS7
*p7
, BIO
*in
, BIO
*out
, int silent
);
70 static int do_detached_signature_verify(BODY
*b
, long msgno
, char *section
);
71 static PERSONAL_CERT
*find_certificate_matching_pkcs7(PKCS7
*p7
);
72 static int do_decoding(BODY
*b
, long msgno
, const char *section
);
73 static void free_smime_struct(SMIME_STUFF_S
**smime
);
74 static void setup_storage_locations(void);
75 static int copy_container_to_dir(WhichCerts which
);
76 static int do_fiddle_smime_message(BODY
*b
, long msgno
, char *section
);
77 void setup_privatekey_storage(void);
78 int smime_extract_and_save_cert(PKCS7
*p7
);
79 int same_cert(X509
*, X509
*);
81 int load_key_and_cert(char *pathkeydir
, char *pathcertdir
, char **keyfile
, char **certfile
, EVP_PKEY
**pkey
, X509
**pcert
);
83 EVP_PKEY
*load_pkey_with_prompt(char *fpath
, char *text
, char *prompt
, int *);
84 void smime_remove_trailing_crlf(char **mimetext
, unsigned long *mimelen
, char **bodytext
, unsigned long *bodylen
);
85 void smime_remove_folding_space(char **mimetext
, unsigned long *mimelen
, char **bodytext
, unsigned long *bodylen
);
86 int smime_validate_extra_test(char *mimetext
, unsigned long mimelen
, char *bodytext
, unsigned long bodylen
, PKCS7
*p7
, int nflag
);
88 int (*pith_opt_smime_get_passphrase
)(void);
89 int (*pith_smime_import_certificate
)(char *, char *, char *, size_t);
90 int (*pith_smime_enter_password
)(char *, char *, size_t);
91 int (*pith_smime_confirm_save
)(char *);
93 static X509_STORE
*s_cert_store
;
95 /* State management for randomness functions below */
96 static int seeded
= 0;
100 * This code does not work in Windows, because of the PASSFILE thing, so
101 * I did not try to fix it. If you think it does need to be applied to
102 * the Windows version of alpine, there are more changes that are needed
103 * than fixing this function in this module. E. Chappa 09/28/17.
105 * load key from pathkeydir and cert from pathcertdir. It chooses the first
106 * key/certificate pair that matches. Delete pairs that you do not want used,
107 * if you do not want them selected. All parameters must be non-null.
108 * Memory freed by caller.
110 * -1 : user cancelled load
111 * 0 : load was successful
112 * 1 : there was an error in the loading.
115 load_key_and_cert(char *pathkeydir
, char *pathcertdir
, char **keyfile
,
116 char **certfile
, EVP_PKEY
**pkey
, X509
**pcert
)
118 char buf
[MAXPATH
+1], pathkey
[MAXPATH
+1], prompt
[MAILTMPLEN
];
121 int b
= 0, ret
= 1; /* assume error */
123 if(pathkeydir
== NULL
|| pathcertdir
== NULL
|| keyfile
== NULL
124 || pkey
== NULL
|| certfile
== NULL
|| pcert
== NULL
)
132 if((dirp
= opendir(pathkeydir
)) != NULL
){
133 while(b
== 0 && (d
=readdir(dirp
)) != NULL
){
136 if((ll
=strlen(d
->d_name
)) && ll
> 4){
137 if(!strcmp(d
->d_name
+ll
-4, ".key")){
138 strncpy(buf
, d
->d_name
, sizeof(buf
));
139 buf
[sizeof(buf
)-1] = '\0';
140 build_path(pathkey
, pathkeydir
, buf
, sizeof(pathkey
));
141 buf
[strlen(buf
)-4] = '\0';
142 snprintf(prompt
, sizeof(prompt
),
143 _("Enter password of key <%s> to unlock password file: "), buf
);
144 if((*pkey
= load_pkey_with_prompt(pathkey
, NULL
, prompt
, &ret
)) != NULL
){
145 if(load_cert_for_key(pathcertdir
, *pkey
, certfile
, pcert
)){
147 *keyfile
= cpystr(buf
);
149 EVP_PKEY_free(*pkey
);
151 q_status_message1(SM_ORDER
, 0, 2,
152 _("Cannot find certificate that matches key <%s>. Continuing..."), buf
);
164 /* setup a key and certificate to encrypt and decrypt a password file.
165 * These files will be saved in the .alpine-smime/.pwd directory, but its
166 * location can be setup in the command line with the -pwdcertdir option.
167 * Here are the rules:
169 * Check if the .alpine-smime/.pwd (or -pwdcertdir directory) exists,
170 * if not create it. If we are successful, move to the next step
172 * - If the user has a key/cert pair, in the .alpine-smime/.pwd dir
173 * setup is successful;
174 * - if the user does not have a key/cert pair, look to see if
175 * ps_global->smime->personal_certs is already setup, if so, use it.
176 * - if ps_global->smime->personal_certs is not set up, see if we can
177 * find a certificate/cert pair in the default locations at compilation
178 * time. (~/.alpine-smime/private and ~/.alpine-smime/public
179 * - if none of this is successful, create a key/certificate pair
180 * (TODO: implement this)
181 * - in any other case, setup is not successful.
183 * If setup is successful, setup ps_global->pwdcert.
184 * If any of this fails, ps_global->pwdcert will be null.
185 * Ok, that should do it.
187 * return values: 0 - everything is normal
188 * 1 - User could not unlock key or no key in directory.
189 * 2 - User cancelled to create self signed certificate
190 * -1 - we do not know which directory to use
191 * -2 - "-pwdcertdir" was given by user, but directory does not exist
192 * -3 - "DF_PASSWORD_DIR" exists but it is not a directory!!??
193 * -4 - we tried to create DF_PASSWORD_DIR but failed.
194 * -5 - password directory exists, but it is empty
198 setup_pwdcert(void **pwdcert
)
202 int setup_dir
= 0; /* make it non zero if we know which dir to use */
204 char pathdir
[MAXPATH
+1], pathkey
[MAXPATH
+1], fpath
[MAXPATH
+1], pathcert
[MAXPATH
+1];
205 char fpath2
[MAXPATH
+1], prompt
[MAILTMPLEN
];
206 char *keyfile
, *certfile
, *text
;
207 EVP_PKEY
*pkey
= NULL
;
209 PERSONAL_CERT
*pc
, *pc2
= NULL
;
210 static int was_here
= 0;
212 if(pwdcert
== NULL
|| was_here
== 1)
216 if(ps_global
->pwdcertdir
){
217 if(our_stat(ps_global
->pwdcertdir
, &sbuf
) == 0
218 && ((sbuf
.st_mode
& S_IFMT
) == S_IFDIR
)){
220 strncpy(pathdir
, ps_global
->pwdcertdir
, sizeof(pathdir
));
221 pathdir
[sizeof(pathdir
)-1] = '\0';
225 smime_path(DF_PASSWORD_DIR
, pathdir
, sizeof(pathdir
));
226 if(our_stat(pathdir
, &sbuf
) == 0){
227 if((sbuf
.st_mode
& S_IFMT
) == S_IFDIR
)
230 } else if(can_access(pathdir
, ACCESS_EXISTS
) != 0
231 && our_mkpath(pathdir
, 0700) == 0)
241 if(load_key_and_cert(pathdir
, pathdir
, &keyfile
, &certfile
, &pkey
, &pcert
) < 0){
246 if(ps_global
->pwdcertdir
== NULL
){ /* save the result of pwdcertdir */
247 ps_global
->pwdcertdir
= cpystr(pathdir
);
248 /* if the user gave a pwdcertdir and there is nothing there, do not
249 * continue. Let the user initialize on their own this directory.
251 if(certfile
== NULL
|| keyfile
== NULL
){
257 if(certfile
&& keyfile
){
258 pc
= (PERSONAL_CERT
*) fs_get(sizeof(PERSONAL_CERT
));
259 memset((void *)pc
, 0, sizeof(PERSONAL_CERT
));
263 pc
->cname
= certfile
;
264 *pwdcert
= (void *) pc
;
269 /* look to see if there are any certificates lying around, first
270 * we try to load ps_global->smime to see if that has information
271 * we can use. If we are the process filling the smime structure
272 * we deinit at the end, since this might not do a full init.
274 if(ps_global
&& ps_global
->smime
&& !ps_global
->smime
->inited
){
279 /* at this point ps_global->smime->inited == 1 */
280 if(ps_global
->smime
&& ps_global
->smime
->personal_certs
!= NULL
){
281 pc
= (PERSONAL_CERT
*) ps_global
->smime
->personal_certs
;
282 if(ps_global
->smime
->privatetype
== Directory
){
283 build_path(pathkey
, ps_global
->smime
->privatepath
, pc
->name
, sizeof(pathkey
));
284 strncat(pathkey
, ".key", 4);
285 pathkey
[sizeof(pathkey
)-1] = '\0';
287 } else if (ps_global
->smime
->privatetype
== Container
){
288 if(pc
->keytext
== NULL
){ /* we should *never* be here, but just in case */
289 if(ps_global
->smime
->privatecontent
!= NULL
){
290 char tmp
[MAILTMPLEN
], *s
, *t
, c
;
291 snprintf(tmp
, sizeof(tmp
), "%s%s", EMAILADDRLEADER
, pc
->name
);
292 tmp
[sizeof(tmp
)-1] = '\0';
293 if((s
= strstr(ps_global
->smime
->privatecontent
, tmp
)) != NULL
){
294 if((t
= strstr(s
+strlen(tmp
), EMAILADDRLEADER
)) != NULL
){
297 pc
->keytext
= cpystr(s
+ strlen(tmp
) + strlen(NEWLINE
));
301 pc
->keytext
= cpystr(s
+ strlen(tmp
) + strlen(NEWLINE
));
305 if(pc
->keytext
!= NULL
) /* we should go straight 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
), sizeof(buf
) - strlen(buf
)-1);
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
), sizeof(buf
) - strlen(buf
));
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 could 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
), sizeof(buf
) - strlen(buf
) -1);
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
), sizeof(buf
) - strlen(buf
) - 1);
896 buf
[sizeof(buf
)-1] = '\0';
899 rc
= our_copy(buf
, full_filename
);
901 else /* if(SMHOLDERTYPE(ctype) == Container){ */
902 rc
= add_file_to_container(ctype
, full_filename
, NULL
);
904 q_status_message(SM_ORDER
, 1, 3, _("Certificate saved"));
906 q_status_message(SM_ORDER
, 1, 3, _("Error saving certificate"));
907 X509_free(cert
); /* not needed anymore */
910 q_status_message(SM_ORDER
, 1, 3, _("Error in certificate file (not a certificate?)"));
914 } else { /* ctype == Public. save certificate, but first validate that it is one */
918 if((ins
= BIO_new_file(full_filename
, "r")) != NULL
){
919 if((cert
= PEM_read_bio_X509(ins
, NULL
, NULL
, NULL
)) != NULL
){
920 if(SMHOLDERTYPE(ctype
) == Directory
){
923 if((email
= get_x509_subject_email(cert
)) != NULL
){
925 for(i
= 0; email
[i
] != NULL
; i
++){
926 save_cert_for(email
[i
], cert
, Public
);
927 fs_give((void **)&email
[i
]);
929 fs_give((void **)email
);
931 if(strcmp(filename
+ strlen(filename
) - 4, ".crt") == 0)
932 filename
[strlen(filename
) - 4] = '\0';
933 save_cert_for(filename
, cert
, Public
);
935 else /* if(SMHOLDERTYPE(ctype) == Container){ */
936 add_file_to_container(ctype
, full_filename
, NULL
);
938 if(ps_global
->smime
->publiccertlist
)
939 ps_global
->smime
->publiccertlist
->data
.renew
= 1;
942 q_status_message(SM_ORDER
, 1, 3, _("Error in certificate file (not a certificate?)"));
946 if(DATACERT(ctype
)) RENEWCERT(DATACERT(ctype
)) = 1;
950 /* itype: information type to add: 0 - public, 1 - private.
951 * Memory freed by caller
954 print_private_key_information(char *email
, int itype
)
959 if(ps_global
->smime
== NULL
960 || ps_global
->smime
->personal_certs
== NULL
961 || (itype
!= 0 && itype
!= 1))
964 for(pc
= ps_global
->smime
->personal_certs
;
965 pc
!= NULL
&& strcmp(pc
->name
, email
) != 0; pc
= pc
->next
);
967 && !load_private_key(pc
)
969 && ps_global
->smime
->need_passphrase
){
970 if (pith_opt_smime_get_passphrase
)
971 (*pith_opt_smime_get_passphrase
)();
972 load_private_key(pc
);
978 out
= BIO_new(BIO_s_mem());
979 if(itype
== 0) /* 0 means public */
980 EVP_PKEY_print_public(out
, pc
->key
, 0, NULL
);
981 else if (itype
== 1) /* 1 means private */
982 EVP_PKEY_print_private(out
, pc
->key
, 0, NULL
);
984 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
985 forget_private_keys();
991 * Forget any cached private keys
994 forget_private_keys(void)
996 PERSONAL_CERT
*pcert
;
1000 dprint((9, "forget_private_keys()"));
1001 if(ps_global
->smime
){
1002 ps_global
->smime
->already_auto_asked
= 0;
1003 for(pcert
=(PERSONAL_CERT
*) ps_global
->smime
->personal_certs
;
1008 EVP_PKEY_free(pcert
->key
);
1013 ps_global
->smime
->entered_passphrase
= 0;
1014 len
= sizeof(ps_global
->smime
->passphrase
);
1015 p
= ps_global
->smime
->passphrase
;
1022 /* modelled after signature_path in reply.c, but uses home dir instead of the
1023 * directory where the .pinerc is located, since according to documentation,
1024 * the .alpine-smime directories are subdirectories of the home directory
1027 smime_path(char *rpath
, char *fpath
, size_t len
)
1030 if(rpath
&& *rpath
){
1031 size_t spl
= strlen(rpath
);
1033 if(IS_REMOTE(rpath
)){
1035 strncpy(fpath
, rpath
, len
-1);
1036 fpath
[len
-1] = '\0';
1038 else if(is_absolute_path(rpath
)){
1039 strncpy(fpath
, rpath
, len
-1);
1040 fpath
[len
-1] = '\0';
1041 fnexpand(fpath
, len
);
1043 else if(ps_global
->VAR_OPER_DIR
){
1044 if(strlen(ps_global
->VAR_OPER_DIR
) + spl
< len
- 1)
1045 build_path(fpath
, ps_global
->VAR_OPER_DIR
, rpath
, len
);
1047 else if(ps_global
->home_dir
){
1048 if(strlen(ps_global
->home_dir
) + spl
< len
- 1)
1049 build_path(fpath
, ps_global
->home_dir
, rpath
, len
);
1052 return fpath
&& *fpath
? 1 : 0;
1058 * taken from openssl/apps/app_rand.c
1061 app_RAND_load_file(const char *file
)
1063 #define RANDBUFLEN 200
1064 char buffer
[RANDBUFLEN
];
1067 file
= RAND_file_name(buffer
, RANDBUFLEN
);
1069 if(file
== NULL
|| !RAND_load_file(file
, -1)){
1070 if(RAND_status() == 0){
1071 dprint((1, "unable to load 'random state'\n"));
1072 dprint((1, "This means that the random number generator has not been seeded\n"));
1073 dprint((1, "with much random data.\n"));
1085 * copied and fiddled from imap/src/osdep/unix/auth_ssl.c
1088 openssl_extra_randomness(void)
1096 /* if system doesn't have /dev/urandom */
1097 if(stat ("/dev/urandom", &sbuf
)){
1099 tf
= temp_nam(NULL
, NULL
);
1101 strncpy(tmp
, tf
, sizeof(tmp
));
1102 tmp
[sizeof(tmp
)-1] = '\0';
1103 fs_give((void **) &tf
);
1106 if((fd
= open(tmp
, O_WRONLY
|O_CREAT
|O_EXCL
, 0600)) < 0)
1107 i
= (unsigned long) tmp
;
1109 unlink(tmp
); /* don't need the file */
1110 fstat(fd
, &sbuf
); /* get information about the file */
1111 i
= sbuf
.st_ino
; /* remember its inode */
1112 close(fd
); /* or its descriptor */
1114 /* not great but it'll have to do */
1115 snprintf(tmp
+strlen(tmp
), sizeof(tmp
)-strlen(tmp
), "%.80s%lx%lx%lx",
1116 tcp_serverhost (),i
,
1117 (unsigned long) (time (0) ^ gethostid ()),
1118 (unsigned long) getpid ());
1119 RAND_seed(tmp
, strlen(tmp
));
1125 /* taken from openssl/apps/app_rand.c */
1127 app_RAND_write_file(const char *file
)
1133 * If we did not manage to read the seed file,
1134 * we should not write a low-entropy seed file back --
1135 * it would suppress a crucial warning the next time
1136 * we want to use it.
1141 file
= RAND_file_name(buffer
, sizeof buffer
);
1143 if(file
== NULL
|| !RAND_write_file(file
)){
1144 dprint((1, "unable to write 'random state'\n"));
1152 certlist_from_personal_certs(PERSONAL_CERT
*pc
)
1160 if((x
= get_cert_for(pc
->name
, Public
, 1)) != NULL
)
1161 cl
= smime_X509_to_cert_info(x
, pc
->name
);
1162 cl
->next
= certlist_from_personal_certs(pc
->next
);
1168 renew_cert_data(CertList
**data
, WhichCerts ctype
)
1171 if(ctype
== Private
){
1173 PERSONAL_CERT
*pc
= (PERSONAL_CERT
*)ps_global
->smime
->personal_certs
;
1175 free_certlist(data
);
1176 free_personal_certs(&pc
);
1177 setup_privatekey_storage();
1178 *data
= certlist_from_personal_certs((PERSONAL_CERT
*)ps_global
->smime
->personal_certs
);
1180 resort_certificates(data
, ctype
);
1181 RENEWCERT(*data
) = 0;
1183 ps_global
->smime
->privatecertlist
= *data
;
1185 if(ps_global
->smime
->privatecertlist
)
1186 RENEWCERT(ps_global
->smime
->privatecertlist
) = 0;
1188 X509_LOOKUP
*lookup
= NULL
;
1189 X509_STORE
*store
= NULL
;
1191 if((store
= X509_STORE_new()) != NULL
){
1192 if((lookup
= X509_STORE_add_lookup(store
, X509_LOOKUP_file())) != NULL
){
1193 free_certlist(data
);
1194 if(SMHOLDERTYPE(ctype
) == Directory
)
1195 add_certs_in_dir(lookup
, PATHCERTDIR(ctype
), EXTCERT(ctype
), data
);
1196 else /* if(SMHOLDERTYPE(ctype) == Container) */
1197 *data
= mem_to_certlist(CONTENTCERTLIST(ctype
), ctype
);
1199 resort_certificates(data
, ctype
);
1200 RENEWCERT(*data
) = 0;
1203 ps_global
->smime
->publiccertlist
= *data
;
1205 ps_global
->smime
->cacertlist
= *data
;
1207 free_x509_store(&store
);
1210 setup_certs_backup_by_type(ctype
);
1220 /* Installed as an atexit() handler to save the random data */
1224 dprint((9, "smime_deinit()"));
1225 app_RAND_write_file(NULL
);
1226 if (s_cert_store
!= NULL
) free_x509_store(&s_cert_store
);
1227 #ifdef ERR_free_strings
1229 #endif /* ERR_free_strings */
1232 #endif /* EVP_cleanup */
1233 free_smime_struct(&ps_global
->smime
);
1236 /* we renew the store when it has changed */
1240 if(ps_global
->smime
->inited
){
1241 if(s_cert_store
!= NULL
)
1242 free_x509_store(&s_cert_store
);
1243 s_cert_store
= get_ca_store();
1247 /* Initialise openssl stuff if needed */
1251 if(F_OFF(F_DONT_DO_SMIME
, ps_global
) && !(ps_global
->smime
&& ps_global
->smime
->inited
)){
1253 dprint((9, "smime_init()"));
1254 if(!ps_global
->smime
)
1255 ps_global
->smime
= new_smime_struct();
1257 setup_storage_locations();
1259 s_cert_store
= get_ca_store();
1260 setup_certs_backup_by_type(CACert
);
1262 #ifdef OPENSSL_1_1_0
1263 OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
|OPENSSL_INIT_ADD_ALL_DIGESTS
|OPENSSL_INIT_LOAD_CRYPTO_STRINGS
, NULL
);
1265 OpenSSL_add_all_algorithms();
1266 ERR_load_crypto_strings();
1267 #endif /* OPENSSL_1_1_0 */
1269 app_RAND_load_file(NULL
);
1270 openssl_extra_randomness();
1271 ps_global
->smime
->inited
= 1;
1278 /* validate a certificate. Return value : 0 for no error, -1 for error.
1279 * In the latter case, set the openssl smime error in *error.
1282 smime_validate_cert(X509
*cert
, long *error
)
1284 X509_STORE_CTX
*csc
;
1288 if((s_cert_store
!= NULL
) && (csc
= X509_STORE_CTX_new()) != NULL
){
1289 X509_STORE_set_flags(s_cert_store
, 0);
1290 if(X509_STORE_CTX_init(csc
,s_cert_store
,cert
,NULL
)
1291 && X509_verify_cert(csc
) <= 0)
1292 *error
= X509_STORE_CTX_get_error(csc
);
1293 X509_STORE_CTX_free(csc
);
1295 return *error
? -1 : 0;
1299 get_personal_certs(char *path
)
1301 PERSONAL_CERT
*result
= NULL
;
1302 char buf2
[MAXPATH
], *fname
;
1308 #else /* _WINDOWS */
1309 struct _finddata_t dbuf
;
1310 char buf
[_MAX_PATH
+ 4];
1312 #endif /* _WINDOWS */
1314 ps_global
->smime
->privatepath
= cpystr(path
);
1317 dirp
= opendir(path
);
1319 while((d
=readdir(dirp
)) != NULL
){
1321 #else /* _WINDOWS */
1322 snprintf(buf
, sizeof(buf
), "%s%s*.*", path
, (path
[strlen(path
)-1] == '\\') ? "" : "\\");
1323 buf
[sizeof(buf
)-1] = '\0';
1324 if((findrv
= _findfirst(buf
, &dbuf
)) < 0)
1328 fname
= fname_to_utf8(dbuf
.name
);
1330 if((ll
=strlen(fname
)) && ll
> 4 && !strcmp(fname
+ll
-4, ".key")){
1332 /* copy file name to temp buffer */
1333 strncpy(buf2
, fname
, sizeof(buf2
)-1);
1334 buf2
[sizeof(buf2
)-1] = '\0';
1335 /* chop off ".key" trailier */
1336 buf2
[strlen(buf2
)-4] = '\0';
1337 /* Look for certificate */
1338 cert
= get_cert_for(buf2
, Public
, 1);
1343 /* create a new PERSONAL_CERT, fill it in */
1345 pc
= (PERSONAL_CERT
*) fs_get(sizeof(*pc
));
1347 pc
->name
= cpystr(buf2
);
1348 strncat(buf2
, EXTCERT(Public
), sizeof(buf2
) - strlen(buf2
) - 1);
1349 pc
->cname
= cpystr(buf2
);
1351 /* Try to load the key with an empty password */
1352 pc
->key
= load_key(pc
, "", SM_NORMALCERT
);
1362 #else /* _WINDOWS */
1363 } while(_findnext(findrv
, &dbuf
) == 0);
1365 #endif /* !_WINDOWS */
1371 setup_privatekey_storage(void)
1373 char path
[MAXPATH
+1], *contents
;
1374 int privatekeycontainer
= 0;
1376 /* private keys in a container */
1377 if(ps_global
->VAR_PRIVATEKEY_CONTAINER
&& ps_global
->VAR_PRIVATEKEY_CONTAINER
[0]){
1379 privatekeycontainer
= 1;
1382 if(!smime_path(ps_global
->VAR_PRIVATEKEY_CONTAINER
, path
, MAXPATH
))
1383 privatekeycontainer
= 0;
1385 if(privatekeycontainer
&& !IS_REMOTE(path
)
1386 && ps_global
->VAR_OPER_DIR
1387 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1388 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1389 /* TRANSLATORS: First arg is the directory name, second is
1390 the file user wants to read but can't. */
1391 _("Can't read file outside %s: %s"),
1392 ps_global
->VAR_OPER_DIR
, path
);
1393 privatekeycontainer
= 0;
1396 if(privatekeycontainer
1397 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1398 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1400 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1401 privatekeycontainer
= 0;
1404 if(privatekeycontainer
&& path
[0]){
1405 ps_global
->smime
->privatetype
= Container
;
1406 ps_global
->smime
->privatepath
= cpystr(path
);
1409 ps_global
->smime
->privatecontent
= contents
;
1410 ps_global
->smime
->personal_certs
= mem_to_personal_certs(contents
);
1415 /* private keys in a directory of files */
1416 if(!privatekeycontainer
){
1417 ps_global
->smime
->privatetype
= Directory
;
1420 if(!(smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, path
, MAXPATH
)
1421 && !IS_REMOTE(path
)))
1422 ps_global
->smime
->privatetype
= Nada
;
1423 else if(can_access(path
, ACCESS_EXISTS
)){
1424 if(our_mkpath(path
, 0700)){
1425 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1426 ps_global
->smime
->privatetype
= Nada
;
1430 if(ps_global
->smime
->privatetype
== Directory
)
1431 ps_global
->smime
->personal_certs
= get_personal_certs(path
);
1433 setup_certs_backup_by_type(Private
);
1437 setup_storage_locations(void)
1439 int publiccertcontainer
= 0, cacertcontainer
= 0;
1440 char path
[MAXPATH
+1], *contents
;
1442 if(!ps_global
->smime
)
1445 #ifdef APPLEKEYCHAIN
1446 if(F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
1447 ps_global
->smime
->publictype
= Keychain
;
1450 #endif /* APPLEKEYCHAIN */
1451 /* Public certificates in a container */
1452 if(ps_global
->VAR_PUBLICCERT_CONTAINER
&& ps_global
->VAR_PUBLICCERT_CONTAINER
[0]){
1454 publiccertcontainer
= 1;
1457 if(!smime_path(ps_global
->VAR_PUBLICCERT_CONTAINER
, path
, MAXPATH
))
1458 publiccertcontainer
= 0;
1460 if(publiccertcontainer
&& !IS_REMOTE(path
)
1461 && ps_global
->VAR_OPER_DIR
1462 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1463 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1464 /* TRANSLATORS: First arg is the directory name, second is
1465 the file user wants to read but can't. */
1466 _("Can't read file outside %s: %s"),
1467 ps_global
->VAR_OPER_DIR
, path
);
1468 publiccertcontainer
= 0;
1471 if(publiccertcontainer
1472 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1473 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1475 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1476 publiccertcontainer
= 0;
1479 if(publiccertcontainer
&& path
[0]){
1480 ps_global
->smime
->publictype
= Container
;
1481 ps_global
->smime
->publicpath
= cpystr(path
);
1484 ps_global
->smime
->publiccontent
= contents
;
1485 ps_global
->smime
->publiccertlist
= mem_to_certlist(contents
, Public
);
1490 /* Public certificates in a directory of files */
1491 if(!publiccertcontainer
){
1492 ps_global
->smime
->publictype
= Directory
;
1495 if(!(smime_path(ps_global
->VAR_PUBLICCERT_DIR
, path
, MAXPATH
)
1496 && !IS_REMOTE(path
)))
1497 ps_global
->smime
->publictype
= Nada
;
1498 else if(can_access(path
, ACCESS_EXISTS
)){
1499 if(our_mkpath(path
, 0700)){
1500 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1501 ps_global
->smime
->publictype
= Nada
;
1505 if(ps_global
->smime
->publictype
== Directory
)
1506 ps_global
->smime
->publicpath
= cpystr(path
);
1509 #ifdef APPLEKEYCHAIN
1511 #endif /* APPLEKEYCHAIN */
1513 setup_privatekey_storage();
1515 /* extra cacerts in a container */
1516 if(ps_global
->VAR_CACERT_CONTAINER
&& ps_global
->VAR_CACERT_CONTAINER
[0]){
1518 cacertcontainer
= 1;
1521 if(!smime_path(ps_global
->VAR_CACERT_CONTAINER
, path
, MAXPATH
))
1522 cacertcontainer
= 0;
1524 if(cacertcontainer
&& !IS_REMOTE(path
)
1525 && ps_global
->VAR_OPER_DIR
1526 && !in_dir(ps_global
->VAR_OPER_DIR
, path
)){
1527 q_status_message2(SM_ORDER
| SM_DING
, 3, 4,
1528 /* TRANSLATORS: First arg is the directory name, second is
1529 the file user wants to read but can't. */
1530 _("Can't read file outside %s: %s"),
1531 ps_global
->VAR_OPER_DIR
, path
);
1532 cacertcontainer
= 0;
1536 && (IS_REMOTE(path
) || can_access(path
, ACCESS_EXISTS
) == 0)){
1537 if(!(IS_REMOTE(path
) && (contents
= simple_read_remote_file(path
, REMOTE_SMIME_SUBTYPE
)))
1539 !(contents
= read_file(path
, READ_FROM_LOCALE
)))
1540 cacertcontainer
= 0;
1543 if(cacertcontainer
&& path
[0]){
1544 ps_global
->smime
->catype
= Container
;
1545 ps_global
->smime
->capath
= cpystr(path
);
1546 ps_global
->smime
->cacontent
= contents
;
1548 ps_global
->smime
->cacertlist
= mem_to_certlist(contents
, CACert
);
1552 if(!cacertcontainer
){
1553 ps_global
->smime
->catype
= Directory
;
1556 if(!(smime_path(ps_global
->VAR_CACERT_DIR
, path
, MAXPATH
)
1557 && !IS_REMOTE(path
)))
1558 ps_global
->smime
->catype
= Nada
;
1559 else if(can_access(path
, ACCESS_EXISTS
)){
1560 if(our_mkpath(path
, 0700)){
1561 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
1562 ps_global
->smime
->catype
= Nada
;
1566 if(ps_global
->smime
->catype
== Directory
)
1567 ps_global
->smime
->capath
= cpystr(path
);
1573 copy_publiccert_dir_to_container(void)
1575 return(copy_dir_to_container(Public
, NULL
));
1580 copy_publiccert_container_to_dir(void)
1582 return(copy_container_to_dir(Public
));
1587 copy_privatecert_dir_to_container(void)
1589 return(copy_dir_to_container(Private
, NULL
));
1594 copy_privatecert_container_to_dir(void)
1596 return(copy_container_to_dir(Private
));
1601 copy_cacert_dir_to_container(void)
1603 return(copy_dir_to_container(CACert
, NULL
));
1608 copy_cacert_container_to_dir(void)
1610 return(copy_container_to_dir(CACert
));
1613 /* Add the contents of a file to a container. Do not check the content
1614 * of the file, just add it using the format for that container. The
1615 * caller must check the format, so that there is no data corruption
1617 * return value: 0 - success,
1621 add_file_to_container(WhichCerts ctype
, char *fpath
, char *altname
)
1623 char *sep
= (ctype
== Public
|| ctype
== Private
)
1624 ? EMAILADDRLEADER
: CACERTSTORELEADER
;
1625 char *content
= ctype
== Public
? ps_global
->smime
->publiccontent
1626 : (ctype
== Private
? ps_global
->smime
->privatecontent
1627 : ps_global
->smime
->cacontent
);
1633 int rv
= -1; /* assume error */
1634 size_t clen
; /* content buffer size */
1636 if(our_stat(fpath
, &sbuf
) < 0
1637 || (in
= so_get(FileStar
, fpath
, READ_ACCESS
| READ_FROM_LOCALE
)) == NULL
)
1642 else if((name
= strrchr(fpath
, '/')) != NULL
){
1644 if((ll
= strlen(++name
)) > 4 && strucmp(name
+ ll
- 4, EXTCERT(ctype
)) == 0)
1645 name
[ll
-strlen(EXTCERT(ctype
))] = '\0';
1651 clen
= strlen(content
) + strlen(sep
) + strlen(name
) + sbuf
.st_size
+ 2*strlen(NEWLINE
) + 1;
1652 fs_resize((void **)&content
, clen
);
1654 content
+= strlen(content
);
1657 clen
= strlen(sep
) + strlen(name
) + sbuf
.st_size
+ strlen(NEWLINE
) + 1;
1658 s
= content
= fs_get(clen
);
1661 strncat(content
, sep
, clen
- strlen(content
));
1662 strncat(content
, name
, clen
- strlen(content
));
1663 content
+= strlen(content
);
1666 #endif /* _WINDOWS */
1669 while(so_readc(&c
, in
))
1670 *content
++ = (char) c
;
1674 case Private
: ps_global
->smime
->privatecontent
= s
; break;
1675 case Public
: ps_global
->smime
->publiccontent
= s
; break;
1676 case CACert
: ps_global
->smime
->cacontent
= s
; break;
1680 rv
= copy_dir_to_container(ctype
, s
);
1683 if(in
) so_give(&in
);
1690 * returns 0 on success, -1 on failure
1691 * contents is an argument which tells this function to write the value
1692 * of this variable instead of reading the contents of the directory.
1693 * If the var contents is not null use its value as the value of the
1697 copy_dir_to_container(WhichCerts which
, char *contents
)
1699 int ret
= 0, container
= 0;
1700 BIO
*bio_out
= NULL
, *bio_in
= NULL
;
1701 char srcpath
[MAXPATH
+1], dstpath
[MAXPATH
+1], emailaddr
[MAXPATH
], file
[MAXPATH
], line
[4096];
1702 char *tempfile
= NULL
, fpath
[MAXPATH
+1], *fname
;
1707 #else /* _WINDOWS */
1708 struct _finddata_t dbuf
;
1709 char buf
[_MAX_PATH
+ 4];
1711 #endif /* _WINDOWS */
1712 REMDATA_S
*rd
= NULL
;
1713 char *configdir
= NULL
;
1714 char *configpath
= NULL
;
1715 char *configcontainer
= NULL
;
1716 char *filesuffix
= NULL
;
1717 char *ret_dir
= NULL
;
1719 dprint((9, "copy_dir_to_container(%s)", which
==Public
? "Public" : which
==Private
? "Private" : which
==CACert
? "CACert" : "?"));
1725 emailaddr
[0] = '\0';
1727 if(which
== Public
){
1728 configdir
= ps_global
->VAR_PUBLICCERT_DIR
;
1729 configpath
= ps_global
->smime
->publicpath
;
1730 configcontainer
= cpystr(DF_PUBLIC_CONTAINER
);
1731 filesuffix
= ".crt";
1733 else if(which
== Private
){
1734 configdir
= ps_global
->VAR_PRIVATEKEY_DIR
;
1735 configpath
= ps_global
->smime
->privatepath
;
1736 configcontainer
= cpystr(DF_PRIVATE_CONTAINER
);
1737 filesuffix
= ".key";
1739 else if(which
== CACert
){
1740 configdir
= ps_global
->VAR_CACERT_DIR
;
1741 configpath
= ps_global
->smime
->capath
;
1742 configcontainer
= cpystr(DF_CA_CONTAINER
);
1743 filesuffix
= ".crt";
1745 container
= SMHOLDERTYPE(which
) == Container
;
1747 if(!(configdir
&& configdir
[0])){
1748 q_status_message(SM_ORDER
, 3, 3, _("Directory not defined"));
1752 if(!(configpath
&& configpath
[0])){
1753 #ifdef APPLEKEYCHAIN
1754 if(which
== Public
&& F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
1755 q_status_message(SM_ORDER
, 3, 3, _("Turn off the Keychain feature above first"));
1758 #endif /* APPLEKEYCHAIN */
1759 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
1763 if(!(filesuffix
&& strlen(filesuffix
) == 4)){
1769 * If there is a legit directory to read from set up the
1770 * container file to write to.
1772 if(smime_path(configdir
, srcpath
, MAXPATH
) && !IS_REMOTE(srcpath
)){
1774 if(IS_REMOTE(configpath
)){
1775 rd
= rd_create_remote(RemImap
, configpath
, REMOTE_SMIME_SUBTYPE
,
1777 _("Can't access remote smime configuration."));
1781 (void) rd_read_metadata(rd
);
1783 if(rd
->access
== MaybeRorW
){
1784 if(rd
->read_status
== 'R')
1785 rd
->access
= ReadOnly
;
1787 rd
->access
= ReadWrite
;
1790 if(rd
->access
!= NoExists
){
1792 rd_check_remvalid(rd
, 1L);
1795 * If the cached info says it is readonly but
1796 * it looks like it's been fixed now, change it to readwrite.
1798 if(rd
->read_status
== 'R'){
1799 rd_check_readonly_access(rd
);
1800 if(rd
->read_status
== 'W'){
1801 rd
->access
= ReadWrite
;
1802 rd
->flags
|= REM_OUTOFDATE
;
1805 rd
->access
= ReadOnly
;
1809 if(rd
->flags
& REM_OUTOFDATE
){
1810 if(rd_update_local(rd
) != 0){
1812 dprint((1, "copy_dir_to_container: rd_update_local failed\n"));
1813 rd_close_remdata(&rd
);
1820 if(rd
->access
!= ReadWrite
|| rd_remote_is_readonly(rd
)){
1821 rd_close_remdata(&rd
);
1825 rd
->flags
|= DO_REMTRIM
;
1827 strncpy(dstpath
, rd
->lf
, sizeof(dstpath
)-1);
1828 dstpath
[sizeof(dstpath
)-1] = '\0';
1831 strncpy(dstpath
, configpath
, sizeof(dstpath
)-1);
1832 dstpath
[sizeof(dstpath
)-1] = '\0';
1836 * dstpath is either the local Container file or the local cache file
1837 * for the remote Container file.
1839 tempfile
= tempfile_in_same_dir(dstpath
, "az", &ret_dir
);
1843 * If there is a legit directory to read from and a tempfile
1844 * to write to we continue.
1846 if(tempfile
&& (bio_out
=BIO_new_file(tempfile
, "w")) != NULL
){
1848 if(contents
!= NULL
){
1849 if(BIO_puts(bio_out
, contents
) < 0)
1854 if((dirp
= opendir(srcpath
)) != NULL
){
1856 while((d
=readdir(dirp
)) && !ret
){
1858 #else /* _WINDOWS */
1859 snprintf(buf
, sizeof(buf
), "%s%s*.*", srcpath
, (srcpath
[strlen(srcpath
)-1] == '\\') ? "" : "\\");
1860 buf
[sizeof(buf
)-1] = '\0';
1861 if((findrv
= _findfirst(buf
, &dbuf
)) < 0)
1865 fname
= fname_to_utf8(dbuf
.name
);
1866 #endif /* ! _WINDOWS */
1867 if((ll
=strlen(fname
)) && ll
> 4 && !strcmp(fname
+ll
-4, filesuffix
)){
1869 /* copy file name to temp buffer */
1870 strncpy(emailaddr
, fname
, sizeof(emailaddr
)-1);
1871 emailaddr
[sizeof(emailaddr
)-1] = '\0';
1872 /* chop off suffix trailier */
1873 emailaddr
[strlen(emailaddr
)-4] = 0;
1876 * This is the separator between the contents of
1879 if(which
== CACert
){
1880 if(!((BIO_puts(bio_out
, CACERTSTORELEADER
) > 0)
1881 && (BIO_puts(bio_out
, emailaddr
) > 0)
1882 && (BIO_puts(bio_out
, NEWLINE
) > 0)))
1886 if(!((BIO_puts(bio_out
, EMAILADDRLEADER
) > 0)
1887 && (BIO_puts(bio_out
, emailaddr
) > 0)
1888 && (BIO_puts(bio_out
, NEWLINE
) > 0)))
1892 /* read then write contents of file */
1893 build_path(file
, srcpath
, fname
, sizeof(file
));
1894 if(!(bio_in
= BIO_new_file(file
, "r")))
1900 while(BIO_gets(bio_in
, line
, sizeof(line
)) > 0){
1901 if(strncmp("-----BEGIN", line
, strlen("-----BEGIN")) == 0)
1905 BIO_puts(bio_out
, line
);
1907 if(strncmp("-----END", line
, strlen("-----END")) == 0)
1918 #else /* _WINDOWS */
1919 } while (_findnext(findrv
, &dbuf
) == 0);
1921 #endif /* ! _WINDOWS */
1927 if(container
&& configpath
&& *configpath
){
1928 strncpy(fpath
, configpath
, sizeof(fpath
));
1929 fpath
[sizeof(fpath
) - 1] = '\0';
1932 if(strlen(dstpath
) + strlen(configcontainer
) - strlen(ret_dir
) + 1 < sizeof(dstpath
))
1933 snprintf(fpath
, sizeof(fpath
), "%s%c%s",
1934 dstpath
, tempfile
[strlen(ret_dir
)], configcontainer
);
1941 if(!IS_REMOTE(configpath
)){
1942 if(rename_file(tempfile
, fpath
) < 0){
1943 q_status_message2(SM_ORDER
, 3, 3,
1944 _("Can't rename %s to %s"), tempfile
, fpath
);
1946 } else q_status_message1(SM_ORDER
, 3, 3,
1947 _("saved container to %s"), fpath
);
1949 else { /* if the container is remote, copy it */
1953 if(rd
!= NULL
&& rename_file(tempfile
, rd
->lf
) < 0){
1954 q_status_message2(SM_ORDER
, 3, 3,
1955 _("Can't rename %s to %s"), tempfile
, rd
->lf
);
1961 if((e
= rd_update_remote(rd
, datebuf
)) != 0){
1963 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1964 _("Error opening temporary smime file %s: %s"),
1965 rd
->lf
, error_description(errno
));
1967 "write_remote_smime: error opening temp file %s\n",
1968 rd
->lf
? rd
->lf
: "?"));
1971 q_status_message2(SM_ORDER
| SM_DING
, 3, 5,
1972 _("Error copying to %s: %s"),
1973 rd
->rn
, error_description(errno
));
1975 "write_remote_smime: error copying from %s to %s\n",
1976 rd
->lf
? rd
->lf
: "?", rd
->rn
? rd
->rn
: "?"));
1979 q_status_message(SM_ORDER
| SM_DING
, 5, 5,
1980 _("Copy of smime key to remote folder failed, NOT saved remotely"));
1983 rd_update_metadata(rd
, datebuf
);
1984 rd
->read_status
= 'W';
1987 rd_close_remdata(&rd
);
1994 fs_give((void **) &tempfile
);
1997 fs_give((void **) &ret_dir
);
2000 fs_give((void **) &configcontainer
);
2007 * returns 0 on success, -1 on failure
2010 copy_container_to_dir(WhichCerts which
)
2012 char path
[MAXPATH
+1], file
[MAXPATH
+1], buf
[MAXPATH
+1];
2014 char *contents
= NULL
;
2015 char *leader
= NULL
;
2016 char *filesuffix
= NULL
;
2017 char *configdir
= NULL
;
2018 char *configpath
= NULL
;
2019 char *tempfile
= NULL
;
2020 char *p
, *q
, *line
, *name
, *certtext
, *save_p
;
2024 dprint((9, "copy_container_to_dir(%s)", which
==Public
? "Public" : which
==Private
? "Private" : which
==CACert
? "CACert" : "?"));
2029 if(which
== Public
){
2030 leader
= EMAILADDRLEADER
;
2031 contents
= ps_global
->smime
->publiccontent
;
2032 configdir
= ps_global
->VAR_PUBLICCERT_DIR
;
2033 configpath
= ps_global
->smime
->publicpath
;
2034 filesuffix
= ".crt";
2035 if(!(configpath
&& configpath
[0])){
2036 #ifdef APPLEKEYCHAIN
2037 if(which
== Public
&& F_ON(F_PUBLICCERTS_IN_KEYCHAIN
, ps_global
)){
2038 q_status_message(SM_ORDER
, 3, 3, _("Turn off the Keychain feature above first"));
2041 #endif /* APPLEKEYCHAIN */
2042 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
2046 fs_give((void **) &ps_global
->smime
->publicpath
);
2049 if(!(smime_path(ps_global
->VAR_PUBLICCERT_DIR
, path
, MAXPATH
)
2050 && !IS_REMOTE(path
))){
2051 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
2055 if(can_access(path
, ACCESS_EXISTS
)){
2056 if(our_mkpath(path
, 0700)){
2057 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
2062 ps_global
->smime
->publicpath
= cpystr(path
);
2063 configpath
= ps_global
->smime
->publicpath
;
2065 else if(which
== Private
){
2066 leader
= EMAILADDRLEADER
;
2067 contents
= ps_global
->smime
->privatecontent
;
2068 configdir
= ps_global
->VAR_PRIVATEKEY_DIR
;
2069 configpath
= ps_global
->smime
->privatepath
;
2070 filesuffix
= ".key";
2071 if(!(configpath
&& configpath
[0])){
2072 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
2076 fs_give((void **) &ps_global
->smime
->privatepath
);
2079 if(!(smime_path(ps_global
->VAR_PRIVATEKEY_DIR
, path
, MAXPATH
)
2080 && !IS_REMOTE(path
))){
2081 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
2085 if(can_access(path
, ACCESS_EXISTS
)){
2086 if(our_mkpath(path
, 0700)){
2087 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
2092 ps_global
->smime
->privatepath
= cpystr(path
);
2093 configpath
= ps_global
->smime
->privatepath
;
2095 else if(which
== CACert
){
2096 leader
= CACERTSTORELEADER
;
2097 contents
= ps_global
->smime
->cacontent
;
2098 configdir
= ps_global
->VAR_CACERT_DIR
;
2099 configpath
= ps_global
->smime
->capath
;
2100 filesuffix
= ".crt";
2101 if(!(configpath
&& configpath
[0])){
2102 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
2106 fs_give((void **) &ps_global
->smime
->capath
);
2109 if(!(smime_path(ps_global
->VAR_CACERT_DIR
, path
, MAXPATH
)
2110 && !IS_REMOTE(path
))){
2111 q_status_message(SM_ORDER
, 3, 3, _("Directory is not defined"));
2115 if(can_access(path
, ACCESS_EXISTS
)){
2116 if(our_mkpath(path
, 0700)){
2117 q_status_message1(SM_ORDER
, 3, 3, _("Can't create directory %s"), path
);
2122 ps_global
->smime
->capath
= cpystr(path
);
2123 configpath
= ps_global
->smime
->capath
;
2126 if(!(configdir
&& configdir
[0])){
2127 q_status_message(SM_ORDER
, 3, 3, _("Directory not defined"));
2131 if(!(configpath
&& configpath
[0])){
2132 q_status_message(SM_ORDER
, 3, 3, _("Container path is not defined"));
2136 if(!(filesuffix
&& strlen(filesuffix
) == 4)){
2141 if(contents
&& *contents
){
2142 for(p
= contents
; *p
!= '\0';){
2145 while(*p
&& *p
!= '\n')
2154 if(strncmp(leader
, line
, strlen(leader
)) == 0){
2155 name
= line
+ strlen(leader
);
2157 if(strncmp("-----BEGIN", certtext
, strlen("-----BEGIN")) == 0){
2158 if((q
= strstr(certtext
, leader
)) != NULL
){
2161 else{ /* end of file */
2162 q
= certtext
+ strlen(certtext
);
2166 strncpy(buf
, name
, sizeof(buf
)-5);
2167 buf
[sizeof(buf
)-5] = '\0';
2168 strncat(buf
, filesuffix
, 5);
2169 build_path(file
, configpath
, buf
, sizeof(file
));
2171 in
= BIO_new_mem_buf(certtext
, q
-certtext
);
2173 tempfile
= tempfile_in_same_dir(file
, "az", NULL
);
2176 out
= BIO_new_file(tempfile
, "w");
2179 while((len
= BIO_read(in
, iobuf
, sizeof(iobuf
))) > 0)
2180 BIO_write(out
, iobuf
, len
);
2184 if(rename_file(tempfile
, file
) < 0){
2185 q_status_message2(SM_ORDER
, 3, 3,
2186 _("Can't rename %s to %s"),
2191 fs_give((void **) &tempfile
);
2208 #ifdef APPLEKEYCHAIN
2211 copy_publiccert_container_to_keychain(void)
2213 /* NOT IMPLEMNTED */
2218 copy_publiccert_keychain_to_container(void)
2220 /* NOT IMPLEMNTED */
2224 #endif /* APPLEKEYCHAIN */
2228 * Get a pointer to a string describing the most recent OpenSSL error.
2229 * It's statically allocated, so don't change or attempt to free it.
2232 openssl_error_string(void)
2235 const char *data
= NULL
;
2238 errn
= ERR_peek_error_line_data(NULL
, NULL
, &data
, NULL
);
2239 errs
= (char*) ERR_reason_error_string(errn
);
2246 return "unknown error";
2250 /* Return true if the body looks like a PKCS7 object */
2252 is_pkcs7_body(BODY
*body
)
2256 result
= body
->type
==TYPEAPPLICATION
&&
2258 (strucmp(body
->subtype
,"pkcs7-mime")==0 ||
2259 strucmp(body
->subtype
,"x-pkcs7-mime")==0 ||
2260 strucmp(body
->subtype
,"pkcs7-signature")==0 ||
2261 strucmp(body
->subtype
,"x-pkcs7-signature")==0);
2268 * Recursively stash a pointer to the decrypted data in our
2269 * manufactured body.
2270 * parameters: type: call of type 1, save the base and header for multipart messages
2271 call of type 0, do not save the base and header for multipart messages
2274 create_local_cache(char *h
, char *base
, BODY
*b
, int type
)
2276 if(b
->type
==TYPEMULTIPART
){
2280 cpytxt(&b
->mime
.text
, h
+b
->mime
.offset
, b
->mime
.text
.size
);
2281 cpytxt(&b
->contents
.text
, base
+ b
->contents
.offset
, b
->size
.bytes
);
2282 } else if(type
== 0){
2284 * We don't really want to copy the real body contents. It shouldn't be
2285 * used, and in the case of a message with attachments, we'll be
2286 * duplicating the files multiple times.
2288 cpytxt(&b
->contents
.text
, "BODY UNAVAILABLE", 16);
2290 for(p
=b
->nested
.part
; p
; p
=p
->next
)
2291 create_local_cache(h
, base
, (BODY
*) p
, type
);
2295 cpytxt(&b
->mime
.text
, h
+b
->mime
.offset
, b
->mime
.text
.size
);
2296 cpytxt(&b
->contents
.text
, base
+ b
->contents
.offset
, b
->size
.bytes
);
2302 rfc822_output_func(void *b
, char *string
)
2304 BIO
*bio
= (BIO
*) b
;
2306 return(string
? *string
? (BIO_puts(bio
, string
) > 0 ? 1L : 0L)
2307 : (BIO_puts(bio
, string
) >= 0 ? 1L : 0L)
2313 * Attempt to load the private key for the given PERSONAL_CERT.
2314 * This sets the appropriate passphrase globals in order to
2315 * interact with the user correctly.
2318 load_private_key(PERSONAL_CERT
*pcert
)
2322 /* Try empty password by default */
2323 char *password
= "";
2326 && (ps_global
->smime
->need_passphrase
2327 || ps_global
->smime
->entered_passphrase
)){
2328 /* We've already been in here and discovered we need a different password */
2330 if(ps_global
->smime
->entered_passphrase
)
2331 password
= (char *) ps_global
->smime
->passphrase
; /* already entered */
2338 if(!(pcert
->key
= load_key(pcert
, password
, SM_NORMALCERT
))){
2339 long err
= ERR_get_error();
2341 /* Couldn't load key... */
2343 if(ps_global
->smime
&& ps_global
->smime
->entered_passphrase
){
2345 /* The user got the password wrong maybe? */
2347 if((ERR_GET_LIB(err
)==ERR_LIB_EVP
&& ERR_GET_REASON(err
)==EVP_R_BAD_DECRYPT
) ||
2348 (ERR_GET_LIB(err
)==ERR_LIB_PEM
&& ERR_GET_REASON(err
)==PEM_R_BAD_DECRYPT
))
2349 q_status_message(SM_ORDER
| SM_DING
, 4, 4, _("Incorrect passphrase"));
2351 q_status_message1(SM_ORDER
, 4, 4, _("Couldn't read key: %s"),(char*)openssl_error_string());
2353 /* This passphrase is no good; forget it */
2354 ps_global
->smime
->entered_passphrase
= 0;
2357 if(ps_global
->smime
){
2358 /* Indicate to the UI that we need re-entry (see mailcmd.c:process_cmd())*/
2359 ps_global
->smime
->need_passphrase
= 1;
2360 if(ps_global
->smime
->passphrase_emailaddr
){
2362 for(i
= 0; ps_global
->smime
->passphrase_emailaddr
[i
] != NULL
; i
++)
2363 fs_give((void **)&ps_global
->smime
->passphrase_emailaddr
[i
]);
2364 fs_give((void **) ps_global
->smime
->passphrase_emailaddr
);
2367 ps_global
->smime
->passphrase_emailaddr
= get_x509_subject_email(pcert
->cert
);
2373 /* This key will be cached, so we won't be called again */
2374 if(ps_global
->smime
){
2375 ps_global
->smime
->entered_passphrase
= 0;
2376 ps_global
->smime
->need_passphrase
= 0;
2388 setup_pkcs7_body_for_signature(BODY
*b
, char *description
, char *type
, char *filename
, char *smime_type
)
2390 b
->type
= TYPEAPPLICATION
;
2391 b
->subtype
= cpystr(type
);
2392 b
->encoding
= ENCBINARY
;
2393 b
->description
= cpystr(description
);
2395 b
->disposition
.type
= cpystr("attachment");
2396 set_parameter(&b
->disposition
.parameter
, "filename", filename
);
2398 set_parameter(&b
->parameter
, "name", filename
);
2399 if(smime_type
&& *smime_type
)
2400 set_parameter(&b
->parameter
, "smime-type", smime_type
);
2405 * Look for a personal certificate matching the
2409 match_personal_cert_to_email(ADDRESS
*a
)
2411 PERSONAL_CERT
*pcert
= NULL
;
2416 if(!a
|| !a
->mailbox
|| !a
->host
)
2419 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2421 if(ps_global
->smime
){
2422 for(pcert
=(PERSONAL_CERT
*) ps_global
->smime
->personal_certs
;
2429 email
= get_x509_subject_email(pcert
->cert
);
2433 for(i
= 0; email
[i
] && strucmp(email
[i
], buf
) != 0; i
++);
2434 if(email
[i
] != NULL
) done
++;
2435 for(i
= 0; email
[i
] != NULL
; i
++)
2436 fs_give((void **)&email
[i
]);
2437 fs_give((void **)email
);
2450 * Look for a personal certificate matching the from
2451 * (or reply_to? in the given envelope)
2454 match_personal_cert(ENVELOPE
*env
)
2456 PERSONAL_CERT
*pcert
;
2458 pcert
= match_personal_cert_to_email(env
->reply_to
);
2460 pcert
= match_personal_cert_to_email(env
->from
);
2467 * Flatten the given body into its MIME representation.
2468 * Return the result in a BIO.
2471 body_to_bio(BODY
*body
)
2476 bio
= BIO_new(BIO_s_mem());
2480 pine_encode_body(body
); /* this attaches random boundary strings to multiparts */
2481 pine_write_body_header(body
, rfc822_output_func
, bio
);
2482 pine_rfc822_output_body(body
, rfc822_output_func
, bio
);
2485 * Now need to truncate by two characters since the above
2488 if((len
=BIO_ctrl_pending(bio
)) > 1){
2489 BUF_MEM
*biobuf
= NULL
;
2491 /* this code used to truncate without closing the bio, and
2492 then resetting the memory, causing non validation in
2493 signatures. Fix contributed by Bernd Edlinger.
2495 BIO_get_mem_ptr(bio
, &biobuf
);
2496 BIO_set_close(bio
, BIO_NOCLOSE
);
2497 BUF_MEM_grow(biobuf
, len
-2); /* remove CRLF */
2498 BIO_set_mem_buf(bio
, biobuf
, BIO_CLOSE
);
2506 bio_from_store(STORE_S
*store
)
2510 if(store
&& store
->src
== BioType
&& store
->txt
){
2511 ret
= (BIO
*) store
->txt
;
2518 * Encrypt file; given a path (char *) fp, replace the file
2519 * by an encrypted version of it. If (char *) text is not null, then
2520 * replace the text of (char *) fp by the encrypted version of (char *) text.
2521 * certpath is the FULL path to the file containing the certificate used for
2523 * return value: 0 - failed to encrypt; 1 - success!
2526 encrypt_file(char *fp
, char *text
, PERSONAL_CERT
*pc
)
2528 const EVP_CIPHER
*cipher
= NULL
;
2529 STACK_OF(X509
) *encerts
= NULL
;
2537 cipher
= EVP_aes_256_cbc();
2538 encerts
= sk_X509_new_null();
2540 sk_X509_push(encerts
, X509_dup(pc
->cert
));
2543 if((out
= BIO_new(BIO_s_mem())) != NULL
){
2544 (void) BIO_reset(out
);
2545 BIO_puts(out
, text
);
2548 else if((out
= BIO_new_file(fp
, "rb")) != NULL
)
2549 BIO_read_filename(out
, fp
);
2551 if((p7
= PKCS7_encrypt(encerts
, out
, cipher
, 0)) != NULL
){
2552 BIO_set_close(out
, BIO_CLOSE
);
2554 if((out
= BIO_new_file(fp
, "w")) != NULL
){
2556 rv
= PEM_write_bio_PKCS7(out
, p7
);
2564 sk_X509_pop_free(encerts
, X509_free
);
2570 * Encrypt a message on the way out. Called from call_mailer in send.c
2571 * The body may be reallocated.
2574 encrypt_outgoing_message(METAENV
*header
, BODY
**bodyP
)
2579 const EVP_CIPHER
*cipher
= NULL
;
2580 STACK_OF(X509
) *encerts
= NULL
;
2581 STORE_S
*outs
= NULL
;
2584 BODY
*body
= *bodyP
;
2585 BODY
*newBody
= NULL
;
2590 dprint((9, "encrypt_outgoing_message()"));
2593 cipher
= EVP_aes_256_cbc();
2595 encerts
= sk_X509_new_null();
2597 /* Look for a certificate for each of the recipients */
2598 for(pf
= header
->local
; pf
&& pf
->name
; pf
= pf
->next
)
2599 if(pf
->type
== Address
&& pf
->rcptto
&& pf
->addr
&& *pf
->addr
){
2600 for(a
=*pf
->addr
; a
; a
=a
->next
){
2601 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2603 if((cert
= get_cert_for(buf
, Public
, 1)) != NULL
){
2604 sk_X509_push(encerts
,cert
);
2606 q_status_message2(SM_ORDER
, 1, 1,
2607 _("Unable to find certificate for <%s@%s>"),
2608 a
->mailbox
, a
->host
);
2614 /* add the sender's certificate so that they can decrypt the message too */
2615 for(a
=header
->env
->from
; a
; a
= a
->next
){
2616 snprintf(buf
, sizeof(buf
), "%s@%s", a
->mailbox
, a
->host
);
2618 if((cert
= get_cert_for(buf
, Public
, 1)) != NULL
2619 && sk_X509_find(encerts
, cert
) == -1)
2620 sk_X509_push(encerts
,cert
);
2623 in
= body_to_bio(body
);
2625 p7
= PKCS7_encrypt(encerts
, in
, cipher
, 0);
2627 outs
= so_get(BioType
, NULL
, EDIT_ACCESS
);
2628 out
= bio_from_store(outs
);
2630 i2d_PKCS7_bio(out
, p7
);
2631 (void) BIO_flush(out
);
2633 so_seek(outs
, 0, SEEK_SET
);
2635 newBody
= mail_newbody();
2637 newBody
->type
= TYPEAPPLICATION
;
2638 newBody
->subtype
= cpystr("pkcs7-mime");
2639 newBody
->encoding
= ENCBINARY
;
2641 newBody
->disposition
.type
= cpystr("attachment");
2642 set_parameter(&newBody
->disposition
.parameter
, "filename", "smime.p7m");
2644 newBody
->description
= cpystr("S/MIME Encrypted Message");
2645 set_parameter(&newBody
->parameter
, "smime-type", "enveloped-data");
2646 set_parameter(&newBody
->parameter
, "name", "smime.p7m");
2648 newBody
->contents
.text
.data
= (unsigned char *) outs
;
2658 sk_X509_pop_free(encerts
, X509_free
);
2660 dprint((9, "encrypt_outgoing_message returns %d", result
));
2666 Get (and decode) the body of the given section of msg
2669 get_part_contents(long msgno
, const char *section
)
2673 STORE_S
*store
= NULL
;
2676 store
= so_get(CharStar
, NULL
, EDIT_ACCESS
);
2678 gf_set_so_writec(&pc
,store
);
2680 err
= detach(ps_global
->mail_stream
, msgno
, (char*) section
, 0L, &len
, pc
, NULL
, 0L);
2682 gf_clear_so_writec(store
);
2684 so_seek(store
, 0, SEEK_SET
);
2695 get_pkcs7_from_part(long msgno
,const char *section
)
2697 STORE_S
*store
= NULL
;
2701 store
= get_part_contents(msgno
, section
);
2704 if(store
->src
== CharStar
){
2708 * We're reaching inside the STORE_S structure. We should
2709 * probably have a way to get the length, instead.
2711 len
= (int) (store
->eod
- store
->dp
);
2712 in
= BIO_new_mem_buf(store
->txt
, len
);
2714 else{ /* just copy it */
2717 in
= BIO_new(BIO_s_mem());
2718 (void) BIO_reset(in
);
2720 so_seek(store
, 0L, 0);
2721 while(so_readc(&c
, store
)){
2722 BIO_write(in
, &c
, 1);
2727 /* dump_bio_to_file(in, "/tmp/decoded-signature"); */
2728 if((p7
=d2i_PKCS7_bio(in
,NULL
)) == NULL
){
2742 same_cert(X509
*x
, X509
*cert
)
2744 char bufcert
[256], bufx
[256];
2747 get_fingerprint(cert
, EVP_md5(), bufcert
, sizeof(bufcert
), ":");
2748 get_fingerprint(x
, EVP_md5(), bufx
, sizeof(bufx
), ":");
2749 if(strcmp(bufx
, bufcert
) == 0)
2756 /* extract and save certificates from a PKCS7 package.
2758 * 0 - no errors. Either the certificate was in public/
2759 * or we could save it there.
2760 * < 0 - the certificate was not in public/ and the user
2761 * did not save it there.
2765 smime_extract_and_save_cert(PKCS7
*p7
)
2767 STACK_OF(X509
) *signers
;
2770 int i
, j
, rv
, already_saved
;
2773 /* any signers for this message? */
2774 if((signers
= PKCS7_get0_signers(p7
, NULL
, 0)) == NULL
)
2778 for(i
= 0; i
< sk_X509_num(signers
); i
++){
2779 if((x
= sk_X509_value(signers
,i
)) == NULL
)
2782 if((email
= get_x509_subject_email(x
)) != NULL
){
2783 for(j
= 0; email
[j
] != NULL
; j
++){
2785 /* check if we have the certificate for this address */
2786 cert
= get_cert_for(email
[j
], Public
, 1);
2787 /* if we have one, check if it is the one packaged */
2789 already_saved
= same_cert(x
, cert
);
2793 /* if not saved, try to save it */
2794 if(already_saved
== 0
2795 && (*pith_smime_confirm_save
)(email
[j
]) == 1){
2796 save_cert_for(email
[j
], x
, Public
);
2797 if(ps_global
->smime
->publiccertlist
) /* renew store */
2798 free_certlist(&ps_global
->smime
->publiccertlist
);
2801 /* check if it got saved */
2802 cert
= get_cert_for(email
[j
], Public
, 1);
2803 /* if saved, all is good */
2806 else /* else, we do not have this certificate saved */
2809 fs_give((void **) &email
[j
]);
2811 fs_give((void **) email
);
2814 sk_X509_free(signers
);
2820 * Try to verify a signature.
2822 * p7 - the pkcs7 object to verify
2823 * in - the plain data to verify (NULL if not detached)
2824 * out - BIO to which to write the opaque data
2825 * silent - if non zero, do not print errors, only print success.
2828 do_signature_verify(PKCS7
*p7
, BIO
*in
, BIO
*out
, int silent
)
2830 STACK_OF(X509
) *otherCerts
= NULL
;
2837 if(!silent
) q_status_message(SM_ORDER
| SM_DING
, 2, 2,
2838 _("Couldn't verify S/MIME signature: No CA Certs were loaded"));
2843 flags
= F_ON(F_USE_CERT_STORE_ONLY
, ps_global
) ? PKCS7_NOINTERN
: 0;
2845 if(ps_global
->smime
->publiccertlist
== NULL
){
2846 renew_cert_data(&ps_global
->smime
->publiccertlist
, Public
);
2847 for(cl
= ps_global
->smime
->publiccertlist
; cl
; cl
= cl
->next
){
2848 if(cl
->x509_cert
== NULL
){
2849 char *s
= strrchr(cl
->name
, '.');
2851 cl
->x509_cert
= get_cert_for(cl
->name
, Public
, 1);
2857 if(ps_global
->smime
->publiccertlist
){
2858 otherCerts
= sk_X509_new_null();
2859 for(cl
= ps_global
->smime
->publiccertlist
; cl
; cl
= cl
->next
)
2860 if(cl
->x509_cert
!= NULL
)
2861 sk_X509_push(otherCerts
, X509_dup(cl
->x509_cert
));
2864 result
= PKCS7_verify(p7
, otherCerts
, s_cert_store
, in
, out
, flags
);
2867 q_status_message(SM_ORDER
, 1, 1, _("S/MIME signature verified ok"));
2870 err
= ERR_peek_error_line_data(NULL
, NULL
, &data
, NULL
);
2872 if(out
&& err
==ERR_PACK(ERR_LIB_PKCS7
,PKCS7_F_PKCS7_VERIFY
,PKCS7_R_CERTIFICATE_VERIFY_ERROR
)){
2875 * verification failed due to an error in verifying a certificate.
2876 * Just write the "out" BIO, and leave. Of course let the user
2877 * know about this. Make two more attempts to get the data out. The
2878 * last one should succeed. In any case, let the user know why it
2881 if(PKCS7_verify(p7
, otherCerts
, s_cert_store
, in
, out
, PKCS7_NOVERIFY
) == 0)
2882 PKCS7_verify(p7
, otherCerts
, s_cert_store
, in
, out
, PKCS7_NOVERIFY
|PKCS7_NOSIGS
);
2884 if (!silent
) q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2885 _("Couldn't verify S/MIME signature: %s"), (char *) openssl_error_string());
2888 sk_X509_pop_free(otherCerts
, X509_free
);
2893 /* Big comment, explaining the mess that exists out there, and how we deal
2894 with it, and also how we solve the problems that are created this way.
2896 When Alpine sends a message, it constructs that message, computes the
2897 signature, but then it forgets the message it signed and reconstructs it
2898 again. Since it signs a message containing a notice about "mime aware
2899 tools", but it does not send that we do not include that in the part
2900 that is signed, and that takes care of much of the problems.
2902 Another problem is what is received from the servers. All servers tested
2903 seem to transmit the message that was signed intact and Alpine can check
2904 the signature correctly. That is not a problem. The problem arises when
2905 the message includes attachments. In this case different servers send
2906 different things, so it will be up to us to figure out what is the text
2907 that was actually signed. Confused? here is the story:
2909 When a message containing and attachment is sent by Alpine, UW-IMAP,
2910 Panda-IMAP, Gmail, and local reading of folders send exactly the message
2911 that was sent by Alpine, but GMX.com, Exchange, and probably other
2912 servers add a trailing \r\n in the message, so when validating the
2913 signature, these messages will not validate. There are several things
2916 1. Add a trailing \r\n to any message that contains attachments, sign that
2917 and send that. In this way, all messages will validate with all
2920 2. Compatibility mode: If a message has an attachment, contains a trailing
2921 \r\n and does not validate (sent by an earlier version of Alpine),
2922 remove the trailing \r\n and try to revalidate again.
2924 3. We do not add \r\n to validate a message that we sent, because that
2925 would only work in Alpine, and not in any other client. That would
2926 not be a good thing to do.
2930 Now we have to deal with encrypted and signed messages. The problem is
2931 that c-client makes all its pointers point to "on disk" content, but
2932 since we decrypted the data earlier, we have to make sure of two things.
2933 One is that we saved that data (so we do not have to decrypt it again)
2934 and second that we can use it.
2936 In order to save the data we use create_local_cache, so that we do not
2937 have to redecrypt the message. Once this is saved, c-client functions will
2938 find it and send it to us in mail_fetch_mime and mail_fetch_body.
2942 When we are trying to verify messages with detached signatures, some
2943 imap servers send incorrect information in the mail_fetch_mime call. By
2944 incorrect I mean that this is not fetched directly from the message, but
2945 it is read from the message, processed, and then the processed part is
2946 sent to us, so this text might not agree with what is in the message,
2947 and so the validation of the signature might fail. However, the good
2948 news is that the message validates if saved to a local folder. This
2949 means that if normal validation does not work we can make it work by
2950 saving the message locally and validating that. This is implemented
2951 below, and causes delay in the display of the message. I am considering
2952 at this time not to do this automatically, but wait for the user to tell
2953 us to do it for them by means of a command available in the
2954 mail_view_screen. This might help in other situations, where a message
2955 is supposed to have an attachment, but it can not be seen in the
2956 processed text. Nevertheless, at this time, this is automatic, and is
2957 causing a delay in the processing of the message, but it is validating
2958 correctly all messages.
2962 When the user sends a message as encrypted and signed, this code used to
2963 encrypt first, and then sign the pkcs7 body, but it turns out that some
2964 other clients can not handle these messages. While we could argue that the
2965 other clients need to improve, we will support reading messages in both
2966 ways, and will send messages using this technique; that is, signed first,
2967 encrypted second. It seems that all tested clients support this way, so it
2968 should be safe to do so.
2971 typedef struct smime_filter_s
{
2975 SMIME_FILTER_S sig_filter
[] = {
2976 {smime_remove_trailing_crlf
},
2977 {smime_remove_folding_space
}
2980 #define TOTAL_FILTERS (sizeof(sig_filter)/sizeof(sig_filter[0]))
2981 #define TOTAL_SIGFLTR (1 << TOTAL_FILTERS) /* not good, keep filters to a low number */
2984 smime_remove_trailing_crlf(char **mimetext
, unsigned long *mimelen
,
2985 char **bodytext
, unsigned long *bodylen
)
2987 if(*bodylen
> 2 && !strncmp(*bodytext
+*bodylen
-2, "\r\n", 2))
2992 smime_remove_folding_space(char **mimetext
, unsigned long *mimelen
,
2993 char **bodytext
, unsigned long *bodylen
)
2996 unsigned long mlen
= *mimelen
;
2999 for (s
= t
= *mimetext
; t
- *mimetext
< *mimelen
; ){
3000 if(*t
== '\r' && *(t
+1) == '\n' && (*(t
+2) == '\t' || *(t
+2) == ' ')){
3013 smime_validate_extra_test(char *mimetext
, unsigned long mimelen
, char *bodytext
, unsigned long bodylen
, PKCS7
*p7
, int nflag
)
3015 int result
, i
, j
, flag
;
3016 char *mtext
, *btext
;
3017 unsigned long mlen
, blen
;
3020 mtext
= mimelen
? fs_get(mimelen
+1) : NULL
;
3021 btext
= fs_get(bodylen
+1);
3024 flag
= 1; /* silence all failures */
3025 for(i
= 1; result
== 0 && i
< TOTAL_SIGFLTR
; i
++){
3026 if((in
= BIO_new(BIO_s_mem())) == NULL
)
3029 (void) BIO_reset(in
);
3031 if(i
+1 == TOTAL_SIGFLTR
)
3035 strncpy(mtext
, mimetext
, mlen
= mimelen
);
3036 strncpy(btext
, bodytext
, blen
= bodylen
);
3037 for(j
= 0; j
< TOTAL_FILTERS
; j
++)
3039 (sig_filter
[j
].filter
)(&mtext
, &mlen
, &btext
, &blen
);
3041 BIO_write(in
, mtext
, mlen
);
3042 BIO_write(in
, btext
, blen
);
3043 result
= do_signature_verify(p7
, in
, NULL
, flag
);
3046 if(mtext
) fs_give((void **)&mtext
);
3047 if(btext
) fs_give((void **)&btext
);
3052 * Given a multipart body of type multipart/signed, attempt to verify it.
3053 * Returns non-zero if the body was changed.
3056 do_detached_signature_verify(BODY
*b
, long msgno
, char *section
)
3061 int result
, modified_the_body
= 0;
3062 int flag
; /* 1 silent, 0 not silent */
3063 unsigned long mimelen
, bodylen
;
3064 char newSec
[100], *mimetext
, *bodytext
;
3068 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"));
3072 /* if it was signed and then encrypted, use the decrypted text
3073 * to check the validity of the signature
3076 if(get_body_sparep_type(b
->sparep
) == SizedText
){
3077 /* bodytext includes mimetext */
3078 st
= (SIZEDTEXT
*) get_body_sparep_data(b
->sparep
);
3079 bodytext
= (char *) st
->data
;
3086 snprintf(newSec
, sizeof(newSec
), "%s%s1", section
? section
: "", (section
&& *section
) ? "." : "");
3087 mimetext
= mail_fetch_mime(ps_global
->mail_stream
, msgno
, (char*) newSec
, &mimelen
, 0);
3089 bodytext
= mail_fetch_body (ps_global
->mail_stream
, msgno
, (char*) newSec
, &bodylen
, 0);
3091 if(mimetext
== NULL
|| bodytext
== NULL
)
3092 return modified_the_body
;
3095 snprintf(newSec
, sizeof(newSec
), "%s%s2", section
? section
: "", (section
&& *section
) ? "." : "");
3097 if((p7
= get_pkcs7_from_part(msgno
, newSec
)) == NULL
3098 || (in
= BIO_new(BIO_s_mem())) == NULL
)
3099 return modified_the_body
;
3101 (void) BIO_reset(in
);
3102 if(mimetext
!= NULL
)
3103 BIO_write(in
, mimetext
, mimelen
);
3104 BIO_write(in
, bodytext
, bodylen
);
3106 smime_extract_and_save_cert(p7
);
3108 if((result
= do_signature_verify(p7
, in
, NULL
, 1)) == 0){
3109 flag
= (mimelen
== 0 || !IS_REMOTE(ps_global
->mail_stream
->mailbox
))
3111 result
= smime_validate_extra_test(mimetext
, mimelen
, bodytext
, bodylen
, p7
, flag
);
3113 return modified_the_body
;
3115 && mimelen
> 0 /* do not do this for encrypted messages */
3116 && IS_REMOTE(ps_global
->mail_stream
->mailbox
)){
3118 unsigned long hlen
, tlen
;
3122 if((in
= BIO_new(BIO_s_mem())) != NULL
3123 && (fetch
= mail_fetch_header(ps_global
->mail_stream
, msgno
, NULL
,
3124 NULL
, &hlen
, FT_PEEK
)) != NULL
3125 && (msg_so
= so_get(CharStar
, NULL
, WRITE_ACCESS
)) != NULL
3126 && so_nputs(msg_so
, fetch
, (long) hlen
)
3127 && (fetch
= pine_mail_fetch_text(ps_global
->mail_stream
, msgno
, NULL
,
3128 &tlen
, FT_PEEK
)) != NULL
3129 && so_nputs(msg_so
, fetch
, tlen
)){
3131 char *h
= (char *) so_text(msg_so
);
3132 char *bstart
= strstr(h
, "\r\n\r\n");
3137 INIT(&bs
, mail_string
, bstart
, tlen
);
3138 rfc822_parse_msg_full(&env
, &body
, h
, bstart
-h
-4, &bs
, BADHOST
, 0, 0);
3139 mail_free_envelope(&env
);
3141 mail_free_body_part(&b
->nested
.part
);
3142 tmpB
= mail_body_section(body
, (unsigned char *) section
);
3143 if(MIME_MSG(tmpB
->type
, tmpB
->subtype
))
3144 b
->nested
.part
= tmpB
->nested
.msg
->body
->nested
.part
;
3146 b
->nested
.part
= tmpB
->nested
.part
;
3147 create_local_cache(bstart
, bstart
, &b
->nested
.part
->body
, 1);
3148 modified_the_body
= 1;
3150 snprintf(newSec
, sizeof(newSec
), "%s%s1", section
? section
: "", (section
&& *section
) ? "." : "");
3152 mimetext
= mail_fetch_mime(ps_global
->mail_stream
, msgno
, (char*) newSec
, &mimelen
, 0);
3155 bodytext
= mail_fetch_body (ps_global
->mail_stream
, msgno
, (char*) newSec
, &bodylen
, 0);
3157 if (mimetext
== NULL
|| bodytext
== NULL
)
3158 return modified_the_body
;
3160 snprintf(newSec
, sizeof(newSec
), "%s%s2", section
? section
: "", (section
&& *section
) ? "." : "");
3162 if((p7
= get_pkcs7_from_part(msgno
, newSec
)) == NULL
)
3163 return modified_the_body
;
3165 (void) BIO_reset(in
);
3166 BIO_write(in
, mimetext
, mimelen
);
3167 BIO_write(in
, bodytext
, bodylen
);
3170 if((result
= do_signature_verify(p7
, in
, NULL
, 1)) == 0){
3171 result
= smime_validate_extra_test(mimetext
, mimelen
, bodytext
, bodylen
, p7
, 0);
3173 return modified_the_body
;
3181 fs_give((void**) &b
->subtype
);
3183 b
->subtype
= cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE
);
3184 b
->encoding
= ENC8BIT
;
3187 fs_give ((void**) &b
->description
);
3189 what_we_did
= result
? _("This message was cryptographically signed.") :
3190 _("This message was cryptographically signed but the signature could not be verified.");
3192 b
->description
= cpystr(what_we_did
);
3194 b
->sparep
= create_body_sparep(P7Type
, p7
);
3198 /* p is signed plaintext */
3200 mail_free_body_part(&p
->next
); /* hide the pkcs7 from the viewer */
3202 modified_the_body
= 1;
3204 return modified_the_body
;
3209 find_certificate_matching_recip_info(PKCS7_RECIP_INFO
*ri
)
3211 PERSONAL_CERT
*x
= NULL
;
3213 if(ps_global
->smime
){
3214 for(x
= (PERSONAL_CERT
*) ps_global
->smime
->personal_certs
; x
; x
=x
->next
){
3219 if(!X509_NAME_cmp(ri
->issuer_and_serial
->issuer
,X509_get_issuer_name(mine
)) &&
3220 !ASN1_INTEGER_cmp(ri
->issuer_and_serial
->serial
,X509_get_serialNumber(mine
))){
3230 static PERSONAL_CERT
*
3231 find_certificate_matching_pkcs7(PKCS7
*p7
)
3234 STACK_OF(PKCS7_RECIP_INFO
) *recips
;
3235 PERSONAL_CERT
*x
= NULL
;
3237 recips
= p7
->d
.enveloped
->recipientinfo
;
3239 for(i
=0; i
<sk_PKCS7_RECIP_INFO_num(recips
); i
++){
3240 PKCS7_RECIP_INFO
*ri
;
3242 ri
= sk_PKCS7_RECIP_INFO_value(recips
, i
);
3244 if((x
=find_certificate_matching_recip_info(ri
))!=0){
3252 /* decrypt an encrypted file.
3253 Args: fp - the path to the encrypted file.
3254 rv - a code that tells the caller what happened inside the function
3255 pcert - a personal certificate that was used to encrypt this file
3256 Returns the decoded text allocated in a char *, whose memory must be
3261 decrypt_file(char *fp
, int *rv
, PERSONAL_CERT
*pc
)
3265 BIO
*in
= NULL
, *out
= NULL
;
3267 long unsigned int len
;
3270 if(pc
== NULL
|| (text
= read_file(fp
, 0)) == NULL
|| *text
== '\0')
3273 tmp
= strchr(text
+ strlen("-----BEGIN PKCS7-----") + strlen(NEWLINE
), '-');
3274 if(tmp
!= NULL
) *tmp
= '\0';
3275 tmp
= text
+ strlen("-----BEGIN PKCS7-----") + strlen(NEWLINE
);
3277 ret
= rfc822_base64((unsigned char *)tmp
, strlen(tmp
), &len
);
3279 if((in
= BIO_new_mem_buf((char *)ret
, len
)) != NULL
){
3280 p7
= d2i_PKCS7_bio(in
, NULL
);
3284 if (text
) fs_give((void **)&text
);
3285 if (ret
) fs_give((void **)&ret
);
3287 if (rv
) *rv
= pc
->key
== NULL
? -1 : 1;
3289 out
= BIO_new(BIO_s_mem());
3290 (void) BIO_reset(out
);
3292 if(PKCS7_decrypt(p7
, pc
->key
, pc
->cert
, out
, 0) != 0){
3293 len
= BIO_get_mem_data(out
, &tmp
);
3294 text
= fs_get((len
+1)*sizeof(char));
3295 strncpy(text
, tmp
, len
);
3299 q_status_message1(SM_ORDER
, 1, 1, _("Error decrypting: %s"),
3300 (char *) openssl_error_string());
3307 * Try to decode (decrypt or verify a signature) a PKCS7 body
3308 * Returns non-zero if something was changed.
3311 do_decoding(BODY
*b
, long msgno
, const char *section
)
3313 int modified_the_body
= 0;
3317 EVP_PKEY
*key
= NULL
;
3318 PERSONAL_CERT
*pcert
= NULL
;
3319 char *what_we_did
= "";
3322 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"));
3327 * Extract binary data from part to an in-memory store
3331 if(get_body_sparep_type(b
->sparep
) == P7Type
)
3332 p7
= (PKCS7
*) get_body_sparep_data(b
->sparep
);
3335 p7
= get_pkcs7_from_part(msgno
, section
&& *section
? section
: "1");
3337 q_status_message1(SM_ORDER
, 2, 2, "Couldn't load PKCS7 object: %s",
3338 (char*) openssl_error_string());
3343 * Save the PKCS7 object for later dealings by the user interface.
3344 * It will be cleaned up when the body is garbage collected.
3346 b
->sparep
= create_body_sparep(P7Type
, p7
);
3349 dprint((1, "type_is_signed = %d, type_is_enveloped = %d", PKCS7_type_is_signed(p7
), PKCS7_type_is_enveloped(p7
)));
3351 if(PKCS7_type_is_signed(p7
)){
3354 out
= BIO_new(BIO_s_mem());
3355 (void) BIO_reset(out
);
3356 BIO_puts(out
, "MIME-Version: 1.0\r\n"); /* needed so rfc822_parse_msg_full believes it's MIME */
3358 sigok
= do_signature_verify(p7
, NULL
, out
, 0);
3360 what_we_did
= sigok
? _("This message was cryptographically signed.") :
3361 _("This message was cryptographically signed but the signature could not be verified.");
3363 /* make sure it's null terminated */
3364 BIO_write(out
, null
, 1);
3366 else if(!PKCS7_type_is_enveloped(p7
)){
3367 q_status_message(SM_ORDER
, 1, 1, "PKCS7 object not recognised.");
3370 else{ /* It *is* enveloped */
3373 what_we_did
= _("This message was encrypted.");
3375 /* now need to find a cert that can decrypt this */
3376 pcert
= find_certificate_matching_pkcs7(p7
);
3379 q_status_message(SM_ORDER
, 3, 3, _("Couldn't find the certificate needed to decrypt."));
3383 recip
= pcert
->cert
;
3385 if(!load_private_key(pcert
)
3387 && ps_global
->smime
->need_passphrase
3388 && !ps_global
->smime
->already_auto_asked
){
3389 /* Couldn't load key with blank password, ask user */
3390 ps_global
->smime
->already_auto_asked
= 1;
3391 if(pith_opt_smime_get_passphrase
){
3392 (*pith_opt_smime_get_passphrase
)();
3393 load_private_key(pcert
);
3401 out
= BIO_new(BIO_s_mem());
3402 (void) BIO_reset(out
);
3403 BIO_puts(out
, "MIME-Version: 1.0\r\n");
3405 decrypt_result
= PKCS7_decrypt(p7
, key
, recip
, out
, 0);
3407 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
3408 forget_private_keys();
3410 if(!decrypt_result
){
3411 q_status_message1(SM_ORDER
, 1, 1, _("Error decrypting: %s"),
3412 (char*) openssl_error_string());
3415 BIO_write(out
, null
, 1);
3419 * We've now produced a flattened MIME object in BIO out.
3420 * It needs to be turned back into a BODY.
3429 BUF_MEM
*bptr
= NULL
;
3432 BIO_get_mem_ptr(out
, &bptr
);
3436 /* look for start of body */
3437 bstart
= strstr(h
, "\r\n\r\n");
3441 * Some clients do not canonicalize before encrypting, so
3442 * look for "\n\n" instead.
3444 bstart
= strstr(h
, "\n\n");
3448 for(lines
= 0, bstart
= h
; (bstart
= strchr(bstart
, '\n')) != NULL
;
3450 h
= t
= fs_get(strlen(bptr
->data
) + lines
+ 1);
3452 for(s
= bptr
->data
; *s
!= '\0'; s
++)
3453 if(*s
== '\n' && *(s
-1) != '\r'){
3460 bstart
= strstr(h
, "\r\n\r\n");
3465 q_status_message(SM_ORDER
, 3, 3, _("Encrypted data couldn't be parsed."));
3469 bstart
+= 4; /* skip over CRLF*2 */
3471 INIT(&s
, mail_string
, bstart
, strlen(bstart
));
3472 rfc822_parse_msg_full(&env
, &body
, h
, bstart
-h
-2, &s
, BADHOST
, 0, 0);
3473 mail_free_envelope(&env
); /* Don't care about this */
3475 if(body
->type
== TYPEMULTIPART
3476 && !strucmp(body
->subtype
, "SIGNED")){
3477 char *cookie
= NULL
;
3479 for (param
= body
->parameter
; param
&& !cookie
; param
= param
->next
)
3480 if (!strucmp (param
->attribute
,"BOUNDARY")) cookie
= param
->value
;
3482 st
= fs_get(sizeof(SIZEDTEXT
));
3483 st
->data
= (void *) cpystr(bstart
+ strlen(cookie
)+4); /* 4 = strlen("--\r\n") */
3484 st
->size
= body
->nested
.part
->next
->body
.mime
.offset
- 2*(strlen(cookie
) + 4);
3485 body
->sparep
= create_body_sparep(SizedText
, (void *)st
);
3488 q_status_message(SM_ORDER
, 3, 3, _("Couldn't find cookie in attachment list."));
3490 body
->mime
.offset
= 0;
3491 body
->mime
.text
.size
= 0;
3494 * Now convert original body (application/pkcs7-mime)
3495 * to a multipart body with one sub-part (the decrypted body).
3496 * Note that the sub-part may also be multipart!
3499 b
->type
= TYPEMULTIPART
;
3501 fs_give((void **) &b
->subtype
);
3504 * This subtype is used in mailview.c to annotate the display of
3505 * encrypted or signed messages. We know for sure then that it's a PKCS7
3506 * part because the sparep field is set to the PKCS7 object (see above).
3508 b
->subtype
= cpystr(OUR_PKCS7_ENCLOSURE_SUBTYPE
);
3509 b
->encoding
= ENC8BIT
;
3512 fs_give((void **) &b
->description
);
3514 b
->description
= cpystr(what_we_did
);
3516 if(b
->disposition
.type
)
3517 fs_give((void **) &b
->disposition
.type
);
3519 if(b
->contents
.text
.data
)
3520 fs_give((void **) &b
->contents
.text
.data
);
3523 mail_free_body_parameter(&b
->parameter
);
3525 /* Allocate mem for the sub-part, and copy over the contents of our parsed body */
3526 b
->nested
.part
= fs_get(sizeof(PART
));
3527 b
->nested
.part
->body
= *body
;
3528 b
->nested
.part
->next
= NULL
;
3530 fs_give((void**) &body
);
3533 * IMPORTANT BIT: set the body->contents.text.data elements to contain
3534 * the decrypted data. Otherwise, it'll try to load it from the original
3537 create_local_cache(bstart
-b
->nested
.part
->body
.mime
.offset
, bstart
, &b
->nested
.part
->body
, 0);
3539 modified_the_body
= 1;
3542 fs_give((void **) &h
);
3549 return modified_the_body
;
3554 * Recursively handle PKCS7 bodies in our message.
3556 * Returns non-zero if some fiddling was done.
3559 do_fiddle_smime_message(BODY
*b
, long msgno
, char *section
)
3561 int modified_the_body
= 0;
3566 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"));
3568 if(is_pkcs7_body(b
)){
3570 if(do_decoding(b
, msgno
, section
)){
3572 * b should now be a multipart message:
3573 * fiddle it too in case it's been multiply-encrypted!
3577 modified_the_body
= 1;
3581 if(b
->type
==TYPEMULTIPART
|| MIME_MSG(b
->type
, b
->subtype
)){
3587 if(MIME_MULT_SIGNED(b
->type
, b
->subtype
)){
3591 * Ahah. We have a multipart signed entity.
3594 * part 1 (signed thing)
3595 * part 2 (the pkcs7 signature)
3597 * We're going to convert that to
3599 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3600 * part 1 (signed thing)
3601 * part 2 has been freed
3603 * We also extract the signature from part 2 and save it
3604 * in the multipart body->sparep, and we add a description
3605 * in the multipart body->description.
3608 * The results of a decrypted message will be similar. It
3611 * Multipart/OUR_PKCS7_ENCLOSURE_SUBTYPE
3612 * part 1 (decrypted thing)
3615 modified_the_body
+= do_detached_signature_verify(b
, msgno
, section
);
3617 else if(MIME_MSG(b
->type
, b
->subtype
)){
3618 modified_the_body
+= do_fiddle_smime_message(b
->nested
.msg
->body
, msgno
, section
);
3622 for(p
=b
->nested
.part
,partNum
=1; p
; p
=p
->next
,partNum
++){
3623 /* Append part number to the section string */
3625 snprintf(newSec
, sizeof(newSec
), "%s%s%d", section
, *section
? "." : "", partNum
);
3627 modified_the_body
+= do_fiddle_smime_message(&p
->body
, msgno
, newSec
);
3632 return modified_the_body
;
3637 * Fiddle a message in-place by decrypting/verifying S/MIME entities.
3638 * Returns non-zero if something was changed.
3641 fiddle_smime_message(BODY
*b
, long msgno
)
3643 return do_fiddle_smime_message(b
, msgno
, "");
3647 /********************************************************************************/
3651 * Output a string in a distinctive style
3654 gf_puts_uline(char *txt
, gf_io_t pc
)
3656 pc(TAG_EMBED
); pc(TAG_BOLDON
);
3658 pc(TAG_EMBED
); pc(TAG_BOLDOFF
);
3661 /* get_chain_for_cert: error and level are mandatory arguments */
3663 get_chain_for_cert(X509
*cert
, int *error
, int *level
)
3665 STACK_OF(X509
) *chain
= NULL
;
3666 X509_STORE_CTX
*ctx
;
3668 int rc
; /* return code */
3673 if((s_cert_store
!= NULL
) && (ctx
= X509_STORE_CTX_new()) != NULL
){
3674 X509_STORE_set_flags(s_cert_store
, 0);
3675 if(!X509_STORE_CTX_init(ctx
, s_cert_store
, cert
, NULL
))
3676 *error
= X509_STORE_CTX_get_error(ctx
);
3677 else if((chain
= sk_X509_new_null()) != NULL
){
3678 for(x
= cert
; ; x
= xtmp
){
3680 sk_X509_push(chain
, X509_dup(x
));
3681 rc
= X509_STORE_CTX_get1_issuer(&xtmp
, ctx
, x
);
3686 if(!X509_check_issued(xtmp
, xtmp
))
3690 X509_STORE_CTX_free(ctx
);
3697 * Sign a message. Called from call_mailer in send.c.
3699 * This takes the header for the outgoing message as well as a pointer
3700 * to the current body (which may be reallocated).
3701 * The last argument (BODY **bp) is an argument that tells Alpine
3702 * if the body has 8 bit. if *bp is not null we compute two signatures
3703 * one for the quoted-printable encoded message, and another for the
3704 * 8bit encoded message. We return the signature for the 8bit encoded
3705 * part in p2->body.mime.text.data.
3706 * The reason why we compute two signatures is so that we can decide
3707 * which one to use later, and we only do it in the case that *bp is
3708 * not null. If we did not do this, then we might not be able to sign
3709 * a message until we log in to the smtp server, so instead of doing
3710 * that, we get ready for any possible situation we might find.
3713 sign_outgoing_message(METAENV
*header
, BODY
**bodyP
, int dont_detach
, BODY
**bp
)
3715 STORE_S
*outs
= NULL
;
3716 STORE_S
*outs_2
= NULL
;
3717 BODY
*body
= *bodyP
;
3718 BODY
*newBody
= NULL
;
3721 PERSONAL_CERT
*pcert
;
3728 STACK_OF(X509
) *chain
;
3729 const EVP_MD
*md
= EVP_sha256(); /* use this digest instead of sha1 */
3730 int result
= 0, error
;
3731 int flags
= dont_detach
? 0 : PKCS7_DETACHED
;
3734 dprint((9, "sign_outgoing_message()"));
3738 /* Look for a private key matching the sender address... */
3740 pcert
= match_personal_cert(header
->env
);
3743 q_status_message(SM_ORDER
, 3, 3, _("Couldn't find the certificate needed to sign."));
3747 if(!load_private_key(pcert
) && ps_global
->smime
&& ps_global
->smime
->need_passphrase
){
3748 /* Couldn't load key with blank password, try again */
3749 if(pith_opt_smime_get_passphrase
){
3750 (*pith_opt_smime_get_passphrase
)();
3751 load_private_key(pcert
);
3758 if(((chain
= get_chain_for_cert(pcert
->cert
, &error
, &level
)) != NULL
&& error
)
3760 sk_X509_pop_free(chain
, X509_free
);
3765 q_status_message(SM_ORDER
, 1, 1,
3766 _("Not all certificates needed to verify signature included in signed message"));
3768 in
= body_to_bio(body
);
3770 flags
|= PKCS7_PARTIAL
;
3771 if((p7
= PKCS7_sign(NULL
, NULL
, chain
, in
, flags
)) != NULL
3772 && PKCS7_sign_add_signer(p7
, pcert
->cert
, pcert
->key
, md
, flags
))
3773 PKCS7_final(p7
, in
, flags
);
3776 int i
, save_encoding
;
3778 for(i
= 0; (i
<= ENCMAX
) && body_encodings
[i
]; i
++);
3780 if(i
> ENCMAX
){ /* no empty encoding slots! */
3784 save_encoding
= (*bp
)->encoding
;
3785 body_encodings
[(*bp
)->encoding
= i
] = body_encodings
[ENC8BIT
];
3787 in_2
= body_to_bio(body
);
3789 body_encodings
[i
] = NULL
;
3790 (*bp
)->encoding
= save_encoding
;
3795 if((p7_2
= PKCS7_sign(NULL
, NULL
, chain
, in_2
, flags
)) != NULL
3796 && PKCS7_sign_add_signer(p7_2
, pcert
->cert
, pcert
->key
, md
, flags
))
3797 PKCS7_final(p7_2
, in_2
, flags
);
3800 if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE
,ps_global
))
3801 forget_private_keys();
3804 sk_X509_pop_free(chain
, X509_free
);
3807 q_status_message(SM_ORDER
, 1, 1, _("Error creating signed object."));
3811 outs
= so_get(BioType
, NULL
, EDIT_ACCESS
);
3812 out
= bio_from_store(outs
);
3814 i2d_PKCS7_bio(out
, p7
);
3815 (void) BIO_flush(out
);
3817 so_seek(outs
, 0, SEEK_SET
);
3819 if(bp
&& *bp
&& p7_2
){
3820 outs_2
= so_get(BioType
, NULL
, EDIT_ACCESS
);
3821 out_2
= bio_from_store(outs_2
);
3823 i2d_PKCS7_bio(out_2
, p7_2
);
3824 (void) BIO_flush(out_2
);
3826 so_seek(outs_2
, 0, SEEK_SET
);
3829 if((flags
&PKCS7_DETACHED
)==0){
3831 /* the simple case: the signed data is in the pkcs7 object */
3833 newBody
= mail_newbody();
3835 setup_pkcs7_body_for_signature(newBody
, "S/MIME Cryptographically Signed Message", "pkcs7-mime", "smime.p7m", "signed-data");
3837 newBody
->contents
.text
.data
= (unsigned char *) outs
;
3846 * We have to create a new body as follows:
3848 * multipart/signed; blah blah blah
3849 * reference to existing body
3854 newBody
= mail_newbody();
3856 newBody
->type
= TYPEMULTIPART
;
3857 newBody
->subtype
= cpystr("signed");
3858 newBody
->encoding
= ENC7BIT
;
3860 set_parameter(&newBody
->parameter
, "protocol", "application/pkcs7-signature");
3861 set_parameter(&newBody
->parameter
, "micalg", "sha-256");
3863 p1
= mail_newbody_part();
3864 p2
= mail_newbody_part();
3867 * This is nasty. We're just copying the body in here,
3868 * but since our newBody is freed at the end of call_mailer,
3869 * we mustn't let this body (the original one) be freed twice.
3871 p1
->body
= *body
; /* ARRGH. This is special cased at the end of call_mailer */
3875 setup_pkcs7_body_for_signature(&p2
->body
, "S/MIME Cryptographic Signature", "pkcs7-signature", "smime.p7s", NULL
);
3876 p2
->body
.mime
.text
.data
= (unsigned char *) outs_2
;
3877 p2
->body
.contents
.text
.data
= (unsigned char *) outs
;
3879 newBody
->nested
.part
= p1
;
3892 if(p7_2
) PKCS7_free(p7_2
);
3896 dprint((9, "sign_outgoing_message returns %d", result
));
3902 new_smime_struct(void)
3904 SMIME_STUFF_S
*ret
= NULL
;
3906 ret
= (SMIME_STUFF_S
*) fs_get(sizeof(*ret
));
3907 memset((void *) ret
, 0, sizeof(*ret
));
3908 ret
->publictype
= Nada
;
3915 free_smime_struct(SMIME_STUFF_S
**smime
)
3917 if(smime
&& *smime
){
3918 if((*smime
)->passphrase_emailaddr
){
3920 for(i
= 0; (*smime
)->passphrase_emailaddr
[i
] != NULL
; i
++)
3921 fs_give((void **) &(*smime
)->passphrase_emailaddr
[i
]);
3922 fs_give((void **) (*smime
)->passphrase_emailaddr
);
3925 if((*smime
)->publicpath
)
3926 fs_give((void **) &(*smime
)->publicpath
);
3928 if((*smime
)->publiccertlist
)
3929 free_certlist(&(*smime
)->publiccertlist
);
3931 if((*smime
)->backuppubliccertlist
)
3932 free_certlist(&(*smime
)->backuppubliccertlist
);
3934 if((*smime
)->cacertlist
)
3935 free_certlist(&(*smime
)->cacertlist
);
3937 if((*smime
)->backupcacertlist
)
3938 free_certlist(&(*smime
)->backupcacertlist
);
3940 if((*smime
)->privatecertlist
)
3941 free_certlist(&(*smime
)->privatecertlist
);
3943 if((*smime
)->backupprivatecertlist
)
3944 free_certlist(&(*smime
)->backupprivatecertlist
);
3946 if((*smime
)->publiccontent
)
3947 fs_give((void **) &(*smime
)->publiccontent
);
3949 if((*smime
)->privatepath
)
3950 fs_give((void **) &(*smime
)->privatepath
);
3952 if((*smime
)->personal_certs
){
3955 pc
= (PERSONAL_CERT
*) (*smime
)->personal_certs
;
3956 free_personal_certs(&pc
);
3957 (*smime
)->personal_certs
= NULL
;
3960 if((*smime
)->privatecontent
)
3961 fs_give((void **) &(*smime
)->privatecontent
);
3963 if((*smime
)->capath
)
3964 fs_give((void **) &(*smime
)->capath
);
3966 if((*smime
)->cacontent
)
3967 fs_give((void **) &(*smime
)->cacontent
);
3969 fs_give((void **) smime
);