summaryrefslogtreecommitdiff
path: root/pith/smime.c
diff options
context:
space:
mode:
authorEduardo Chappa <chappa@washington.edu>2014-03-09 14:26:50 -0600
committerEduardo Chappa <chappa@washington.edu>2014-03-09 14:26:50 -0600
commite9554c597f7f33c6ebebaa47087b4db878a59913 (patch)
tree9b8ff514f4db831a456185034cc614cf712b43e2 /pith/smime.c
parentac368b05842ee45ed29cd997840eb788649da268 (diff)
downloadalpine-e9554c597f7f33c6ebebaa47087b4db878a59913.tar.xz
* Forwarding messages with attachments of content-type multipart,
failed when attempting to sign it, with and "Error writing pipe" error. * Using a .pinerc file outside the home directory made Alpine not find the .alpine-smime directory with certificates. * Configuration screen for S/MIME adds ability to manage certificates. (currently available to users who manage certificates in directories, not in containers, which will be available in the next alpha release.)
Diffstat (limited to 'pith/smime.c')
-rw-r--r--pith/smime.c573
1 files changed, 471 insertions, 102 deletions
diff --git a/pith/smime.c b/pith/smime.c
index 469a381e..e691ae68 100644
--- a/pith/smime.c
+++ b/pith/smime.c
@@ -45,20 +45,15 @@ static char rcsid[] = "$Id: smime.c 1176 2008-09-29 21:16:42Z hubert@u.washingto
#include <openssl/buffer.h>
-
-typedef enum {Public, Private, CACert} WhichCerts;
-
-
/* internal prototypes */
static void forget_private_keys(void);
static int app_RAND_load_file(const char *file);
static void openssl_extra_randomness(void);
static int app_RAND_write_file(const char *file);
-static void smime_init(void);
static const char *openssl_error_string(void);
+static int load_private_key(PERSONAL_CERT *pcert);
static void create_local_cache(char *h, char *base, BODY *b);
static long rfc822_output_func(void *b, char *string);
-static int load_private_key(PERSONAL_CERT *pcert);
static void setup_pkcs7_body_for_signature(BODY *b, char *description,
char *type, char *filename);
static BIO *body_to_bio(BODY *body);
@@ -73,10 +68,14 @@ static void free_smime_struct(SMIME_STUFF_S **smime);
static void setup_storage_locations(void);
static int copy_dir_to_container(WhichCerts which);
static int copy_container_to_dir(WhichCerts which);
-
+int smime_path(char *rpath, char *fpath, size_t len);
+int smime_extract_and_save_cert(PKCS7 *p7);
+int same_cert(X509 *, X509 *);
+CertList * certlist_from_personal_certs(PERSONAL_CERT *pc);
int (*pith_opt_smime_get_passphrase)(void);
-
+int (*pith_smime_import_certificate)(char *, char *, size_t);
+char *(*pith_enter_password)(char *prompt, char *, size_t);
static X509_STORE *s_cert_store;
@@ -85,6 +84,189 @@ static int seeded = 0;
static int egdsocket = 0;
+int
+import_certificate(WhichCerts ctype)
+{
+ int r = 1;
+ char filename[MAXPATH+1], full_filename[MAXPATH+1], buf[MAXPATH+1];
+
+ if(pith_smime_import_certificate == NULL)
+ q_status_message(SM_ORDER, 0, 2,
+ _("import of certificates not implemented yet!"));
+
+ smime_init();
+
+ r = (*pith_smime_import_certificate)(filename, full_filename, sizeof(filename) - 20);
+ if(r < 0){
+ switch(r){
+ default:
+ case -1:
+ cmd_cancelled("Import certificate");
+ break;
+
+ case -2:
+ q_status_message1(SM_ORDER, 0, 2,
+ _("Can't import certificate outside of %s"),
+ ps_global->VAR_OPER_DIR);
+ break;
+ }
+ } else if (ctype == Private){
+ char prompt[500], *s, *t;
+ char pass[MAILTMPLEN+1];
+ BIO *in;
+ EVP_PKEY *key = NULL;
+ PERSONAL_CERT *pc;
+
+ if(!ps_global->smime->privatecertdata){
+ ps_global->smime->privatecertdata = fs_get(sizeof(CertList));
+ memset((void *)DATACERT(ctype), 0, sizeof(CertList));
+ }
+
+ if(!(in = BIO_new_file(full_filename, "r")))
+ return -1;
+
+ for(s = t = filename; (t = strstr(s, ".key")) != NULL; s = t + 1);
+ if(s) *(s-1) = 0;
+
+ snprintf(prompt, sizeof(prompt),
+ _("Enter passphrase for <%s>: "), s ? s : filename);
+
+ pass[0] = '\0';
+ if(pith_enter_password)
+ (*pith_enter_password)(prompt, (char *)pass, sizeof(pass));
+
+ if((key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass)) != NULL){
+ if(SMHOLDERTYPE(ctype) == Directory){
+ STORE_S *in_cert, *out_cert;
+ char c;
+
+ build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
+ if(strcmp(buf + strlen(buf) - 4, EXTCERT(ctype)) != 0 && strlen(buf) + 4 < sizeof(buf))
+ strcat(buf, EXTCERT(ctype));
+
+ in_cert = so_get(FileStar, full_filename, READ_ACCESS | READ_FROM_LOCALE);
+ out_cert = so_get(FileStar, buf, WRITE_ACCESS | WRITE_TO_LOCALE);
+
+ if(in_cert != NULL && out_cert != NULL){
+ while(so_readc(&c, in_cert) > 0)
+ so_writec(c, out_cert);
+ q_status_message(SM_ORDER, 1, 3, _("Certificate saved"));
+ }
+ else
+ q_status_message(SM_ORDER, 1, 3, _("Error saving certificate"));
+
+ so_give(&in_cert);
+ so_give(&out_cert);
+ }
+ if(ps_global->smime->publiccertdata)
+ ps_global->smime->publiccertdata->data.renew = 1;
+ }
+ else
+ q_status_message(SM_ORDER, 1, 3, _("Problem unlocking key (not a certificate and/or wrong password)"));
+ BIO_free(in);
+ } else if (ctype == CACert){
+ BIO *ins;
+ X509 *cert;
+
+ if((ins = BIO_new_file(full_filename, "r")) != NULL){
+ if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
+ if(SMHOLDERTYPE(ctype) == Directory){
+ STORE_S *in_cert, *out_cert;
+ char c;
+
+ build_path(buf, PATHCERTDIR(ctype), filename, sizeof(buf));
+ if(strcmp(buf + strlen(buf) - 4, ".crt") != 0 && strlen(buf) + 4 < sizeof(buf))
+ strcat(buf, EXTCERT(ctype));
+
+ in_cert = so_get(FileStar, full_filename, READ_ACCESS | READ_FROM_LOCALE);
+ out_cert = so_get(FileStar, buf, WRITE_ACCESS | WRITE_TO_LOCALE);
+
+ if(in_cert != NULL && out_cert != NULL){
+ while(so_readc(&c, in_cert) > 0)
+ so_writec(c, out_cert);
+ q_status_message(SM_ORDER, 1, 3, _("Certificate saved"));
+ }
+ else
+ q_status_message(SM_ORDER, 1, 3, _("Error saving certificate"));
+
+ so_give(&in_cert);
+ so_give(&out_cert);
+ }
+ X509_free(cert); /* not needed anymore */
+ }
+ else
+ q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
+ BIO_free(ins);
+ }
+ renew_store();
+ } else { /* ctype == Public. save certificate, but first validate that it is one */
+ BIO *ins;
+ X509 *cert;
+
+ if((ins = BIO_new_file(full_filename, "r")) != NULL){
+ if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){
+ char **email = get_x509_subject_email(cert);
+ int i;
+
+ for(i = 0; email[i] != NULL; i++){
+ save_cert_for(email[i], cert, Public);
+ fs_give((void **)&email[i]);
+ }
+ fs_give((void **)email);
+ X509_free(cert);
+ if(ps_global->smime->publiccertdata)
+ ps_global->smime->publiccertdata->data.renew = 1;
+ }
+ else
+ q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)"));
+ BIO_free(ins);
+ }
+ }
+ if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1;
+ ps_global->mangled_screen = 1;
+ return 0;
+}
+
+/* itype: information type to add: 0 - public, 1 - private.
+ * Memory freed by caller
+ */
+BIO *
+print_private_key_information(char *email, int itype)
+{
+ BIO *out;
+ PERSONAL_CERT *pc;
+
+ if(ps_global->smime == NULL
+ || ps_global->smime->personal_certs == NULL
+ || (itype != 0 && itype != 1))
+ return NULL;
+
+ for(pc = ps_global->smime->personal_certs;
+ pc != NULL && strcmp(pc->name, email) != 0; pc = pc->next);
+ if(pc->key == NULL
+ && !load_private_key(pc)
+ && ps_global->smime
+ && ps_global->smime->need_passphrase){
+ if (*pith_opt_smime_get_passphrase)
+ (*pith_opt_smime_get_passphrase)();
+ load_private_key(pc);
+ }
+
+ if(pc->key == NULL)
+ return NULL;
+
+ out = BIO_new(BIO_s_mem());
+ if(itype == 0) /* 0 means public */
+ EVP_PKEY_print_public(out, pc->key, 0, NULL);
+ else if (itype == 1) /* 1 means private */
+ EVP_PKEY_print_private(out, pc->key, 0, NULL);
+
+ if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global))
+ forget_private_keys();
+
+ return out;
+}
+
/*
* Forget any cached private keys
*/
@@ -116,6 +298,41 @@ forget_private_keys(void)
}
}
+/* modelled after signature_path in reply.c, but uses home dir instead of the
+ * directory where the .pinerc is located, since according to documentation,
+ * the .alpine-smime directories are subdirectories of the home directory
+ */
+
+int smime_path(char *rpath, char *fpath, size_t len)
+{
+ *fpath = '\0';
+ if(rpath && *rpath){
+ size_t spl = strlen(rpath);
+
+ *fpath = '\0';
+ if(IS_REMOTE(rpath)){
+ if(spl < len - 1)
+ strncpy(fpath, rpath, len-1);
+ fpath[len-1] = '\0';
+ }
+ else if(is_absolute_path(rpath)){
+ strncpy(fpath, rpath, len-1);
+ fpath[len-1] = '\0';
+ fnexpand(fpath, len);
+ }
+ else if(ps_global->VAR_OPER_DIR){
+ if(strlen(ps_global->VAR_OPER_DIR) + spl < len - 1)
+ build_path(fpath, ps_global->VAR_OPER_DIR, rpath, len);
+ }
+ else if(ps_global->home_dir){
+ if(strlen(ps_global->home_dir) + spl < len - 1)
+ build_path(fpath, ps_global->home_dir, rpath, len);
+ }
+ }
+ return fpath && *fpath ? 1 : 0;
+}
+
+
/*
* taken from openssl/apps/app_rand.c
@@ -216,6 +433,66 @@ app_RAND_write_file(const char *file)
return 1;
}
+CertList *
+certlist_from_personal_certs(PERSONAL_CERT *pc)
+{
+ CertList *cl;
+
+ if(pc == NULL)
+ return NULL;
+
+ cl = fs_get(sizeof(CertList));
+ memset((void *)cl, 0, sizeof(CertList));
+ cl->name = cpystr(pc->name);
+ cl->next = certlist_from_personal_certs(pc->next);
+
+ return cl;
+}
+
+
+void
+renew_cert_data(CertList **data, WhichCerts ctype)
+{
+ smime_init();
+ if(ctype == Private){
+ if(data){
+ PERSONAL_CERT *pc = (PERSONAL_CERT *)ps_global->smime->personal_certs;
+ if(*data)
+ free_certlist(data);
+ free_personal_certs(&pc);
+ ps_global->smime->personal_certs = (void *) get_personal_certs(ps_global->smime->privatepath);
+ *data = certlist_from_personal_certs((PERSONAL_CERT *)ps_global->smime->personal_certs);
+ if(data && *data)
+ RENEWCERT(*data) = 0;
+ ps_global->smime->privatecertdata = (CertList *) *data;
+ }
+ if(ps_global->smime->privatecertdata)
+ RENEWCERT(ps_global->smime->privatecertdata) = 0;
+ } else {
+ X509_LOOKUP *lookup = NULL;
+ X509_STORE *store = NULL;
+
+ if((store = X509_STORE_new()) != NULL)
+ if ((lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) == NULL){
+ X509_STORE_free(store);
+ store = NULL;
+ }
+ else{
+ if(PATHCERTDIR(ctype)){
+ free_certlist(data);
+ add_certs_in_dir(lookup, PATHCERTDIR(ctype), EXTCERT(ctype), data);
+ if(data && *data)
+ RENEWCERT(*data) = 0;
+ if(ctype == Public)
+ ps_global->smime->publiccertdata = *data;
+ else
+ ps_global->smime->cacertdata = *data;
+ }
+ }
+ }
+}
+
+
/* Installed as an atexit() handler to save the random data */
void
@@ -226,9 +503,18 @@ smime_deinit(void)
free_smime_struct(&ps_global->smime);
}
+/* we renew the store when it has changed */
+void renew_store(void)
+{
+ if(ps_global->smime->inited){
+ if(s_cert_store != NULL)
+ X509_STORE_free(s_cert_store);
+ s_cert_store = get_ca_store();
+ }
+}
/* Initialise openssl stuff if needed */
-static void
+void
smime_init(void)
{
if(F_OFF(F_DONT_DO_SMIME, ps_global) && !(ps_global->smime && ps_global->smime->inited)){
@@ -254,6 +540,73 @@ smime_init(void)
}
+/* validate a certificate. Return value : 0 for no error, -1 for error.
+ * In the latter case, set the openssl smime error in *error.
+ */
+int smime_validate_cert(X509 *cert, long *error)
+{
+ X509_STORE_CTX *csc;
+
+ ERR_clear_error();
+ *error = 0;
+ if((csc = X509_STORE_CTX_new()) != NULL){
+ X509_STORE_set_flags(s_cert_store, 0);
+ if(X509_STORE_CTX_init(csc,s_cert_store,cert,NULL)
+ && X509_verify_cert(csc) <= 0)
+ *error = X509_STORE_CTX_get_error(csc);
+ X509_STORE_CTX_free(csc);
+ }
+ return *error ? -1 : 0;
+}
+
+PERSONAL_CERT *
+get_personal_certs(char *path)
+{
+ PERSONAL_CERT *result = NULL;
+ char buf2[MAXPATH];
+ struct dirent *d;
+ DIR *dirp;
+
+ ps_global->smime->privatepath = cpystr(path);
+ dirp = opendir(path);
+ if(dirp){
+ while((d=readdir(dirp)) != NULL){
+ X509 *cert;
+ size_t ll;
+
+ if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, ".key")){
+
+ /* copy file name to temp buffer */
+ strncpy(buf2, d->d_name, sizeof(buf2)-1);
+ buf2[sizeof(buf2)-1] = '\0';
+ /* chop off ".key" trailier */
+ buf2[strlen(buf2)-4] = 0;
+ /* Look for certificate */
+ cert = get_cert_for(buf2, Public);
+
+ if(cert){
+ PERSONAL_CERT *pc;
+
+ /* create a new PERSONAL_CERT, fill it in */
+
+ pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
+ pc->cert = cert;
+ pc->name = cpystr(buf2);
+
+ /* Try to load the key with an empty password */
+ pc->key = load_key(pc, "");
+
+ pc->next = result;
+ result = pc;
+ }
+ }
+ }
+ closedir(dirp);
+ }
+ return result;
+}
+
+
static void
setup_storage_locations(void)
{
@@ -275,7 +628,7 @@ setup_storage_locations(void)
publiccertcontainer = 1;
contents = NULL;
path[0] = '\0';
- if(!signature_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
+ if(!smime_path(ps_global->VAR_PUBLICCERT_CONTAINER, path, MAXPATH))
publiccertcontainer = 0;
if(publiccertcontainer && !IS_REMOTE(path)
@@ -313,7 +666,7 @@ setup_storage_locations(void)
ps_global->smime->publictype = Directory;
path[0] = '\0';
- if(!(signature_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
+ if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
&& !IS_REMOTE(path)))
ps_global->smime->publictype = Nada;
else if(can_access(path, ACCESS_EXISTS)){
@@ -337,7 +690,7 @@ setup_storage_locations(void)
privatekeycontainer = 1;
contents = NULL;
path[0] = '\0';
- if(!signature_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
+ if(!smime_path(ps_global->VAR_PRIVATEKEY_CONTAINER, path, MAXPATH))
privatekeycontainer = 0;
if(privatekeycontainer && !IS_REMOTE(path)
@@ -377,7 +730,7 @@ setup_storage_locations(void)
ps_global->smime->privatetype = Directory;
path[0] = '\0';
- if(!(signature_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
+ if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
&& !IS_REMOTE(path)))
ps_global->smime->privatetype = Nada;
else if(can_access(path, ACCESS_EXISTS)){
@@ -387,52 +740,8 @@ setup_storage_locations(void)
}
}
- if(ps_global->smime->privatetype == Directory){
- char buf2[MAXPATH];
- struct dirent *d;
- DIR *dirp;
-
- ps_global->smime->privatepath = cpystr(path);
- dirp = opendir(path);
- if(dirp){
-
- while((d=readdir(dirp)) != NULL){
- X509 *cert;
- size_t ll;
-
- if((ll=strlen(d->d_name)) && ll > 4 && !strcmp(d->d_name+ll-4, ".key")){
-
- /* copy file name to temp buffer */
- strncpy(buf2, d->d_name, sizeof(buf2)-1);
- buf2[sizeof(buf2)-1] = '\0';
- /* chop off ".key" trailier */
- buf2[strlen(buf2)-4] = 0;
- /* Look for certificate */
- cert = get_cert_for(buf2);
-
- if(cert){
- PERSONAL_CERT *pc;
-
- /* create a new PERSONAL_CERT, fill it in */
-
- pc = (PERSONAL_CERT *) fs_get(sizeof(*pc));
- pc->cert = cert;
- pc->name = cpystr(buf2);
-
- /* Try to load the key with an empty password */
- pc->key = load_key(pc, "");
-
- pc->next = result;
- result = pc;
- }
- }
- }
-
- closedir(dirp);
- }
- }
-
- ps_global->smime->personal_certs = result;
+ if(ps_global->smime->privatetype == Directory)
+ ps_global->smime->personal_certs = get_personal_certs(path);
}
/* extra cacerts in a container */
@@ -441,7 +750,7 @@ setup_storage_locations(void)
cacertcontainer = 1;
contents = NULL;
path[0] = '\0';
- if(!signature_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
+ if(!smime_path(ps_global->VAR_CACERT_CONTAINER, path, MAXPATH))
cacertcontainer = 0;
if(cacertcontainer && !IS_REMOTE(path)
@@ -474,7 +783,7 @@ setup_storage_locations(void)
ps_global->smime->catype = Directory;
path[0] = '\0';
- if(!(signature_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
+ if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
&& !IS_REMOTE(path)))
ps_global->smime->catype = Nada;
else if(can_access(path, ACCESS_EXISTS)){
@@ -598,7 +907,7 @@ copy_dir_to_container(WhichCerts which)
* If there is a legit directory to read from set up the
* container file to write to.
*/
- if(signature_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
+ if(smime_path(configdir, srcpath, MAXPATH) && !IS_REMOTE(srcpath)){
if(IS_REMOTE(configpath)){
rd = rd_create_remote(RemImap, configpath, REMOTE_SMIME_SUBTYPE,
@@ -829,7 +1138,7 @@ copy_container_to_dir(WhichCerts which)
fs_give((void **) &ps_global->smime->publicpath);
path[0] = '\0';
- if(!(signature_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
+ if(!(smime_path(ps_global->VAR_PUBLICCERT_DIR, path, MAXPATH)
&& !IS_REMOTE(path))){
q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
return -1;
@@ -859,7 +1168,7 @@ copy_container_to_dir(WhichCerts which)
fs_give((void **) &ps_global->smime->privatepath);
path[0] = '\0';
- if(!(signature_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
+ if(!(smime_path(ps_global->VAR_PRIVATEKEY_DIR, path, MAXPATH)
&& !IS_REMOTE(path))){
q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
return -1;
@@ -889,7 +1198,7 @@ copy_container_to_dir(WhichCerts which)
fs_give((void **) &ps_global->smime->capath);
path[0] = '\0';
- if(!(signature_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
+ if(!(smime_path(ps_global->VAR_CACERT_DIR, path, MAXPATH)
&& !IS_REMOTE(path))){
q_status_message(SM_ORDER, 3, 3, _("Directory is not defined"));
return -1;
@@ -1342,7 +1651,7 @@ encrypt_file(char *fp, char *text)
cipher = EVP_aes_256_cbc();
encerts = sk_X509_new_null();
- if((cert = get_cert_for(pcert->name)) != NULL)
+ if((cert = get_cert_for(pcert->name, Public)) != NULL)
sk_X509_push(encerts, cert);
else
goto end;
@@ -1414,7 +1723,7 @@ encrypt_outgoing_message(METAENV *header, BODY **bodyP)
snprintf(buf, sizeof(buf), "%s@%s", a->mailbox, a->host);
- cert = get_cert_for(buf);
+ cert = get_cert_for(buf, Public);
if(cert)
sk_X509_push(encerts,cert);
else{
@@ -1545,6 +1854,88 @@ get_pkcs7_from_part(long msgno,const char *section)
return p7;
}
+int same_cert(X509 *x, X509 *cert)
+{
+ char bufcert[256], bufx[256];
+ int rv = 0;
+
+ get_fingerprint(cert, EVP_md5(), bufcert, sizeof(bufcert));
+ get_fingerprint(x, EVP_md5(), bufx, sizeof(bufx));
+ if(strcmp(bufx, bufcert) == 0)
+ rv = 1;
+
+ return rv;
+}
+
+
+/* extract and save certificates from a PKCS7 package. The ctype variable
+ * tells us if we want to extract it to a public/ or a ca/ directory. The
+ * later makes sense only for recoverable errors (errors that can be fixed
+ * by saving to the ca/ directory before we verify the signature).
+ * Return value:
+ * 0 - no errors (in public/) no need to try again,
+ * or validated self signed certificate (in ca/)
+ * 1 - self signed certificate was saved in public/, try again to validate it
+ * 2 - user was prompted about self-signed certificate, but certificate was not saved
+ * < 0 - certificate error is not recoverable, don't even think about it.
+ */
+
+int smime_extract_and_save_cert(PKCS7 *p7)
+{
+ STACK_OF(X509) *signers;
+ X509 *x, *cert;
+ char **email;
+ int i, j, rv;
+ long error;
+
+ if((signers = PKCS7_get0_signers(p7, NULL, 0)) == NULL)
+ return -1;
+
+ rv = 0; /* assume no error */
+ for(i = 0; i < sk_X509_num(signers); i++){
+ if((x = sk_X509_value(signers,i)) == NULL)
+ continue;
+
+ if((email = get_x509_subject_email(x)) != NULL){
+ for(j = 0; email[j] != NULL; j++){
+ if((cert = get_cert_for(email[j], Public)) == NULL || same_cert(x, cert) == 0)
+ save_cert_for(email[j], x, Public);
+
+ if(smime_validate_cert(cert, &error) < 0){
+ const char *error_string = X509_verify_cert_error_string(error);
+ dprint((1, "Certificate verify error code %lu for <%s>", error, email[j]));
+ switch(error){
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ rv = 2; /* recoverable error */
+ break;
+ default : rv = -1; /* not recoverable error */
+ break;
+ }
+ sprintf(tmp_20k_buf, "%s <%s>: %s", _("Error in certificate for "), email[j], error_string);
+ q_status_message(SM_ORDER | SM_DING, 2, 2, tmp_20k_buf);
+#if 0
+ if(pith_opt_action_certificate_error)
+ switch((*pith_opt_action_certificate_error)(cert_error)){
+ case 1 : save_cert_for(email[j], x, CACert);
+ renew_store();
+ rv = 0;
+ break;
+ case 0 :
+ default : break;
+ }
+#endif
+ }
+ X509_free(cert);
+ }
+ fs_give((void **) &email[i]);
+ }
+ fs_give((void **) email);
+ }
+ sk_X509_free(signers);
+
+ return rv;
+}
/*
* Try to verify a signature.
@@ -1558,15 +1949,10 @@ static int
do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
{
STACK_OF(X509) *otherCerts = NULL;
- int result;
+ int result;
const char *data;
long err;
-#if 0
- if (in)
- dump_bio_to_file(in,"/tmp/verified-data");
-#endif
-
if(!s_cert_store){
if(!silent) q_status_message(SM_ORDER | SM_DING, 2, 2,
_("Couldn't verify S/MIME signature: No CA Certs were loaded"));
@@ -1574,6 +1960,8 @@ do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
return -1;
}
+ smime_extract_and_save_cert(p7);
+
result = PKCS7_verify(p7, otherCerts, s_cert_store, in, out, 0);
if(result){
@@ -1593,34 +1981,6 @@ do_signature_verify(PKCS7 *p7, BIO *in, BIO *out, int silent)
_("Couldn't verify S/MIME signature: %s"), (char*) openssl_error_string());
}
- /* now try to extract the certificates of any signers */
- {
- STACK_OF(X509) *signers;
- int i;
-
- if((signers = PKCS7_get0_signers(p7, NULL, 0)) != NULL)
- for(i=0; i<sk_X509_num(signers); i++){
- char **email;
- X509 *x, *cert;
-
- if((x = sk_X509_value(signers,i)) == NULL)
- continue;
-
- if((email = get_x509_subject_email(x)) != NULL){
- int i;
- for(i = 0; email[i] != NULL; i++){
- if((cert = get_cert_for(email[i])) != NULL)
- X509_free(cert);
- else
- save_cert_for(email[i], x);
- fs_give((void **) &email[i]);
- }
- fs_give((void **) email);
- }
- }
- sk_X509_free(signers);
- }
-
return result;
}
@@ -1880,7 +2240,7 @@ decrypt_file(char *fp, int *rv)
if((key = pcert->key) == NULL)
goto end;
- recip = get_cert_for(pcert->name);
+ recip = get_cert_for(pcert->name, Public);
out = BIO_new(BIO_s_mem());
(void) BIO_reset(out);
@@ -2383,6 +2743,15 @@ free_smime_struct(SMIME_STUFF_S **smime)
if((*smime)->publiccertlist)
free_certlist(&(*smime)->publiccertlist);
+ if((*smime)->publiccertdata)
+ free_certlist(&(*smime)->publiccertdata);
+
+ if((*smime)->privatecertdata)
+ free_certlist(&(*smime)->privatecertdata);
+
+ if((*smime)->cacertdata)
+ free_certlist(&(*smime)->cacertdata);
+
if((*smime)->publiccontent)
fs_give((void **) &(*smime)->publiccontent);