diff options
author | Peter Tribble <peter.tribble@gmail.com> | 2017-03-13 20:25:34 +0000 |
---|---|---|
committer | Gordon Ross <gwr@nexenta.com> | 2017-03-29 19:13:24 -0400 |
commit | 32991bedc3a6475f1401855c2318ae5b15f8a16b (patch) | |
tree | 3360e7844885746ed96019a9e3a42cd7e5e73409 /usr/src/lib/libpkg/common/keystore.c | |
parent | 4383d9578c9d399b19edc33e552a4c543ebf9468 (diff) | |
download | illumos-gate-32991bedc3a6475f1401855c2318ae5b15f8a16b.tar.gz |
5188 SVR4 packaging shouldn't depend on openssl or libwanboot
Reviewed by: Igor Kozhukhov <igor@dilos.org>
Reviewed by: Alexander Eremin <alexander.r.eremin@gmail.com>
Approved by: Gordon Ross <gordon.w.ross@gmail.com>
Diffstat (limited to 'usr/src/lib/libpkg/common/keystore.c')
-rw-r--r-- | usr/src/lib/libpkg/common/keystore.c | 2474 |
1 files changed, 0 insertions, 2474 deletions
diff --git a/usr/src/lib/libpkg/common/keystore.c b/usr/src/lib/libpkg/common/keystore.c deleted file mode 100644 index 633c04b58e..0000000000 --- a/usr/src/lib/libpkg/common/keystore.c +++ /dev/null @@ -1,2474 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ - -/* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -/* - * Module: keystore.c - * Description: This module contains the structure definitions for processing - * package keystore files. - */ - -#include <errno.h> -#include <fcntl.h> -#include <unistd.h> -#include <strings.h> -#include <libintl.h> -#include <time.h> -#include <ctype.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <openssl/evp.h> -#include <openssl/x509.h> -#include <openssl/pkcs12.h> -#include <openssl/asn1.h> -#include <openssl/pem.h> -#include <openssl/err.h> -#include <openssl/safestack.h> -#include <openssl/stack.h> -#include "p12lib.h" -#include "pkgerr.h" -#include "keystore.h" -#include "pkglib.h" -#include "pkglibmsgs.h" - -typedef struct keystore_t { - boolean_t dirty; - boolean_t new; - char *path; - char *passphrase; - /* truststore handles */ - int cafd; - STACK_OF(X509) *cacerts; - char *capath; - - /* user certificate handles */ - STACK_OF(X509) *clcerts; - char *clpath; - - /* private key handles */ - STACK_OF(EVP_PKEY) *pkeys; - char *keypath; -} keystore_t; - -/* local routines */ -static keystore_t *new_keystore(void); -static void free_keystore(keystore_t *); -static boolean_t verify_keystore_integrity(PKG_ERR *, keystore_t *); -static boolean_t check_password(PKCS12 *, char *); -static boolean_t resolve_paths(PKG_ERR *, char *, char *, - long, keystore_t *); -static boolean_t lock_keystore(PKG_ERR *, long, keystore_t *); - -static boolean_t unlock_keystore(PKG_ERR *, keystore_t *); -static boolean_t read_keystore(PKG_ERR *, keystore_t *, - keystore_passphrase_cb); -static boolean_t write_keystore(PKG_ERR *, keystore_t *, - keystore_passphrase_cb); -static boolean_t write_keystore_file(PKG_ERR *, char *, PKCS12 *); -static boolean_t clear_keystore_file(PKG_ERR *, char *); -static PKCS12 *read_keystore_file(PKG_ERR *, char *); -static char *get_time_string(ASN1_TIME *); - -/* locking routines */ -static boolean_t restore_keystore_file(PKG_ERR *, char *); -static int file_lock(int, int, int); -static int file_unlock(int); -static boolean_t file_lock_test(int, int); -static boolean_t file_empty(char *); -static boolean_t get_keystore_passwd(PKG_ERR *err, PKCS12 *p12, - keystore_passphrase_cb cb, keystore_t *keystore); -static boolean_t wait_restore(int, char *, char *, char *); - -#define KEYSTORE_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) - -/* wait on other keystore access for 1 minute before giving up */ -#define LOCK_TIMEOUT 60 - -/* - * print_certs - prints certificates out of a keystore, to a file. - * - * Arguments: - * err - Error object to append errors to - * keystore - Keystore on which to operate - * alias - Name of certificate to print, NULL means print all - * format - Format in which to print certificates - * outfile - Where to print certificates - * - * Returns: - * 0 - Success - * non-zero - Failure, errors added to err - */ -int -print_certs(PKG_ERR *err, keystore_handle_t keystore_h, char *alias, - keystore_encoding_format_t format, FILE *outfile) -{ - int i; - X509 *cert; - char *fname = NULL; - boolean_t found = B_FALSE; - keystore_t *keystore = keystore_h; - - if (keystore->clcerts != NULL) { - /* print out each client cert */ - for (i = 0; i < sk_X509_num(keystore->clcerts); i++) { - cert = sk_X509_value(keystore->clcerts, i); - (void) sunw_get_cert_fname(GETDO_COPY, cert, - &fname); - - if (fname == NULL) { - /* no name recorded, keystore is corrupt */ - pkgerr_add(err, PKGERR_CORRUPT, - gettext(ERR_KEYSTORE_NO_ALIAS), - get_subject_display_name(cert)); - return (1); - } - - if ((alias != NULL) && (!streq(alias, fname))) { - /* name does not match, skip it */ - (void) OPENSSL_free(fname); - fname = NULL; - continue; - } else { - found = B_TRUE; - (void) print_cert(err, cert, format, - fname, B_FALSE, outfile); - (void) OPENSSL_free(fname); - fname = NULL; - } - } - } - - if (fname != NULL) { - (void) OPENSSL_free(fname); - fname = NULL; - } - - if (keystore->cacerts != NULL) { - /* print out each trusted cert */ - for (i = 0; i < sk_X509_num(keystore->cacerts); i++) { - cert = sk_X509_value(keystore->cacerts, i); - (void) sunw_get_cert_fname(GETDO_COPY, - cert, &fname); - - if (fname == NULL) { - /* no name recorded, keystore is corrupt */ - pkgerr_add(err, PKGERR_CORRUPT, - gettext(ERR_KEYSTORE_NO_ALIAS), - get_subject_display_name(cert)); - return (1); - } - - if ((alias != NULL) && (!streq(alias, fname))) { - /* name does not match, skip it */ - (void) OPENSSL_free(fname); - fname = NULL; - continue; - } else { - found = B_TRUE; - (void) print_cert(err, cert, format, - fname, B_TRUE, outfile); - (void) OPENSSL_free(fname); - fname = NULL; - } - } - } - - if (fname != NULL) { - (void) OPENSSL_free(fname); - fname = NULL; - } - - if (found) { - return (0); - } else { - /* no certs printed */ - if (alias != NULL) { - pkgerr_add(err, PKGERR_NOALIASMATCH, - gettext(ERR_KEYSTORE_NOCERT), - alias, keystore->path); - } else { - pkgerr_add(err, PKGERR_NOPUBKEY, - gettext(ERR_KEYSTORE_NOPUBCERTS), - keystore->path); - pkgerr_add(err, PKGERR_NOCACERT, - gettext(ERR_KEYSTORE_NOCACERTS), - keystore->path); - } - return (1); - } -} - -/* - * print_cert - prints a single certificate, to a file - * - * Arguments: - * err - Error object to append errors to - * x - The certificate to print - * alias - Name of certificate to print - * format - Format in which to print certificate - * outfile - Where to print certificate - * - * Returns: - * 0 - Success - * non-zero - Failure, errors added to err - */ -int print_cert(PKG_ERR *err, X509 *x, - keystore_encoding_format_t format, char *alias, boolean_t is_trusted, - FILE *outfile) -{ - - char *vdb_str; - char *vda_str; - char vd_str[ATTR_MAX]; - int ret = 0; - char *cn_str, *icn_str, *typ_str; - char *tmp; - char *md5_fp; - char *sha1_fp; - int len; - - /* need to localize the word "Fingerprint", hence these pointers */ - char md5_label[ATTR_MAX]; - char sha1_label[ATTR_MAX]; - - if (is_trusted) { - typ_str = gettext(MSG_KEYSTORE_TRUSTED); - } else { - typ_str = gettext(MSG_KEYSTORE_UNTRUSTED); - } - - if ((cn_str = get_subject_display_name(x)) == NULL) { - cn_str = gettext(MSG_KEYSTORE_UNKNOWN); - } - - if ((icn_str = get_issuer_display_name(x)) == NULL) { - icn_str = gettext(MSG_KEYSTORE_UNKNOWN); - } - - vdb_str = xstrdup(get_time_string(X509_get_notBefore(x))); - vda_str = xstrdup(get_time_string(X509_get_notAfter(x))); - if (((len = snprintf(vd_str, ATTR_MAX, "<%s> - <%s>", - vdb_str, vda_str)) < 0) || (len >= ATTR_MAX)) { - pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN), vdb_str); - ret = 1; - goto cleanup; - } - - if ((tmp = get_fingerprint(x, EVP_md5())) == NULL) { - md5_fp = gettext(MSG_KEYSTORE_UNKNOWN); - } else { - /* - * make a copy, otherwise the next call to get_fingerprint - * will overwrite this one - */ - md5_fp = xstrdup(tmp); - } - - if ((tmp = get_fingerprint(x, EVP_sha1())) == NULL) { - sha1_fp = gettext(MSG_KEYSTORE_UNKNOWN); - } else { - sha1_fp = xstrdup(tmp); - } - - (void) snprintf(md5_label, ATTR_MAX, "%s %s", - OBJ_nid2sn(EVP_MD_type(EVP_md5())), - /* i18n: 14 characters max */ - gettext(MSG_KEYSTORE_FP)); - - (void) snprintf(sha1_label, ATTR_MAX, "%s %s", - OBJ_nid2sn(EVP_MD_type(EVP_sha1())), - /* i18n: 14 characters max */ - gettext(MSG_KEYSTORE_FP)); - - switch (format) { - case KEYSTORE_FORMAT_PEM: - (void) PEM_write_X509(outfile, x); - break; - case KEYSTORE_FORMAT_DER: - (void) i2d_X509_fp(outfile, x); - break; - case KEYSTORE_FORMAT_TEXT: - (void) fprintf(outfile, "%18s: %s\n", - /* i18n: 18 characters max */ - gettext(MSG_KEYSTORE_AL), alias); - (void) fprintf(outfile, "%18s: %s\n", - /* i18n: 18 characters max */ - gettext(MSG_KEYSTORE_CN), cn_str); - (void) fprintf(outfile, "%18s: %s\n", - /* i18n: 18 characters max */ - gettext(MSG_KEYSTORE_TY), typ_str); - (void) fprintf(outfile, "%18s: %s\n", - /* i18n: 18 characters max */ - gettext(MSG_KEYSTORE_IN), icn_str); - (void) fprintf(outfile, "%18s: %s\n", - /* i18n: 18 characters max */ - gettext(MSG_KEYSTORE_VD), vd_str); - (void) fprintf(outfile, "%18s: %s\n", md5_label, md5_fp); - (void) fprintf(outfile, "%18s: %s\n", sha1_label, sha1_fp); - (void) fprintf(outfile, "\n"); - break; - default: - pkgerr_add(err, PKGERR_INTERNAL, - gettext(ERR_KEYSTORE_INTERNAL), - __FILE__, __LINE__); - ret = 1; - goto cleanup; - } - -cleanup: - if (md5_fp != NULL) - free(md5_fp); - if (sha1_fp != NULL) - free(sha1_fp); - if (vda_str != NULL) - free(vda_str); - if (vdb_str != NULL) - free(vdb_str); - return (ret); -} - -/* - * open_keystore - Initialize new keystore object for - * impending access. - * - * Arguments: - * err - Error object to append errors to - * keystore_file - Base filename or directory of keystore - * app - Application making request - * passwd - Password used to decrypt keystore - * flags - Control flags used to control access mode and behavior - * result - Resulting keystore object stored here on success - * - * Returns: - * 0 - Success - result contains a pointer to the opened keystore - * non-zero - Failure, errors added to err - */ -int -open_keystore(PKG_ERR *err, char *keystore_file, char *app, - keystore_passphrase_cb cb, long flags, keystore_handle_t *result) -{ - int ret = 0; - keystore_t *tmpstore; - - tmpstore = new_keystore(); - - tmpstore->dirty = B_FALSE; - tmpstore->new = B_FALSE; - tmpstore->path = xstrdup(keystore_file); - - if (!resolve_paths(err, keystore_file, app, flags, tmpstore)) { - /* unable to determine keystore paths */ - pkgerr_add(err, PKGERR_CORRUPT, gettext(ERR_KEYSTORE_REPAIR), - keystore_file); - ret = 1; - goto cleanup; - } - - if (!verify_keystore_integrity(err, tmpstore)) { - /* unable to repair keystore */ - pkgerr_add(err, PKGERR_CORRUPT, gettext(ERR_KEYSTORE_REPAIR), - keystore_file); - ret = 1; - goto cleanup; - } - - if (!lock_keystore(err, flags, tmpstore)) { - pkgerr_add(err, PKGERR_LOCKED, gettext(ERR_KEYSTORE_LOCKED), - keystore_file); - ret = 1; - goto cleanup; - } - - /* now that we have locked the keystore, go ahead and read it */ - if (!read_keystore(err, tmpstore, cb)) { - pkgerr_add(err, PKGERR_READ, gettext(ERR_PARSE), - keystore_file); - ret = 1; - goto cleanup; - } - - *result = tmpstore; - tmpstore = NULL; - -cleanup: - if (tmpstore != NULL) - free_keystore(tmpstore); - return (ret); -} - -/* - * new_keystore - Allocates and initializes a Keystore object - * - * Arguments: - * NONE - * - * Returns: - * NULL - out of memory - * otherwise, returns a pointer to the newly allocated object, - * which should be freed with free_keystore() when no longer - * needed. - */ -static keystore_t -*new_keystore(void) -{ - keystore_t *tmpstore; - - if ((tmpstore = (keystore_t *)malloc(sizeof (keystore_t))) == NULL) { - return (NULL); - } - tmpstore->dirty = B_FALSE; - tmpstore->new = B_FALSE; - tmpstore->path = NULL; - tmpstore->passphrase = NULL; - tmpstore->cafd = -1; - tmpstore->cacerts = NULL; - tmpstore->capath = NULL; - tmpstore->clcerts = NULL; - tmpstore->clpath = NULL; - tmpstore->pkeys = NULL; - tmpstore->keypath = NULL; - - return (tmpstore); -} - -/* - * free_keystore - Deallocates a Keystore object - * - * Arguments: - * keystore - The keystore to deallocate - * - * Returns: - * NONE - */ -static void -free_keystore(keystore_t *keystore) -{ - if (keystore->path != NULL) - free(keystore->path); - if (keystore->capath != NULL) - free(keystore->capath); - if (keystore->passphrase != NULL) - free(keystore->passphrase); - if (keystore->clpath != NULL) - free(keystore->clpath); - if (keystore->keypath != NULL) - free(keystore->keypath); - - if (keystore->pkeys != NULL) { - sk_EVP_PKEY_pop_free(keystore->pkeys, - sunw_evp_pkey_free); - } - if (keystore->clcerts != NULL) - sk_X509_free(keystore->clcerts); - if (keystore->cacerts != NULL) - sk_X509_free(keystore->cacerts); - free(keystore); -} - -/* - * close_keystore - Writes keystore to disk if needed, then - * unlocks and closes keystore. - * - * Arguments: - * err - Error object to append errors to - * keystore - Keystore which should be closed - * passwd - Password used to encrypt keystore - * - * Returns: - * 0 - Success - keystore is committed to disk, and unlocked - * non-zero - Failure, errors added to err - */ -int -close_keystore(PKG_ERR *err, keystore_handle_t keystore_h, - keystore_passphrase_cb cb) -{ - int ret = 0; - keystore_t *keystore = keystore_h; - - if (keystore->dirty) { - /* write out the keystore first */ - if (!write_keystore(err, keystore, cb)) { - pkgerr_add(err, PKGERR_WRITE, - gettext(ERR_KEYSTORE_WRITE), - keystore->path); - ret = 1; - goto cleanup; - } - } - - if (!unlock_keystore(err, keystore)) { - pkgerr_add(err, PKGERR_UNLOCK, gettext(ERR_KEYSTORE_UNLOCK), - keystore->path); - ret = 1; - goto cleanup; - } - - free_keystore(keystore); -cleanup: - return (ret); -} - -/* - * merge_ca_cert - Adds a trusted certificate (trust anchor) to a keystore. - * certificate checked for validity dates and non-duplicity. - * - * Arguments: - * err - Error object to add errors to - * cacert - Certificate which to merge into keystore - * keystore - The keystore into which the certificate is merged - * - * Returns: - * 0 - Success - Certificate passes validity, and - * is merged into keystore - * non-zero - Failure, errors recorded in err - */ -int -merge_ca_cert(PKG_ERR *err, X509 *cacert, keystore_handle_t keystore_h) -{ - - int ret = 0; - X509 *existing = NULL; - char *fname; - keystore_t *keystore = keystore_h; - - /* check validity dates */ - if (check_cert(err, cacert) != 0) { - ret = 1; - goto cleanup; - } - - /* create the certificate's friendlyName */ - fname = get_subject_display_name(cacert); - - if (sunw_set_fname(fname, NULL, cacert) != 0) { - pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM)); - ret = 1; - goto cleanup; - } - - /* merge certificate into the keystore */ - if (keystore->cacerts == NULL) { - /* no existing truststore, so make a new one */ - if ((keystore->cacerts = sk_X509_new_null()) == NULL) { - pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM)); - ret = 1; - goto cleanup; - } - } else { - /* existing truststore, make sure there's no duplicate */ - if (sunw_find_fname(fname, NULL, keystore->cacerts, - NULL, &existing) < 0) { - pkgerr_add(err, PKGERR_INTERNAL, - gettext(ERR_KEYSTORE_INTERNAL), - __FILE__, __LINE__); - ERR_print_errors_fp(stderr); - ret = 1; - goto cleanup; - /* could not search properly! */ - } - if (existing != NULL) { - /* whoops, found one already */ - pkgerr_add(err, PKGERR_DUPLICATE, - gettext(ERR_KEYSTORE_DUPLICATECERT), fname); - ret = 1; - goto cleanup; - } - } - - (void) sk_X509_push(keystore->cacerts, cacert); - keystore->dirty = B_TRUE; -cleanup: - if (existing != NULL) - X509_free(existing); - return (ret); -} - -/* - * find_key_cert_pair - Searches a keystore for a matching - * public key certificate and private key, given an alias. - * - * Arguments: - * err - Error object to add errors to - * ks - Keystore to search - * alias - Name to used to match certificate's alias - * key - Resulting key is placed here - * cert - Resulting cert is placed here - * - * Returns: - * 0 - Success - Matching cert/key pair placed in key and cert. - * non-zero - Failure, errors recorded in err - */ -int -find_key_cert_pair(PKG_ERR *err, keystore_handle_t ks_h, char *alias, - EVP_PKEY **key, X509 **cert) -{ - X509 *tmpcert = NULL; - EVP_PKEY *tmpkey = NULL; - int ret = 0; - int items_found; - keystore_t *ks = ks_h; - - if (key == NULL || cert == NULL) { - pkgerr_add(err, PKGERR_NOPUBKEY, - gettext(ERR_KEYSTORE_NOPUBCERTS), ks->path); - ret = 1; - goto cleanup; - } - - if (ks->clcerts == NULL) { - /* no public certs */ - pkgerr_add(err, PKGERR_NOPUBKEY, - gettext(ERR_KEYSTORE_NOCERTS), ks->path); - ret = 1; - goto cleanup; - } - if (ks->pkeys == NULL) { - /* no private keys */ - pkgerr_add(err, PKGERR_NOPRIVKEY, - gettext(ERR_KEYSTORE_NOKEYS), ks->path); - ret = 1; - goto cleanup; - } - - /* try the easy case first */ - if ((sk_EVP_PKEY_num(ks->pkeys) == 1) && - (sk_X509_num(ks->clcerts) == 1)) { - tmpkey = sk_EVP_PKEY_value(ks->pkeys, 0); - tmpcert = sk_X509_value(ks->clcerts, 0); - if (sunw_check_keys(tmpcert, tmpkey)) { - /* - * only one private key and public key cert, and they - * match, so use them - */ - *key = tmpkey; - tmpkey = NULL; - *cert = tmpcert; - tmpcert = NULL; - goto cleanup; - } - } - - /* Attempt to find the right pair given the alias */ - items_found = sunw_find_fname(alias, ks->pkeys, ks->clcerts, - &tmpkey, &tmpcert); - - if ((items_found < 0) || - (items_found & (FOUND_PKEY | FOUND_CERT)) == 0) { - /* no key/cert pair found. bail. */ - pkgerr_add(err, PKGERR_BADALIAS, - gettext(ERR_KEYSTORE_NOMATCH), alias); - ret = 1; - goto cleanup; - } - - /* success */ - *key = tmpkey; - tmpkey = NULL; - *cert = tmpcert; - tmpcert = NULL; - -cleanup: - - if (tmpcert != NULL) - (void) X509_free(tmpcert); - - if (tmpkey != NULL) - sunw_evp_pkey_free(tmpkey); - - return (ret); -} - -/* - * find_ca_certs - Searches a keystore for trusted certificates - * - * Arguments: - * err - Error object to add errors to - * ks - Keystore to search - * cacerts - resulting set of trusted certs are placed here - * - * Returns: - * 0 - Success - trusted cert list returned in cacerts - * non-zero - Failure, errors recorded in err - */ -int -find_ca_certs(PKG_ERR *err, keystore_handle_t ks_h, STACK_OF(X509) **cacerts) -{ - - keystore_t *ks = ks_h; - - /* easy */ - if (cacerts == NULL) { - pkgerr_add(err, PKGERR_INTERNAL, - gettext(ERR_KEYSTORE_INTERNAL), __FILE__, __LINE__); - return (1); - } - - *cacerts = ks->cacerts; - return (0); -} - -/* - * find_cl_certs - Searches a keystore for user certificates - * - * Arguments: - * err - Error object to add errors to - * ks - Keystore to search - * cacerts - resulting set of user certs are placed here - * - * No matching of any kind is performed. - * Returns: - * 0 - Success - trusted cert list returned in cacerts - * non-zero - Failure, errors recorded in err - */ -/* ARGSUSED */ -int -find_cl_certs(PKG_ERR *err, keystore_handle_t ks_h, STACK_OF(X509) **clcerts) -{ - keystore_t *ks = ks_h; - - /* easy */ - *clcerts = ks->clcerts; - return (0); -} - - -/* - * merge_cert_and_key - Adds a user certificate and matching - * private key to a keystore. - * certificate checked for validity dates and non-duplicity. - * - * Arguments: - * err - Error object to add errors to - * cert - Certificate which to merge into keystore - * key - matching private key to 'cert' - * alias - Name which to store the cert and key under - * keystore - The keystore into which the certificate is merged - * - * Returns: - * 0 - Success - Certificate passes validity, and - * is merged into keystore, along with key - * non-zero - Failure, errors recorded in err - */ -int -merge_cert_and_key(PKG_ERR *err, X509 *cert, EVP_PKEY *key, char *alias, - keystore_handle_t keystore_h) -{ - X509 *existingcert = NULL; - EVP_PKEY *existingkey = NULL; - int ret = 0; - keystore_t *keystore = keystore_h; - - /* check validity dates */ - if (check_cert(err, cert) != 0) { - ret = 1; - goto cleanup; - } - - /* set the friendlyName of the key and cert to the supplied alias */ - if (sunw_set_fname(alias, key, cert) != 0) { - pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM)); - ret = 1; - goto cleanup; - } - - /* merge certificate and key into the keystore */ - if (keystore->clcerts == NULL) { - /* no existing truststore, so make a new one */ - if ((keystore->clcerts = sk_X509_new_null()) == NULL) { - pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM)); - ret = 1; - goto cleanup; - } - } else { - /* existing certstore, make sure there's no duplicate */ - if (sunw_find_fname(alias, NULL, keystore->clcerts, - NULL, &existingcert) < 0) { - pkgerr_add(err, PKGERR_INTERNAL, - gettext(ERR_KEYSTORE_INTERNAL), - __FILE__, __LINE__); - ERR_print_errors_fp(stderr); - ret = 1; - goto cleanup; - /* could not search properly! */ - } - if (existingcert != NULL) { - /* whoops, found one already */ - pkgerr_add(err, PKGERR_DUPLICATE, - gettext(ERR_KEYSTORE_DUPLICATECERT), alias); - ret = 1; - goto cleanup; - } - } - - if (keystore->pkeys == NULL) { - /* no existing keystore, so make a new one */ - if ((keystore->pkeys = sk_EVP_PKEY_new_null()) == NULL) { - pkgerr_add(err, PKGERR_NOMEM, gettext(ERR_MEM)); - ret = 1; - goto cleanup; - } - } else { - /* existing keystore, so make sure there's no duplicate entry */ - if (sunw_find_fname(alias, keystore->pkeys, NULL, - &existingkey, NULL) < 0) { - pkgerr_add(err, PKGERR_INTERNAL, - gettext(ERR_KEYSTORE_INTERNAL), - __FILE__, __LINE__); - ERR_print_errors_fp(stderr); - ret = 1; - goto cleanup; - /* could not search properly! */ - } - if (existingkey != NULL) { - /* whoops, found one already */ - pkgerr_add(err, PKGERR_DUPLICATE, - gettext(ERR_KEYSTORE_DUPLICATEKEY), alias); - ret = 1; - goto cleanup; - } - } - - (void) sk_X509_push(keystore->clcerts, cert); - (void) sk_EVP_PKEY_push(keystore->pkeys, key); - keystore->dirty = B_TRUE; -cleanup: - if (existingcert != NULL) - (void) X509_free(existingcert); - if (existingkey != NULL) - (void) sunw_evp_pkey_free(existingkey); - return (ret); -} - -/* - * delete_cert_and_keys - Deletes one or more certificates - * and matching private keys from a keystore. - * - * Arguments: - * err - Error object to add errors to - * ks - The keystore from which certs and keys are deleted - * alias - Name which to search for certificates and keys - * to delete - * - * Returns: - * 0 - Success - All trusted certs which match 'alias' - * are deleted. All user certificates - * which match 'alias' are deleted, along - * with the matching private key. - * non-zero - Failure, errors recorded in err - */ -int -delete_cert_and_keys(PKG_ERR *err, keystore_handle_t ks_h, char *alias) -{ - X509 *existingcert; - EVP_PKEY *existingkey; - int i; - char *fname = NULL; - boolean_t found = B_FALSE; - keystore_t *ks = ks_h; - - /* delete any and all client certs with the supplied name */ - if (ks->clcerts != NULL) { - for (i = 0; i < sk_X509_num(ks->clcerts); i++) { - existingcert = sk_X509_value(ks->clcerts, i); - if (sunw_get_cert_fname(GETDO_COPY, - existingcert, &fname) >= 0) { - if (streq(fname, alias)) { - /* match, so nuke it */ - existingcert = - sk_X509_delete(ks->clcerts, i); - X509_free(existingcert); - existingcert = NULL; - found = B_TRUE; - } - (void) OPENSSL_free(fname); - fname = NULL; - } - } - if (sk_X509_num(ks->clcerts) <= 0) { - /* we deleted all the client certs */ - sk_X509_free(ks->clcerts); - ks->clcerts = NULL; - } - } - - /* and now the private keys */ - if (ks->pkeys != NULL) { - for (i = 0; i < sk_EVP_PKEY_num(ks->pkeys); i++) { - existingkey = sk_EVP_PKEY_value(ks->pkeys, i); - if (sunw_get_pkey_fname(GETDO_COPY, - existingkey, &fname) >= 0) { - if (streq(fname, alias)) { - /* match, so nuke it */ - existingkey = - sk_EVP_PKEY_delete(ks->pkeys, i); - sunw_evp_pkey_free(existingkey); - existingkey = NULL; - found = B_TRUE; - } - (void) OPENSSL_free(fname); - fname = NULL; - } - } - if (sk_EVP_PKEY_num(ks->pkeys) <= 0) { - /* we deleted all the private keys */ - sk_EVP_PKEY_free(ks->pkeys); - ks->pkeys = NULL; - } - } - - /* finally, remove any trust anchors that match */ - - if (ks->cacerts != NULL) { - for (i = 0; i < sk_X509_num(ks->cacerts); i++) { - existingcert = sk_X509_value(ks->cacerts, i); - if (sunw_get_cert_fname(GETDO_COPY, - existingcert, &fname) >= 0) { - if (streq(fname, alias)) { - /* match, so nuke it */ - existingcert = - sk_X509_delete(ks->cacerts, i); - X509_free(existingcert); - existingcert = NULL; - found = B_TRUE; - } - (void) OPENSSL_free(fname); - fname = NULL; - } - } - if (sk_X509_num(ks->cacerts) <= 0) { - /* we deleted all the CA certs */ - sk_X509_free(ks->cacerts); - ks->cacerts = NULL; - } - } - - if (found) { - ks->dirty = B_TRUE; - return (0); - } else { - /* no certs or keys deleted */ - pkgerr_add(err, PKGERR_NOALIASMATCH, - gettext(ERR_KEYSTORE_NOCERTKEY), - alias, ks->path); - return (1); - } -} - -/* - * check_cert - Checks certificate validity. This routine - * checks that the current time falls within the period - * of validity for the cert. - * - * Arguments: - * err - Error object to add errors to - * cert - The certificate to check - * - * Returns: - * 0 - Success - Certificate checks out - * non-zero - Failure, errors and reasons recorded in err - */ -int -check_cert(PKG_ERR *err, X509 *cert) -{ - char currtimestr[ATTR_MAX]; - time_t currtime; - char *r; - /* get current time */ - if ((currtime = time(NULL)) == (time_t)-1) { - pkgerr_add(err, PKGERR_TIME, gettext(ERR_CURR_TIME)); - return (1); - } - - (void) strlcpy(currtimestr, ctime(&currtime), ATTR_MAX); - - /* trim whitespace from end of time string */ - for (r = (currtimestr + strlen(currtimestr) - 1); isspace(*r); r--) { - *r = '\0'; - } - /* check validity of cert */ - switch (sunw_check_cert_times(CHK_BOTH, cert)) { - case CHKERR_TIME_OK: - /* Current time meets requested checks */ - break; - case CHKERR_TIME_BEFORE_BAD: - /* 'not before' field is invalid */ - case CHKERR_TIME_AFTER_BAD: - /* 'not after' field is invalid */ - pkgerr_add(err, PKGERR_TIME, gettext(ERR_CERT_TIME_BAD)); - return (1); - case CHKERR_TIME_IS_BEFORE: - /* Current time is before 'not before' */ - case CHKERR_TIME_HAS_EXPIRED: - /* - * Ignore expiration time since the trust cert used to - * verify the certs used to sign Sun patches is already - * expired. Once the patches get resigned with the new - * cert we will check expiration against the time the - * patch was signed and not the time it is installed. - */ - return (0); - default: - pkgerr_add(err, PKGERR_INTERNAL, - gettext(ERR_KEYSTORE_INTERNAL), - __FILE__, __LINE__); - return (1); - } - - /* all checks ok */ - return (0); -} - -/* - * check_cert - Checks certificate validity. This routine - * checks everything that check_cert checks, and additionally - * verifies that the private key and corresponding public - * key are indeed a pair. - * - * Arguments: - * err - Error object to add errors to - * cert - The certificate to check - * key - the key to check - * Returns: - * 0 - Success - Certificate checks out - * non-zero - Failure, errors and reasons recorded in err - */ -int -check_cert_and_key(PKG_ERR *err, X509 *cert, EVP_PKEY *key) -{ - - /* check validity dates */ - if (check_cert(err, cert) != 0) { - return (1); - } - - /* check key pair match */ - if (sunw_check_keys(cert, key) == 0) { - pkgerr_add(err, PKGERR_VERIFY, gettext(ERR_MISMATCHED_KEYS), - get_subject_display_name(cert)); - return (1); - } - - /* all checks OK */ - return (0); -} - -/* ------------------ private functions ---------------------- */ - -/* - * verify_keystore_integrity - Searches for the remnants - * of a failed or aborted keystore modification, and - * cleans up the files, retstores the keystore to a known - * state. - * - * Arguments: - * err - Error object to add errors to - * keystore_file - Base directory or filename of keystore - * app - Application making request - * - * Returns: - * 0 - Success - Keystore is restored, or untouched in the - * case that cleanup was unnecessary - * non-zero - Failure, errors and reasons recorded in err - */ -static boolean_t -verify_keystore_integrity(PKG_ERR *err, keystore_t *keystore) -{ - if (keystore->capath != NULL) { - if (!restore_keystore_file(err, keystore->capath)) { - return (B_FALSE); - } - } - if (keystore->clpath != NULL) { - if (!restore_keystore_file(err, keystore->clpath)) { - return (B_FALSE); - } - } - if (keystore->keypath != NULL) { - if (!restore_keystore_file(err, keystore->keypath)) { - return (B_FALSE); - } - } - return (B_TRUE); -} - -/* - * restore_keystore_file - restores a keystore file to - * a known state. - * - * Keystore files can possibly be corrupted by a variety - * of error conditions during reading/writing. This - * routine, along with write_keystore_file, tries to - * maintain keystore integrity by writing the files - * out in a particular order, minimizing the time period - * that the keystore is in an indeterminate state. - * - * With the current implementation, there are some failures - * that are wholly unrecoverable, such as disk corruption. - * These routines attempt to minimize the risk, but not - * eliminate it. When better, atomic operations are available - * (such as a trued atabase with commit, rollback, and - * guaranteed atomicity), this implementation should use that. - * - * Arguments: - * err - Error object to add errors to - * keystore_file - keystore file path to restore. - * - * Returns: - * 0 - Success - Keystore file is restored, or untouched in the - * case that cleanup was unnecessary - * non-zero - Failure, errors and reasons recorded in err - */ -/* ARGSUSED */ -static boolean_t -restore_keystore_file(PKG_ERR *err, char *keystore_file) -{ - char newpath[MAXPATHLEN]; - char backuppath[MAXPATHLEN]; - int newfd; - struct stat buf; - int len; - - if (((len = snprintf(newpath, MAXPATHLEN, "%s.new", - keystore_file)) < 0) || - (len >= ATTR_MAX)) { - pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN), keystore_file); - return (B_FALSE); - } - - if (((len = snprintf(backuppath, MAXPATHLEN, "%s.bak", - keystore_file)) < 0) || - (len >= ATTR_MAX)) { - pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN), keystore_file); - return (B_FALSE); - } - - if ((newfd = open(newpath, O_RDWR|O_NONBLOCK, 0)) != -1) { - if (fstat(newfd, &buf) != -1) { - if (S_ISREG(buf.st_mode)) { - /* - * restore the file, waiting on it - * to be free for locking, or for - * it to disappear - */ - if (!wait_restore(newfd, keystore_file, - newpath, backuppath)) { - pkgerr_add(err, PKGERR_WRITE, - gettext(ERR_WRITE), - newpath, strerror(errno)); - (void) close(newfd); - return (B_FALSE); - } else { - return (B_TRUE); - } - } else { - /* "new" file is not a regular file */ - pkgerr_add(err, PKGERR_WRITE, - gettext(ERR_NOT_REG), newpath); - (void) close(newfd); - return (B_FALSE); - } - } else { - /* couldn't stat "new" file */ - pkgerr_add(err, PKGERR_WRITE, - gettext(ERR_WRITE), newpath, - strerror(errno)); - (void) close(newfd); - return (B_FALSE); - } - } else { - /* "new" file doesn't exist */ - return (B_TRUE); - } -} - -static boolean_t -wait_restore(int newfd, char *keystore_file, - char *origpath, char *backuppath) -{ - struct stat buf; - FILE *newstream; - PKCS12 *p12; - - (void) alarm(LOCK_TIMEOUT); - if (file_lock(newfd, F_WRLCK, 1) == -1) { - /* could not lock file */ - (void) alarm(0); - return (B_FALSE); - } - (void) alarm(0); - - if (fstat(newfd, &buf) != -1) { - if (S_ISREG(buf.st_mode)) { - /* - * The new file still - * exists, with no - * owner. It must be - * the result of an - * aborted update. - */ - newstream = fdopen(newfd, "r"); - if ((p12 = - d2i_PKCS12_fp(newstream, - NULL)) != NULL) { - /* - * The file - * appears - * complete. - * Replace the - * exsisting - * keystore - * file with - * this one - */ - (void) rename(keystore_file, backuppath); - (void) rename(origpath, keystore_file); - PKCS12_free(p12); - } else { - /* The file is not complete. Remove it */ - (void) remove(origpath); - } - /* remove backup file */ - (void) remove(backuppath); - (void) fclose(newstream); - (void) close(newfd); - return (B_TRUE); - } else { - /* - * new file exists, but is not a - * regular file - */ - (void) close(newfd); - return (B_FALSE); - } - } else { - /* - * could not stat file. Unless - * the reason was that the file - * is now gone, this is an error - */ - if (errno != ENOENT) { - (void) close(newfd); - return (B_FALSE); - } - /* - * otherwise, file is gone. The process - * that held the lock must have - * successfully cleaned up and - * exited with a valid keystore - * state - */ - (void) close(newfd); - return (B_TRUE); - } -} - -/* - * resolve_paths - figure out if we are dealing with a single-file - * or multi-file keystore - * - * The flags tell resolve_paths how to behave: - * - * KEYSTORE_PATH_SOFT - * If the keystore file does not exist at <base>/<app> then - * use <base> as the path to the keystore. This can be used, - * for example, to access an app-specific keystore iff it - * exists, otherwise revert back to an app-generic keystore. - * - * KEYSTORE_PATH_HARD - * Always use the keystore located at <keystore_path>/<app>. - * In read/write mode, if the files do not exist, then - * they will be created. This is used to avoid falling - * back to an app-generic keystore path when the app-specific - * one does not exist. - * - * Arguments: - * err - Error object to add errors to - * keystore_file - base keystore file path to lock - * app - Application making requests - * flags - Control flags (see above description) - * keystore - object which is being locked - * - * Returns: - * B_TRUE - Success - Keystore file is locked, paths to - * appropriate files placed in keystore. - * B_FALSE - Failure, errors and reasons recorded in err - */ -static boolean_t -resolve_paths(PKG_ERR *err, char *keystore_file, char *app, - long flags, keystore_t *keystore) -{ - char storepath[PATH_MAX]; - struct stat buf; - boolean_t multi = B_FALSE; - int fd1, fd2, len; - - /* - * figure out whether we are dealing with a single-file keystore - * or a multi-file keystore - */ - if (app != NULL) { - if (((len = snprintf(storepath, PATH_MAX, "%s/%s", - keystore_file, app)) < 0) || - (len >= ATTR_MAX)) { - pkgerr_add(err, PKGERR_WEB, gettext(ERR_LEN), - keystore_file); - return (B_FALSE); - } - - if (((fd1 = open(storepath, O_NONBLOCK|O_RDONLY)) == -1) || - (fstat(fd1, &buf) == -1) || - !S_ISDIR(buf.st_mode)) { - /* - * app-specific does not exist - * fallback to app-generic, if flags say we can - */ - if ((flags & KEYSTORE_PATH_MASK) == - KEYSTORE_PATH_SOFT) { - - if (((fd2 = open(keystore_file, - O_NONBLOCK|O_RDONLY)) != -1) && - (fstat(fd2, &buf) != -1)) { - if (S_ISDIR(buf.st_mode)) { - /* - * app-generic dir - * exists, so use it - * as a multi-file - * keystore - */ - multi = B_TRUE; - app = NULL; - } else if (S_ISREG(buf.st_mode)) { - /* - * app-generic file exists, so - * use it as a single file ks - */ - multi = B_FALSE; - app = NULL; - } - } - } - } - if (fd1 != -1) - (void) close(fd1); - if (fd2 != -1) - (void) close(fd2); - } else { - if (((fd1 = open(keystore_file, - O_NONBLOCK|O_RDONLY)) != -1) && - (fstat(fd1, &buf) != -1) && - S_ISDIR(buf.st_mode)) { - /* - * app-generic dir exists, so use - * it as a multi-file keystore - */ - multi = B_TRUE; - } - if (fd1 != -1) - (void) close(fd1); - } - - if (app != NULL) { - /* app-specific keystore */ - (void) snprintf(storepath, PATH_MAX, "%s/%s/%s", - keystore_file, app, TRUSTSTORE); - keystore->capath = xstrdup(storepath); - (void) snprintf(storepath, PATH_MAX, "%s/%s/%s", - keystore_file, app, CERTSTORE); - keystore->clpath = xstrdup(storepath); - (void) snprintf(storepath, PATH_MAX, "%s/%s/%s", - keystore_file, app, KEYSTORE); - keystore->keypath = xstrdup(storepath); - } else { - /* app-generic keystore */ - if (!multi) { - /* single-file app-generic keystore */ - keystore->capath = xstrdup(keystore_file); - keystore->keypath = NULL; - keystore->clpath = NULL; - } else { - /* multi-file app-generic keystore */ - (void) snprintf(storepath, PATH_MAX, "%s/%s", - keystore_file, TRUSTSTORE); - keystore->capath = xstrdup(storepath); - (void) snprintf(storepath, PATH_MAX, "%s/%s", - keystore_file, CERTSTORE); - keystore->clpath = xstrdup(storepath); - (void) snprintf(storepath, PATH_MAX, "%s/%s", - keystore_file, KEYSTORE); - keystore->keypath = xstrdup(storepath); - } - } - - return (B_TRUE); -} - -/* - * lock_keystore - Locks a keystore for shared (read-only) - * or exclusive (read-write) access. - * - * The flags tell lock_keystore how to behave: - * - * KEYSTORE_ACCESS_READONLY - * opens keystore read-only. Attempts to modify results in an error - * - * KEYSTORE_ACCESS_READWRITE - * opens keystore read-write - * - * KEYSTORE_PATH_SOFT - * If the keystore file does not exist at <base>/<app> then - * use <base> as the path to the keystore. This can be used, - * for example, to access an app-specific keystore iff it - * exists, otherwise revert back to an app-generic keystore. - * - * KEYSTORE_PATH_HARD - * Always use the keystore located at <keystore_path>/<app>. - * In read/write mode, if the files do not exist, then - * they will be created. This is used to avoid falling - * back to an app-generic keystore path when the app-specific - * one does not exist. - * - * Arguments: - * err - Error object to add errors to - * flags - Control flags (see above description) - * keystore - object which is being locked - * - * Returns: - * 0 - Success - Keystore file is locked, paths to - * appropriate files placed in keystore. - * non-zero - Failure, errors and reasons recorded in err - */ -static boolean_t -lock_keystore(PKG_ERR *err, long flags, keystore_t *keystore) -{ - boolean_t ret = B_TRUE; - struct stat buf; - - switch (flags & KEYSTORE_ACCESS_MASK) { - case KEYSTORE_ACCESS_READONLY: - if ((keystore->cafd = - open(keystore->capath, O_NONBLOCK|O_RDONLY)) == -1) { - if (errno == ENOENT) { - /* - * no keystore. try to create an - * empty one so we can lock on it and - * prevent others from gaining - * exclusive access. It will be - * deleted when the keystore is closed. - */ - if ((keystore->cafd = - open(keystore->capath, - O_NONBLOCK|O_RDWR|O_CREAT|O_EXCL, - S_IRUSR|S_IWUSR)) == -1) { - pkgerr_add(err, PKGERR_READ, - gettext(ERR_NO_KEYSTORE), - keystore->capath); - ret = B_FALSE; - goto cleanup; - } - } else { - pkgerr_add(err, PKGERR_READ, - gettext(ERR_KEYSTORE_OPEN), - keystore->capath, strerror(errno)); - ret = B_FALSE; - goto cleanup; - } - } - if (fstat(keystore->cafd, &buf) != -1) { - if (S_ISREG(buf.st_mode)) { - if (file_lock(keystore->cafd, F_RDLCK, - 0) == -1) { - pkgerr_add(err, PKGERR_LOCKED, - gettext(ERR_KEYSTORE_LOCKED_READ), - keystore->capath); - ret = B_FALSE; - goto cleanup; - } - } else { - /* ca file not a regular file! */ - pkgerr_add(err, PKGERR_READ, - gettext(ERR_NOT_REG), - keystore->capath); - ret = B_FALSE; - goto cleanup; - } - } else { - pkgerr_add(err, PKGERR_READ, - gettext(ERR_KEYSTORE_OPEN), - keystore->capath, strerror(errno)); - ret = B_FALSE; - goto cleanup; - } - break; - case KEYSTORE_ACCESS_READWRITE: - - if ((keystore->cafd = open(keystore->capath, - O_RDWR|O_NONBLOCK)) == -1) { - /* does not exist. try to create an empty one */ - if (errno == ENOENT) { - if ((keystore->cafd = - open(keystore->capath, - O_NONBLOCK|O_RDWR|O_CREAT|O_EXCL, - S_IRUSR|S_IWUSR)) == -1) { - pkgerr_add(err, PKGERR_READ, - gettext(ERR_KEYSTORE_WRITE), - keystore->capath); - ret = B_FALSE; - goto cleanup; - } - } else { - pkgerr_add(err, PKGERR_READ, - gettext(ERR_KEYSTORE_OPEN), - keystore->capath, strerror(errno)); - ret = B_FALSE; - goto cleanup; - } - } - if (fstat(keystore->cafd, &buf) != -1) { - if (S_ISREG(buf.st_mode)) { - if (file_lock(keystore->cafd, F_WRLCK, - 0) == -1) { - pkgerr_add(err, PKGERR_LOCKED, - gettext(ERR_KEYSTORE_LOCKED), - keystore->capath); - ret = B_FALSE; - goto cleanup; - } - } else { - /* ca file not a regular file! */ - pkgerr_add(err, PKGERR_READ, - gettext(ERR_NOT_REG), - keystore->capath); - ret = B_FALSE; - goto cleanup; - } - } else { - pkgerr_add(err, PKGERR_READ, - gettext(ERR_KEYSTORE_OPEN), - keystore->capath, strerror(errno)); - ret = B_FALSE; - goto cleanup; - } - - break; - default: - pkgerr_add(err, PKGERR_INTERNAL, - gettext(ERR_KEYSTORE_INTERNAL), - __FILE__, __LINE__); - ret = B_FALSE; - goto cleanup; - } - -cleanup: - if (!ret) { - if (keystore->cafd > 0) { - (void) file_unlock(keystore->cafd); - (void) close(keystore->cafd); - keystore->cafd = -1; - } - - if (keystore->capath != NULL) - free(keystore->capath); - if (keystore->clpath != NULL) - free(keystore->clpath); - if (keystore->keypath != NULL) - free(keystore->keypath); - keystore->capath = NULL; - keystore->clpath = NULL; - keystore->keypath = NULL; - } - - return (ret); -} - -/* - * unlock_keystore - Unocks a keystore - * - * Arguments: - * err - Error object to add errors to - * keystore - keystore object to unlock - * Returns: - * 0 - Success - Keystore files are unlocked, files are closed, - * non-zero - Failure, errors and reasons recorded in err - */ -/* ARGSUSED */ -static boolean_t -unlock_keystore(PKG_ERR *err, keystore_t *keystore) -{ - - /* - * Release lock on the CA file. - * Delete file if it is empty - */ - if (file_empty(keystore->capath)) { - (void) remove(keystore->capath); - } - - (void) file_unlock(keystore->cafd); - (void) close(keystore->cafd); - return (B_TRUE); -} - -/* - * read_keystore - Reads keystore files of disk, parses - * into internal structures. - * - * Arguments: - * err - Error object to add errors to - * keystore - keystore object to read into - * cb - callback to get password, if required - * Returns: - * 0 - Success - Keystore files are read, and placed - * into keystore structure. - * non-zero - Failure, errors and reasons recorded in err - */ -static boolean_t -read_keystore(PKG_ERR *err, keystore_t *keystore, keystore_passphrase_cb cb) -{ - boolean_t ret = B_TRUE; - PKCS12 *p12 = NULL; - boolean_t ca_empty; - boolean_t have_passwd = B_FALSE; - boolean_t cl_empty = B_TRUE; - boolean_t key_empty = B_TRUE; - - ca_empty = file_empty(keystore->capath); - - if (keystore->clpath != NULL) - cl_empty = file_empty(keystore->clpath); - if (keystore->keypath != NULL) - key_empty = file_empty(keystore->keypath); - - if (ca_empty && cl_empty && key_empty) { - keystore->new = B_TRUE; - } - - if (!ca_empty) { - /* first read the ca file */ - if ((p12 = read_keystore_file(err, - keystore->capath)) == NULL) { - pkgerr_add(err, PKGERR_CORRUPT, - gettext(ERR_KEYSTORE_CORRUPT), keystore->capath); - ret = B_FALSE; - goto cleanup; - } - - /* Get password, using callback if necessary */ - if (!have_passwd) { - if (!get_keystore_passwd(err, p12, cb, keystore)) { - ret = B_FALSE; - goto cleanup; - } - have_passwd = B_TRUE; - } - - /* decrypt and parse keystore file */ - if (sunw_PKCS12_contents(p12, keystore->passphrase, - &keystore->pkeys, &keystore->cacerts) < 0) { - /* could not parse the contents */ - pkgerr_add(err, PKGERR_CORRUPT, - gettext(ERR_KEYSTORE_CORRUPT), keystore->capath); - ret = B_FALSE; - goto cleanup; - } - - PKCS12_free(p12); - p12 = NULL; - } else { - - /* - * truststore is empty, so we don't have any trusted - * certs - */ - keystore->cacerts = NULL; - } - - /* - * if there is no cl file or key file, use the cl's and key's found - * in the ca file - */ - if (keystore->clpath == NULL && !ca_empty) { - if (sunw_split_certs(keystore->pkeys, keystore->cacerts, - &keystore->clcerts, NULL) < 0) { - pkgerr_add(err, PKGERR_CORRUPT, - gettext(ERR_KEYSTORE_CORRUPT), keystore->capath); - ret = B_FALSE; - goto cleanup; - } - } else { - /* - * files are in separate files. read keys out of the keystore - * certs out of the certstore, if they are not empty - */ - if (!cl_empty) { - if ((p12 = read_keystore_file(err, - keystore->clpath)) == NULL) { - pkgerr_add(err, PKGERR_CORRUPT, - gettext(ERR_KEYSTORE_CORRUPT), - keystore->clpath); - ret = B_FALSE; - goto cleanup; - } - - /* Get password, using callback if necessary */ - if (!have_passwd) { - if (!get_keystore_passwd(err, p12, cb, - keystore)) { - ret = B_FALSE; - goto cleanup; - } - have_passwd = B_TRUE; - } - - if (check_password(p12, - keystore->passphrase) == B_FALSE) { - /* - * password in client cert file - * is different than - * the one in the other files! - */ - pkgerr_add(err, PKGERR_BADPASS, - gettext(ERR_MISMATCHPASS), - keystore->clpath, - keystore->capath, keystore->path); - ret = B_FALSE; - goto cleanup; - } - - if (sunw_PKCS12_contents(p12, keystore->passphrase, - NULL, &keystore->clcerts) < 0) { - /* could not parse the contents */ - pkgerr_add(err, PKGERR_CORRUPT, - gettext(ERR_KEYSTORE_CORRUPT), - keystore->clpath); - ret = B_FALSE; - goto cleanup; - } - - PKCS12_free(p12); - p12 = NULL; - } else { - keystore->clcerts = NULL; - } - - if (!key_empty) { - if ((p12 = read_keystore_file(err, - keystore->keypath)) == NULL) { - pkgerr_add(err, PKGERR_CORRUPT, - gettext(ERR_KEYSTORE_CORRUPT), - keystore->keypath); - ret = B_FALSE; - goto cleanup; - } - - /* Get password, using callback if necessary */ - if (!have_passwd) { - if (!get_keystore_passwd(err, p12, cb, - keystore)) { - ret = B_FALSE; - goto cleanup; - } - have_passwd = B_TRUE; - } - - if (check_password(p12, - keystore->passphrase) == B_FALSE) { - pkgerr_add(err, PKGERR_BADPASS, - gettext(ERR_MISMATCHPASS), - keystore->keypath, - keystore->capath, keystore->path); - ret = B_FALSE; - goto cleanup; - } - - if (sunw_PKCS12_contents(p12, keystore->passphrase, - &keystore->pkeys, NULL) < 0) { - /* could not parse the contents */ - pkgerr_add(err, PKGERR_CORRUPT, - gettext(ERR_KEYSTORE_CORRUPT), - keystore->keypath); - ret = B_FALSE; - goto cleanup; - } - - PKCS12_free(p12); - p12 = NULL; - } else { - keystore->pkeys = NULL; - } - } - -cleanup: - if (p12 != NULL) - PKCS12_free(p12); - return (ret); -} - -/* - * get_keystore_password - retrieves pasword used to - * decrypt PKCS12 structure. - * - * Arguments: - * err - Error object to add errors to - * p12 - PKCS12 structure which returned password should - * decrypt - * cb - callback to collect password. - * keystore - The keystore in which the PKCS12 structure - * will eventually populate. - * Returns: - * B_TRUE - success. - * keystore password is set in keystore->passphrase. - * B_FALSE - failure, errors logged - */ -static boolean_t -get_keystore_passwd(PKG_ERR *err, PKCS12 *p12, keystore_passphrase_cb cb, - keystore_t *keystore) -{ - char *passwd; - char passbuf[KEYSTORE_PASS_MAX + 1]; - keystore_passphrase_data data; - - /* see if no password is the right password */ - if (check_password(p12, "") == B_TRUE) { - passwd = ""; - } else if (check_password(p12, NULL) == B_TRUE) { - passwd = NULL; - } else { - /* oops, it's encrypted. get password */ - data.err = err; - if (cb(passbuf, KEYSTORE_PASS_MAX, 0, - &data) == -1) { - /* could not get password */ - return (B_FALSE); - } - - if (check_password(p12, passbuf) == B_FALSE) { - /* wrong password */ - pkgerr_add(err, PKGERR_BADPASS, - gettext(ERR_BADPASS)); - return (B_FALSE); - } - - /* - * make copy of password buffer, since it - * goes away upon return - */ - passwd = xstrdup(passbuf); - } - keystore->passphrase = passwd; - return (B_TRUE); -} - -/* - * write_keystore - Writes keystore files to disk - * - * Arguments: - * err - Error object to add errors to - * keystore - keystore object to write from - * passwd - password used to encrypt keystore - * Returns: - * 0 - Success - Keystore contents are written out to - * the same locations as read from - * non-zero - Failure, errors and reasons recorded in err - */ -static boolean_t -write_keystore(PKG_ERR *err, keystore_t *keystore, - keystore_passphrase_cb cb) -{ - PKCS12 *p12 = NULL; - boolean_t ret = B_TRUE; - keystore_passphrase_data data; - char passbuf[KEYSTORE_PASS_MAX + 1]; - - if (keystore->capath != NULL && keystore->clpath == NULL && - keystore->keypath == NULL) { - - /* - * keystore is a file. - * just write out a single file - */ - if ((keystore->pkeys == NULL) && - (keystore->clcerts == NULL) && - (keystore->cacerts == NULL)) { - if (!clear_keystore_file(err, keystore->capath)) { - /* - * no keys or certs to write out, so - * blank the ca file. we do not - * delete it since it is used as a - * lock by lock_keystore() in - * subsequent invocations - */ - pkgerr_add(err, PKGERR_WRITE, - gettext(ERR_KEYSTORE_WRITE), - keystore->capath); - ret = B_FALSE; - goto cleanup; - } - } else { - /* - * if the keystore is being created for the first time, - * prompt for a passphrase for encryption - */ - if (keystore->new) { - data.err = err; - if (cb(passbuf, KEYSTORE_PASS_MAX, - 1, &data) == -1) { - ret = B_FALSE; - goto cleanup; - } - } else { - /* - * use the one used when the keystore - * was read - */ - (void) strlcpy(passbuf, keystore->passphrase, - KEYSTORE_PASS_MAX); - } - - p12 = sunw_PKCS12_create(passbuf, keystore->pkeys, - keystore->clcerts, keystore->cacerts); - - if (p12 == NULL) { - pkgerr_add(err, PKGERR_WRITE, - gettext(ERR_KEYSTORE_FORM), - keystore->capath); - ret = B_FALSE; - goto cleanup; - } - - if (!write_keystore_file(err, keystore->capath, p12)) { - pkgerr_add(err, PKGERR_WRITE, - gettext(ERR_KEYSTORE_WRITE), - keystore->capath); - ret = B_FALSE; - goto cleanup; - } - } - - } else { - /* files are seprate. Do one at a time */ - - /* - * if the keystore is being created for the first time, - * prompt for a passphrase for encryption - */ - if (keystore->new && ((keystore->pkeys != NULL) || - (keystore->clcerts != NULL) || - (keystore->cacerts != NULL))) { - data.err = err; - if (cb(passbuf, KEYSTORE_PASS_MAX, - 1, &data) == -1) { - ret = B_FALSE; - goto cleanup; - } - } else { - /* use the one used when the keystore was read */ - (void) strlcpy(passbuf, keystore->passphrase, - KEYSTORE_PASS_MAX); - } - - /* do private keys first */ - if (keystore->pkeys != NULL) { - p12 = sunw_PKCS12_create(passbuf, keystore->pkeys, - NULL, NULL); - - if (p12 == NULL) { - pkgerr_add(err, PKGERR_WRITE, - gettext(ERR_KEYSTORE_FORM), - keystore->keypath); - ret = B_FALSE; - goto cleanup; - } - - if (!write_keystore_file(err, keystore->keypath, - p12)) { - pkgerr_add(err, PKGERR_WRITE, - gettext(ERR_KEYSTORE_WRITE), - keystore->keypath); - ret = B_FALSE; - goto cleanup; - } - - PKCS12_free(p12); - } else { - if ((remove(keystore->keypath) != 0) && - (errno != ENOENT)) { - pkgerr_add(err, PKGERR_WRITE, - gettext(ERR_KEYSTORE_REMOVE), - keystore->keypath); - ret = B_FALSE; - goto cleanup; - } - } - - /* do user certs next */ - if (keystore->clcerts != NULL) { - p12 = sunw_PKCS12_create(passbuf, NULL, - keystore->clcerts, NULL); - - if (p12 == NULL) { - pkgerr_add(err, PKGERR_WRITE, - gettext(ERR_KEYSTORE_FORM), - keystore->clpath); - ret = B_FALSE; - goto cleanup; - } - - if (!write_keystore_file(err, keystore->clpath, p12)) { - pkgerr_add(err, PKGERR_WRITE, - gettext(ERR_KEYSTORE_WRITE), - keystore->clpath); - ret = B_FALSE; - goto cleanup; - } - - PKCS12_free(p12); - } else { - if ((remove(keystore->clpath) != 0) && - (errno != ENOENT)) { - pkgerr_add(err, PKGERR_WRITE, - gettext(ERR_KEYSTORE_REMOVE), - keystore->clpath); - ret = B_FALSE; - goto cleanup; - } - } - - - /* finally do CA cert file */ - if (keystore->cacerts != NULL) { - p12 = sunw_PKCS12_create(passbuf, NULL, - NULL, keystore->cacerts); - - if (p12 == NULL) { - pkgerr_add(err, PKGERR_WRITE, - gettext(ERR_KEYSTORE_FORM), - keystore->capath); - ret = B_FALSE; - goto cleanup; - } - - if (!write_keystore_file(err, keystore->capath, p12)) { - pkgerr_add(err, PKGERR_WRITE, - gettext(ERR_KEYSTORE_WRITE), - keystore->capath); - ret = B_FALSE; - goto cleanup; - } - - PKCS12_free(p12); - p12 = NULL; - } else { - /* - * nothing to write out, so truncate the file - * (it will be deleted during close_keystore) - */ - if (!clear_keystore_file(err, keystore->capath)) { - pkgerr_add(err, PKGERR_WRITE, - gettext(ERR_KEYSTORE_WRITE), - keystore->capath); - ret = B_FALSE; - goto cleanup; - } - } - } - -cleanup: - if (p12 != NULL) - PKCS12_free(p12); - - return (ret); -} - -/* - * clear_keystore_file - Clears (zeros out) a keystore file. - * - * Arguments: - * err - Error object to add errors to - * dest - Path of keystore file to zero out. - * Returns: - * 0 - Success - Keystore file is truncated to zero length - * non-zero - Failure, errors and reasons recorded in err - */ -static boolean_t -clear_keystore_file(PKG_ERR *err, char *dest) -{ - int fd; - struct stat buf; - - fd = open(dest, O_RDWR|O_NONBLOCK); - if (fd == -1) { - /* can't open for writing */ - pkgerr_add(err, PKGERR_WRITE, gettext(MSG_OPEN), - errno); - return (B_FALSE); - } - - if ((fstat(fd, &buf) == -1) || !S_ISREG(buf.st_mode)) { - /* not a regular file */ - (void) close(fd); - pkgerr_add(err, PKGERR_WRITE, gettext(ERR_NOT_REG), - dest); - return (B_FALSE); - } - - if (ftruncate(fd, 0) == -1) { - (void) close(fd); - pkgerr_add(err, PKGERR_WRITE, gettext(ERR_WRITE), - dest, strerror(errno)); - return (B_FALSE); - } - - (void) close(fd); - return (B_TRUE); -} - -/* - * write_keystore_file - Writes keystore file to disk. - * - * Keystore files can possibly be corrupted by a variety - * of error conditions during reading/writing. This - * routine, along with restore_keystore_file, tries to - * maintain keystore integity by writing the files - * out in a particular order, minimizing the time period - * that the keystore is in an indeterminate state. - * - * With the current implementation, there are some failures - * that are wholly unrecoverable, such as disk corruption. - * These routines attempt to minimize the risk, but not - * eliminate it. When better, atomic operations are available - * (such as a true database with commit, rollback, and - * guaranteed atomicity), this implementation should use that. - * - * - * Arguments: - * err - Error object to add errors to - * dest - Destination filename - * contents - Contents to write to the file - * Returns: - * 0 - Success - Keystore contents are written out to - * the destination. - * non-zero - Failure, errors and reasons recorded in err - */ -static boolean_t -write_keystore_file(PKG_ERR *err, char *dest, PKCS12 *contents) -{ - FILE *newfile = NULL; - boolean_t ret = B_TRUE; - char newpath[MAXPATHLEN]; - char backuppath[MAXPATHLEN]; - struct stat buf; - int fd; - - (void) snprintf(newpath, MAXPATHLEN, "%s.new", dest); - (void) snprintf(backuppath, MAXPATHLEN, "%s.bak", dest); - - if ((fd = open(newpath, O_CREAT|O_EXCL|O_WRONLY|O_NONBLOCK, - S_IRUSR|S_IWUSR)) == -1) { - pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN), - newpath, strerror(errno)); - ret = B_FALSE; - goto cleanup; - } - - if (fstat(fd, &buf) == -1) { - pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN), - newpath, strerror(errno)); - ret = B_FALSE; - goto cleanup; - } - - if (!S_ISREG(buf.st_mode)) { - pkgerr_add(err, PKGERR_READ, gettext(ERR_NOT_REG), - newpath); - ret = B_FALSE; - goto cleanup; - } - - if ((newfile = fdopen(fd, "w")) == NULL) { - pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN), - newpath, strerror(errno)); - ret = B_FALSE; - goto cleanup; - } - - if (i2d_PKCS12_fp(newfile, contents) == 0) { - pkgerr_add(err, PKGERR_WRITE, gettext(ERR_KEYSTORE_WRITE), - newpath); - ret = B_FALSE; - goto cleanup; - } - - /* flush, then close */ - (void) fflush(newfile); - (void) fclose(newfile); - newfile = NULL; - - /* now back up the original file */ - (void) rename(dest, backuppath); - - /* put new one in its place */ - (void) rename(newpath, dest); - - /* remove backup */ - (void) remove(backuppath); - -cleanup: - if (newfile != NULL) - (void) fclose(newfile); - if (fd != -1) - (void) close(fd); - - return (ret); -} - -/* - * read_keystore_file - Reads single keystore file - * off disk in PKCS12 format. - * - * Arguments: - * err - Error object to add errors to - * file - File path to read - * Returns: - * PKCS12 contents of file, or NULL if an error occurred. - * errors recorded in 'err'. - */ -static PKCS12 -*read_keystore_file(PKG_ERR *err, char *file) -{ - int fd; - struct stat buf; - FILE *newfile; - PKCS12 *p12 = NULL; - - if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) { - pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN), - file, strerror(errno)); - goto cleanup; - } - - if (fstat(fd, &buf) == -1) { - pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN), - file, strerror(errno)); - goto cleanup; - } - - if (!S_ISREG(buf.st_mode)) { - pkgerr_add(err, PKGERR_READ, gettext(ERR_NOT_REG), - file); - goto cleanup; - } - - if ((newfile = fdopen(fd, "r")) == NULL) { - pkgerr_add(err, PKGERR_READ, gettext(ERR_KEYSTORE_OPEN), - file, strerror(errno)); - goto cleanup; - } - - if ((p12 = d2i_PKCS12_fp(newfile, NULL)) == NULL) { - pkgerr_add(err, PKGERR_CORRUPT, - gettext(ERR_KEYSTORE_CORRUPT), file); - goto cleanup; - } - -cleanup: - if (newfile != NULL) - (void) fclose(newfile); - if (fd != -1) - (void) close(fd); - - return (p12); -} - - -/* - * Locks the specified file. - */ -static int -file_lock(int fd, int type, int wait) -{ - struct flock lock; - - lock.l_type = type; - lock.l_start = 0; - lock.l_whence = SEEK_SET; - lock.l_len = 0; - - if (!wait) { - if (file_lock_test(fd, type)) { - /* - * The caller would have to wait to get the - * lock on this file. - */ - return (-1); - } - } - - return (fcntl(fd, F_SETLKW, &lock)); -} - -/* - * Returns FALSE if the file is not locked; TRUE - * otherwise. - */ -static boolean_t -file_lock_test(int fd, int type) -{ - struct flock lock; - - lock.l_type = type; - lock.l_start = 0; - lock.l_whence = SEEK_SET; - lock.l_len = 0; - - if (fcntl(fd, F_GETLK, &lock) != -1) { - if (lock.l_type != F_UNLCK) { - /* - * The caller would have to wait to get the - * lock on this file. - */ - return (B_TRUE); - } - } - - /* - * The file is not locked. - */ - return (B_FALSE); -} - -/* - * Unlocks the specified file. - */ -static int -file_unlock(int fd) -{ - struct flock lock; - - lock.l_type = F_UNLCK; - lock.l_start = 0; - lock.l_whence = SEEK_SET; - lock.l_len = 0; - - return (fcntl(fd, F_SETLK, &lock)); -} - -/* - * Determines if file has a length of 0 or not - */ -static boolean_t -file_empty(char *path) -{ - struct stat buf; - - /* file is empty if size = 0 or it doesn't exist */ - if (lstat(path, &buf) == 0) { - if (buf.st_size == 0) { - return (B_TRUE); - } - } else { - if (errno == ENOENT) { - return (B_TRUE); - } - } - - return (B_FALSE); -} - -/* - * Name: get_time_string - * Description: Generates a human-readable string from an ASN1_TIME - * - * Arguments: intime - The time to convert - * - * Returns : A pointer to a static string representing the passed-in time. - */ -static char -*get_time_string(ASN1_TIME *intime) -{ - - static char time[ATTR_MAX]; - BIO *mem; - char *p; - - if (intime == NULL) { - return (NULL); - } - if ((mem = BIO_new(BIO_s_mem())) == NULL) { - return (NULL); - } - - if (ASN1_TIME_print(mem, intime) == 0) { - (void) BIO_free(mem); - return (NULL); - } - - if (BIO_gets(mem, time, ATTR_MAX) <= 0) { - (void) BIO_free(mem); - return (NULL); - } - - (void) BIO_free(mem); - - /* trim the end of the string */ - for (p = time + strlen(time) - 1; isspace(*p); p--) { - *p = '\0'; - } - - return (time); -} - -/* - * check_password - do various password checks to see if the current password - * will work or we need to prompt for a new one. - * - * Arguments: - * pass - password to check - * - * Returns: - * B_TRUE - Password is OK. - * B_FALSE - Password not valid. - */ -static boolean_t -check_password(PKCS12 *p12, char *pass) -{ - boolean_t ret = B_TRUE; - - /* - * If password is zero length or NULL then try verifying both cases - * to determine which password is correct. The reason for this is that - * under PKCS#12 password based encryption no password and a zero - * length password are two different things... - */ - - /* Check the mac */ - if (pass == NULL || *pass == '\0') { - if (PKCS12_verify_mac(p12, NULL, 0) == 0 && - PKCS12_verify_mac(p12, "", 0) == 0) - ret = B_FALSE; - } else if (PKCS12_verify_mac(p12, pass, -1) == 0) { - ret = B_FALSE; - } - return (ret); -} |