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