OpenSolaris_b135/lib/libpkg/common/keystore.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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, *before_str, *after_str;
	/* 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
				 */
				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 */
			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);
}