diff options
author | Eduardo Chappa <chappa@washington.edu> | 2016-02-28 14:12:49 -0700 |
---|---|---|
committer | Eduardo Chappa <chappa@washington.edu> | 2016-02-28 14:12:49 -0700 |
commit | 4bf825141cd564a2c5a23c55f79e04665c428641 (patch) | |
tree | 4fd033202e91d414985f4896fe63b0039adc2cf2 | |
parent | dc1062254da60a0f2bf8d52e861dbf4fb7b8cab9 (diff) | |
download | alpine-4bf825141cd564a2c5a23c55f79e04665c428641.tar.xz |
* Add the ability to change the private key and certificates used
to encrypt a password file in the SMIME setup configuration screen.
-rw-r--r-- | alpine/imap.c | 52 | ||||
-rw-r--r-- | alpine/keymenu.c | 47 | ||||
-rw-r--r-- | alpine/keymenu.h | 7 | ||||
-rw-r--r-- | alpine/smime.c | 214 | ||||
-rw-r--r-- | pith/conftype.h | 51 | ||||
-rw-r--r-- | pith/imap.c | 52 | ||||
-rw-r--r-- | pith/imap.h | 4 | ||||
-rw-r--r-- | pith/pine.hlp | 54 | ||||
-rw-r--r-- | pith/smime.c | 261 | ||||
-rw-r--r-- | pith/smime.h | 2 | ||||
-rw-r--r-- | pith/smkeys.c | 90 | ||||
-rw-r--r-- | pith/smkeys.h | 4 | ||||
-rw-r--r-- | pith/state.h | 9 |
13 files changed, 681 insertions, 166 deletions
diff --git a/alpine/imap.c b/alpine/imap.c index 20b98068..99c03e4e 100644 --- a/alpine/imap.c +++ b/alpine/imap.c @@ -115,7 +115,6 @@ int save_password = 1; #ifdef PASSFILE char xlate_in(int); char xlate_out(char); -char *passfile_name(char *, char *, size_t); int line_get(char *, size_t, char **); #endif /* PASSFILE */ @@ -1990,57 +1989,6 @@ xlate_out(char c) else return(c); } - - -char * -passfile_name(char *pinerc, char *path, size_t len) -{ - struct stat sbuf; - char *p = NULL; - int i, j; - - if(!path || !((pinerc && pinerc[0]) || ps_global->passfile)) - return(NULL); - - if(ps_global->passfile) - strncpy(path, ps_global->passfile, len-1); - else{ - if((p = last_cmpnt(pinerc)) && *(p-1) && *(p-1) != PASSFILE[0]) - for(i = 0; pinerc < p && i < len; pinerc++, i++) - path[i] = *pinerc; - else - i = 0; - - for(j = 0; (i < len) && (path[i] = PASSFILE[j]); i++, j++) - ; - - } - - path[len-1] = '\0'; - - dprint((9, "Looking for passfile \"%s\"\n", - path ? path : "?")); - -#if defined(DOS) || defined(OS2) - return((our_stat(path, &sbuf) == 0 - && ((sbuf.st_mode & S_IFMT) == S_IFREG)) - ? path : NULL); -#else - /* First, make sure it's ours and not sym-linked */ - if(our_lstat(path, &sbuf) == 0 - && ((sbuf.st_mode & S_IFMT) == S_IFREG) - && sbuf.st_uid == getuid()){ - /* if too liberal permissions, fix them */ - if((sbuf.st_mode & 077) != 0) - if(our_chmod(path, sbuf.st_mode & ~077) != 0) - return(NULL); - - return(path); - } - else - return(NULL); -#endif -} #endif /* PASSFILE */ diff --git a/alpine/keymenu.c b/alpine/keymenu.c index beb6e7ea..7c8d167e 100644 --- a/alpine/keymenu.c +++ b/alpine/keymenu.c @@ -2665,6 +2665,21 @@ struct key config_smime_add_certs_keys[] = NULL_MENU}; INST_KEY_MENU(config_smime_add_certs_keymenu, config_smime_add_certs_keys); +struct key config_smime_add_new_key[] = + {HELP_MENU, + NULL_MENU, + EXIT_SETUP_MENU, + {"I", N_("Import Key"), {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_new_key_keymenu, config_smime_add_new_key); + struct key config_smime_manage_certs_work_keys[] = {HELP_MENU, OTHER_MENU, @@ -2693,6 +2708,21 @@ struct key config_smime_manage_certs_work_keys[] = ENDKEY_MENU}; INST_KEY_MENU(config_smime_manage_certs_work_keymenu, config_smime_manage_certs_work_keys); +struct key config_smime_view_cert[] = + {HELP_MENU, + OTHER_MENU, + EXIT_SETUP_MENU, + {"V", "[" N_("View Info") "]", {MC_CHOICE,3,{'v',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_manage_view_cert_keymenu, config_smime_view_cert); + struct key smime_certificate_info_keys[] = {HELP_MENU, OTHER_MENU, @@ -2722,6 +2752,23 @@ struct key smime_certificate_info_keys[] = INST_KEY_MENU(smime_certificate_info_keymenu, smime_certificate_info_keys); +struct key config_smime_manage_password_file_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_password_file_menu_keymenu, config_smime_manage_password_file_menu_keys); + + + /* * Internal prototypes */ diff --git a/alpine/keymenu.h b/alpine/keymenu.h index b79b1fe2..b2d82412 100644 --- a/alpine/keymenu.h +++ b/alpine/keymenu.h @@ -560,6 +560,8 @@ struct key_menu { #define TRUST_KEY 3 #define PUBLIC_KEY 6 #define PRIVATE_KEY 7 +#define DELETE_CERT_KEY 4 +#define UNDELETE_CERT_KEY 5 extern struct key_menu cancel_keymenu, ab_keymenu, @@ -661,7 +663,10 @@ extern struct key_menu cancel_keymenu, smime_info_keymenu, config_smime_manage_certs_menu_keymenu, config_smime_manage_certs_work_keymenu, - smime_certificate_info_keymenu; + config_smime_manage_password_file_menu_keymenu, + smime_certificate_info_keymenu, + config_smime_add_new_key_keymenu, + config_smime_manage_view_cert_keymenu; extern struct key rev_msg_keys[]; diff --git a/alpine/smime.c b/alpine/smime.c index 4cfdeae8..3ba7d397 100644 --- a/alpine/smime.c +++ b/alpine/smime.c @@ -56,13 +56,16 @@ 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 num); -int manage_certs_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags); -int manage_certificate_info_tool(int, MSGNO_S *, SCROLL_S *); -void smime_setup_size(char **, size_t, size_t); +#ifdef PASSFILE +void manage_password_file_certificates(struct pine *); +#endif /* PASSFILE */ +void smime_manage_certs_init (struct pine *, CONF_S **, CONF_S **, WhichCerts, int); +void smime_manage_password_file_certs_init(struct pine *, CONF_S **, CONF_S **, int, int *); +void display_certificate_information(struct pine *, X509 *, char *, WhichCerts, int num); +int manage_certs_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags); +int manage_certificate_info_tool(int, MSGNO_S *, SCROLL_S *); +void smime_setup_size(char **, size_t, size_t); /* @@ -1028,6 +1031,7 @@ smime_config_init_display(struct pine *ps, CONF_S **ctmp, CONF_S **first_line) (*ctmp)->help = h_config_smime_public_certificates; (*ctmp)->value = cpystr(_("Manage Public Certificates")); (*ctmp)->varmem = 9; + (*ctmp)->d.s.ctype = Public; /* manage private keys */ new_confline(ctmp); @@ -1036,6 +1040,7 @@ smime_config_init_display(struct pine *ps, CONF_S **ctmp, CONF_S **first_line) (*ctmp)->help = h_config_smime_private_keys; (*ctmp)->value = cpystr(_("Manage Private Keys")); (*ctmp)->varmem = 10; + (*ctmp)->d.s.ctype = Private; /* manage Certificate Authorities */ new_confline(ctmp); @@ -1044,6 +1049,36 @@ smime_config_init_display(struct pine *ps, CONF_S **ctmp, CONF_S **first_line) (*ctmp)->help = h_config_smime_certificate_authorities; (*ctmp)->value = cpystr(_("Manage Certificate Authorities")); (*ctmp)->varmem = 11; + (*ctmp)->d.s.ctype = CACert; + +#ifdef PASSFILE + 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 Key and Certificate for Password File")); + + new_confline(ctmp); + (*ctmp)->flags |= CF_NOSELECT; + (*ctmp)->value = cpystr(tmp); + + new_confline(ctmp)->var = vtmp; + (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE; + + /* manage password file certificates */ + new_confline(ctmp); + (*ctmp)->tool = smime_helper_tool; + (*ctmp)->keymenu = &config_smime_manage_password_file_menu_keymenu; + (*ctmp)->help = h_config_smime_password_file_certificates; + (*ctmp)->value = cpystr(_("Manage Password File Key and Certificate")); + (*ctmp)->varmem = 12; + (*ctmp)->d.s.ctype = Password; +#endif /* PASSFILE */ (*ctmp)->next = NULL; } @@ -1198,6 +1233,10 @@ void display_certificate_information(struct pine *ps, X509 *cert, char *email, W clrbitn(PUBLIC_KEY, scrollargs.keys.bitmap); clrbitn(PRIVATE_KEY, scrollargs.keys.bitmap); } + if(ctype == Password){ + clrbitn(DELETE_CERT_KEY, scrollargs.keys.bitmap); + clrbitn(UNDELETE_CERT_KEY, scrollargs.keys.bitmap); + } cmd = scrolltool(&scrollargs); @@ -1338,6 +1377,121 @@ smime_setup_size(char **s, size_t buflen, size_t n) *s = t; } +#ifdef PASSFILE + /* state: 0 = first time, + * 1 = second or another time + */ +void +smime_manage_password_file_certs_init(struct pine *ps, CONF_S **ctmp, CONF_S **first_line, int fline, int *state) +{ + char tmp[200]; + char *ext; + CertList *cl; + int i; + void *pwdcert = NULL; /* this is our current password file */ + PERSONAL_CERT *pc; + X509_LOOKUP *lookup = NULL; + X509_STORE *store = NULL; + + if(*state == 0){ /* first time around? */ + setup_pwdcert(&pwdcert); + if(pwdcert == NULL) return; + if(ps->pwdcert == NULL) + ps->pwdcert = pwdcert; + else + free_personal_certs((PERSONAL_CERT **) &pwdcert); + (*state)++; + } + + pc = (PERSONAL_CERT *) ps_global->pwdcert; + ps->pwdcertlist = cl = smime_X509_to_cert_info(pc->cert, pc->name); + + 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; + (*ctmp)->value = cpystr(_("Manage Certificates and Keys Used to Encrypt your Password File")); + + new_confline(ctmp); + (*ctmp)->flags |= CF_NOSELECT; + (*ctmp)->value = cpystr(tmp); + + new_confline(ctmp); + (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE; + + if(cl){ + int s, e, df, dt, md5; /* sizes of certain fields */ + int nf; /* number of fields */ + char u[MAILTMPLEN], *t; + + e = MIN(strlen(cl->name), ps->ttyo->screen_cols/3); /* do not use too much screen */ + nf = 5; /* there are 5 fields */ + s = 3; /* status has fixed size */ + df = dt = 10; /* date from and date to have fixed size */ + md5 = ps->ttyo->screen_cols - s - df - dt - e - (nf - 1); + + t = u; + smime_setup_size(&t, sizeof(u), s); + smime_setup_size(&t, sizeof(u) - strlen(t), e); + smime_setup_size(&t, sizeof(u) - strlen(t), df); + *t++ = ' '; /* leave an extra space between dates */ + smime_setup_size(&t, sizeof(u) - strlen(t), dt); + *t++ = ' '; /* and another space between date and md5 sum */ + smime_setup_size(&t, sizeof(u) - strlen(t), md5); + *t = '\0'; /* tie off */ + + new_confline(ctmp); + (*ctmp)->flags |= CF_NOSELECT; + (*ctmp)->value = cpystr(_("New Public Certificate and Key:")); + + new_confline(ctmp); + (*ctmp)->d.s.ctype = Password; + (*ctmp)->help = h_config_smime_password_file_certificates; + (*ctmp)->tool = manage_certs_tool; + (*ctmp)->keymenu = &config_smime_add_new_key_keymenu; + s += 2; + for(i = 0; i < s; i++) tmp[i] = ' '; + tmp[i] = '\0'; + strncpy(tmp+s, _("Press \"RETURN\" to add new personal key"), sizeof(tmp)-s-1); + for(i = strlen(tmp); i < (ps->ttyo ? ps->ttyo->screen_cols : sizeof(tmp) - 1); i++) + tmp[i] = ' '; + tmp[i] = '\0'; + (*ctmp)->value = cpystr(tmp); + *first_line = *ctmp; + + new_confline(ctmp); + (*ctmp)->flags |= CF_NOSELECT | CF_B_LINE; + + new_confline(ctmp); + (*ctmp)->flags |= CF_NOSELECT; + (*ctmp)->value = cpystr(_("Current Public Certificate and Key:")); + + new_confline(ctmp); + (*ctmp)->d.s.ctype = Password; + (*ctmp)->d.s.deleted = 0; + (*ctmp)->help = h_config_smime_password_file_certificates; + (*ctmp)->tool = manage_certs_tool; + (*ctmp)->keymenu = &config_smime_manage_view_cert_keymenu; + (*ctmp)->varmem = 0; + (*ctmp)->help = h_config_smime_manage_public_menu; + strncpy((*ctmp)->d.s.address, cl->name, sizeof((*ctmp)->d.s.address)); + (*ctmp)->d.s.address[sizeof((*ctmp)->d.s.address) - 1] = '\0'; + snprintf(tmp, sizeof(tmp), u, + (*ctmp)->d.s.deleted ? "D" : " ", + cl->name, + DATEFROMCERT(cl), DATETOCERT(cl), MD5CERT(cl)); + (*ctmp)->value = cpystr(tmp); + + } +} +#endif /* PASSFILE */ + + void smime_manage_certs_init(struct pine *ps, CONF_S **ctmp, CONF_S **first_line, WhichCerts ctype, int fline) { char tmp[200]; @@ -1480,6 +1634,43 @@ void manage_certificates(struct pine *ps, WhichCerts ctype) smime_reinit(); } +void manage_password_file_certificates(struct pine *ps) +{ + OPT_SCREEN_S screen; + int readonly_warning = 0, rv = 10, fline, state = 0; + + dprint((9, "manage_password_file_certificates")); + ps->next_screen = SCREEN_FUN_NULL; + + do { + CONF_S *ctmp = NULL, *first_line = NULL; + + fline = rv >= 10 ? rv - 10 : 0; + + smime_init(); + + smime_manage_password_file_certs_init(ps, &ctmp, &first_line, fline, &state); + + if(ctmp == NULL){ + ps->mangled_screen = 1; + smime_reinit(); + return; + } + + memset(&screen, 0, sizeof(screen)); + screen.deferred_ro_warning = readonly_warning; + rv = conf_scroll_screen(ps, &screen, first_line, + _("MANAGE PASSWORD FILE CERTS"), + /* TRANSLATORS: Print something1 using something2. + configuration is something1 */ + _("configuration"), 0, NULL); + } while (rv != 0); + + ps->mangled_screen = 1; + smime_reinit(); +} + + int smime_helper_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags) { @@ -1582,6 +1773,10 @@ smime_helper_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned flags) case 10: manage_certificates(ps, Private); break; case 11: manage_certificates(ps, CACert) ; break; +#ifdef PASSFILE + case 12: manage_password_file_certificates(ps); break; +#endif /* PASSFILE */ + default: rv = -1; break; @@ -1594,12 +1789,7 @@ smime_helper_tool(struct pine *ps, int cmd, CONF_S **cl, unsigned 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); - } + rv = import_certificate((*cl)->d.s.ctype); break; default: diff --git a/pith/conftype.h b/pith/conftype.h index abae2b0d..3c9316fa 100644 --- a/pith/conftype.h +++ b/pith/conftype.h @@ -675,7 +675,7 @@ typedef enum {Main, Post, None} EditWhich; typedef enum {Directory, Container, Keychain, Nada} SmimeHolderType; -typedef enum {Public, Private, CACert} WhichCerts; +typedef enum {Public, Private, CACert, Password} WhichCerts; typedef struct certdata { unsigned deleted:1; /* certificate is marked deleted */ @@ -721,7 +721,6 @@ typedef struct smime_stuff { char *privatecontent; CertList *privatecertlist; CertList *backupprivatecertlist; - void *backuppersonal_certs; /* this is type (PERSONAL_CERT *) */ void *personal_certs; /* this is type (PERSONAL_CERT *) */ SmimeHolderType catype; @@ -732,25 +731,35 @@ typedef struct smime_stuff { } SMIME_STUFF_S; -#define BACKUPDATACERT(X) (((X) == Public ? ps_global->smime->backuppubliccertlist \ - : ((X) == Private ? ps_global->smime->backupprivatecertlist \ - : ps_global->smime->backupcacertlist))) - -#define DATACERT(X) (((X) == Public ? ps_global->smime->publiccertlist \ - : ((X) == Private ? ps_global->smime->privatecertlist \ - : ps_global->smime->cacertlist))) - -#define PATHCERTDIR(X) (((X) == Public ? ps_global->smime->publicpath \ - : ((X) == Private ? ps_global->smime->privatepath \ - : ((X) == CACert ? ps_global->smime->capath : NULL)))) - -#define CONTENTCERTLIST(X) (((X) == Public ? ps_global->smime->publiccontent \ - : ((X) == Private ? ps_global->smime->privatecontent \ - : ((X) == CACert ? ps_global->smime->cacontent : NULL)))) - -#define SMHOLDERTYPE(X) (((X) == Public ? ps_global->smime->publictype \ - : ((X) == Private ? ps_global->smime->privatetype \ - : ((X) == CACert ? ps_global->smime->catype : Nada)))) +#define BACKUPDATACERT(X) ((X) == Public ? ps_global->smime->backuppubliccertlist : \ + (X) == Private ? ps_global->smime->backupprivatecertlist : \ + (X) == CACert ? ps_global->smime->backupcacertlist : \ + (X) == Password ? (CertList *) ps_global->backuppassword : \ + NULL) + +#define DATACERT(X) ((X) == Public ? ps_global->smime->publiccertlist : \ + (X) == Private ? ps_global->smime->privatecertlist : \ + (X) == CACert ? ps_global->smime->cacertlist : \ + (X) == Password ? (CertList *) ps_global->pwdcertlist : \ + NULL) + +#define PATHCERTDIR(X) ((X) == Public ? ps_global->smime->publicpath : \ + (X) == Private ? ps_global->smime->privatepath : \ + (X) == CACert ? ps_global->smime->capath : \ + (X) == Password ? ps_global->pwdcertdir : \ + NULL) + +#define CONTENTCERTLIST(X) ((X) == Public ? ps_global->smime->publiccontent : \ + (X) == Private ? ps_global->smime->privatecontent : \ + (X) == CACert ? ps_global->smime->cacontent : \ + (X) == Password ? ps_global->pwdcertcontent : \ + NULL) + +#define SMHOLDERTYPE(X) ((X) == Public ? ps_global->smime->publictype : \ + (X) == Private ? ps_global->smime->privatetype : \ + (X) == CACert ? ps_global->smime->catype : \ + (X) == Password ? Directory : \ + Nada) #define EXTCERT(X) (((X) == Public ? ".crt" \ : ((X) == Private ? ".key" \ diff --git a/pith/imap.c b/pith/imap.c index da338712..e02f46ba 100644 --- a/pith/imap.c +++ b/pith/imap.c @@ -1109,3 +1109,55 @@ ps_get(size_t size) return(block); } + +#ifdef PASSFILE +char * +passfile_name(char *pinerc, char *path, size_t len) +{ + struct stat sbuf; + char *p = NULL; + int i, j; + + if(!path || !((pinerc && pinerc[0]) || ps_global->passfile)) + return(NULL); + + if(ps_global->passfile) + strncpy(path, ps_global->passfile, len-1); + else{ + if((p = last_cmpnt(pinerc)) && *(p-1) && *(p-1) != PASSFILE[0]) + for(i = 0; pinerc < p && i < len; pinerc++, i++) + path[i] = *pinerc; + else + i = 0; + + for(j = 0; (i < len) && (path[i] = PASSFILE[j]); i++, j++) + ; + + } + + path[len-1] = '\0'; + + dprint((9, "Looking for passfile \"%s\"\n", + path ? path : "?")); + +#if defined(DOS) || defined(OS2) + return((our_stat(path, &sbuf) == 0 + && ((sbuf.st_mode & S_IFMT) == S_IFREG)) + ? path : NULL); +#else + /* First, make sure it's ours and not sym-linked */ + if(our_lstat(path, &sbuf) == 0 + && ((sbuf.st_mode & S_IFMT) == S_IFREG) + && sbuf.st_uid == getuid()){ + /* if too liberal permissions, fix them */ + if((sbuf.st_mode & 077) != 0) + if(our_chmod(path, sbuf.st_mode & ~077) != 0) + return(NULL); + + return(path); + } + else + return(NULL); +#endif +} +#endif /* PASSFILE */ diff --git a/pith/imap.h b/pith/imap.h index 86a0b533..1b30e456 100644 --- a/pith/imap.h +++ b/pith/imap.h @@ -131,5 +131,9 @@ void imap_flush_passwd_cache(int); void set_read_predicted(int); void mm_login_work (NETMBX *mb,char *user,char *pwd,long trial,char *usethisprompt, char *altuserforcache); +/* this is necessary to figure out the name of the password file of the application */ +#ifdef PASSFILE +char *passfile_name(char *, char *, size_t); +#endif /* PASSFILE */ #endif /* PITH_IMAP_INCLUDED */ diff --git a/pith/pine.hlp b/pith/pine.hlp index de174d2b..2e0fd0c4 100644 --- a/pith/pine.hlp +++ b/pith/pine.hlp @@ -197,6 +197,10 @@ Additions include: in lower case, as some SMTP servers, such as those of libero.it reject messages if the boundary attribute is in uppercase. + <LI> Add the ability to change the private key and certificates used + to encrypt a password file in the SMIME setup configuration screen. + <A HREF="h_config_smime_password_file_certificates">Learn more</A> + <LI> SMIME: The ctrl-E command that gives information on the certificate is only available for messages that have a signed or encrypted part. @@ -35422,6 +35426,56 @@ import a command to this collection. <End of help on this topic> </BODY> </HTML> +====== h_config_smime_password_file_certificates ===== +<HTML> +<HEAD> +<TITLE>S/MIME: Manage Password File Certificates</TITLE> +</HEAD> +<BODY> +<H1>S/MIME: Manage Password File Certificates</H1> + +UNIX Alpine only. +<P> +This option allows you to manage the certificates that are used to +encrypt and decrypt your password file. This is useful in case you +want to change the certificates used to encrypt your password file. +<P> +In order to avoid unauthorized use of this option, you are asked to +enter the password of the current private key used to encrypt your +password file. +<P> +Once you have entered your password for the current key, you enter a +screen where you can import your new key, and see the information on your +current key. +<P> +To import a new key press "RETURN" and enter the location of +the new key. You will be asked to enter the password of the new key. If +this part of the process is successful, Alpine will search for the +certificate that matches that key. If your key is named +"your_email@address.com.key", then Alpine will look for your +certificate in the same directory in the file named +"your_email@address.com.crt", otherwise it will look for it +as part of your key (that is, it will look to see if your certificate +is in the file "your_email@address.com.key"), if all of this +fails, Alpine will ask you to enter the location of the certificate +that matches the key you unlocked. If a certificate is found, it will be +used, and in this case, the password file will be read, decrypted with the +old key and encrypted with the new key. Once this is done, the new key and +certificates are saved, and the old keys are permanently deleted. +<P> +Alpine does not create a backup of your password file, or your old keys +that will be replaced. If you need to keep old copies, you will have to do +this operation outside Alpine. +<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> +<End of help on this topic> +</BODY> +</HTML> ====== h_certificate_information ===== <HTML> <HEAD> diff --git a/pith/smime.c b/pith/smime.c index 6f9efc7f..d32a97c9 100644 --- a/pith/smime.c +++ b/pith/smime.c @@ -42,6 +42,9 @@ static char rcsid[] = "$Id: smime.c 1176 2008-09-29 21:16:42Z hubert@u.washingto #include "../pith/tempfile.h" #include "../pith/readfile.h" #include "../pith/remote.h" +#ifdef PASSFILE +#include "../pith/imap.h" +#endif /* PASSFILE */ #include <openssl/buffer.h> #include <openssl/x509v3.h> @@ -70,10 +73,8 @@ static void setup_storage_locations(void); static int copy_container_to_dir(WhichCerts which); static int do_fiddle_smime_message(BODY *b, long msgno, char *section); void setup_privatekey_storage(void); -int smime_path(char *rpath, char *fpath, size_t len); int smime_extract_and_save_cert(PKCS7 *p7, int check_cert); int same_cert(X509 *, X509 *); -CertList * certlist_from_personal_certs(PERSONAL_CERT *pc); #ifdef PASSFILE int load_key_and_cert(char *pathkeydir, char *pathcertdir, char **keyfile, char **certfile, EVP_PKEY **pkey, X509 **pcert); #endif /* PASSFILE */ @@ -245,14 +246,18 @@ setup_pwdcert(void **pwdcert) return; } + + if(ps_global->pwdcertdir == NULL) /* save the result of pwdcertdir */ + ps_global->pwdcertdir = cpystr(pathdir); + if(certfile && keyfile){ pc = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT)); memset((void *)pc, 0, sizeof(PERSONAL_CERT)); pc->name = keyfile; pc->key = pkey; pc->cert = pcert; + pc->cname = certfile; *pwdcert = (void *) pc; - fs_give((void **)&certfile); was_here = 0; return; } @@ -596,8 +601,10 @@ load_pkey_with_prompt(char *fpath, char *text, char *prompt, int *ret) return pkey; } - - +/* This is a tool for conf_screen, The return value must be zero when + * nothing changed, so if there is a failure in the import return 0 + * and return 1 when we succeeded + */ int import_certificate(WhichCerts ctype) { @@ -607,18 +614,221 @@ import_certificate(WhichCerts ctype) if(pith_smime_import_certificate == NULL){ q_status_message(SM_ORDER, 0, 2, _("import of certificates not implemented yet!")); - return -1; + return 0; } - smime_init(); - r = (*pith_smime_import_certificate)(filename, full_filename, sizeof(filename) - 20); + if(r < 0) + return 0; + + /* we are trying to import a new key for the password file. First we ask for the + * private key. Once this is loaded, we make a reasonable attempt to find the + * public key in the same directory as the key was loaded from. We do this by + * looking for a file with the correct public certificate name, then we look + * in the same private key, and if not, we ask the user for its location. If all + * of this works, we import the key and public to the password directory. + */ + if(ctype == Password){ + char PrivateKeyPath[MAXPATH+1], PublicCertPath[MAXPATH+1], s[MAXPATH+1]; + char full_name_key[MAXPATH+1], full_name_cert[MAXPATH+1]; + char *use_this_file; + char prompt[500]; + EVP_PKEY *key = NULL; + + rc = 1; /* assume success :) */ + if(strlen(filename) > 4){ + strncpy(s, filename, sizeof(s)); + s[sizeof(s)-1] = '\0'; + if(!strcmp(s + strlen(s) - strlen(EXTCERT(Private)), EXTCERT(Private))) + s[strlen(s) - strlen(EXTCERT(Private))] = '\0'; + else + rc = 0; + } else rc = 0; + + if(rc == 0){ + q_status_message(SM_ORDER, 1, 3, _("Error in key name. Check file extension")); + return 0; + } + + snprintf(prompt, sizeof(prompt), _("Enter passphrase for <%s>: "), filename); + prompt[sizeof(prompt)-1] = '\0'; + if((key = load_pkey_with_prompt(full_filename, NULL, prompt, NULL)) != NULL){ + BIO *ins = NULL; + X509 *cert = NULL; + + strncpy(full_name_key, full_filename, sizeof(full_filename)); + full_name_key[sizeof(full_name_key)-1] = '\0'; + + build_path(buf, PATHCERTDIR(ctype), s, sizeof(buf)); + + strncpy(PrivateKeyPath, buf, sizeof(PrivateKeyPath)); + PrivateKeyPath[sizeof(PrivateKeyPath)-1] = '\0'; + if(strlen(PrivateKeyPath) + 4 < sizeof(PrivateKeyPath)){ + strncat(PrivateKeyPath, EXTCERT(Private), 4); + PrivateKeyPath[sizeof(PrivateKeyPath)-1] = '\0'; + } + + /* remove .key extension and replace it with .crt extension */ + strncpy(full_name_cert, full_name_key, sizeof(full_name_key)); + full_name_cert[sizeof(full_name_cert)-1] = '\0'; + full_name_cert[strlen(full_name_cert) - strlen(EXTCERT(Private))] = '\0'; + strncat(full_name_cert, EXTCERT(Public), 4); + full_name_cert[sizeof(full_name_cert)-1] = '\0'; + + + /* set up path to location where we will save public cert */ + strncpy(PublicCertPath, buf, sizeof(PublicCertPath)); + PublicCertPath[sizeof(PublicCertPath)-1] = '\0'; + if(strlen(PublicCertPath) + 4 < sizeof(PublicCertPath)){ + strncat(PublicCertPath, EXTCERT(Public), 4); + PublicCertPath[sizeof(PublicCertPath)-1] = '\0'; + } + + /* attempt #1 to guess public cert name, use .crt extension */ + if((ins = BIO_new_file(full_name_cert, "r")) != NULL){ + if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){ + use_this_file = &full_name_cert[0]; + } + } + else{ + /* attempt #2 to guess public cert name: user the original key */ + if((ins = BIO_new_file(full_name_key, "r")) != NULL){ + if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL){ + use_this_file = &full_name_key[0]; + } + } + else { + int done = 0; + /* attempt #3, ask the user */ + do { + r = (*pith_smime_import_certificate)(filename, use_this_file, sizeof(filename) - 20); + if(r < 0){ + if(ins != NULL) BIO_free(ins); + if(cert != NULL) X509_free(cert); + return 0; + } + if((ins = BIO_new_file(use_this_file, "r")) != NULL){ + if((cert = PEM_read_bio_X509(ins, NULL, NULL, NULL)) != NULL) + done++; + else + q_status_message(SM_ORDER, 1, 3, _("Error parsing certificate")); + } + else + q_status_message(SM_ORDER, 1, 3, _("Error reading certificate")); + } while (done == 0); + } + } + if(ins != NULL){ + if(cert != NULL){ /* check that certificate matches key */ + if(!X509_check_private_key(cert, key)){ + rc = 0; + q_status_message(SM_ORDER, 1, 3, _("Certificate does not match key")); + } + else + rc = 1; /* Success! */ + } + else + q_status_message(SM_ORDER, 1, 3, _("Error in certificate file (not a certificate?)")); + } + if(rc == 1){ /* if everything has been successful, + * copy the files to their final destination */ + if(our_copy(PrivateKeyPath, full_filename) == 0){ /* <-- save the private key */ + q_status_message(SM_ORDER, 1, 3, _("Private key saved")); + if(our_copy(PublicCertPath, use_this_file) == 0){ + char tmp[MAILTMPLEN]; + FILE *fp; + + if(!passfile_name(ps_global->pinerc, tmp, sizeof(tmp)) + || !(fp = our_fopen(tmp, "rb"))){ + q_status_message(SM_ORDER, 1, 3, _("Error reading password file!")); + rc = 0; + } + else { + char tmp2[MAILTMPLEN]; + int encrypted = 0; + char *text; + PERSONAL_CERT *pwdcert, *pc; + + pwdcert = (PERSONAL_CERT *) ps_global->pwdcert; + if(pwdcert == NULL) + setup_pwdcert((void **)&pwdcert); + + tmp2[0] = '\0'; + fgets(tmp2, sizeof(tmp2), fp); + fclose(fp); + if(strcmp(tmp2, "-----BEGIN PKCS7-----\n")){ + if(encrypt_file((char *)tmp, NULL, pwdcert)) + encrypted++; + } + else + encrypted++; + + if(encrypted){ + text = decrypt_file((char *)tmp, NULL, pwdcert); + if(text != NULL){ + pc = fs_get(sizeof(PERSONAL_CERT)); + memset((void *)pc, 0, sizeof(PERSONAL_CERT)); + filename[strlen(filename)-strlen(EXTCERT(Private))] = '\0'; + pc->name = cpystr(filename); + snprintf(buf, sizeof(buf), "%s%s", filename, EXTCERT(Public)); + buf[sizeof(buf)-1] = '\0'; + pc->cname = cpystr(buf); + pc->key = key; + pc->cert = cert; + + if(encrypt_file((char *)tmp, text, pc)){ /* we did it! */ + build_path(buf, PATHCERTDIR(ctype), pwdcert->name, sizeof(buf)); + strncat(buf, EXTCERT(Private), 4); + buf[sizeof(buf)-1] = '\0'; + if(unlink(buf) < 0) + q_status_message(SM_ORDER, 1, 3, _("Failed to remove old key")); + build_path(buf, PATHCERTDIR(ctype), pwdcert->cname, sizeof(buf)); + if(unlink(buf) < 0) + q_status_message(SM_ORDER, 1, 3, _("Failed to remove old certificate")); + free_personal_certs((PERSONAL_CERT **)&ps_global->pwdcert); + ps_global->pwdcert = pc; + rc = 1; + q_status_message(SM_ORDER, 1, 3, _("Password file reencrypted")); + } else { + q_status_message(SM_ORDER, 1, 3, _("Failed to reencrypt password file")); + rc = 0; + } + } else { + q_status_message(SM_ORDER, 1, 3, _("Error decrypting Password file")); + } + } else { + q_status_message(SM_ORDER, 1, 3, _("Password file not encrypted and coulr not encrypt")); + rc = 0; + } + } + } + else{ + q_status_message(SM_ORDER, 1, 3, _("Error saving public certificate")); + if(our_unlink(PrivateKeyPath) < 0) + q_status_message(SM_ORDER, 1, 3, _("Error while cleaning private key")); + rc = 0; + } + } + else{ + rc = 0; + q_status_message(SM_ORDER, 1, 3, _("Error saving private key")); + } + if(ins != NULL) BIO_free(ins); + if(rc == 0 && cert != NULL) X509_free(cert); + } + } else { + rc = 0; + q_status_message(SM_ORDER, 1, 3, _("Error unlocking private key")); + } + + return rc; + } + + smime_init(); ps_global->mangled_screen = 1; - if(r < 0) - return r; - else if (ctype == Private){ + if (ctype == Private){ char prompt[500], *s, *t; EVP_PKEY *key = NULL; @@ -651,7 +861,7 @@ import_certificate(WhichCerts ctype) ps_global->smime->publiccertlist->data.renew = 1; } else - q_status_message(SM_ORDER, 1, 3, _("Problem unlocking key (not a certificate and/or wrong password)")); + q_status_message(SM_ORDER, 1, 3, _("Problem unlocking key (not a certificate or wrong password)")); } else if (ctype == CACert){ BIO *ins; X509 *cert; @@ -713,7 +923,7 @@ import_certificate(WhichCerts ctype) } } if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1; - return 0; + return 1; } /* itype: information type to add: 0 - public, 1 - private. @@ -920,23 +1130,12 @@ certlist_from_personal_certs(PERSONAL_CERT *pc) { CertList *cl; X509 *x; - char buf[MAXPATH]; if(pc == NULL) return NULL; - - cl = fs_get(sizeof(CertList)); - memset((void *)cl, 0, sizeof(CertList)); - cl->name = cpystr(pc->name); - x = get_cert_for(pc->name, Public, 1); - if(x){ - if(x->cert_info){ - cl->data.date_from = smime_get_date(x->cert_info->validity->notBefore); - cl->data.date_to = smime_get_date(x->cert_info->validity->notAfter); - get_fingerprint(x, EVP_md5(), buf, sizeof(buf), NULL); - cl->data.md5 = cpystr(buf); - cl->cn = smime_get_cn(x->cert_info->subject); - } + + if((x = get_cert_for(pc->name, Public, 1)) != NULL){ + cl = smime_X509_to_cert_info(x, pc->name); X509_free(x); } cl->next = certlist_from_personal_certs(pc->next); @@ -3641,14 +3840,6 @@ free_smime_struct(SMIME_STUFF_S **smime) (*smime)->personal_certs = NULL; } - if((*smime)->backuppersonal_certs){ - PERSONAL_CERT *pc; - - pc = (PERSONAL_CERT *) (*smime)->backuppersonal_certs; - free_personal_certs(&pc); - (*smime)->backuppersonal_certs = NULL; - } - if((*smime)->privatecontent) fs_give((void **) &(*smime)->privatecontent); diff --git a/pith/smime.h b/pith/smime.h index 636d8805..01e4cdc0 100644 --- a/pith/smime.h +++ b/pith/smime.h @@ -85,6 +85,8 @@ void *create_smime_sparep(SpareType stype, void *s); SpareType get_smime_sparep_type(void *s); void *get_smime_sparep_data(void *s); STACK_OF(X509) *get_chain_for_cert(X509 *cert, int *error, int *level); +CertList *certlist_from_personal_certs(PERSONAL_CERT *pc); +int smime_path(char *rpath, char *fpath, size_t len); #endif /* PITH_SMIME_INCLUDED */ #endif /* SMIME */ diff --git a/pith/smkeys.c b/pith/smkeys.c index 18509622..415bc190 100644 --- a/pith/smkeys.c +++ b/pith/smkeys.c @@ -49,6 +49,30 @@ static char *emailstrclean(char *string); static int mem_add_extra_cacerts(char *contents, X509_LOOKUP *lookup); int compare_certs_by_name(const void *data1, const void *data2); + +CertList * +smime_X509_to_cert_info(X509 *x, char *name) +{ + CertList *cert; + char buf[MAXPATH+1]; + + if(x == NULL) return NULL; + + cert = fs_get(sizeof(CertList)); + memset((void *)cert, 0, sizeof(CertList)); + cert->x509_cert = x; + cert->name = name ? cpystr(name) : NULL; + if(x && x->cert_info){ + cert->data.date_from = smime_get_date(x->cert_info->validity->notBefore); + cert->data.date_to = smime_get_date(x->cert_info->validity->notAfter); + cert->cn = smime_get_cn(x->cert_info->subject); + } + get_fingerprint(x, EVP_md5(), buf, sizeof(buf), NULL); + cert->data.md5 = cpystr(buf); + + return cert; +} + #define SMIME_BACKUP_DIR ".backup" #define MAX_TRY_BACKUP 100 @@ -199,25 +223,11 @@ setup_certs_backup_by_type(WhichCerts ctype) if((in = BIO_new_file(buf2, "r"))!=0){ x = PEM_read_bio_X509(in, NULL, NULL, NULL); if(x && x->cert_info){ /* for now copy this information */ - X509_NAME_ENTRY *e; - - cert = fs_get(sizeof(CertList)); - memset((void *)cert, 0, sizeof(CertList)); - cert->x509_cert = x; - cert->data.date_from = smime_get_date(x->cert_info->validity->notBefore); - cert->data.date_to = smime_get_date(x->cert_info->validity->notAfter); - get_fingerprint(x, EVP_md5(), buf, sizeof(buf), NULL); - cert->data.md5 = cpystr(buf); - cert->name = cpystr(df->d_name); - cert->cn = smime_get_cn(x->cert_info->subject); + cert = smime_X509_to_cert_info(x, df->d_name); /* we will use the cert->data.md5 variable to find a backup certificate, not the name */ - if(data == NULL) - data = cert; - else{ - for (cl2 = data; cl2 && cl2->next; cl2 = cl2->next); - cl2->next = cert; - } + cert->next = data; + data = cert; } BIO_free(in); } @@ -872,6 +882,25 @@ get_cert_for(char *email, WhichCerts ctype, int tolower) X509 *cert = NULL; BIO *in; + if(ctype == Password){ + build_path(certfilename, PATHCERTDIR(ctype), email, sizeof(certfilename)); + strncat(certfilename, EXTCERT(Public), sizeof(certfilename)-1-strlen(certfilename)); + certfilename[sizeof(certfilename)-1] = 0; + + if((in = BIO_new_file(certfilename, "r"))!=0){ + + cert = PEM_read_bio_X509(in, NULL, NULL, NULL); + + if(cert){ + /* could check email addr in cert matches */ + } + + BIO_free(in); + } + + return cert; + } + if(!ps_global->smime) return cert; @@ -1274,33 +1303,14 @@ certlist_to_file(char *filename, CertList *certlist) void add_to_end_of_certlist(CertList **cl, char *name, X509 *cert) { - CertList *new, *clp; - char buf[MAILTMPLEN]; + CertList *new; if(!cl) return; - new = (CertList *) fs_get(sizeof(*new)); - memset((void *) new, 0, sizeof(*new)); - new->x509_cert = cert; - new->name = name ? cpystr(name) : NULL; - if(cert && cert->cert_info){ - new->data.date_from = smime_get_date(cert->cert_info->validity->notBefore); - new->data.date_to = smime_get_date(cert->cert_info->validity->notAfter); - get_fingerprint(cert, EVP_md5(), buf, sizeof(buf), NULL); - new->data.md5 = cpystr(buf); - new->cn = smime_get_cn(cert->cert_info->subject); - } - - if(!*cl){ - *cl = new; - } - else{ - for(clp = (*cl); clp->next; clp = clp->next) - ; - - clp->next = new; - } + new = smime_X509_to_cert_info(cert, name); + new->next = *cl; + *cl = new; } diff --git a/pith/smkeys.h b/pith/smkeys.h index 0a2b0065..8c23d905 100644 --- a/pith/smkeys.h +++ b/pith/smkeys.h @@ -38,7 +38,8 @@ typedef struct personal_cert { X509 *cert; EVP_PKEY *key; - char *name; + char *name; /* name of key */ + char *cname; /* name of cert */ char *keytext; struct personal_cert *next; } PERSONAL_CERT; @@ -67,6 +68,7 @@ char *smime_get_date(ASN1_GENERALIZEDTIME *tm); void resort_certificates(CertList **data, WhichCerts ctype); int setup_certs_backup_by_type(WhichCerts ctype); char *smime_get_cn(X509_NAME *); +CertList *smime_X509_to_cert_info(X509 *, char *); #endif /* PITH_SMKEYS_INCLUDED */ diff --git a/pith/state.h b/pith/state.h index 481d8b41..52628cf0 100644 --- a/pith/state.h +++ b/pith/state.h @@ -272,9 +272,6 @@ struct pine { #endif #ifdef PASSFILE *passfile, -#ifdef SMIME - *pwdcertdir, -#endif /* SMIME inside PASSFILE */ #endif /* PASSFILE */ *pinerc, /* Location of user's pinerc */ *exceptions, /* Location of user's exceptions */ @@ -359,7 +356,11 @@ struct pine { char *smimedir; SMIME_STUFF_S *smime; #ifdef PASSFILE - void *pwdcert; /* this is of type PERSONAL_CERT */ + char *pwdcertdir; /* path to location of certificates for password file */ + char *pwdcertcontent; /* No comment yet */ + void *pwdcert; /* this is of type PERSONAL_CERT */ + void *backuppassword; /* this is of type CertList */ + void *pwdcertlist; /* this is of type CertList */ #endif /* PASSFILE inside SMIME */ #endif /* SMIME */ |