diff options
Diffstat (limited to 'pith')
-rw-r--r-- | pith/charconv/filesys.c | 1 | ||||
-rw-r--r-- | pith/conftype.h | 3 | ||||
-rw-r--r-- | pith/options.h | 7 | ||||
-rw-r--r-- | pith/pine.hlp | 44 | ||||
-rw-r--r-- | pith/readfile.c | 28 | ||||
-rw-r--r-- | pith/readfile.h | 2 | ||||
-rw-r--r-- | pith/smime.c | 430 | ||||
-rw-r--r-- | pith/smime.h | 12 | ||||
-rw-r--r-- | pith/state.c | 4 | ||||
-rw-r--r-- | pith/state.h | 3 |
10 files changed, 412 insertions, 122 deletions
diff --git a/pith/charconv/filesys.c b/pith/charconv/filesys.c index 3be45a5f..63ac7b13 100644 --- a/pith/charconv/filesys.c +++ b/pith/charconv/filesys.c @@ -663,7 +663,6 @@ our_getenv(char *env_variable) #endif /* !_WINDOWS */ } - int our_access(char *path, int mode) { diff --git a/pith/conftype.h b/pith/conftype.h index 38097f9e..11fe18f7 100644 --- a/pith/conftype.h +++ b/pith/conftype.h @@ -693,6 +693,9 @@ typedef struct smime_stuff { unsigned already_auto_asked:1; /* asked for passphrase automatically, not again */ volatile char passphrase[100]; /* storage for the entered passphrase */ char **passphrase_emailaddr; /* pointer to allocated storage */ +#ifdef PASSFILE + void *pwdcert; /* this has type (PERSONAL_CERT *) */ +#endif /* PASSFILE */ /* * If we are using the Container type it is easiest if we diff --git a/pith/options.h b/pith/options.h index c452b97b..4eadc9b7 100644 --- a/pith/options.h +++ b/pith/options.h @@ -232,6 +232,11 @@ 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); +extern int (*pith_smime_enter_password)(char *, char *, size_t); + +/* + * used to ask for data to users + */ +extern int (*pith_opt_get_data_prompt)(char *, char *, size_t); #endif /* PITH_OPTIONS_INCLUDED */ diff --git a/pith/pine.hlp b/pith/pine.hlp index af3d8207..59ab5574 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 48 2014-03-09 14:26:45 +Alpine Commit 52 2014-04-08 23:57:42 ============= h_news ================= <HTML> <HEAD> @@ -191,6 +191,8 @@ Additions include: HREF="h_index_cmd_expunge">help</A> for the expunge command. <LI> Pico: New subcommand of the search command, allows to reverse the direction of search. + <LI> If a password file is defined, and S/MIME is enabled, the key that is + used to encrypt the password file is saved under the name password_file_key. <LI> Add /tls1, /tls1_1, /tls1_2 and /dtls1 to the definition of a server to use different ways to connect using ssl, for example {server.com/tls1} will attempt to connect to @@ -248,7 +250,10 @@ Bugs that have been addressed include: <LI> S/MIME: Forwarding messages with multipart content-type failed to be signed with "Error writing pipe" 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> S/MIME: Certificates are lost when using a pinerc file outside of the + home directory. + <LI> S/MIME: accessing the S/MIME configuration screen would deinitialize + SMIME making it not possible to sign or encrypt messages. <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. @@ -649,7 +654,7 @@ Changes included: Details on changes in previous (prerelease) versions of Alpine may be found at the following URL: <P> -<CENTER><SAMP><A HREF="http://www.washington.edu/alpine/changes.html">http://www.washington.edu/alpine/changes.html</A></SAMP></CENTER> +<CENTER><SAMP><A HREF="http://patches.freeiz.com/alpine/release/">http://patches.freeiz.com/alpine/release/</A></SAMP></CENTER> <P> <HR WIDTH="75%"><P> @@ -685,27 +690,25 @@ General Alpine configuration information can be found <A HREF="h_news_config">here</A>. <P> This is revision (<!--#echo var="ALPINE_REVISION"-->) of the Alpine software. -Alpine mailbox and <A HREF="http://www.washington.edu/imap/">IMAP</A> server +Alpine mailbox and <A HREF="https://github.com/jonabbey/panda-imap/">IMAP</A> server access is provided by the IMAP Toolkit Environment (c-client library) version <!--#echo var="C_CLIENT_VERSION"-->. <P> Alpine was developed until 2009 by the Office of Computing & Communications at the University of Washington in Seattle. Since then, the effort of developing Alpine has been continued by -a community of volunteers who dedicate their time to make a good software -even better! -software +a community of volunteers who make a good software even better! <P> Alpine Copyright 2013-2014 Eduardo Chappa, <BR> Copyright 2006-2008 University of Washington. <P> -Additional legal notices can be found <A HREF="h_news_legal">here</A> -or at the web URL: +Additional legal notices can be found <A HREF="h_news_legal">here</A>, +or instead you can find the Apache License, version 2.0 at the web URL: <P> -<CENTER><A HREF="http://www.washington.edu/alpine/overview/legal.html">http://www.washington.edu/alpine/overview/legal</A></CENTER> +<CENTER><A HREF="http://www.apache.org/licenses/LICENSE-2.0">http://www.apache.org/licenses/LICENSE-2.0</A></CENTER> <P> <End of Release Notes> @@ -1363,6 +1366,11 @@ listing of changes in Alpine <!--#echo var="ALPINE_VERSION"--> a change in the way an aspect of Alpine works. There, you will also find notes on Alpine configuration.<P> + <LI> The main site for Alpine contains information on configuring and solving problems + with Alpine, it can be found at + +<CENTER><A HREF="http://patches.freeiz.com/alpine/">http://patches.freeiz.com/alpine/</A></CENTER> + <LI> The Alpine Information Center (maintained by the University of Washington) World Wide Web site contains, among other things <UL> @@ -20017,6 +20025,18 @@ example. <P> </DD> +<DT>Loser</DT> +<DD>This option makes sense only for IMAP servers that do not perform +a SEARCH command correctly. If your filtering rules +fail to filter some messages, that should have been filtered, then this +option will make Alpine download all data necessary to perform that search. +There is a performance penalty when using this option. Downloading the +data to perfom the search will take longer than requesting the IMAP +server to perform the filtering, but the filtering will be done correctly. +<P> +</DD> + + <DT>Service</DT> <DD>This parameter requires an associated value. The default value is "IMAP" which indicates communication with the server based @@ -29315,11 +29335,11 @@ When this feature is set (the default) Alpine will select possible web hostnames from the displayed text and display them in boldface for selection. This can be useful when you receive messages referencing World Wide Web sites without the use of complete URLs; for example, specifying only -"www.washington.edu/alpine/" (which will <B>not</B> become a +"www.patches.freeiz.com/alpine/" (which will <B>not</B> become a selectable item by setting <A HREF="h_config_enable_view_url">"<!--#echo var="FEAT_enable-msg-view-urls"-->"</A>) rather than explicitly -"http://www.washington.edu/alpine/". +"http://www.patches.freeiz.com/alpine/". <P> The first available hostname is displayed in inverse. This is the "selected" hostname. Pressing RETURN will cause Alpine to display diff --git a/pith/readfile.c b/pith/readfile.c index 4fc9fa3e..cae03fc1 100644 --- a/pith/readfile.c +++ b/pith/readfile.c @@ -69,3 +69,31 @@ read_file(char *filename, int so_get_flags) return(return_text); } + +/* our copy, to_file and from_file must be full paths. from_file + * must exist. + */ +int +our_copy(char *to_file, char *from_file) +{ + STORE_S *in_cert, *out_cert; + unsigned char c; + + in_cert = so_get(FileStar, from_file, READ_ACCESS | READ_FROM_LOCALE); + if (in_cert == NULL) + return -1; + + out_cert = so_get(FileStar, to_file, WRITE_ACCESS | WRITE_TO_LOCALE); + if (out_cert == NULL){ + so_give(&in_cert); + return -1; + } + + while(so_readc(&c, in_cert) > 0) + so_writec(c, out_cert); + + so_give(&in_cert); + so_give(&out_cert); + + return 0; +} diff --git a/pith/readfile.h b/pith/readfile.h index 485144be..4be766f1 100644 --- a/pith/readfile.h +++ b/pith/readfile.h @@ -30,6 +30,6 @@ * Exported Prototypes */ char *read_file(char *, int); - +int our_copy(char *, char *); #endif /* PITH_READFILE_INCLUDED */ diff --git a/pith/smime.c b/pith/smime.c index e691ae68..05d41e7a 100644 --- a/pith/smime.c +++ b/pith/smime.c @@ -72,10 +72,14 @@ 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); +#ifdef PASSFILE +void load_key_and_cert(char *pathdir, char **keyfile, char **certfile, EVP_PKEY **pkey, X509 **pcert); +#endif /* PASSFILE */ 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); +int (*pith_smime_enter_password)(char *prompt, char *, size_t); +int (*pith_opt_get_data_prompt)(char *prompt, char *, size_t); static X509_STORE *s_cert_store; @@ -84,33 +88,277 @@ static int seeded = 0; static int egdsocket = 0; +#ifdef PASSFILE +void +load_key_and_cert(char *pathdir, char **keyfile, + char **certfile, EVP_PKEY **pkey, X509 **pcert) +{ + BIO *in; + char buf[MAXPATH+1], pathkey[MAXPATH+1], prompt[MAILTMPLEN], pass[MAILTMPLEN]; + DIR *dirp; + struct dirent *d; + int rc; + + if(keyfile) *keyfile = NULL; + if(certfile) *certfile = NULL; + if(pkey) *pkey = NULL; + if(pcert) *pcert = NULL; + + if(pathdir == NULL) return; + + if((dirp = opendir(pathdir)) != NULL){ + while((d=readdir(dirp)) != NULL){ + size_t ll; + + if((ll=strlen(d->d_name)) && ll > 4){ + if(keyfile && !strcmp(d->d_name+ll-4, ".key") && pkey == NULL){ + strncpy(buf, d->d_name, sizeof(buf)-1); + buf[sizeof(buf)-1] = '\0'; + build_path(pathkey, pathdir, buf, sizeof(pathkey)); + buf[strlen(buf)-4] = '\0'; + if((in = BIO_new_file(pathkey, "r")) != NULL){ + + snprintf(prompt, sizeof(prompt), + _("Enter password of key <%s> to unlock password file: "), buf); + + do { + rc = 0; + pass[0] = '\0'; + if(pith_smime_enter_password) + rc = (*pith_smime_enter_password)((char *)prompt, (char *)pass, sizeof(pass)); + } while (rc!=0 && rc!=1 && rc>0); + + if((*pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, pass)) != NULL){ + *keyfile = cpystr(buf); + } + else + q_status_message1(SM_ORDER, 0, 2, + _("loading private key <%s> failed. Continuing..."), buf); + BIO_free(in); + } + } + + if(certfile && !strcmp(d->d_name+ll-4, ".crt") && pcert == NULL){ + strncpy(buf, d->d_name, sizeof(buf)-1); + buf[sizeof(buf)-1] = '\0'; + build_path(pathkey, pathdir, buf, sizeof(pathkey)); + if((in = BIO_new_file(pathkey, "r")) != NULL){ + if((*pcert = PEM_read_bio_X509(in, NULL, NULL, NULL)) != NULL) + *certfile = cpystr(buf); + else{ + q_status_message1(SM_ORDER, 0, 2, + _("loading public certificate %s failed. Continuing..."), buf); + } + BIO_free(in); + } + } + } + } + } +} + + +/* setup a key and certificate to encrypt and decrypt a password file. + * These files will be saved in the .alpine-smime/.pwd directory, but its + * location can be setup in the command line with the -pwdcertdir option. + * Here are the rules: + * + * Check if the .alpine-smime/.pwd (or -pwdcertdir directory) exists, + * if not create it. If we are successful, move to the next step + * + * - If the user has a key/cert pair, setup is successful; + * - if the used does not have a key/cert pair, or one cannot be found + * create one, setup is successful; (TODO: implement this) + * - in any other case, setup is not successful. + * + * If setup is successful, setup ps_global->smime->pwdcert. + * If any of this fails, ps_global->smime->pwdcert will be null. + * Ok, that should do it. + */ +void +setup_pwdcert(void) +{ + int setup_dir = 0; /* make it non zero if we know which dir to use */ + int rc,i,j,k; + struct stat sbuf; + char pathdir[MAXPATH+1], pathkey[MAXPATH+1], fpath[MAXPATH+1], pass[200]; + char prompt[MAILTMPLEN]; + EVP_PKEY *pkey = NULL; + X509 *pcert = NULL; + BIO *in; + PERSONAL_CERT *pc, *pc2 = NULL; + + smime_init(); + + if(ps_global->pwdcertdir){ + if(our_stat(ps_global->pwdcertdir, &sbuf) == 0 + && ((sbuf.st_mode & S_IFMT) == S_IFDIR)){ + setup_dir++; + strncpy(pathdir, ps_global->pwdcertdir, sizeof(pathdir)); + pathdir[sizeof(pathdir)-1] = '\0'; + } + } else { + smime_path(DF_PASSWORD_DIR, pathdir, sizeof(pathdir)); + if(our_stat(pathdir, &sbuf) == 0){ + if((sbuf.st_mode & S_IFMT) == S_IFDIR) + setup_dir++; + } else { + if(can_access(pathdir, ACCESS_EXISTS) != 0 + && our_mkpath(pathdir, 0700) == 0) + setup_dir++; + } + } + + if(setup_dir == 0) + return; + + /* reuse setup dir to mean setup_key */ + setup_dir = 0; + + if(ps_global->smime->personal_certs){ + pc = (PERSONAL_CERT *) ps_global->smime->personal_certs; + if(ps_global->smime->privatetype == Directory){ + build_path(pathkey, ps_global->smime->privatepath, pc->name, sizeof(pathkey)); + strncat(pathkey, ".key", 4); + pathkey[sizeof(pathkey)-1] = '\0'; + if((in = BIO_new_file(pathkey, "r")) != NULL){ + snprintf(prompt, sizeof(prompt), + _("Enter password of key <%s> to unlock password file: "), pc->name); + + do { + rc = 0; + pass[0] = '\0'; + if(pith_smime_enter_password) + rc = (*pith_smime_enter_password)((char *)prompt, (char *)pass, sizeof(pass)); + } while (rc!=0 && rc!=1 && rc>0); + + if((pkey = PEM_read_bio_PrivateKey(in, NULL, NULL, pass)) != NULL){ + pc2 = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT)); + memset((void *)pc2, 0, sizeof(PERSONAL_CERT)); + pc2->name = cpystr(pc->name); + pc2->key = pkey; + pc2->cert = X509_dup(pc->cert); + build_path(fpath, pathdir, pc->name, sizeof(fpath)); + strncat(fpath, ".key", 4); + fpath[sizeof(fpath)-1] = '\0'; + if(our_stat(fpath, &sbuf) == 0){ + if((sbuf.st_mode & S_IFMT) == S_IFREG) + setup_dir++; + } + else if(our_copy(fpath, pathkey) == 0) + setup_dir++; + + if(setup_dir){ + setup_dir = 0; + + build_path(pathkey, ps_global->smime->publicpath, pc->name, sizeof(pathkey)); + strncat(pathkey, ".crt", 4); + pathkey[sizeof(pathkey)-1] = '\0'; + + build_path(fpath, pathdir, pc->name, sizeof(fpath)); + strncat(fpath, ".crt", 4); + fpath[sizeof(fpath)-1] = '\0'; + + if(our_stat(fpath, &sbuf) == 0){ + if((sbuf.st_mode & S_IFMT) == S_IFREG) + setup_dir++; + } + else if(our_copy(fpath, pathkey) == 0) + setup_dir++; + } + + if(setup_dir) + ps_global->smime->pwdcert = (void *) pc2; + else{ + free_personal_certs(&pc2); + return; + } + } + } + } + } /* end of if(ps_global->personal_certs) */ + else { + /* if we are here it might be because we have not read the .pinerc file + * or because there are no certificates to copy. Which one is it? + */ + /* look for a private key and public cert in pathdir + look for a public cert in pathdir + if not successful find one in DF_PRIVATE_DIR, etc. + if not successful, create a self-signed certificate and save it in pathdir. + if successful, load the keys. + if successful, set these keys as the keys to encrypt/decrypt. + */ + char buf[MAXPATH+1]; + char *keyfile; + char *certfile; + DIR *dirp; + struct dirent *d; + + load_key_and_cert(pathdir, &keyfile, &certfile, &pkey, &pcert); + if(certfile && keyfile) + setup_dir++; + + if(setup_dir == 0){ + if(keyfile == NULL){ + /* PATHCERTDIR(Private) must be null, so create a path */ + set_current_val(&ps_global->vars[V_PRIVATEKEY_DIR], TRUE, TRUE); + smime_path(ps_global->VAR_PRIVATEKEY_DIR, pathkey, MAXPATH); + load_key_and_cert(pathkey, &keyfile, NULL, &pkey, NULL); + } + if(certfile == NULL){ + /* PATHCERTDIR(Public) must be null, so create a path */ + set_current_val(&ps_global->vars[V_PUBLICCERT_DIR], TRUE, TRUE); + smime_path(ps_global->VAR_PUBLICCERT_DIR, pathkey, MAXPATH); + load_key_and_cert(pathkey, NULL, &certfile, NULL, &pcert); + } + + smime_reinit(); + if(certfile && keyfile) + setup_dir++; + } + + /* TODO: create self signed certificate */ + if(setup_dir == 0) + q_status_message(SM_ORDER, 0, 2, + _("No key/certificate pair found. Password file not encrypted.")); + + if(keyfile && certfile){ + pc2 = (PERSONAL_CERT *) fs_get(sizeof(PERSONAL_CERT)); + memset((void *)pc, 0, sizeof(PERSONAL_CERT)); + pc2->name = keyfile; + pc2->key = pkey; + pc2->cert = pcert; + ps_global->smime->pwdcert = (void *) pc2; + } + + if(certfile) + fs_give((void **)&certfile); + } +} +#endif /* PASSFILE */ + + 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) + if(pith_smime_import_certificate == NULL){ q_status_message(SM_ORDER, 0, 2, _("import of certificates not implemented yet!")); + return -1; + } 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){ + ps_global->mangled_screen = 1; + + if(r < 0) + return r; + else if (ctype == Private){ char prompt[500], *s, *t; char pass[MAILTMPLEN+1]; BIO *in; @@ -132,8 +380,8 @@ import_certificate(WhichCerts ctype) _("Enter passphrase for <%s>: "), s ? s : filename); pass[0] = '\0'; - if(pith_enter_password) - (*pith_enter_password)(prompt, (char *)pass, sizeof(pass)); + if(pith_smime_enter_password) + (*pith_smime_enter_password)(prompt, (char *)pass, sizeof(pass)); if((key = PEM_read_bio_PrivateKey(in, NULL, NULL, pass)) != NULL){ if(SMHOLDERTYPE(ctype) == Directory){ @@ -223,7 +471,6 @@ import_certificate(WhichCerts ctype) } } if(DATACERT(ctype)) RENEWCERT(DATACERT(ctype)) = 1; - ps_global->mangled_screen = 1; return 0; } @@ -302,14 +549,12 @@ forget_private_keys(void) * 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); @@ -492,7 +737,12 @@ renew_cert_data(CertList **data, WhichCerts ctype) } } - +void +smime_reinit(void) +{ + smime_deinit(); + smime_init(); +} /* Installed as an atexit() handler to save the random data */ void @@ -531,7 +781,6 @@ smime_init(void) ERR_load_crypto_strings(); app_RAND_load_file(NULL); - openssl_extra_randomness(); ps_global->smime->inited = 1; } @@ -1631,57 +1880,56 @@ bio_from_store(STORE_S *store) * Encrypt file; given a path (char *) fp, replace the file * by an encrypted version of it. If (char *) text is not null, then * replace the text of (char *) fp by the encrypted version of (char *) text. + * certpath is the FULL path to the file containing the certificate used for + * encryption. */ int -encrypt_file(char *fp, char *text) +encrypt_file(char *fp, char *text, PERSONAL_CERT *pc) { const EVP_CIPHER *cipher = NULL; STACK_OF(X509) *encerts = NULL; X509 *cert; - PERSONAL_CERT *pcert; - BIO *in; + BIO *out; PKCS7 *p7 = NULL; FILE *fpp; - int rv = 0; + int i, rv = 0; + char *fname; + char emailaddress[MAILTMPLEN]; smime_init(); - if((pcert = ps_global->smime->personal_certs) == NULL) - return 0; - cipher = EVP_aes_256_cbc(); encerts = sk_X509_new_null(); - - if((cert = get_cert_for(pcert->name, Public)) != NULL) - sk_X509_push(encerts, cert); - else - goto end; + + if(pc == NULL) + goto end; + + sk_X509_push(encerts, pc->cert); if(text){ - in = BIO_new(BIO_s_mem()); - if(in == NULL) + if((out = BIO_new(BIO_s_mem())) == NULL) goto end; - (void) BIO_reset(in); - BIO_puts(in, text); + (void) BIO_reset(out); + BIO_puts(out, text); } else{ - if(!(in = BIO_new_file(fp, "rb"))) + if(!(out = BIO_new_file(fp, "rb"))) goto end; - BIO_read_filename(in, fp); + BIO_read_filename(out, fp); } - if((p7 = PKCS7_encrypt(encerts, in, cipher, 0)) == NULL) + if((p7 = PKCS7_encrypt(encerts, out, cipher, 0)) == NULL) goto end; - BIO_set_close(in, BIO_CLOSE); - BIO_free(in); - if(!(in = BIO_new_file(fp, "w"))) + BIO_set_close(out, BIO_CLOSE); + BIO_free(out); + if(!(out = BIO_new_file(fp, "w"))) goto end; - BIO_reset(in); - rv = PEM_write_bio_PKCS7(in, p7); - BIO_flush(in); + BIO_reset(out); + rv = PEM_write_bio_PKCS7(out, p7); + BIO_flush(out); end: - BIO_free(in); + BIO_free(out); PKCS7_free(p7); sk_X509_pop_free(encerts, X509_free); @@ -2171,94 +2419,60 @@ find_certificate_matching_pkcs7(PKCS7 *p7) /* decrypt an encrypted file. Args: fp - the path to the encrypted file. - rv - a code that thells the caller what happened inside the function + rv - a code that tells the caller what happened inside the function + pcert - a personal certificate that was used to encrypt this file Returns the decoded text allocated in a char *, whose memory must be freed by caller */ char * -decrypt_file(char *fp, int *rv) +decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc) { PKCS7 *p7 = NULL; - char *text, *tmp; BIO *in = NULL, *out = NULL; - EVP_PKEY *pkey = NULL, *key = NULL; - PERSONAL_CERT *pcert = NULL; - X509 *recip, *cert; - STORE_S *outs = NULL, *store, *ins; - int i, j; + int i; long unsigned int len; + char *text, *s; void *ret; smime_init(); - if((text = read_file(fp, 0)) == NULL) + if(rv) *rv = -1; /* assume error initially */ + + if((text = read_file(fp, 0)) == NULL + || pc == NULL + || pc->key == NULL) return NULL; - tmp = fs_get(strlen(text) + (strlen(text) << 6) + 1); - for(j = 0, i = strlen("-----BEGIN PKCS7-----") + 1; text[i] != '\0' - && text[i] != '-'; j++, i++) - tmp[j] = text[i]; - tmp[j] = '\0'; + i = strlen("-----BEGIN PKCS7-----") + 1; + if((s = strchr(text+i, '-')) != NULL) + *s = '\0'; - ret = rfc822_base64(tmp, strlen(tmp), &len); + ret = rfc822_base64(text+i, strlen(text+i), &len); if((in = BIO_new_mem_buf((char *)ret, len)) != NULL){ p7 = d2i_PKCS7_bio(in, NULL); BIO_free(in); } - if(text) fs_give((void **)&text); - if(ret) fs_give((void **)&ret); - - if((pcert = ps_global->smime->personal_certs) == NULL) - goto end; - - if((i = load_private_key(pcert)) == 0 - && ps_global->smime - && ps_global->smime->need_passphrase - && !ps_global->smime->already_auto_asked) - for(; i == 0;){ - ps_global->smime->already_auto_asked = 1; - if(pith_opt_smime_get_passphrase){ - switch((*pith_opt_smime_get_passphrase)()){ - case 0 : i = load_private_key(pcert); - break; - - case 1 : i = -1; - break; - - default: break; /* repeat until we cancel */ - } - } - else - i = -2; - } - - if(rv) *rv = i; + if (ret) fs_give((void **)&ret); - if((key = pcert->key) == NULL) - goto end; - - recip = get_cert_for(pcert->name, Public); out = BIO_new(BIO_s_mem()); (void) BIO_reset(out); - i = PKCS7_decrypt(p7, key, recip, out, 0); - - if(F_OFF(F_REMEMBER_SMIME_PASSPHRASE,ps_global)) - forget_private_keys(); - - if(i == 0){ + if(PKCS7_decrypt(p7, pc->key, pc->cert, out, 0) == 0){ q_status_message1(SM_ORDER, 1, 1, _("Error decrypting: %s"), (char*) openssl_error_string()); goto end; } - BIO_get_mem_data(out, &tmp); - - text = cpystr(tmp); + BIO_get_mem_data(out, &s); + i = strlen(s); + fs_resize((void **)&text, i+1); + strncpy(text, s, i+1); + text[i] = '\0'; BIO_free(out); + if (rv) *rv = 1; end: PKCS7_free(p7); @@ -2766,6 +2980,14 @@ free_smime_struct(SMIME_STUFF_S **smime) (*smime)->personal_certs = NULL; } + if((*smime)->pwdcert){ + PERSONAL_CERT *pc; + + pc = (PERSONAL_CERT *) (*smime)->pwdcert; + free_personal_certs(&pc); + (*smime)->pwdcert = NULL; + } + if((*smime)->privatecontent) fs_give((void **) &(*smime)->privatecontent); diff --git a/pith/smime.h b/pith/smime.h index a8ca8dd2..3d65d924 100644 --- a/pith/smime.h +++ b/pith/smime.h @@ -27,14 +27,17 @@ #include <openssl/rand.h> #include <openssl/err.h> +#ifdef PASSFILE +#define DF_PASSWORD_DIR ".alpine-smime/.pwd" +#endif #define OUR_PKCS7_ENCLOSURE_SUBTYPE "x-pkcs7-enclosure" /* 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 encrypt_file(char *fp, char *text, PERSONAL_CERT *pc); +char *decrypt_file(char *fp, int *rv, PERSONAL_CERT *pc); int is_pkcs7_body(BODY *b); int fiddle_smime_message(BODY *b, long msgno); int encrypt_outgoing_message(METAENV *header, BODY **bodyP); @@ -45,6 +48,7 @@ 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 smime_reinit(void); void renew_store(void); void renew_cert_data(CertList **data, WhichCerts ctype); BIO *print_private_key_information(char *email, int itype); @@ -61,7 +65,9 @@ int copy_publiccert_container_to_keychain(void); int copy_publiccert_keychain_to_container(void); #endif /* APPLEKEYCHAIN */ int import_certificate(WhichCerts); - +#ifdef PASSFILE +void setup_pwdcert(void); +#endif /* PASSFILE */ #endif /* PITH_SMIME_INCLUDED */ #endif /* SMIME */ diff --git a/pith/state.c b/pith/state.c index 07b77131..bf5ff24e 100644 --- a/pith/state.c +++ b/pith/state.c @@ -178,6 +178,10 @@ free_pine_struct(struct pine **pps) #ifdef PASSFILE if((*pps)->passfile) fs_give((void **)&(*pps)->passfile); +#ifdef SMIME + if((*pps)->pwdcertdir) + fs_give((void **)&(*pps)->pwdcertdir); +#endif /* SMIME inside PASSFILE */ #endif /* PASSFILE */ if((*pps)->hdr_colors) diff --git a/pith/state.h b/pith/state.h index b1a20da3..3fb4f2ee 100644 --- a/pith/state.h +++ b/pith/state.h @@ -271,6 +271,9 @@ 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 */ |