diff options
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 */ |