diff options
author | Eduardo Chappa <chappa@washington.edu> | 2014-03-09 14:26:50 -0600 |
---|---|---|
committer | Eduardo Chappa <chappa@washington.edu> | 2014-03-09 14:26:50 -0600 |
commit | e9554c597f7f33c6ebebaa47087b4db878a59913 (patch) | |
tree | 9b8ff514f4db831a456185034cc614cf712b43e2 /pith | |
parent | ac368b05842ee45ed29cd997840eb788649da268 (diff) | |
download | alpine-e9554c597f7f33c6ebebaa47087b4db878a59913.tar.xz |
* Forwarding messages with attachments of content-type multipart,
failed when attempting to sign it, with and "Error writing pipe"
error.
* Using a .pinerc file outside the home directory made Alpine not find
the .alpine-smime directory with certificates.
* Configuration screen for S/MIME adds ability to manage certificates.
(currently available to users who manage certificates in
directories, not in containers, which will be available in the next
alpha release.)
Diffstat (limited to 'pith')
-rw-r--r-- | pith/conftype.h | 43 | ||||
-rw-r--r-- | pith/filter.c | 2 | ||||
-rw-r--r-- | pith/options.h | 9 | ||||
-rw-r--r-- | pith/pine.hlp | 261 | ||||
-rw-r--r-- | pith/smime.c | 573 | ||||
-rw-r--r-- | pith/smime.h | 9 | ||||
-rw-r--r-- | pith/smkeys.c | 188 | ||||
-rw-r--r-- | pith/smkeys.h | 11 |
8 files changed, 953 insertions, 143 deletions
diff --git a/pith/conftype.h b/pith/conftype.h index 091481d1..38097f9e 100644 --- a/pith/conftype.h +++ b/pith/conftype.h @@ -2,8 +2,8 @@ * $Id: conftype.h 1155 2008-08-21 18:33:21Z hubert@u.washington.edu $ * * ======================================================================== - * Copyright 2006-2008 University of Washington * Copyright 2013-2014 Eduardo Chappa + * Copyright 2006-2008 University of Washington * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -670,11 +670,19 @@ typedef enum {Main, Post, None} EditWhich; typedef enum {Directory, Container, Keychain, Nada} SmimeHolderType; +typedef enum {Public, Private, CACert} WhichCerts; + +typedef struct certdata { + unsigned deleted:1; /* certificate is marked deleted */ + unsigned renew:1; /* we must renew this list, set at top cert */ +} CertData; + typedef struct certlist { char *name; void *x509_cert; /* this is type (X509 *) */ + CertData data; struct certlist *next; -}CertList; +} CertList; typedef struct smime_stuff { unsigned inited:1; @@ -690,25 +698,52 @@ typedef struct smime_stuff { * If we are using the Container type it is easiest if we * read in and maintain a list of certs and then write them * out all at once. For Directory type we just leave the data - * in the individual files and read or write the individual - * files when needed, so we don't have a list of all the certs. + * in the individual files and read the list of files in the + * directory. */ SmimeHolderType publictype; char *publicpath; char *publiccontent; + CertList *publiccertdata; CertList *publiccertlist; SmimeHolderType privatetype; char *privatepath; char *privatecontent; + CertList *privatecertdata; void *personal_certs; /* this is type (PERSONAL_CERT *) */ SmimeHolderType catype; char *capath; char *cacontent; + CertList *cacertdata; + CertList *cacertlist; } SMIME_STUFF_S; +#define DATACERT(X) (((X) == Public ? ps_global->smime->publiccertdata \ + : ((X) == Private ? ps_global->smime->privatecertdata \ + : ((X) == CACert ? ps_global->smime->cacertdata : NULL)))) + +#define PATHCERTDIR(X) (((X) == Public ? ps_global->smime->publicpath \ + : ((X) == Private ? ps_global->smime->privatepath \ + : ((X) == CACert ? ps_global->smime->capath : NULL)))) + +#define SMHOLDERTYPE(X) (((X) == Public ? ps_global->smime->publictype \ + : ((X) == Private ? ps_global->smime->privatetype \ + : ((X) == CACert ? ps_global->smime->catype : Nada)))) + +#define SMCERTLIST(X) (((X) == Public ? ps_global->smime->publiccertlist \ + : ((X) == Private ? ps_global->smime->privatecertdata \ + : ((X) == CACert ? ps_global->smime->cacertlist : NULL)))) + +#define EXTCERT(X) (((X) == Public ? ".crt" \ + : ((X) == Private ? ".key" \ + : ((X) == CACert ? ".crt" : NULL)))) + +#define DELETEDCERT(X) ((X)->data.deleted) +#define RENEWCERT(X) ((X)->data.renew) + #endif /* SMIME */ diff --git a/pith/filter.c b/pith/filter.c index 2800a3ba..d22b1215 100644 --- a/pith/filter.c +++ b/pith/filter.c @@ -11284,7 +11284,7 @@ gf_local_nvtnl(FILTER_S *f, int flg) GF_PUTC(f->next, '\015'); GF_PUTC(f->next, '\012'); } - else + else if(c != '\015') /* do not copy isolated \015 into source */ GF_PUTC(f->next, c); } diff --git a/pith/options.h b/pith/options.h index 0d5412a1..c452b97b 100644 --- a/pith/options.h +++ b/pith/options.h @@ -224,5 +224,14 @@ extern char *(*pith_opt_user_agent_prefix)(void); */ extern int (*pith_opt_smime_get_passphrase)(void); +/* + * Required call to interface for input of file to import. + */ +extern int (*pith_smime_import_certificate)(char *, char *, size_t); + +/* + * Required call to ask user to enter a password, with a given char * prompt + */ +extern void (*pith_enter_password)(char *, char *, size_t); #endif /* PITH_OPTIONS_INCLUDED */ diff --git a/pith/pine.hlp b/pith/pine.hlp index 66299dfa..6231a466 100644 --- a/pith/pine.hlp +++ b/pith/pine.hlp @@ -140,7 +140,7 @@ with help text for the config screen and the composer that didn't have any reasonable place to be called from. Dummy change to get revision in pine.hlp ============= h_revision ================= -Alpine Commit 47 2014-02-20 00:06:00 +Alpine Commit 48 2014-03-09 14:26:45 ============= h_news ================= <HTML> <HEAD> @@ -187,6 +187,9 @@ Additions include: direction of search. <LI> Upgrade UW-IMAP to Panda IMAP from <A HREF="https://github.com/jonabbey/panda-imap">https://github.com/jonabbey/panda-imap</A>. + <LI> S/MIME: When a signed message is received from a user whose + certificate has not been imported, should the certificate fail to + validate, the user will be shown a warning, and asked how to proceed. <LI> Replace tabs by spaces in From and Subject fields to control for size in screen of these fields. Change only in index screen display. <LI> Add support to selective expunge through a subcommand of the @@ -247,6 +250,7 @@ Bugs that have been addressed include: <LI> S/MIME: Forwarding messages with multipart content-type failed to be signed with "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> 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. <End of help on this topic> </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> +<End of help on this topic> +</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> +<End of help on this topic> +</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> +<End of help on this topic> +</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> +<End of help on this topic> +</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> +<End of help on this topic> +</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> +<End of help on this topic> +</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 ".crt" +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> +<End of help on this topic> +</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 */ |