summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--alpine/alpine.c2
-rw-r--r--alpine/confscroll.c2
-rw-r--r--alpine/conftype.h4
-rw-r--r--alpine/keymenu.c88
-rw-r--r--alpine/keymenu.h16
-rw-r--r--alpine/mailcmd.c40
-rw-r--r--alpine/mailcmd.h2
-rw-r--r--alpine/smime.c463
-rw-r--r--alpine/smime.h1
-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
17 files changed, 1531 insertions, 183 deletions
diff --git a/alpine/alpine.c b/alpine/alpine.c
index a6882145..8266c30e 100644
--- a/alpine/alpine.c
+++ b/alpine/alpine.c
@@ -188,7 +188,9 @@ main(int argc, char **argv)
pith_opt_pretty_feature_name = pretty_feature_name;
pith_opt_closing_stream = titlebar_stream_closing;
pith_opt_current_expunged = mm_expunged_current;
+ pith_enter_password = alpine_get_password;
#ifdef SMIME
+ pith_smime_import_certificate = smime_import_certificate;
pith_opt_smime_get_passphrase = smime_get_passphrase;
#endif
#ifdef ENABLE_LDAP
diff --git a/alpine/confscroll.c b/alpine/confscroll.c
index 1e295c96..5ca2702b 100644
--- a/alpine/confscroll.c
+++ b/alpine/confscroll.c
@@ -3147,7 +3147,7 @@ update_option_screen(struct pine *ps, OPT_SCREEN_S *screen, Pos *cursor_pos)
}
value = (ctmp->flags & CF_INHERIT) ? INHERIT : ctmp->value;
- dprint((1, "value = %s", ctmp->value));
+
if(value){
char *p;
int i, j;
diff --git a/alpine/conftype.h b/alpine/conftype.h
index fee9a75d..ccd5c4b6 100644
--- a/alpine/conftype.h
+++ b/alpine/conftype.h
@@ -60,6 +60,10 @@ typedef struct conf_line {
struct flag_table **ftbl; /* address of start of table */
struct flag_table *fp; /* pointer into table for each row */
} f;
+ struct smime_data {
+ WhichCerts ctype;
+ int deleted;
+ } s;
struct context_and_screen {
CONTEXT_S *ct;
CONT_SCR_S *cs;
diff --git a/alpine/keymenu.c b/alpine/keymenu.c
index 9ccba791..3e90f1b7 100644
--- a/alpine/keymenu.c
+++ b/alpine/keymenu.c
@@ -4,8 +4,8 @@ static char rcsid[] = "$Id: keymenu.c 1074 2008-06-04 00:08:43Z hubert@u.washing
/*
* ========================================================================
- * 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.
@@ -2617,6 +2617,92 @@ struct key config_smime_helper_keys[] =
ENDKEY_MENU};
INST_KEY_MENU(config_smime_helper_keymenu, config_smime_helper_keys);
+struct key config_smime_manage_certs_menu_keys[] =
+ {HELP_MENU,
+ WHEREIS_MENU,
+ EXIT_SETUP_MENU,
+ {"S", "[" N_("Select") "]", {MC_CHOICE,3,{'s',ctrl('M'),ctrl('J')}}, KS_NONE},
+ {"I", N_("Import Cert"), {MC_IMPORT,1,{'i'}}, KS_NONE},
+ NULL_MENU,
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_smime_manage_certs_menu_keymenu, config_smime_manage_certs_menu_keys);
+
+struct key config_smime_add_certs_keys[] =
+ {HELP_MENU,
+ NULL_MENU,
+ EXIT_SETUP_MENU,
+ {"I", N_("Import Cert"), {MC_IMPORT,3,{'i', ctrl('M'), ctrl('J')}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU};
+INST_KEY_MENU(config_smime_add_certs_keymenu, config_smime_add_certs_keys);
+
+struct key config_smime_manage_certs_work_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ EXIT_SETUP_MENU,
+ {"V", "[" N_("View Info") "]", {MC_CHOICE,3,{'v',ctrl('M'),ctrl('J')}}, KS_NONE},
+ {"I", N_("Import Cert"), {MC_IMPORT,1,{'i'}}, KS_NONE},
+ NULL_MENU,
+ {"D", N_("Delete"), {MC_DELETE,1,{'d'}}, KS_NONE},
+ {"U", N_("Undelete"), {MC_UNDELETE,1,{'u'}}, KS_NONE},
+ {"X", N_("Expunge"), {MC_EXPUNGE,1,{'x'}}, KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ WHEREIS_MENU,
+
+ PREV_MENU,
+ NEXT_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(config_smime_manage_certs_work_keymenu, config_smime_manage_certs_work_keys);
+
+struct key smime_certificate_info_keys[] =
+ {HELP_MENU,
+ OTHER_MENU,
+ {"E",N_("Exit Viewer"),{MC_EXIT,1,{'e'}},KS_EXITMODE},
+ {"T",N_("Trust Cert"), {MC_TRUST,1,{'t'}},KS_NONE},
+ {"D",N_("Delete"), {MC_DELETE,1,{'d'}},KS_NONE},
+ {"U",N_("Undelete"), {MC_UNDELETE,1,{'u'}},KS_NONE},
+ {"B",N_("Public Key"), {MC_PUBLIC,1,{'b'}},KS_NONE},
+ {"R",N_("Private Key"),{MC_PRIVATE,1,{'r'}},KS_NONE},
+ NULL_MENU,
+ NULL_MENU,
+ FWDEMAIL_MENU,
+ {"S", N_("Save"), {MC_SAVETEXT,1,{'s'}}, KS_SAVE},
+
+ HELP_MENU,
+ OTHER_MENU,
+ PREVPAGE_MENU,
+ NEXTPAGE_MENU,
+ PRYNTTXT_MENU,
+ WHEREIS_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ NULL_MENU,
+ HOMEKEY_MENU,
+ ENDKEY_MENU};
+INST_KEY_MENU(smime_certificate_info_keymenu, smime_certificate_info_keys);
+
/*
* Internal prototypes
diff --git a/alpine/keymenu.h b/alpine/keymenu.h
index 0ce6b14c..a4cee7f3 100644
--- a/alpine/keymenu.h
+++ b/alpine/keymenu.h
@@ -216,6 +216,12 @@ struct key_menu {
#define MC_QUOTA 803
#define MC_ADDHEADER 804
+
+/* Commands for S/MIME screens */
+#define MC_TRUST 900
+#define MC_PUBLIC 901
+#define MC_PRIVATE 902
+
/*
* Some standard Key/Command Bindings
*/
@@ -549,7 +555,9 @@ struct key_menu {
#define SMIME_PARENT_KEY 2
#define DECRYPT_KEY (VIEW_PIPE_KEY + 7)
#define SECURITY_KEY (DECRYPT_KEY + 1)
-
+#define TRUST_KEY 3
+#define PUBLIC_KEY 6
+#define PRIVATE_KEY 7
extern struct key_menu cancel_keymenu,
ab_keymenu,
@@ -647,7 +655,11 @@ extern struct key_menu cancel_keymenu,
take_export_keymenu_sm,
take_export_keymenu_lm,
config_smime_helper_keymenu,
- smime_info_keymenu;
+ config_smime_add_certs_keymenu,
+ smime_info_keymenu,
+ config_smime_manage_certs_menu_keymenu,
+ config_smime_manage_certs_work_keymenu,
+ smime_certificate_info_keymenu;
extern struct key rev_msg_keys[];
diff --git a/alpine/mailcmd.c b/alpine/mailcmd.c
index 1d252a38..a9fca5da 100644
--- a/alpine/mailcmd.c
+++ b/alpine/mailcmd.c
@@ -304,6 +304,46 @@ static ESCKEY_S flag_text_opt[] = {
{-1, 0, NULL, NULL}
};
+void
+alpine_get_password(char *prompt, char *pass, size_t len)
+{
+ int rc, flags;
+ do { /* transform this to a (*pith_)() function */
+ flags = OE_PASSWD | OE_DISALLOW_HELP;
+ pass[0] = '\0';
+ rc = optionally_enter(pass, -FOOTER_ROWS(ps_global), 0, len,
+ prompt, NULL, NO_HELP, &flags);
+ } while (rc!=0 && rc!=1 && rc>0);
+}
+
+int smime_import_certificate(char *filename, char *full_filename, size_t len)
+{
+ int r = 1;
+ static HISTORY_S *history = NULL;
+ static ESCKEY_S eopts[] = {
+ {ctrl('T'), 10, "^T", N_("To Files")},
+ {-1, 0, NULL, NULL},
+ {-1, 0, NULL, NULL}};
+
+ if(F_ON(F_ENABLE_TAB_COMPLETE,ps_global)){
+ eopts[r].ch = ctrl('I');
+ eopts[r].rval = 11;
+ eopts[r].name = "TAB";
+ eopts[r].label = N_("Complete");
+ }
+
+ eopts[++r].ch = -1;
+
+ filename[0] = '\0';
+ full_filename[0] = '\0';
+
+ r = get_export_filename(ps_global, filename, NULL, full_filename,
+ len, "certificate", "IMPORT", eopts, NULL,
+ -FOOTER_ROWS(ps_global), GE_IS_IMPORT, &history);
+
+ return r;
+}
+
/*----------------------------------------------------------------------
The giant switch on the commands for index and viewing
diff --git a/alpine/mailcmd.h b/alpine/mailcmd.h
index 7f15d1f6..d26d321c 100644
--- a/alpine/mailcmd.h
+++ b/alpine/mailcmd.h
@@ -63,6 +63,8 @@ typedef enum {View, MsgIndx, ThrdIndx} CmdWhere;
/* exported protoypes */
+void alpine_get_password(char *, char *, size_t);
+int smime_import_certificate(char *, char *, size_t);
int process_cmd(struct pine *, MAILSTREAM *, MSGNO_S *, int, CmdWhere, int *);
char *pretty_command(UCS);
void bogus_command(UCS, char *);
diff --git a/alpine/smime.c b/alpine/smime.c
index b0bf16de..6d4ea49c 100644
--- a/alpine/smime.c
+++ b/alpine/smime.c
@@ -4,6 +4,7 @@ static char rcsid[] = "$Id: smime.c 1074 2008-06-04 00:08:43Z hubert@u.washingto
/*
* ========================================================================
+ * Copyright 2013-2014 Eduardo Chappa
* Copyright 2008 University of Washington
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -41,20 +42,24 @@ static char rcsid[] = "$Id: smime.c 1074 2008-06-04 00:08:43Z hubert@u.washingto
#include "setup.h"
#include "smime.h"
-
/* internal prototypes */
void format_smime_info(int pass, BODY *body, long msgno, gf_io_t pc);
void print_separator_line(int percent, int ch, gf_io_t pc);
void output_cert_info(X509 *cert, gf_io_t pc);
void output_X509_NAME(X509_NAME *name, gf_io_t pc);
void side_by_side(STORE_S *left, STORE_S *right, gf_io_t pc);
-void get_fingerprint(X509 *cert, const EVP_MD *type, char *buf, size_t maxLen);
STORE_S *wrap_store(STORE_S *in, int width);
void smime_config_init_display(struct pine *, CONF_S **, CONF_S **);
void revert_to_saved_smime_config(struct pine *ps, SAVED_CONFIG_S *vsave);
SAVED_CONFIG_S *save_smime_config_vars(struct pine *ps);
void free_saved_smime_config(struct pine *ps, SAVED_CONFIG_S **vsavep);
int smime_helper_tool(struct pine *, int, CONF_S **, unsigned);
+int smime_public_certs_tool(struct pine *, int, CONF_S **, unsigned);
+void manage_certificates(struct pine *, WhichCerts);
+void smime_manage_certs_init (struct pine *, CONF_S **, CONF_S **, WhichCerts, int);
+void display_certificate_information(struct pine *, X509 *, char *, WhichCerts);
+int manage_certs_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags);
+int manage_certificate_info_tool(int, MSGNO_S *, SCROLL_S *);
/*
@@ -456,6 +461,7 @@ side_by_side(STORE_S *left, STORE_S *right, gf_io_t pc)
STORE_S *right_wrapped;
char buf_l[256];
char buf_r[256];
+ char *l, *r;
char *b;
int i;
int w = ps_global->ttyo->screen_cols/2 - 1;
@@ -471,10 +477,9 @@ side_by_side(STORE_S *left, STORE_S *right, gf_io_t pc)
for(;;){
- if(!so_fgets(left_wrapped, buf_l, sizeof(buf_l)))
- break;
-
- if(!so_fgets(right_wrapped, buf_r, sizeof(buf_r)))
+ l = so_fgets(left_wrapped, buf_l, sizeof(buf_l));
+ r = so_fgets(right_wrapped, buf_r, sizeof(buf_r));
+ if(l == NULL && r == NULL)
break;
for(i=0, b=buf_l; i<w && *b && *b!='\r' && *b!='\n'; i++,b++){
@@ -486,9 +491,10 @@ side_by_side(STORE_S *left, STORE_S *right, gf_io_t pc)
if(buf_r[0]){
while(i<w){
- pc(' ');
+ pc(' ');
i++;
}
+ pc(' ');
for(i=0, b=buf_r; i<w && *b && *b!='\r' && *b!='\n'; i++,b++)
pc(*b);
@@ -501,33 +507,6 @@ side_by_side(STORE_S *left, STORE_S *right, gf_io_t pc)
so_give(&right_wrapped);
}
-
-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;
- }
-}
-
-
/*
* Wrap the text in the given store to the given width.
* A new store is created for the result.
@@ -779,9 +758,11 @@ smime_config_init_display(struct pine *ps, CONF_S **ctmp, CONF_S **first_line)
new_confline(ctmp);
(*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+ for(i = 0; i < sizeof(tmp) && i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp)); i++)
+ tmp[i] = '-';
new_confline(ctmp);
(*ctmp)->flags |= CF_NOSELECT;
- (*ctmp)->value = cpystr("---------------------------------------------------------------------------");
+ (*ctmp)->value = cpystr(tmp);
new_confline(ctmp);
(*ctmp)->flags |= CF_NOSELECT;
@@ -789,7 +770,7 @@ smime_config_init_display(struct pine *ps, CONF_S **ctmp, CONF_S **first_line)
new_confline(ctmp);
(*ctmp)->flags |= CF_NOSELECT;
- (*ctmp)->value = cpystr("---------------------------------------------------------------------------");
+ (*ctmp)->value = cpystr(tmp);
new_confline(ctmp);
(*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
@@ -867,8 +848,405 @@ smime_config_init_display(struct pine *ps, CONF_S **ctmp, CONF_S **first_line)
(*ctmp)->varmem = 8;
#endif /* APPLEKEYCHAIN */
+
+ new_confline(ctmp)->var = vtmp;
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(tmp);
+
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(_("Manage your own certificates"));
+
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(tmp);
+
+ new_confline(ctmp)->var = vtmp;
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+
+ /* manage public certificates */
+ new_confline(ctmp);
+ (*ctmp)->tool = smime_helper_tool;
+ (*ctmp)->keymenu = &config_smime_manage_certs_menu_keymenu;
+ (*ctmp)->help = h_config_smime_public_certificates;
+ (*ctmp)->value = cpystr(_("Manage Public Certificates"));
+ (*ctmp)->varmem = 9;
+
+ /* manage private keys */
+ new_confline(ctmp);
+ (*ctmp)->tool = smime_helper_tool;
+ (*ctmp)->keymenu = &config_smime_manage_certs_menu_keymenu;
+ (*ctmp)->help = h_config_smime_private_keys;
+ (*ctmp)->value = cpystr(_("Manage Private Keys"));
+ (*ctmp)->varmem = 10;
+
+ /* manage Certificate Authorities */
+ new_confline(ctmp);
+ (*ctmp)->tool = smime_helper_tool;
+ (*ctmp)->keymenu = &config_smime_manage_certs_menu_keymenu;
+ (*ctmp)->help = h_config_smime_certificate_authorities;
+ (*ctmp)->value = cpystr(_("Manage Certificate Authorities"));
+ (*ctmp)->varmem = 11;
+}
+
+void display_certificate_information(struct pine *ps, X509 *cert, char *email, WhichCerts ctype)
+{
+ STORE_S *store;
+ SCROLL_S scrollargs;
+ int cmd, offset, i;
+ int pub_cert, priv_cert, new_store;
+ long error;
+ BIO *out = NULL;
+
+ cmd = offset = pub_cert = priv_cert = 0;
+ new_store = 1;
+ ps->next_screen = SCREEN_FUN_NULL;
+ do {
+ /* MC_PRIVATE and MC_PUBLIC cancel each other,
+ * they can not be active at the same time
+ */
+ switch(cmd){
+ case MC_PRIVATE:
+ pub_cert = 0;
+ priv_cert = ++priv_cert % 2;
+ smime_certificate_info_keymenu.keys[PUBLIC_KEY].label = N_("Public Key");
+ smime_certificate_info_keymenu.keys[PRIVATE_KEY].label = priv_cert ? N_("No Priv Key") : N_("Pivate Key");
+ break;
+
+ case MC_PUBLIC:
+ priv_cert = 0;
+ pub_cert = ++pub_cert % 2;
+ smime_certificate_info_keymenu.keys[PRIVATE_KEY].label = priv_cert ? N_("No Priv Key") : N_("Pivate Key");
+ smime_certificate_info_keymenu.keys[PUBLIC_KEY].label = N_("Public Key");
+ break;
+
+ case MC_TRUST:
+ save_cert_for(email, cert, CACert);
+ renew_store();
+ break;
+
+ case MC_DELETE:
+ if (get_cert_deleted(ctype, email) != 0)
+ q_status_message(SM_ORDER, 1, 3, _("Certificate already deleted"));
+ else{
+ mark_cert_deleted(ctype, email, 1);
+ q_status_message(SM_ORDER, 1, 3, _("Certificate marked deleted"));
+ }
+ break;
+
+ case MC_UNDELETE:
+ if (get_cert_deleted(ctype, email) != 0)
+ q_status_message(SM_ORDER, 1, 3, _("Certificate not marked deleted"));
+ else{
+ mark_cert_deleted(ctype, email, 0);
+ q_status_message(SM_ORDER, 1, 3, _("Certificate will not be deleted"));
+ }
+ break;
+
+ default: break;
+ }
+
+ if((pub_cert || priv_cert)
+ && (out = print_private_key_information(email, priv_cert)) == NULL)
+ q_status_message(SM_ORDER, 1, 3, _("Problem Reading Private Certificate Information"));
+
+ if(new_store){
+ store = so_get(CharStar, NULL, EDIT_ACCESS);
+ view_writec_init(store, NULL, HEADER_ROWS(ps),
+ HEADER_ROWS(ps) + ps->ttyo->screen_rows - (HEADER_ROWS(ps)+ FOOTER_ROWS(ps)));
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,"%s", _("Certificate Information"));
+ gf_puts_uline(tmp_20k_buf, view_writec);
+ gf_puts(NEWLINE, view_writec);
+ print_separator_line(100, '-', view_writec);
+
+ output_cert_info(cert, view_writec);
+ gf_puts(NEWLINE, view_writec);
+
+ if(smime_validate_cert(cert, &error) < 0){
+ const char *errorp = X509_verify_cert_error_string(error);
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,_("Error validating certificate: %s"), errorp);
+ } else
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s", _("Certificate validated without errors"));
+
+ gf_puts_uline(tmp_20k_buf, view_writec);
+ gf_puts(NEWLINE, view_writec);
+
+ if(out != NULL){ /* print private key information */
+ unsigned char ch[2];
+
+ gf_puts(NEWLINE, view_writec);
+ ch[1] = '\0';
+ while(BIO_read(out, ch, 1) >= 1)
+ gf_puts(ch, view_writec);
+ gf_puts(NEWLINE, view_writec);
+ q_status_message1(SM_ORDER, 1, 3, _("%s information shown at bottom of certificate information"), pub_cert ? _("Public") : _("Private"));
+ BIO_free_all(out);
+ out = NULL;
+ }
+ view_writec_destroy();
+ new_store = 0;
+ }
+
+ memset(&scrollargs, 0, sizeof(SCROLL_S));
+
+ scrollargs.text.text = so_text(store);
+ scrollargs.text.src = CharStar;
+ scrollargs.text.desc = "certificate information";
+ scrollargs.body_valid = 1;
+
+ if(offset){ /* resize? preserve paging! */
+ scrollargs.start.on = Offset;
+ scrollargs.start.loc.offset = offset;
+ scrollargs.body_valid = 0;
+ offset = 0L;
+ }
+
+ scrollargs.use_indexline_color = 1;
+
+ scrollargs.bar.title = _("CERTIFICATE INFORMATION");
+ scrollargs.proc.tool = manage_certificate_info_tool;
+ scrollargs.resize_exit = 1;
+ scrollargs.help.text = h_certificate_information;
+ scrollargs.help.title = _("HELP FOR MESSAGE TEXT VIEW");
+ scrollargs.keys.what = FirstMenu;
+ scrollargs.keys.menu = &smime_certificate_info_keymenu;
+ setbitmap(scrollargs.keys.bitmap);
+ if(ctype != Public || error != X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)
+ clrbitn(TRUST_KEY, scrollargs.keys.bitmap);
+ if(ctype != Private){
+ clrbitn(PUBLIC_KEY, scrollargs.keys.bitmap);
+ clrbitn(PRIVATE_KEY, scrollargs.keys.bitmap);
+ }
+
+ cmd = scrolltool(&scrollargs);
+
+ switch(cmd){
+ case MC_RESIZE :
+ case MC_PRIVATE:
+ case MC_PUBLIC : if(scrollargs.start.on == Offset)
+ offset = scrollargs.start.loc.offset;
+ new_store = 1;
+ default: break;
+ }
+ if(new_store)
+ so_give(&store);
+ } while (cmd != MC_EXIT);
+ ps->mangled_screen = 1;
+}
+
+/*
+ * This is silly, we just need this function so that we can tell scrolltool
+ * that some commands are recognized. We use scrolltool because we do not
+ * want to rewrite output_cert_info.
+ */
+int
+manage_certificate_info_tool(int cmd, MSGNO_S *msgmap, SCROLL_S *sparms)
+{
+ int rv;
+ switch(cmd){
+ case MC_DELETE:
+ case MC_UNDELETE:
+ case MC_PRIVATE:
+ case MC_PUBLIC:
+ case MC_TRUST: rv = 1; break;
+ default: rv = 0; break;
+ }
+ return rv;
+}
+
+
+int
+manage_certs_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags)
+{
+ int rv = 0;
+ char *file, *ext;
+ char tmp[200], buf[MAXPATH], *path;
+ X509 *cert = NULL;
+ BIO *in;
+ WhichCerts ctype = (*cl)->d.s.ctype;
+ SMIME_STUFF_S *smime = ps->smime;
+
+ switch(cmd){
+ case MC_CHOICE:
+ if(PATHCERTDIR(ctype) == NULL)
+ return 0;
+
+ if((cert = get_cert_for((*cl)->value+3, ctype)) == NULL){
+ q_status_message(SM_ORDER, 1, 3, _("Problem Reading Certificate"));
+ rv = 0;
+ }
+ else{
+ display_certificate_information(ps, cert, (*cl)->value+3, ctype);
+ rv = 10 + (*cl)->varmem;
+ }
+ break;
+
+ case MC_DELETE:
+ if ((*cl)->d.s.deleted != 0)
+ q_status_message(SM_ORDER, 1, 3, _("Certificate already deleted"));
+ else{
+ (*cl)->d.s.deleted = 1;
+ rv = 10 + (*cl)->varmem; /* forces redraw */
+ mark_cert_deleted(ctype, (*cl)->value+3, 1);
+ q_status_message(SM_ORDER, 1, 3, _("Certificate marked deleted"));
+ }
+ break;
+
+ case MC_UNDELETE:
+ if ((*cl)->d.s.deleted == 0)
+ q_status_message(SM_ORDER, 1, 3, _("Certificate not marked deleted"));
+ else{
+ (*cl)->d.s.deleted = 0;
+ mark_cert_deleted(ctype, (*cl)->value+3, 0);
+ rv = 10 + (*cl)->varmem; /* forces redraw */
+ q_status_message(SM_ORDER, 1, 3, _("Certificate will not be deleted"));
+ }
+ break;
+
+ case MC_EXPUNGE:
+ { CertList *cl;
+
+ for(cl = DATACERT(ctype); cl != NULL && DELETEDCERT(cl) == 0; cl = cl->next);
+ if(cl != NULL && DELETEDCERT(cl) != 0)
+ smime_expunge_cert(ctype);
+ else
+ q_status_message(SM_ORDER, 3, 3, _("No certificates marked deleted"));
+ rv = 10; /* forces redraw */
+ break;
+ }
+ case MC_IMPORT:
+ import_certificate(ctype);
+ rv = 10; /* forces redraw */
+ break;
+
+ case MC_EXIT:
+ rv = config_exit_cmd(flags);
+ break;
+
+ default:
+ rv = -1;
+ break;
+ }
+
+ BIO_free(in);
+ X509_free(cert);
+ return rv;
+}
+
+void smime_manage_certs_init(struct pine *ps, CONF_S **ctmp, CONF_S **first_line, WhichCerts ctype, int fline)
+{
+ char tmp[200];
+ char *ext;
+ CertList *data;
+ int i;
+
+ smime_init();
+
+ data = DATACERT(ctype);
+ ext = EXTCERT(ctype);
+
+ if(data == NULL || RENEWCERT(data))
+ renew_cert_data(&data, ctype);
+
+ for(i = 0; i < sizeof(tmp) && i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp)); i++)
+ tmp[i] = '-';
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(tmp);
+
+ (*ctmp)->keymenu = &config_text_keymenu;
+
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT;
+ sprintf(tmp, _("List of %scertificates"), ctype == Public ? _("public ")
+ : (ctype == Private ? _("private ")
+ : (ctype == CACert ? _("certificate authority ") : "unknown (?) ")));
+ (*ctmp)->value = cpystr(tmp);
+
+ for(i = 0; i < sizeof(tmp) && i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp)); i++)
+ tmp[i] = '-';
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT;
+ (*ctmp)->value = cpystr(tmp);
+
+ new_confline(ctmp);
+ (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE;
+
+ if(data){
+ CertList *cl; int i;
+ PERSONAL_CERT *pc;
+
+ for(cl = data, i = 0; cl; cl = cl->next)
+ if(cl->name){
+ char *s, *t;
+
+ new_confline(ctmp);
+ (*ctmp)->d.s.ctype = ctype;
+ (*ctmp)->d.s.deleted = get_cert_deleted(ctype, cl->name);
+ (*ctmp)->tool = manage_certs_tool;
+ (*ctmp)->keymenu = &config_smime_manage_certs_work_keymenu;
+ (*ctmp)->varmem = i++;
+ (*ctmp)->help = ctype == Public ? h_config_smime_manage_public_menu
+ : (ctype == Private ? h_config_smime_manage_private_menu
+ : h_config_smime_manage_cacerts_menu);
+ snprintf(tmp, sizeof(tmp), " %s\t%s", (*ctmp)->d.s.deleted ? "D" : " ", cl->name);
+ (*ctmp)->value = cpystr(tmp);
+ for(s = (*ctmp)->value; s && (t = strstr(s, ext)) != NULL; s = t+1);
+ if(s) *(s-1) = '\0';
+ if(i == fline+1 && first_line && !*first_line)
+ *first_line = *ctmp;
+ }
+ }
+ else {
+ new_confline(ctmp);
+ (*ctmp)->d.s.ctype = ctype;
+ (*ctmp)->tool = manage_certs_tool;
+ (*ctmp)->keymenu = &config_smime_add_certs_keymenu;
+ (*ctmp)->value = cpystr(_(" \tNo certificates found, press \"RETURN\" to add one."));
+ if(first_line && !*first_line)
+ *first_line = *ctmp;
+ }
}
+void manage_certificates(struct pine *ps, WhichCerts ctype)
+{
+ OPT_SCREEN_S screen;
+ int ew, readonly_warning = 0, rv = 10, fline;
+
+ dprint((9, "manage_certificates(ps, %s)", ctype == Public ? _("Public") : (ctype == Private ? _("Private") : (ctype == CACert ? _("certificate authority") : _("unknown")))));
+ ps->next_screen = SCREEN_FUN_NULL;
+
+ do {
+ CONF_S *ctmp = NULL, *first_line = NULL;
+
+ fline = rv >= 10 ? rv - 10 : 0;
+
+ smime_init();
+
+ smime_manage_certs_init(ps, &ctmp, &first_line, ctype, fline);
+
+ if(ctmp == NULL){
+ ps->mangled_screen = 1;
+ smime_deinit();
+ return;
+ }
+
+ memset(&screen, 0, sizeof(screen));
+ screen.deferred_ro_warning = readonly_warning;
+ rv = conf_scroll_screen(ps, &screen, first_line,
+ _("MANAGE CERTIFICATES"),
+ /* TRANSLATORS: Print something1 using something2.
+ configuration is something1 */
+ _("configuration"), 0);
+ } while (rv != 0);
+
+ ps->mangled_screen = 1;
+ smime_deinit();
+}
int
smime_helper_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags)
@@ -968,6 +1346,10 @@ smime_helper_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags)
break;
#endif /* APPLEKEYCHAIN */
+ case 9: manage_certificates(ps, Public) ; break;
+ case 10: manage_certificates(ps, Private); break;
+ case 11: manage_certificates(ps, CACert) ; break;
+
default:
rv = -1;
break;
@@ -979,6 +1361,15 @@ smime_helper_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags)
rv = config_exit_cmd(flags);
break;
+ case MC_IMPORT:
+ { WhichCerts ctype;
+ /* keep this selection consistent with the codes above */
+ ctype = (*cl)->varmem == 9 ? Public
+ : ((*cl)->varmem == 10 ? Private : CACert);
+ rv = import_certificate(ctype);
+ }
+ break;
+
default:
rv = -1;
break;
diff --git a/alpine/smime.h b/alpine/smime.h
index 048a497c..d0367e0e 100644
--- a/alpine/smime.h
+++ b/alpine/smime.h
@@ -25,6 +25,7 @@
/* exported protoypes */
int smime_get_passphrase(void);
+int smime_certificate_error_ask(int error);
void smime_info_screen(struct pine *ps);
void smime_config_screen(struct pine *, int edit_exceptions);
int smime_related_var(struct pine *, struct variable *);
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 */