summaryrefslogtreecommitdiff
path: root/pith
diff options
context:
space:
mode:
Diffstat (limited to 'pith')
-rw-r--r--pith/conftype.h43
-rw-r--r--pith/filter.c2
-rw-r--r--pith/options.h9
-rw-r--r--pith/pine.hlp261
-rw-r--r--pith/smime.c573
-rw-r--r--pith/smime.h9
-rw-r--r--pith/smkeys.c188
-rw-r--r--pith/smkeys.h11
8 files changed, 953 insertions, 143 deletions
diff --git a/pith/conftype.h b/pith/conftype.h
index 091481d1..38097f9e 100644
--- a/pith/conftype.h
+++ b/pith/conftype.h
@@ -2,8 +2,8 @@
* $Id: conftype.h 1155 2008-08-21 18:33:21Z hubert@u.washington.edu $
*
* ========================================================================
- * Copyright 2006-2008 University of Washington
* Copyright 2013-2014 Eduardo Chappa
+ * Copyright 2006-2008 University of Washington
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -670,11 +670,19 @@ typedef enum {Main, Post, None} EditWhich;
typedef enum {Directory, Container, Keychain, Nada} SmimeHolderType;
+typedef enum {Public, Private, CACert} WhichCerts;
+
+typedef struct certdata {
+ unsigned deleted:1; /* certificate is marked deleted */
+ unsigned renew:1; /* we must renew this list, set at top cert */
+} CertData;
+
typedef struct certlist {
char *name;
void *x509_cert; /* this is type (X509 *) */
+ CertData data;
struct certlist *next;
-}CertList;
+} CertList;
typedef struct smime_stuff {
unsigned inited:1;
@@ -690,25 +698,52 @@ typedef struct smime_stuff {
* If we are using the Container type it is easiest if we
* read in and maintain a list of certs and then write them
* out all at once. For Directory type we just leave the data
- * in the individual files and read or write the individual
- * files when needed, so we don't have a list of all the certs.
+ * in the individual files and read the list of files in the
+ * directory.
*/
SmimeHolderType publictype;
char *publicpath;
char *publiccontent;
+ CertList *publiccertdata;
CertList *publiccertlist;
SmimeHolderType privatetype;
char *privatepath;
char *privatecontent;
+ CertList *privatecertdata;
void *personal_certs; /* this is type (PERSONAL_CERT *) */
SmimeHolderType catype;
char *capath;
char *cacontent;
+ CertList *cacertdata;
+ CertList *cacertlist;
} SMIME_STUFF_S;
+#define DATACERT(X) (((X) == Public ? ps_global->smime->publiccertdata \
+ : ((X) == Private ? ps_global->smime->privatecertdata \
+ : ((X) == CACert ? ps_global->smime->cacertdata : NULL))))
+
+#define PATHCERTDIR(X) (((X) == Public ? ps_global->smime->publicpath \
+ : ((X) == Private ? ps_global->smime->privatepath \
+ : ((X) == CACert ? ps_global->smime->capath : NULL))))
+
+#define SMHOLDERTYPE(X) (((X) == Public ? ps_global->smime->publictype \
+ : ((X) == Private ? ps_global->smime->privatetype \
+ : ((X) == CACert ? ps_global->smime->catype : Nada))))
+
+#define SMCERTLIST(X) (((X) == Public ? ps_global->smime->publiccertlist \
+ : ((X) == Private ? ps_global->smime->privatecertdata \
+ : ((X) == CACert ? ps_global->smime->cacertlist : NULL))))
+
+#define EXTCERT(X) (((X) == Public ? ".crt" \
+ : ((X) == Private ? ".key" \
+ : ((X) == CACert ? ".crt" : NULL))))
+
+#define DELETEDCERT(X) ((X)->data.deleted)
+#define RENEWCERT(X) ((X)->data.renew)
+
#endif /* SMIME */
diff --git a/pith/filter.c b/pith/filter.c
index 2800a3ba..d22b1215 100644
--- a/pith/filter.c
+++ b/pith/filter.c
@@ -11284,7 +11284,7 @@ gf_local_nvtnl(FILTER_S *f, int flg)
GF_PUTC(f->next, '\015');
GF_PUTC(f->next, '\012');
}
- else
+ else if(c != '\015') /* do not copy isolated \015 into source */
GF_PUTC(f->next, c);
}
diff --git a/pith/options.h b/pith/options.h
index 0d5412a1..c452b97b 100644
--- a/pith/options.h
+++ b/pith/options.h
@@ -224,5 +224,14 @@ extern char *(*pith_opt_user_agent_prefix)(void);
*/
extern int (*pith_opt_smime_get_passphrase)(void);
+/*
+ * Required call to interface for input of file to import.
+ */
+extern int (*pith_smime_import_certificate)(char *, char *, size_t);
+
+/*
+ * Required call to ask user to enter a password, with a given char * prompt
+ */
+extern void (*pith_enter_password)(char *, char *, size_t);
#endif /* PITH_OPTIONS_INCLUDED */
diff --git a/pith/pine.hlp b/pith/pine.hlp
index 66299dfa..6231a466 100644
--- a/pith/pine.hlp
+++ b/pith/pine.hlp
@@ -140,7 +140,7 @@ with help text for the config screen and the composer that didn't have any
reasonable place to be called from.
Dummy change to get revision in pine.hlp
============= h_revision =================
-Alpine Commit 47 2014-02-20 00:06:00
+Alpine Commit 48 2014-03-09 14:26:45
============= h_news =================
<HTML>
<HEAD>
@@ -187,6 +187,9 @@ Additions include:
direction of search.
<LI> Upgrade UW-IMAP to Panda IMAP from
<A HREF="https://github.com/jonabbey/panda-imap">https://github.com/jonabbey/panda-imap</A>.
+ <LI> S/MIME: When a signed message is received from a user whose
+ certificate has not been imported, should the certificate fail to
+ validate, the user will be shown a warning, and asked how to proceed.
<LI> Replace tabs by spaces in From and Subject fields to control for
size in screen of these fields. Change only in index screen display.
<LI> Add support to selective expunge through a subcommand of the
@@ -247,6 +250,7 @@ Bugs that have been addressed include:
<LI> S/MIME: Forwarding messages with multipart content-type failed to be signed
with &quot;Error writing pipe&quot; message. Reported by Andreas Schamanek
and Stefan Mueller.
+ <LI> S/MIME: Certificates are lost when using a pinerc file outside of the home directory.
<LI> Crash when tcp connection to NNTP server was lost after connection
had been established, but lost immediately afterwards.
<LI> WebAlpine: add _GNU_SOURCE to make pubcookie build.
@@ -3834,6 +3838,9 @@ There are also additional details on
<li><a href="h_config_smime_transfer_priv_to_dir">S/MIME: Transfer Private Keys to Directory</a>
<li><a href="h_config_smime_transfer_pub_to_con">S/MIME: Transfer Public Certs to Container</a>
<li><a href="h_config_smime_transfer_pub_to_dir">S/MIME: Transfer Public Certs to Directory</a>
+<li><a href="h_config_smime_public_certificates">S/MIME: Manage Public Certificates</a>
+<li><a href="h_config_smime_private_keys">S/MIME: Manage Private Keys</a>
+<li><a href="h_config_smime_certificate_authorities">S/MIME: Manage Certificate Authorities</a>
<li><a href="h_index_cmd_sort">Sort Command</a>
<li><a href="h_compose_spell">Spell Check Command</a>
<li><a href="h_common_suspend">Suspend Command</a>
@@ -34028,8 +34035,6 @@ Some limitations:
<LI> Because the implementation currently uses OpenSSL, there is only a very
limited integration with the Mac OS Keychain (the storing and access of
public certificates).
- <LI> There is no way to view or manipulate the lists of certificates from
- within Alpine.
</UL>
<P>
The S/MIME configuration screen is reached by going to the Main Menu and typing
@@ -34789,6 +34794,256 @@ the Keychain to store your public certs.
&lt;End of help on this topic&gt;
</BODY>
</HTML>
+====== h_config_smime_public_certificates =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME: Manage Public Certificates</TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME: Manage Public Certificates</H1>
+
+UNIX Alpine only.
+<P>
+This menu item allows you to manage your public certificates, this
+may include your own public certificate, but it normally includes
+certificates of people you correspond with. These certificates are
+saved by Alpine automatically when they are found in signed messages
+that you receive. This interface allows you to manage them, by
+giving you the option to delete them, or trust them (in the case
+of self-signed certificates).
+
+<P>
+Please note that Alpine will not validate a message that was sent to you
+using a self-signed certificate, unless you decide to trust that certificate.
+Internally, a certificate is trusted by copying it to the
+<A HREF="h_config_smime_certificate_authorities">Certificate Authorities</A>
+collection. If you decide that you want to stop trusting a self-signed
+certificate, you must delete such certificate from such collection.
+<P>
+The <B>I</B> Import command available in this screen allows you to
+import a command to this collection.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_private_keys =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME: Manage Private Keys</TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME: Manage Private Keys</H1>
+
+UNIX Alpine only.
+<P>
+This option allows you to manage your private key. Normally a person has only
+one key, in the same way that a person only has one valid passport, or ID card,
+at any given time. This option allows you to manage private keys. You can
+delete them or import them. Additionally, you can view information
+about your public certificate, such as the issuer and the dates of validity
+of such certificate, among others.
+
+<P>
+If you have more than one e-mail address for which you want to use the
+same private key, you must add all those addresses to the private key at
+the moment that the key is generated. When you receive a signed message using
+a key generated for several e-mail addresses, Alpine will save a
+certificate for each e-mail address included in such certificate.
+<P>
+The <B>I</B> Import command available in this screen allows you to
+import a command to this collection.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_certificate_authorities =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME: Manage Certificate Authorities</TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME: Manage Certificate Authorities</H1>
+
+UNIX Alpine only.
+<P>
+This collection contains certificates that are needed to validate the
+certificate of another person, and therefore contains certificates that
+you trust. Typically a certificate is signed by another entity, called a
+certificate authority. This option allows you to manage which certificates
+you trust, allowing you to import them and to delete them or view information
+about each certificate, such as the issuer and the dates of validity
+of such certificate.
+<P>
+The <B>I</B> Import command available in this screen allows you to
+import a command to this collection.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_certificate_information =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME: Certificate Information Screen</TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME: Certificate Information Screen</H1>
+
+UNIX Alpine only.
+<P>
+The CERTIFICATE INFORMATION screen shows you information contained in a certificate
+such as its owner, e-mail address, issuer, and interval of validity,
+among others.
+<P>
+In the case of public certificates, this screen shows you if there was a
+failure when attempting to validate such message. If the certificate is
+self-signed, then the <B>T</B> Trust command will be available, which
+you can use to trust such certificate and make Alpine not fail validating
+signatures signed with such certificate.
+<P>
+You can also mark a certificate deleted, with the <B>D</B> command, or
+remove the deleted mark with the <B>U</B> undelete command.
+<P>
+In the case of your private key, Alpine shows you the information
+from your public key. Additionally, Alpine allows you to see public
+and private information about your key, with the <B>B</B> and
+<B>R</B> commands respectively.
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_manage_public_menu =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME: Menu of Commands to Manage Public Certificates</TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME: Commands that Manage Public Certificates</H1>
+
+UNIX Alpine only.
+<P>
+This screen allows you to manage your public certificates. Available commands and
+a short description of what they do follows.
+<UL>
+<LI> <B>I</B> Imports a public certificate to this collection.
+<LI> <B>V</B> View information about a certificate such as the name of the person the
+certificate was issued to, its dates of validity, and validity status.
+<LI> <B>D</B> Marks a certificate deleted.
+<LI> <B>U</B> Removes the deletion mark on a certificate.
+<LI> <B>X</B> Removes all certificates marked deleted permanently (cannot be undone).
+<LI> <B>T</B> This command is only available for self-signed certificates, and allows you to
+trust a certificate by copying it to the collection of trusted certificates.
+</UL>
+<P>
+All commands provide feedback to let you know about their success or failure.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_manage_private_menu =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME: Menu of Commands to Manage Private Keys</TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME: Commands that Manage Private Keys</H1>
+
+UNIX Alpine only.
+<P>
+This screen allows you to manage your private key. Available commands and
+a short description of what they do follows.
+<UL>
+<LI> <B>I</B> Imports a new public key to this collection.
+<LI> <B>V</B> View information about the public certificate corresponding to this
+key.
+<LI> <B>D</B> Marks a key to be deleted.
+<LI> <B>U</B> Removes the deletion mark on a key.
+<LI> <B>X</B> Removes all keys marked deleted permanently (cannot be undone).
+Note that expunging a private key does not remove the public key, which must
+be removed separately.
+</UL>
+<P>
+All commands provide feedback to let you know about their success or failure.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
+====== h_config_smime_manage_cacerts_menu =====
+<HTML>
+<HEAD>
+<TITLE>S/MIME: Menu of Commands to Manage Certificate Authorities</TITLE>
+</HEAD>
+<BODY>
+<H1>S/MIME: Commands that Manage Certificate Authorities</H1>
+
+UNIX Alpine only.
+<P>
+This screen allows you to manage your collection of certificates that you
+trust. Available commands and a short description of what they do follows.
+<UL>
+<LI> <B>I</B> Imports a trusted certificate to this collection. This is
+done by reading the certificate and validating it. Once a certificate
+is found to be valid, it is saved, adding the extension &quot;.crt&quot;
+to the certificate, if necessary.
+<LI> <B>V</B> View information about this certificate, such as its issuer
+and validity dates.
+<LI> <B>D</B> Marks a certificate to be deleted.
+<LI> <B>U</B> Removes the deletion mark on a certificate.
+<LI> <B>X</B> Removes all certificates marked deleted permanently (cannot be undone).
+</UL>
+<P>
+All commands provide feedback to let you know about their success or failure.
+<P>
+<UL>
+<LI><A HREF="h_mainhelp_smime">General S/MIME help</A>
+</UL><P>
+<P>
+<UL>
+<LI><A HREF="h_finding_help">Finding more information and requesting help</A>
+</UL><P>
+&lt;End of help on this topic&gt;
+</BODY>
+</HTML>
====== h_config_lame_list_mode =====
<HTML>
<HEAD>
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);
diff --git a/pith/smime.h b/pith/smime.h
index 5a89b372..a8ca8dd2 100644
--- a/pith/smime.h
+++ b/pith/smime.h
@@ -2,6 +2,7 @@
* $Id: smime.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
*
* ========================================================================
+ * Copyright 2013-2014 Eduardo Chappa
* Copyright 2008 University of Washington
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -31,6 +32,7 @@
/* exported protoypes */
+int smime_validate_cert(X509 *cert, long *error);
int encrypt_file(char *fp, char *text);
char *decrypt_file(char *fp, int *rv);
int is_pkcs7_body(BODY *b);
@@ -40,7 +42,13 @@ void free_smime_body_sparep(void **sparep);
int sign_outgoing_message(METAENV *header, BODY **bodyP, int dont_detach);
void gf_puts_uline(char *txt, gf_io_t pc);
PERSONAL_CERT *find_certificate_matching_recip_info(PKCS7_RECIP_INFO *ri);
+PERSONAL_CERT *get_personal_certs(char *path);
+void smime_init(void);
void smime_deinit(void);
+void renew_store(void);
+void renew_cert_data(CertList **data, WhichCerts ctype);
+BIO *print_private_key_information(char *email, int itype);
+
SMIME_STUFF_S *new_smime_struct(void);
int copy_publiccert_dir_to_container(void);
int copy_publiccert_container_to_dir(void);
@@ -52,6 +60,7 @@ int copy_cacert_container_to_dir(void);
int copy_publiccert_container_to_keychain(void);
int copy_publiccert_keychain_to_container(void);
#endif /* APPLEKEYCHAIN */
+int import_certificate(WhichCerts);
#endif /* PITH_SMIME_INCLUDED */
diff --git a/pith/smkeys.c b/pith/smkeys.c
index e815a59a..7aac6e4c 100644
--- a/pith/smkeys.c
+++ b/pith/smkeys.c
@@ -4,6 +4,7 @@ static char rcsid[] = "$Id: smkeys.c 1266 2009-07-14 18:39:12Z hubert@u.washingt
/*
* ========================================================================
+ * Copyright 2013-2014 Eduardo Chappa
* Copyright 2008 University of Washington
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,6 +31,7 @@ static char rcsid[] = "$Id: smkeys.c 1266 2009-07-14 18:39:12Z hubert@u.washingt
#include "../pith/tempfile.h"
#include "../pith/busy.h"
#include "../pith/osdep/lstcmpnt.h"
+#include "../pith/util.h"
#include "smkeys.h"
#ifdef APPLEKEYCHAIN
@@ -42,10 +44,121 @@ static char rcsid[] = "$Id: smkeys.c 1266 2009-07-14 18:39:12Z hubert@u.washingt
/* internal prototypes */
static char *emailstrclean(char *string);
-static int add_certs_in_dir(X509_LOOKUP *lookup, char *path);
static int certlist_to_file(char *filename, CertList *certlist);
static int mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup);
+/* smime_expunge_cert.
+ * Return values: < 0 there was an error.
+ * >=0 the number of messages expunged
+ */
+int
+smime_expunge_cert(WhichCerts ctype)
+{
+ int count;
+ CertList *cl, *dummy, *data;
+ char *path, *ext, buf[MAXPATH+1];
+
+ if(DATACERT(ctype)== NULL)
+ return -1;
+
+ /* data cert is the way we unify certificate management across functions, but it is
+ * not where we really save the information in the case ctype is equal to Private.
+ * What we will do is to update the datacert, and in the case of ctype equal to Private
+ * use the updated certdata to update the personal_certs data.
+ */
+
+ path = PATHCERTDIR(ctype);
+ ext = EXTCERT(ctype);
+
+ if(path){
+ /* add a fake certificate at the beginning of the list */
+ dummy = fs_get(sizeof(CertList));
+ memset((void *)dummy, 0, sizeof(CertList));
+ dummy->next = DATACERT(ctype);
+
+ for(cl = dummy, count = 0; cl && cl->next;){
+ if(cl->next->data.deleted == 0){
+ cl = cl->next;
+ continue;
+ }
+
+ build_path(buf, path, cl->next->name, sizeof(buf));
+ if(ctype == Private && strlen(buf) + strlen(EXTCERT(Private)) < sizeof(buf))
+ strcat(buf, EXTCERT(Private));
+
+ if(our_unlink(buf) < 0)
+ q_status_message1(SM_ORDER, 3, 3, _("Error removing certificate %s"), cl->next->name);
+ else {
+ count++; /* count it! */
+ data = cl->next;
+ cl->next = data->next;
+ if(data->name) fs_give((void **)&data->name);
+ fs_give((void **)&data);
+ }
+ }
+ } else
+ q_status_message1(SM_ORDER, 3, 3, _("Error removing certificate %s"), cl->name);
+
+ switch(ctype){
+ case Private: ps_global->smime->privatecertdata = dummy->next; break;
+ case Public : ps_global->smime->publiccertdata = dummy->next; break;
+ case CACert : ps_global->smime->cacertdata = dummy->next; break;
+ default : break;
+ }
+ fs_give((void **)&dummy);
+ if(count > 0)
+ q_status_message2(SM_ORDER, 3, 3, _("Removed %s certificate%s"), comatose(count), plural(count));
+ else
+ q_status_message(SM_ORDER, 3, 3, _("Error: No certificates were removed"));
+ return count;
+}
+
+void
+mark_cert_deleted(WhichCerts ctype, char *email, unsigned state)
+{
+ CertList *cl;
+ char tmp[200];
+
+ snprintf(tmp, sizeof(tmp), "%s%s", email, ctype == Private ? "" : ".crt");
+ tmp[sizeof(tmp)-1] = '\0';
+ for(cl = DATACERT(ctype); cl != NULL && strcmp(cl->name, tmp); cl = cl->next);
+ cl->data.deleted = state;
+}
+
+unsigned
+get_cert_deleted(WhichCerts ctype, char *email)
+{
+ CertList *cl;
+
+ for(cl = DATACERT(ctype); cl != NULL && strcmp(cl->name, email); cl = cl->next);
+ return (cl && cl->data.deleted) ? 1 : 0;
+}
+
+void
+get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen)
+{
+ unsigned char md[128];
+ char *b;
+ unsigned int len, i;
+
+ len = sizeof(md);
+
+ X509_digest(cert, type, md, &len);
+
+ b = buf;
+ *b = 0;
+ for(i=0; i<len; i++){
+ if(b-buf+3>=maxLen)
+ break;
+
+ if(i != 0)
+ *b++ = ':';
+
+ snprintf(b, maxLen - (b-buf), "%02x", md[i]);
+ b+=2;
+ }
+}
+
/*
* Remove leading whitespace, trailing whitespace and convert
@@ -87,24 +200,36 @@ emailstrclean(char *string)
/*
* Add a lookup for each "*.crt" file in the given directory.
*/
-static int
-add_certs_in_dir(X509_LOOKUP *lookup, char *path)
+int
+add_certs_in_dir(X509_LOOKUP *lookup, char *path, char *ext, CertList **cdata)
{
char buf[MAXPATH];
struct direct *d;
DIR *dirp;
+ CertList *cert, *cl;
int ret = 0;
- dirp = opendir(path);
- if(dirp){
-
+ if((dirp = opendir(path)) != NULL){
while(!ret && (d=readdir(dirp)) != NULL){
- if(srchrstr(d->d_name, ".crt")){
+ if(srchrstr(d->d_name, ext)){
build_path(buf, path, d->d_name, sizeof(buf));
if(!X509_LOOKUP_load_file(lookup, buf, X509_FILETYPE_PEM)){
q_status_message1(SM_ORDER, 3, 3, _("Error loading file %s"), buf);
ret = -1;
+ } else {
+ if(cdata){
+ cert = fs_get(sizeof(CertList));
+ memset((void *)cert, 0, sizeof(CertList));
+ cert->name = cpystr(d->d_name);
+ if(*cdata == NULL)
+ *cdata = cert;
+ else{
+ for (cl = *cdata; cl && cl->next; cl = cl->next);
+ cl->next = cert;
+ }
+ }
+
}
}
@@ -151,7 +276,7 @@ get_ca_store(void)
}
else if(ps_global->smime && ps_global->smime->catype == Directory
&& ps_global->smime->capath){
- if(add_certs_in_dir(lookup, ps_global->smime->capath) < 0){
+ if(add_certs_in_dir(lookup, ps_global->smime->capath, ".crt", &ps_global->smime->cacertdata) < 0){
X509_STORE_free(store);
return NULL;
}
@@ -298,14 +423,16 @@ get_x509_subject_email(X509 *x)
* the email address that has come from the certificate.
*
* The argument email is destroyed.
+ *
+ * args: ctype says where the user wants to save the certificate
*/
void
-save_cert_for(char *email, X509 *cert)
+save_cert_for(char *email, X509 *cert, WhichCerts ctype)
{
- if(!ps_global->smime)
+ if(!ps_global->smime || ctype == Private)
return;
- dprint((9, "save_cert_for(%s)", email ? email : "?"));
+ dprint((9, "save_cert_for(%s, %s)", email ? email : "?", ctype == Public ? _("Public") : ctype == Private ? _("Private") : "CACert"));
emailstrclean(email);
if(ps_global->smime->publictype == Keychain){
@@ -354,20 +481,22 @@ save_cert_for(char *email, X509 *cert)
else if(ps_global->smime->publictype == Container){
REMDATA_S *rd = NULL;
char path[MAXPATH];
+ char *upath = PATHCERTDIR(ctype);
char *tempfile = NULL;
int err = 0;
add_to_end_of_certlist(&ps_global->smime->publiccertlist, email, X509_dup(cert));
- if(!ps_global->smime->publicpath)
+ if(!upath)
return;
- if(IS_REMOTE(ps_global->smime->publicpath)){
- rd = rd_create_remote(RemImap, ps_global->smime->publicpath, REMOTE_SMIME_SUBTYPE,
+ if(IS_REMOTE(upath)){
+ rd = rd_create_remote(RemImap, upath, REMOTE_SMIME_SUBTYPE,
NULL, "Error: ",
_("Can't access remote smime configuration."));
- if(!rd)
+ if(!rd){
return;
+ }
(void) rd_read_metadata(rd);
@@ -419,7 +548,7 @@ save_cert_for(char *email, X509 *cert)
path[sizeof(path)-1] = '\0';
}
else{
- strncpy(path, ps_global->smime->publicpath, sizeof(path)-1);
+ strncpy(path, upath, sizeof(path)-1);
path[sizeof(path)-1] = '\0';
}
@@ -436,7 +565,7 @@ save_cert_for(char *email, X509 *cert)
}
}
- if(!err && IS_REMOTE(ps_global->smime->publicpath)){
+ if(!err && IS_REMOTE(upath)){
int e, we_cancel;
char datebuf[200];
@@ -479,11 +608,11 @@ save_cert_for(char *email, X509 *cert)
}
}
else if(ps_global->smime->publictype == Directory){
+ char *path = PATHCERTDIR(ctype);
char certfilename[MAXPATH];
BIO *bio_out;
- build_path(certfilename, ps_global->smime->publicpath,
- email, sizeof(certfilename));
+ build_path(certfilename, path, email, sizeof(certfilename));
strncat(certfilename, ".crt", sizeof(certfilename)-1-strlen(certfilename));
certfilename[sizeof(certfilename)-1] = 0;
@@ -505,7 +634,7 @@ save_cert_for(char *email, X509 *cert)
* The caller should free the cert.
*/
X509 *
-get_cert_for(char *email)
+get_cert_for(char *email, WhichCerts ctype)
{
char *path;
char certfilename[MAXPATH];
@@ -516,8 +645,10 @@ get_cert_for(char *email)
if(!ps_global->smime)
return cert;
- dprint((9, "get_cert_for(%s)", email ? email : "?"));
+ dprint((9, "get_cert_for(%s, %s)", email ? email : "?", "none yet"));
+ if(ctype == Private) /* there is no private certificate info */
+ ctype = Public; /* return public information instead */
strncpy(emailaddr, email, sizeof(emailaddr)-1);
emailaddr[sizeof(emailaddr)-1] = 0;
@@ -585,23 +716,20 @@ get_cert_for(char *email)
#endif /* APPLEKEYCHAIN */
}
- else if(ps_global->smime->publictype == Container){
- if(ps_global->smime->publiccertlist){
+ else if(SMHOLDERTYPE(ctype) == Container){
CertList *cl;
- for(cl = ps_global->smime->publiccertlist; cl; cl = cl->next){
+ for(cl = SMCERTLIST(ctype); cl; cl = cl->next){
if(cl->name && !strucmp(emailaddr, cl->name))
break;
}
if(cl)
cert = X509_dup((X509 *) cl->x509_cert);
- }
}
- else if(ps_global->smime->publictype == Directory){
- path = ps_global->smime->publicpath;
- build_path(certfilename, path, emailaddr, sizeof(certfilename));
- strncat(certfilename, ".crt", sizeof(certfilename)-1-strlen(certfilename));
+ else if(SMHOLDERTYPE(ctype) == Directory){
+ build_path(certfilename, PATHCERTDIR(ctype), emailaddr, sizeof(certfilename));
+ strncat(certfilename, EXTCERT(ctype), sizeof(certfilename)-1-strlen(certfilename));
certfilename[sizeof(certfilename)-1] = 0;
if((in = BIO_new_file(certfilename, "r"))!=0){
@@ -642,7 +770,7 @@ mem_to_personal_certs(char *contents)
if(strncmp(EMAILADDRLEADER, line, strlen(EMAILADDRLEADER)) == 0){
name = line + strlen(EMAILADDRLEADER);
- cert = get_cert_for(name);
+ cert = get_cert_for(name, Public);
keytext = p;
/* advance p past this record */
diff --git a/pith/smkeys.h b/pith/smkeys.h
index d3c9031f..818f46ac 100644
--- a/pith/smkeys.h
+++ b/pith/smkeys.h
@@ -2,6 +2,7 @@
* $Id: smkeys.h 1074 2008-06-04 00:08:43Z hubert@u.washington.edu $
*
* ========================================================================
+ * Copyrighr 2013-2014 Eduardo Chappa
* Copyright 2008 University of Washington
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -44,10 +45,11 @@ typedef struct personal_cert {
/* exported protoypes */
+int add_certs_in_dir(X509_LOOKUP *lookup, char *path, char *ext, CertList **cdata);
X509_STORE *get_ca_store(void);
PERSONAL_CERT *get_personal_certs(char *d);
-X509 *get_cert_for(char *email);
-void save_cert_for(char *email, X509 *cert);
+X509 *get_cert_for(char *email, WhichCerts ctype);
+void save_cert_for(char *email, X509 *cert, WhichCerts ctype);
char **get_x509_subject_email(X509 *x);
EVP_PKEY *load_key(PERSONAL_CERT *pc, char *pass);
CertList *mem_to_certlist(char *contents);
@@ -55,7 +57,10 @@ void add_to_end_of_certlist(CertList **cl, char *name, X509 *cert);
void free_certlist(CertList **cl);
PERSONAL_CERT *mem_to_personal_certs(char *contents);
void free_personal_certs(PERSONAL_CERT **pc);
-
+void get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen);
+void mark_cert_deleted(WhichCerts ctype, char *email, unsigned state);
+unsigned get_cert_deleted(WhichCerts ctype, char *email);
+int smime_expunge_cert(WhichCerts ctype);
#endif /* PITH_SMKEYS_INCLUDED */
#endif /* SMIME */