OpenSolaris_b135/lib/pkcs11/pkcs11_softtoken/common/softKeystoreUtil.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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Functions used for manipulating the keystore
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/types.h>
#include <dirent.h>
#include <limits.h>
#include <strings.h>
#include <security/cryptoki.h>
#include <cryptoutil.h>
#include "softGlobal.h"
#include "softObject.h"
#include "softSession.h"
#include "softKeystore.h"
#include "softKeystoreUtil.h"

#define	MAXPATHLEN	1024
#define	SUNW_PATH	".sunw"		/* top level Sun directory */
#define	KEYSTORE_PATH	"pkcs11_softtoken"	/* keystore directory */
#define	PUB_OBJ_DIR	"public"	/* directory for public objects */
#define	PRI_OBJ_DIR	"private"	/* directory for private objects */
#define	DS_FILE		"objstore_info"	/* keystore description file */
#define	TMP_DS_FILE	"t_info"	/* temp name for keystore desc. file */
#define	OBJ_PREFIX	"obj"	/* prefix of the keystore object file names */
#define	OBJ_PREFIX_LEN	sizeof (OBJ_PREFIX) - 1	/* length of prefix */
#define	TMP_OBJ_PREFIX	"t_o"	/* prefix of the temp object file names */

/*
 * KEYSTORE DESCRIPTION FILE:
 *
 * The following describes the content of the keystore description file
 *
 * The order AND data type of the fields are very important.
 * All the code in this file assume that they are in the order specified
 * below.  If either order of the fields or their data type changed,
 * you must make sure the ALL the pre-define values are still valid
 *
 * 1) PKCS#11 release number.  It's 2.20 in this release (uchar_t[32])
 * 2) keystore version number: used for synchronizing when different
 *    processes access the keystore at the same time.  It is incremented
 *    when there is a change to the keystore. (uint_32)
 * 3) monotonic-counter: last counter value for name of token object file.
 *    used for assigning unique name to each token (uint_32)
 * 4) salt used for generating encryption key (uint_16)
 * 5) salt used for generating key used for doing HMAC (uint_16)
 * 6) Length of salt used for generating hashed pin (length of salt
 *    is variable)
 * 7) Salt used for generating hashed pin.
 * 8) Hashed pin len (length of hashed pin could be variable, the offset of
 *    where this value lives in the file is calculated at run time)
 * 9) Hashed pin
 *
 */

/* Keystore description file pre-defined values */
#define	KS_PKCS11_VER		"2.20"
#define	KS_PKCS11_OFFSET	0
#define	KS_PKCS11_VER_SIZE	32

#define	KS_VER_OFFSET		(KS_PKCS11_OFFSET + KS_PKCS11_VER_SIZE)
#define	KS_VER_SIZE	4	/* size in bytes of keystore version value */

#define	KS_COUNTER_OFFSET	(KS_VER_OFFSET + KS_VER_SIZE)
#define	KS_COUNTER_SIZE	4	/* size in bytes of the monotonic counter */

#define	KS_KEY_SALT_OFFSET	(KS_COUNTER_OFFSET + KS_COUNTER_SIZE)
#define	KS_KEY_SALT_SIZE	PBKD2_SALT_SIZE

#define	KS_HMAC_SALT_OFFSET	(KS_KEY_SALT_OFFSET + KS_KEY_SALT_SIZE)
#define	KS_HMAC_SALT_SIZE	PBKD2_SALT_SIZE

/* Salt for hashed pin */
#define	KS_HASHED_PIN_SALT_LEN_OFFSET (KS_HMAC_SALT_OFFSET + KS_HMAC_SALT_SIZE)
#define	KS_HASHED_PIN_SALT_LEN_SIZE 8 /* stores length of hashed pin salt */

#define	KS_HASHED_PIN_SALT_OFFSET \
		(KS_HASHED_PIN_SALT_LEN_OFFSET + KS_HASHED_PIN_SALT_LEN_SIZE)

/*
 * hashed pin
 *
 * hashed_pin length offset will be calculated at run time since
 * there's the hashed pin salt size is variable.
 *
 * The offset will be calculated at run time by calling the
 * function calculate_hashed_pin_offset()
 */
static off_t	ks_hashed_pinlen_offset = -1;
#define	KS_HASHED_PINLEN_SIZE	8

/* End of Keystore description file pre-defined values */

/*
 * Metadata for each object
 *
 * The order AND data type of all the fields is very important.
 * All the code in this file assume that they are in the order specified
 * below.  If either order of the fields or their data type is changed,
 * you must make sure the following pre-define value is still valid
 * Each object will have the meta data at the beginning of the object file.
 *
 * 1) object_version: used by softtoken to see if the object
 *    has been modified since it last reads it. (uint_32)
 * 2) iv: initialization vector for encrypted data in the object.  This
 *    value will be 0 for public objects.  (uchar_t[16])
 * 3) obj_hmac: keyed hash as verifier to detect private object
 *    being tampered this value will be 0 for public objects (uchar_t[16])
 */

/* Object metadata pre-defined values */
#define	OBJ_VER_OFFSET	0
#define	OBJ_VER_SIZE	4	/* size of object version in bytes */
#define	OBJ_IV_OFFSET	(OBJ_VER_OFFSET + OBJ_VER_SIZE)
#define	OBJ_IV_SIZE	16
#define	OBJ_HMAC_OFFSET	(OBJ_IV_OFFSET + OBJ_IV_SIZE)
#define	OBJ_HMAC_SIZE	16	/* MD5 HMAC keyed hash */
#define	OBJ_DATA_OFFSET	(OBJ_HMAC_OFFSET + OBJ_HMAC_SIZE)
/* End of object metadata pre-defined values */

#define	ALTERNATE_KEYSTORE_PATH	"SOFTTOKEN_DIR"

static soft_object_t	*enc_key = NULL;
static soft_object_t	*hmac_key = NULL;
static char		keystore_path[MAXPATHLEN];
static boolean_t	keystore_path_initialized = B_FALSE;
static int		desc_fd = 0;


static char *
get_user_home_sunw_path(char *home_path)
{
	struct passwd pwd, *user_info;
	char pwdbuf[PWD_BUFFER_SIZE];

	(void) getpwuid_r(getuid(), &pwd, pwdbuf, PWD_BUFFER_SIZE, &user_info);

	(void) snprintf(home_path, MAXPATHLEN, "%s/%s",
	    user_info ? user_info->pw_dir : "", SUNW_PATH);

	return (home_path);
}

static char *
get_keystore_path()
{
	char *env_val;
	char home_path[MAXPATHLEN];

	if (!keystore_path_initialized) {
		env_val = getenv(ALTERNATE_KEYSTORE_PATH);
		bzero(keystore_path, sizeof (keystore_path));
		/*
		 * If it isn't set or is set to the empty string use the
		 * default location.  We need to check for the empty string
		 * because some users "unset" environment variables by giving
		 * them no value, this isn't the same thing as removing it
		 * from the environment.
		 *
		 * We don't want that to attempt to open /.sunw/pkcs11_sofftoken
		 */
		if ((env_val == NULL) || (strcmp(env_val, "") == 0)) {
			/* alternate path not specified, use user's home dir */
			(void) snprintf(keystore_path, MAXPATHLEN, "%s/%s",
			    get_user_home_sunw_path(home_path), KEYSTORE_PATH);
		} else {
			(void) snprintf(keystore_path, MAXPATHLEN, "%s/%s",
			    env_val, KEYSTORE_PATH);
		}
		keystore_path_initialized = B_TRUE;
	}
	return (keystore_path);
}

static char *
get_pub_obj_path(char *name)
{
	bzero(name, sizeof (name));
	(void) snprintf(name, MAXPATHLEN, "%s/%s",
	    get_keystore_path(), PUB_OBJ_DIR);
	return (name);
}

static char *
get_pri_obj_path(char *name)
{
	bzero(name, sizeof (name));
	(void) snprintf(name, MAXPATHLEN, "%s/%s",
	    get_keystore_path(), PRI_OBJ_DIR);
	return (name);
}

static char *
get_desc_file_path(char *name)
{
	bzero(name, sizeof (name));
	(void) snprintf(name, MAXPATHLEN, "%s/%s",
	    get_keystore_path(), DS_FILE);
	return (name);
}

static char *
get_tmp_desc_file_path(char *name)
{
	bzero(name, sizeof (name));
	(void) snprintf(name, MAXPATHLEN, "%s/%s",
	    get_keystore_path(), TMP_DS_FILE);
	return (name);
}

/*
 * Calculates the offset for hashed_pin length and hashed pin
 *
 * Returns 0 if successful, -1 if there's any error.
 *
 * If successful, global variables "ks_hashed_pinlen_offset" will be set.
 *
 */
static int
calculate_hashed_pin_offset(int fd)
{
	uint64_t salt_length;

	if (lseek(fd, KS_HASHED_PIN_SALT_LEN_OFFSET, SEEK_SET)
	    != KS_HASHED_PIN_SALT_LEN_OFFSET) {
		return (-1);
	}

	if (readn_nointr(fd, (char *)&salt_length,
	    KS_HASHED_PIN_SALT_LEN_SIZE) != KS_HASHED_PIN_SALT_LEN_SIZE) {
		return (-1);
	}
	salt_length = SWAP64(salt_length);

	ks_hashed_pinlen_offset = KS_HASHED_PIN_SALT_LEN_OFFSET
	    + KS_HASHED_PIN_SALT_LEN_SIZE + salt_length;

	return (0);

}

/*
 * acquire or release read/write lock on a specific file
 *
 * read_lock: true for read lock; false for write lock
 * set_lock:  true to set a lock; false to release a lock
 */
static int
lock_file(int fd, boolean_t read_lock, boolean_t set_lock)
{

	flock_t lock_info;
	int r;

	lock_info.l_whence = SEEK_SET;
	lock_info.l_start = 0;
	lock_info.l_len = 0; /* l_len == 0 means until end of  file */

	if (read_lock) {
		lock_info.l_type = F_RDLCK;
	} else {
		lock_info.l_type = F_WRLCK;
	}

	if (set_lock) {
		while ((r = fcntl(fd, F_SETLKW, &lock_info)) == -1) {
			if (errno != EINTR)
				break;
		}
		if (r == -1) {
			return (-1);
		}
	} else {
		lock_info.l_type = F_UNLCK;
		while ((r = fcntl(fd, F_SETLKW, &lock_info)) == -1) {
			if (errno != EINTR)
				break;
		}
		if (r == -1) {
			return (-1);
		}
	}

	return (0);
}

static int
create_keystore()
{
	int fd, buf;
	uint64_t hashed_pin_len, hashed_pin_salt_len, ulong_buf;
	uchar_t ver_buf[KS_PKCS11_VER_SIZE];
	char pub_obj_path[MAXPATHLEN], pri_obj_path[MAXPATHLEN],
	    ks_desc_file[MAXPATHLEN];
	CK_BYTE salt[KS_KEY_SALT_SIZE];
	char *hashed_pin = NULL, *hashed_pin_salt = NULL;
	char *env_val;

	/* keystore doesn't exist, create keystore directory */
	if (mkdir(get_keystore_path(), S_IRUSR|S_IWUSR|S_IXUSR) < 0) {
		if (errno == EEXIST) {
			return (0);
		}

		if (errno == EACCES) {
			return (-1);
		}

		/* can't create keystore directory */
		if (errno == ENOENT) { /* part of the path doesn't exist */
			/*
			 * try to create $HOME/.sunw if it doesn't
			 * exist.  If it is a alternate path provided by the
			 * user, it should have existed.  Will not
			 * create for them.
			 */
			env_val = getenv(ALTERNATE_KEYSTORE_PATH);
			if ((env_val == NULL) || (strcmp(env_val, "") == 0)) {
				char sunw_path[MAXPATHLEN];

				/* create $HOME/.sunw */
				if (mkdir(get_user_home_sunw_path(sunw_path),
				    S_IRUSR|S_IWUSR|S_IXUSR) < 0) {
					return (-1);
				}

				/* create $HOME/.sunw/pkcs11_softtoken */
				if (mkdir(get_keystore_path(),
				    S_IRUSR|S_IWUSR|S_IXUSR) < 0) {
					return (-1);
				}
			} else {
				return (-1);
			}
		}
	}

	/* create keystore description file */
	fd = open_nointr(get_desc_file_path(ks_desc_file),
	    O_RDWR|O_CREAT|O_EXCL|O_NONBLOCK, S_IRUSR|S_IWUSR);
	if (fd < 0) {
		if (errno == EEXIST) {
			return (0);
		} else {
			/* can't create keystore description file */
			(void) rmdir(get_keystore_path());
			return (-1);
		}
	}

	if (lock_file(fd, B_FALSE, B_TRUE) != 0) {
		(void) unlink(ks_desc_file);
		(void) close(fd);
		(void) rmdir(get_keystore_path());
		return (-1);
	}

	if (mkdir(get_pub_obj_path(pub_obj_path),
	    S_IRUSR|S_IWUSR|S_IXUSR) < 0) {
		/* can't create directory for public objects */
		(void) lock_file(fd, B_FALSE, B_FALSE);
		(void) unlink(ks_desc_file);
		(void) close(fd);
		(void) rmdir(get_keystore_path());
		return (-1);
	}

	if (mkdir(get_pri_obj_path(pri_obj_path),
	    S_IRUSR|S_IWUSR|S_IXUSR) < 0) {
		/* can't create directory for private objects */
		(void) lock_file(fd, B_FALSE, B_FALSE);
		(void) unlink(ks_desc_file);
		(void) close(fd);
		(void) rmdir(get_keystore_path());
		(void) rmdir(pub_obj_path);
		return (-1);
	}


	/* write file format release number */
	bzero(ver_buf, sizeof (ver_buf));
	(void) strcpy((char *)ver_buf, KS_PKCS11_VER);
	if ((writen_nointr(fd, (char *)ver_buf, sizeof (ver_buf)))
	    != sizeof (ver_buf)) {
		goto cleanup;
	}

	/* write version number, version = 0 since keystore just created */
	buf = SWAP32(0);
	if (writen_nointr(fd, (void *)&buf, KS_VER_SIZE) != KS_VER_SIZE) {
		goto cleanup;
	}

	/* write monotonic-counter.  Counter for keystore objects start at 1 */
	buf = SWAP32(1);
	if (writen_nointr(fd, (void *)&buf, KS_COUNTER_SIZE)
	    != KS_COUNTER_SIZE) {
		goto cleanup;
	}

	/* initial encryption key salt should be all NULL */
	bzero(salt, sizeof (salt));
	if (writen_nointr(fd, (void *)salt, KS_KEY_SALT_SIZE)
	    != KS_KEY_SALT_SIZE) {
		goto cleanup;
	}

	/* initial HMAC key salt should also be all NULL */
	if (writen_nointr(fd, (void *)salt, KS_HMAC_SALT_SIZE)
	    != KS_HMAC_SALT_SIZE) {
		goto cleanup;
	}

	/* generate the hashed pin salt, and MD5 hashed pin of default pin */
	if (soft_gen_hashed_pin((CK_CHAR_PTR)SOFT_DEFAULT_PIN, &hashed_pin,
	    &hashed_pin_salt) < 0) {
		goto cleanup;
	}

	if ((hashed_pin_salt == NULL) || (hashed_pin == NULL)) {
		goto cleanup;
	}

	hashed_pin_salt_len = (uint64_t)strlen(hashed_pin_salt);
	hashed_pin_len = (uint64_t)strlen(hashed_pin);

	/* write hashed pin salt length */
	ulong_buf = SWAP64(hashed_pin_salt_len);
	if (writen_nointr(fd, (void *)&ulong_buf, KS_HASHED_PIN_SALT_LEN_SIZE)
	    != KS_HASHED_PIN_SALT_LEN_SIZE) {
		goto cleanup;
	}

	if (writen_nointr(fd, (void *)hashed_pin_salt,
	    hashed_pin_salt_len) != hashed_pin_salt_len) {
		goto cleanup;
	}

	/* write MD5 hashed pin of the default pin */
	ulong_buf = SWAP64(hashed_pin_len);
	if (writen_nointr(fd, (void *)&ulong_buf, KS_HASHED_PINLEN_SIZE)
	    != KS_HASHED_PINLEN_SIZE) {
		goto cleanup;
	}

	if (writen_nointr(fd, (void *)hashed_pin, hashed_pin_len)
	    != hashed_pin_len) {
		goto cleanup;
	}

	(void) lock_file(fd, B_FALSE, B_FALSE);

	(void) close(fd);
	if (hashed_pin_salt)
		free(hashed_pin_salt);
	return (0);

cleanup:
	(void) lock_file(fd, B_FALSE, B_FALSE);
	(void) unlink(ks_desc_file);
	(void) close(fd);
	(void) rmdir(get_keystore_path());
	(void) rmdir(pub_obj_path);
	(void) rmdir(pri_obj_path);
	return (-1);
}

/*
 * Determines if the file referenced by "fd" has the same
 * inode as the file referenced by "fname".
 *
 * The argument "same" contains the result of determining
 * if the inode is the same or not
 *
 * Returns 0 if there's no error.
 * Returns 1 if there's any error with opening the file.
 *
 *
 */
static int
is_inode_same(int fd, char *fname, boolean_t *same)
{
	struct stat fn_stat, fd_stat;

	if (fstat(fd, &fd_stat) != 0) {
		return (-1);
	}

	if (stat(fname, &fn_stat) != 0) {
		return (-1);
	}

	/* It's the same file if both st_ino and st_dev match */
	if ((fd_stat.st_ino == fn_stat.st_ino) &&
	    (fd_stat.st_dev == fn_stat.st_dev)) {
		*same = B_TRUE;
	} else {
		*same = B_FALSE;
	}
	return (0);
}

static int
acquire_file_lock(int *fd, char *fname, mode_t mode) {

	boolean_t read_lock = B_TRUE, same_inode;

	if ((mode == O_RDWR) || (mode == O_WRONLY)) {
		read_lock = B_FALSE;
	}

	if (lock_file(*fd, read_lock, B_TRUE) != 0) {
		return (-1);
	}

	/*
	 * make sure another process did not modify the file
	 * while we were trying to get the lock
	 */
	if (is_inode_same(*fd, fname, &same_inode) != 0) {
		(void) lock_file(*fd, B_TRUE, B_FALSE); /* unlock file */
		return (-1);
	}

	while (!same_inode) {
		/*
		 * need to unlock file, close, re-open the file,
		 * and re-acquire the lock
		 */

		/* unlock file */
		if (lock_file(*fd, B_TRUE, B_FALSE) != 0) {
			return (-1);
		}

		(void) close(*fd);

		/* re-open */
		*fd = open_nointr(fname, mode|O_NONBLOCK);
		if (*fd < 0) {
			return (-1);
		}

		/* acquire lock again */
		if (lock_file(*fd, read_lock, B_TRUE) != 0) {
			return (-1);
		}

		if (is_inode_same(*fd, fname, &same_inode) != 0) {
			(void) lock_file(*fd, B_TRUE, B_FALSE); /* unlock */
			return (-1);
		}

	}

	return (0);
}

/*
 * Open the keystore description file in the specified mode.
 * If the keystore doesn't exist, the "do_create_keystore"
 * argument determines if the keystore should be created
 */
static int
open_and_lock_keystore_desc(mode_t mode, boolean_t do_create_keystore,
    boolean_t lock_held)
{

	int fd;
	char *fname, ks_desc_file[MAXPATHLEN];

	/* open the keystore description file in requested mode */
	fname = get_desc_file_path(ks_desc_file);
	fd = open_nointr(fname, mode|O_NONBLOCK);
	if (fd < 0) {
		if ((errno == ENOENT) && (do_create_keystore)) {
			if (create_keystore() < 0) {
				goto done;
			}
			fd = open_nointr(fname, mode|O_NONBLOCK);
			if (fd < 0) {
				goto done;
			}
		} else {
			goto done;
		}
	}

	if (lock_held) {
		/* already hold the lock */
		return (fd);
	}

	if (acquire_file_lock(&fd, fname, mode) != 0) {
		if (fd > 0) {
			(void) close(fd);
		}
		return (-1);
	}

done:
	return (fd);
}


/*
 * Set or remove read or write lock on keystore description file
 *
 * read_lock: true for read lock, false for write lock
 * set_lock: true for set a lock, false to remove a lock
 */
static int
lock_desc_file(boolean_t read_lock, boolean_t set_lock)
{

	char ks_desc_file[MAXPATHLEN];

	if (set_lock) {
		int oflag;

		/*
		 * make sure desc_fd is not already used.  If used, it means
		 * some other lock is already set on the file
		 */
		if (desc_fd > 0) {
			return (-1);
		}

		(void) get_desc_file_path(ks_desc_file);

		if (read_lock) {
			oflag = O_RDONLY;
		} else {
			oflag = O_WRONLY;
		}
		if ((desc_fd = open_and_lock_keystore_desc(oflag,
		    B_FALSE, B_FALSE)) < 0) {
			return (-1);
		}
	} else {
		/* make sure we have a valid fd */
		if (desc_fd <= 0) {
			return (-1);
		}

		if (lock_file(desc_fd, read_lock, B_FALSE) == 1) {
			return (-1);
		}

		(void) close(desc_fd);
		desc_fd = 0;

	}
	return (0);
}

static int
open_and_lock_object_file(ks_obj_handle_t *ks_handle, int oflag,
    boolean_t lock_held)
{
	char obj_fname[MAXPATHLEN];
	int fd;

	if (ks_handle->public) {
		char pub_obj_path[MAXPATHLEN];
		(void) snprintf(obj_fname, MAXPATHLEN, "%s/%s",
		    get_pub_obj_path(pub_obj_path), ks_handle->name);
	} else {
		char pri_obj_path[MAXPATHLEN];
		(void) snprintf(obj_fname, MAXPATHLEN, "%s/%s",
		    get_pri_obj_path(pri_obj_path), ks_handle->name);
	}

	fd = open_nointr(obj_fname, oflag|O_NONBLOCK);
	if (fd < 0) {
		return (-1);
	}

	if (lock_held) {
		/* already hold the lock */
		return (fd);
	}

	if (acquire_file_lock(&fd, obj_fname, oflag) != 0) {
		if (fd > 0) {
			(void) close(fd);
		}
		return (-1);
	}


	return (fd);
}


/*
 * Update file version number in a temporary file that's
 * a copy of the keystore description file.
 * The update is NOT made to the original keystore description
 * file.  It makes the update in a tempoary file.
 *
 * Name of the temporary file is assumed to be provided, but
 * the file is assumed to not exist.
 *
 * return 0 if creating temp file is successful, returns -1 otherwise
 */
static int
create_updated_keystore_version(int fd, char *tmp_fname)
{
	int version, tmp_fd;
	char buf[BUFSIZ];
	size_t nread;

	/* first, create the tempoary file */
	tmp_fd = open_nointr(tmp_fname,
	    O_WRONLY|O_CREAT|O_EXCL|O_NONBLOCK, S_IRUSR|S_IWUSR);
	if (tmp_fd < 0) {
		return (-1);
	}

	/*
	 * copy everything from keystore version to temp file except
	 * the keystore version.  Keystore version is updated
	 *
	 */

	/* pkcs11 version */
	if (readn_nointr(fd, buf, KS_PKCS11_VER_SIZE) != KS_PKCS11_VER_SIZE) {
		goto cleanup;
	}

	if (writen_nointr(tmp_fd, buf, KS_PKCS11_VER_SIZE) !=
	    KS_PKCS11_VER_SIZE) {
		goto cleanup;
	}

	/* version number, it needs to be updated */

	/* read the current version number */
	if (readn_nointr(fd, &version, KS_VER_SIZE) != KS_VER_SIZE) {
		goto cleanup;
	}

	version = SWAP32(version);
	version++;
	version = SWAP32(version);

	/* write the updated value to the tmp file */
	if (writen_nointr(tmp_fd, (void *)&version, KS_VER_SIZE)
	    != KS_VER_SIZE) {
		goto cleanup;
	}

	/* read rest of information, nothing needs to be updated */
	nread = readn_nointr(fd, buf, BUFSIZ);
	while (nread > 0) {
		if (writen_nointr(tmp_fd, buf, nread) != nread) {
			goto cleanup;
		}
		nread = readn_nointr(fd, buf, BUFSIZ);
	}

	(void) close(tmp_fd);
	return (0);	/* no error */

cleanup:
	(void) close(tmp_fd);
	(void) remove(tmp_fname);
	return (-1);
}

static CK_RV
get_all_objs_in_dir(DIR *dirp, ks_obj_handle_t *ks_handle,
    ks_obj_t **result_obj_list, boolean_t lock_held)
{
	struct dirent *dp;
	ks_obj_t *obj;
	CK_RV rv;

	while ((dp = readdir(dirp)) != NULL) {

		if (strncmp(dp->d_name, OBJ_PREFIX, OBJ_PREFIX_LEN) != 0)
			continue;

		(void) strcpy((char *)ks_handle->name, dp->d_name);
		rv = soft_keystore_get_single_obj(ks_handle, &obj, lock_held);
		if (rv != CKR_OK) {
			return (rv);
		}
		if (obj != NULL) {
			if (*result_obj_list == NULL) {
				*result_obj_list = obj;
			} else {
				obj->next = *result_obj_list;
				*result_obj_list = obj;
			}
		}
	}
	return (CKR_OK);
}

/*
 * This function prepares the obj data for encryption by prepending
 * the FULL path of the file that will be used for storing
 * the object.  Having full path of the file as part of
 * of the data for the object will prevent an attacker from
 * copying a "bad" object into the keystore undetected.
 *
 * This function will always allocate:
 *	MAXPATHLEN + buf_len
 * amount of data.  If the full path of the filename doesn't occupy
 * the whole MAXPATHLEN, the rest of the space will just be empty.
 * It is the caller's responsibility to free the buffer allocated here.
 *
 * The allocated buffer is returned in the variable "prepared_buf"
 * if there's no error.
 *
 * Returns 0 if there's no error, -1 otherwise.
 */
static int
prepare_data_for_encrypt(char *obj_path, unsigned char *buf, CK_ULONG buf_len,
    unsigned char **prepared_buf, CK_ULONG *prepared_len)
{
	*prepared_len = MAXPATHLEN + buf_len;
	*prepared_buf = malloc(*prepared_len);
	if (*prepared_buf == NULL) {
		return (-1);
	}

	/*
	 * only zero out the space for the path name.  I could zero out
	 * the whole buffer, but that will be a waste of processing
	 * cycle since the rest of the buffer will be 100% filled all
	 * the time
	 */
	bzero(*prepared_buf, MAXPATHLEN);
	(void) memcpy(*prepared_buf, obj_path, strlen(obj_path));
	(void) memcpy(*prepared_buf + MAXPATHLEN, buf, buf_len);
	return (0);
}

/*
 * retrieves the hashed pin from the keystore
 */
static CK_RV
get_hashed_pin(int fd, char **hashed_pin)
{
	uint64_t hashed_pin_size;

	if (ks_hashed_pinlen_offset == -1) {
		if (calculate_hashed_pin_offset(fd) != 0) {
			return (CKR_FUNCTION_FAILED);
		}
	}

	/* first, get size of the hashed pin */
	if (lseek(fd, ks_hashed_pinlen_offset, SEEK_SET)
	    != ks_hashed_pinlen_offset) {
		return (CKR_FUNCTION_FAILED);
	}

	if (readn_nointr(fd, (char *)&hashed_pin_size,
	    KS_HASHED_PINLEN_SIZE) != KS_HASHED_PINLEN_SIZE) {
		return (CKR_FUNCTION_FAILED);
	}

	hashed_pin_size = SWAP64(hashed_pin_size);

	*hashed_pin = malloc(hashed_pin_size + 1);
	if (*hashed_pin == NULL) {
		return (CKR_HOST_MEMORY);
	}

	if ((readn_nointr(fd, *hashed_pin, hashed_pin_size))
	    != (ssize_t)hashed_pin_size) {
		free(*hashed_pin);
		*hashed_pin = NULL;
		return (CKR_FUNCTION_FAILED);
	}
	(*hashed_pin)[hashed_pin_size] = '\0';
	return (CKR_OK);
}


/*
 *	FUNCTION: soft_keystore_lock
 *
 *	ARGUMENTS:
 *		set_lock: TRUE to set readlock on the keystore object file,
 *		          FALSE to remove readlock on keystore object file.
 *
 *	RETURN VALUE:
 *
 *		0: success
 *		-1: failure
 *
 *	DESCRIPTION:
 *
 *		set or remove readlock on the keystore description file.
 */
int
soft_keystore_readlock(boolean_t set_lock)
{

	return (lock_desc_file(B_TRUE, set_lock));
}


/*
 *	FUNCTION: soft_keystore_writelock
 *
 *	ARGUMENTS:
 * 		set_lock: TRUE to set writelock on the keystore description file
 *		        FALSE to remove write lock on keystore description file.
 *
 *	RETURN VALUE:
 *
 *		0: no error
 *		1: some error occurred
 *
 *	DESCRIPTION:
 *		set/reset writelock on the keystore description file.
 */
int
soft_keystore_writelock(boolean_t set_lock)
{
	return (lock_desc_file(B_FALSE, set_lock));

}

/*
 *
 *	FUNCTION: soft_keystore_lock_object
 *
 *	ARGUMENTS:
 *
 *		ks_handle: handle of the keystore object file to be accessed.
 * 		read_lock: TRUE to set readlock on the keystore object file,
 *			  FALSE to set writelock on keystore object file.
 *
 *	RETURN VALUE:
 *
 *		If no error, file descriptor of locked file will be returned
 *		-1: some error occurred
 *
 *	DESCRIPTION:
 *
 *		set readlock or writelock on the keystore object file.
 */
int
soft_keystore_lock_object(ks_obj_handle_t *ks_handle, boolean_t read_lock)
{
	int fd;
	int oflag;

	if (read_lock) {
		oflag = O_RDONLY;
	} else {
		oflag = O_WRONLY;
	}

	if ((fd = open_and_lock_object_file(ks_handle, oflag, B_FALSE)) < 0) {
		return (-1);
	}

	return (fd);
}

/*
 *	FUNCTION: soft_keystore_unlock_object
 *
 *	ARGUMENTS:
 *		fd: file descriptor returned from soft_keystore_lock_object
 *
 *	RETURN VALUE:
 *		0: no error
 *		1: some error occurred while getting the pin
 *
 *	DESCRIPTION:
 *		set/reset writelock on the keystore object file.
 */
int
soft_keystore_unlock_object(int fd)
{
	if (lock_file(fd, B_TRUE, B_FALSE) != 0) {
		return (1);
	}

	(void) close(fd);
	return (0);
}



/*
 *	FUNCTION: soft_keystore_get_version
 *
 *	ARGUMENTS:
 *		version: pointer to caller allocated memory for storing
 *			 the version of the keystore.
 *		lock_held: TRUE if the lock is held by caller.
 *
 *	RETURN VALUE:
 *
 *		0: no error
 *		-1: some error occurred while getting the version number
 *
 *	DESCRIPTION:
 *		get the version number of the keystore from keystore
 *		description file.
 */
int
soft_keystore_get_version(uint_t *version, boolean_t lock_held)
{
	int fd, ret_val = 0;
	uint_t buf;

	if ((fd = open_and_lock_keystore_desc(O_RDONLY,
	    B_TRUE, lock_held)) < 0) {
		return (-1);
	}

	if (lseek(fd, KS_VER_OFFSET, SEEK_SET) != KS_VER_OFFSET) {
		ret_val = -1;
		goto cleanup;
	}

	if (readn_nointr(fd, (char *)&buf, KS_VER_SIZE) != KS_VER_SIZE) {
		ret_val = -1;
		goto cleanup;
	}
	*version = SWAP32(buf);

cleanup:

	if (!lock_held) {
		if (lock_file(fd, B_TRUE, B_FALSE) < 0) {
			ret_val = -1;
		}
	}

	(void) close(fd);
	return (ret_val);
}

/*
 *	FUNCTION: soft_keystore_get_object_version
 *
 *	ARGUMENTS:
 *
 *		ks_handle: handle of the key store object to be accessed.
 *		version:
 *			pointer to caller allocated memory for storing
 *			the version of the object.
 *		lock_held: TRUE if the lock is held by caller.
 *
 *	RETURN VALUE:
 *
 *		0: no error
 *		-1: some error occurred while getting the pin
 *
 *	DESCRIPTION:
 *		get the version number of the specified token object.
 */
int
soft_keystore_get_object_version(ks_obj_handle_t *ks_handle,
    uint_t *version, boolean_t lock_held)
{
	int fd, ret_val = 0;
	uint_t tmp;

	if ((fd = open_and_lock_object_file(ks_handle, O_RDONLY,
	    lock_held)) < 0) {
		return (-1);
	}

	/*
	 * read version.  Version is always first item in object file
	 * so, no need to do lseek
	 */
	if (readn_nointr(fd, (char *)&tmp, OBJ_VER_SIZE) != OBJ_VER_SIZE) {
		ret_val = -1;
		goto cleanup;
	}

	*version = SWAP32(tmp);

cleanup:
	if (!lock_held) {
		if (lock_file(fd, B_TRUE, B_FALSE) < 0) {
			ret_val = -1;
		}
	}


	(void) close(fd);
	return (ret_val);
}

/*
 *		FUNCTION: soft_keystore_getpin
 *
 *		ARGUMENTS:
 *			hashed_pin: pointer to caller allocated memory
 *				for storing the pin to be returned.
 *			lock_held: TRUE if the lock is held by caller.
 *
 *		RETURN VALUE:
 *
 *			0: no error
 *			-1: some error occurred while getting the pin
 *
 *		DESCRIPTION:
 *
 *			Reads the MD5 hash from the keystore description
 *			file and return it to the caller in the provided
 *			buffer. If there is no PIN in the description file
 *			because the file is just created, this function
 *			will get a MD5 digest of the string "changeme",
 *			store it in the file, and also return this
 *			string to the caller.
 */
int
soft_keystore_getpin(char **hashed_pin, boolean_t lock_held)
{
	int fd, ret_val = -1;
	CK_RV rv;

	if ((fd = open_and_lock_keystore_desc(O_RDONLY, B_TRUE,
	    lock_held)) < 0) {
		return (-1);
	}

	rv = get_hashed_pin(fd, hashed_pin);
	if (rv == CKR_OK) {
		ret_val = 0;
	}

cleanup:
	if (!lock_held) {
		if (lock_file(fd, B_TRUE, B_FALSE) < 0) {
			ret_val = -1;
		}
	}

	(void) close(fd);
	return (ret_val);
}


/*
 * Generate a 16-byte Initialization Vector (IV).
 */
CK_RV
soft_gen_iv(CK_BYTE *iv)
{
	return (pkcs11_get_nzero_urandom(iv, 16) < 0 ?
	    CKR_DEVICE_ERROR : CKR_OK);
}


/*
 * This function reads all the data until the end of the file, and
 * put the data into the "buf" in argument.  Memory for buf will
 * be allocated in this function.  It is the caller's responsibility
 * to free it.  The number of bytes read will be returned
 * in the argument "bytes_read"
 *
 * returns CKR_OK if no error.  Other CKR error codes if there's an error
 */
static CK_RV
read_obj_data(int old_fd, char **buf, ssize_t *bytes_read)
{

	ssize_t nread, loop_count;
	char *buf1 = NULL;

	*buf = malloc(BUFSIZ);
	if (*buf == NULL) {
		return (CKR_HOST_MEMORY);
	}

	nread = readn_nointr(old_fd, *buf, BUFSIZ);
	if (nread < 0) {
		free(*buf);
		return (CKR_FUNCTION_FAILED);
	}
	loop_count = 1;
	while (nread == (loop_count * BUFSIZ)) {
		ssize_t nread_tmp;

		loop_count++;
		/* more than BUFSIZ of data */
		buf1 = realloc(*buf, loop_count * BUFSIZ);
		if (buf1 == NULL) {
			free(*buf);
			return (CKR_HOST_MEMORY);
		}
		*buf = buf1;
		nread_tmp = readn_nointr(old_fd,
		    *buf + ((loop_count - 1) * BUFSIZ), BUFSIZ);
		if (nread_tmp < 0) {
			free(*buf);
			return (CKR_FUNCTION_FAILED);
		}
		nread += nread_tmp;
	}
	*bytes_read = nread;
	return (CKR_OK);
}

/*
 * Re-encrypt an object using the provided new_enc_key.  The new HMAC
 * is calculated using the new_hmac_key.  The global static variables
 * enc_key, and hmac_key will be used for decrypting the original
 * object, and verifying its signature.
 *
 * The re-encrypted object will be stored in the file named
 * in the "new_obj_name" variable.  The content of the "original"
 * file named in "orig_obj_name" is not disturbed.
 *
 * Returns 0 if there's no error, returns -1 otherwise.
 *
 */
static int
reencrypt_obj(soft_object_t *new_enc_key, soft_object_t *new_hmac_key,
    char *orig_obj_name, char *new_obj_name) {

	int old_fd, new_fd, version, ret_val = -1;
	CK_BYTE iv[OBJ_IV_SIZE], old_iv[OBJ_IV_SIZE];
	ssize_t nread;
	CK_ULONG decrypted_len, encrypted_len, hmac_len;
	CK_BYTE hmac[OBJ_HMAC_SIZE], *decrypted_buf = NULL, *buf = NULL;

	old_fd = open_nointr(orig_obj_name, O_RDONLY|O_NONBLOCK);
	if (old_fd < 0) {
		return (-1);
	}

	if (acquire_file_lock(&old_fd, orig_obj_name, O_RDONLY) != 0) {
		if (old_fd > 0) {
			(void) close(old_fd);
		}
		return (-1);
	}

	new_fd = open_nointr(new_obj_name,
	    O_WRONLY|O_CREAT|O_EXCL|O_NONBLOCK, S_IRUSR|S_IWUSR);
	if (new_fd < 0) {
		(void) close(old_fd);
		return (-1);
	}

	if (lock_file(new_fd, B_FALSE, B_TRUE) != 0) {
		/* unlock old file */
		(void) lock_file(old_fd, B_TRUE, B_FALSE);
		(void) close(old_fd);
		(void) close(new_fd);
		return (-1);
	}

	/* read version, increment, and write to tmp file */
	if (readn_nointr(old_fd, (char *)&version, OBJ_VER_SIZE)
	    != OBJ_VER_SIZE) {
		goto cleanup;
	}

	version = SWAP32(version);
	version++;
	version = SWAP32(version);

	if (writen_nointr(new_fd, (char *)&version, OBJ_VER_SIZE)
	    != OBJ_VER_SIZE) {
		goto cleanup;
	}

	/* read old iv */
	if (readn_nointr(old_fd, (char *)old_iv, OBJ_IV_SIZE) != OBJ_IV_SIZE) {
		goto cleanup;
	}

	/* generate new IV */
	if (soft_gen_iv(iv) != CKR_OK) {
		goto cleanup;
	}

	if (writen_nointr(new_fd, (char *)iv, OBJ_IV_SIZE) != OBJ_IV_SIZE) {
		goto cleanup;
	}

	/* seek to the original encrypted data, and read all of them */
	if (lseek(old_fd, OBJ_DATA_OFFSET, SEEK_SET) != OBJ_DATA_OFFSET) {
		goto cleanup;
	}

	if (read_obj_data(old_fd, (char **)&buf, &nread) != CKR_OK) {
		goto cleanup;
	}

	/* decrypt data using old key */
	decrypted_len = 0;
	if (soft_keystore_crypt(enc_key, old_iv, B_FALSE, buf, nread,
	    NULL, &decrypted_len) != CKR_OK) {
		free(buf);
		goto cleanup;
	}

	decrypted_buf = malloc(decrypted_len);
	if (decrypted_buf == NULL) {
		free(buf);
		goto cleanup;
	}

	if (soft_keystore_crypt(enc_key, old_iv, B_FALSE, buf, nread,
	    decrypted_buf, &decrypted_len) != CKR_OK) {
		free(buf);
		free(decrypted_buf);
		goto cleanup;
	}

	free(buf);

	/* re-encrypt with new key */
	encrypted_len = 0;
	if (soft_keystore_crypt(new_enc_key, iv, B_TRUE, decrypted_buf,
	    decrypted_len, NULL, &encrypted_len) != CKR_OK) {
		free(decrypted_buf);
		goto cleanup;
	}

	buf = malloc(encrypted_len);
	if (buf == NULL) {
		free(decrypted_buf);
		goto cleanup;
	}

	if (soft_keystore_crypt(new_enc_key, iv, B_TRUE, decrypted_buf,
	    decrypted_len, buf, &encrypted_len) != CKR_OK) {
		free(buf);
		free(decrypted_buf);
		goto cleanup;
	}

	free(decrypted_buf);

	/* calculate hmac on re-encrypted data using new hmac key */
	hmac_len = OBJ_HMAC_SIZE;
	if (soft_keystore_hmac(new_hmac_key, B_TRUE, buf,
	    encrypted_len, hmac, &hmac_len) != CKR_OK) {
		free(buf);
		goto cleanup;
	}

	/* just for sanity check */
	if (hmac_len != OBJ_HMAC_SIZE) {
		free(buf);
		goto cleanup;
	}

	/* write new hmac */
	if (writen_nointr(new_fd, (char *)hmac, OBJ_HMAC_SIZE)
	    != OBJ_HMAC_SIZE) {
		free(buf);
		goto cleanup;
	}

	/* write re-encrypted buffer to temp file */
	if (writen_nointr(new_fd, (void *)buf, encrypted_len)
	    != encrypted_len) {
		free(buf);
		goto cleanup;
	}
	free(buf);
	ret_val = 0;

cleanup:
	/* unlock the files */
	(void) lock_file(old_fd, B_TRUE, B_FALSE);
	(void) lock_file(new_fd, B_FALSE, B_FALSE);

	(void) close(old_fd);
	(void) close(new_fd);
	if (ret_val != 0) {
		(void) remove(new_obj_name);
	}
	return (ret_val);
}

/*
 *	FUNCTION: soft_keystore_setpin
 *
 *	ARGUMENTS:
 *		newpin: new pin entered by the user.
 *		lock_held: TRUE if the lock is held by caller.
 *
 *	RETURN VALUE:
 *		0: no error
 *		-1: failure
 *
 *	DESCRIPTION:
 *
 *		This function does the following:
 *
 *		1) Generates crypted value of newpin and store it
 *		   in keystore description file.
 *		2) Dervies the new encryption key from the newpin.  This key
 *		   will be used to re-encrypt the private token objects.
 *		3) Re-encrypt all of this user's existing private token
 *		   objects (if any).
 *		4) Increments the keystore version number.
 */
int
soft_keystore_setpin(uchar_t *oldpin, uchar_t *newpin, boolean_t lock_held)
{
	int fd, tmp_ks_fd, version, ret_val = -1;
	soft_object_t *new_crypt_key = NULL, *new_hmac_key = NULL;
	char filebuf[BUFSIZ];
	DIR	*pri_dirp;
	struct dirent *pri_ent;
	char pri_obj_path[MAXPATHLEN], ks_desc_file[MAXPATHLEN],
	    tmp_ks_desc_name[MAXPATHLEN];
	typedef struct priobjs {
		char orig_name[MAXPATHLEN];
		char tmp_name[MAXPATHLEN];
		struct priobjs *next;
	} priobjs_t;
	priobjs_t *pri_objs = NULL, *tmp;
	CK_BYTE *crypt_salt = NULL, *hmac_salt = NULL;
	boolean_t pin_never_set = B_FALSE, user_logged_in;
	char *new_hashed_pin = NULL;
	uint64_t hashed_pin_salt_length, new_hashed_pin_len, swaped_val;
	char *hashed_pin_salt = NULL;
	priobjs_t *obj;

	if ((enc_key == NULL) ||
	    (enc_key->magic_marker != SOFTTOKEN_OBJECT_MAGIC)) {
		user_logged_in = B_FALSE;
	} else {
		user_logged_in = B_TRUE;
	}

	if ((fd = open_and_lock_keystore_desc(O_RDWR, B_FALSE,
	    lock_held)) < 0) {
		return (-1);
	}

	(void) get_desc_file_path(ks_desc_file);
	(void) get_tmp_desc_file_path(tmp_ks_desc_name);

	/*
	 * create a tempoary file for the keystore description
	 * file for updating version and counter information
	 */
	tmp_ks_fd = open_nointr(tmp_ks_desc_name,
	    O_RDWR|O_CREAT|O_EXCL|O_NONBLOCK, S_IRUSR|S_IWUSR);
	if (tmp_ks_fd < 0) {
		(void) close(fd);
		return (-1);
	}

	/* read and write PKCS version to temp file */
	if (readn_nointr(fd, filebuf, KS_PKCS11_VER_SIZE)
	    != KS_PKCS11_VER_SIZE) {
		goto cleanup;
	}

	if (writen_nointr(tmp_ks_fd, filebuf, KS_PKCS11_VER_SIZE)
	    != KS_PKCS11_VER_SIZE) {
		goto cleanup;
	}

	/* get version number, and write updated number to temp file */
	if (readn_nointr(fd, &version, KS_VER_SIZE) != KS_VER_SIZE) {
		goto cleanup;
	}

	version = SWAP32(version);
	version++;
	version = SWAP32(version);

	if (writen_nointr(tmp_ks_fd, (void *)&version, KS_VER_SIZE)
	    != KS_VER_SIZE) {
		goto cleanup;
	}


	/* read and write counter, no modification necessary */
	if (readn_nointr(fd, filebuf, KS_COUNTER_SIZE) != KS_COUNTER_SIZE) {
		goto cleanup;
	}

	if (writen_nointr(tmp_ks_fd, filebuf, KS_COUNTER_SIZE)
	    != KS_COUNTER_SIZE) {
		goto cleanup;
	}

	/* read old encryption salt */
	crypt_salt = malloc(KS_KEY_SALT_SIZE);
	if (crypt_salt == NULL) {
		goto cleanup;
	}
	if (readn_nointr(fd, (char *)crypt_salt, KS_KEY_SALT_SIZE)
	    != KS_KEY_SALT_SIZE) {
		goto cleanup;
	}

	/* read old hmac salt */
	hmac_salt = malloc(KS_HMAC_SALT_SIZE);
	if (hmac_salt == NULL) {
		goto cleanup;
	}
	if (readn_nointr(fd, (char *)hmac_salt, KS_HMAC_SALT_SIZE)
	    != KS_HMAC_SALT_SIZE) {
		goto cleanup;
	}

	/* just create some empty bytes */
	bzero(filebuf, sizeof (filebuf));

	if (memcmp(crypt_salt, filebuf, KS_KEY_SALT_SIZE) == 0) {
		/* PIN as never been set */
		CK_BYTE *new_crypt_salt = NULL, *new_hmac_salt = NULL;

		pin_never_set = B_TRUE;
		if (soft_gen_crypt_key(newpin, &new_crypt_key, &new_crypt_salt)
		    != CKR_OK) {
			goto cleanup;
		}
		if (writen_nointr(tmp_ks_fd, (void *)new_crypt_salt,
		    KS_KEY_SALT_SIZE) != KS_KEY_SALT_SIZE) {
			free(new_crypt_salt);
			(void) soft_cleanup_object(new_crypt_key);
			goto cleanup;
		}
		free(new_crypt_salt);

		if (soft_gen_hmac_key(newpin, &new_hmac_key, &new_hmac_salt)
		    != CKR_OK) {
			(void) soft_cleanup_object(new_crypt_key);
			goto cleanup;
		}
		if (writen_nointr(tmp_ks_fd, (void *)new_hmac_salt,
		    KS_HMAC_SALT_SIZE) != KS_HMAC_SALT_SIZE) {
			free(new_hmac_salt);
			goto cleanup3;
		}
		free(new_hmac_salt);
	} else {
		if (soft_gen_crypt_key(newpin, &new_crypt_key,
		    (CK_BYTE **)&crypt_salt) != CKR_OK) {
			goto cleanup;
		}
		/* no change to the encryption salt */
		if (writen_nointr(tmp_ks_fd, (void *)crypt_salt,
		    KS_KEY_SALT_SIZE) != KS_KEY_SALT_SIZE) {
			(void) soft_cleanup_object(new_crypt_key);
			goto cleanup;
		}

		if (soft_gen_hmac_key(newpin, &new_hmac_key,
		    (CK_BYTE **)&hmac_salt) != CKR_OK) {
			(void) soft_cleanup_object(new_crypt_key);
			goto cleanup;
		}

		/* no change to the hmac salt */
		if (writen_nointr(tmp_ks_fd, (void *)hmac_salt,
		    KS_HMAC_SALT_SIZE) != KS_HMAC_SALT_SIZE) {
			goto cleanup3;
		}
	}

	/*
	 * read hashed pin salt, and write to updated keystore description
	 * file unmodified.
	 */
	if (readn_nointr(fd, (char *)&hashed_pin_salt_length,
	    KS_HASHED_PIN_SALT_LEN_SIZE) != KS_HASHED_PIN_SALT_LEN_SIZE) {
		goto cleanup3;
	}

	if (writen_nointr(tmp_ks_fd, (void *)&hashed_pin_salt_length,
	    KS_HASHED_PIN_SALT_LEN_SIZE) != KS_HASHED_PIN_SALT_LEN_SIZE) {
		goto cleanup3;
	}

	hashed_pin_salt_length = SWAP64(hashed_pin_salt_length);

	hashed_pin_salt = malloc(hashed_pin_salt_length + 1);
	if (hashed_pin_salt == NULL) {
		goto cleanup3;
	}

	if ((readn_nointr(fd, hashed_pin_salt, hashed_pin_salt_length)) !=
	    (ssize_t)hashed_pin_salt_length) {
		free(hashed_pin_salt);
		goto cleanup3;
	}

	if ((writen_nointr(tmp_ks_fd, hashed_pin_salt, hashed_pin_salt_length))
	    != (ssize_t)hashed_pin_salt_length) {
		free(hashed_pin_salt);
		goto cleanup3;
	}

	hashed_pin_salt[hashed_pin_salt_length] = '\0';

	/* old hashed pin length and value can be ignored, generate new one */
	if (soft_gen_hashed_pin(newpin, &new_hashed_pin,
	    &hashed_pin_salt) < 0) {
		free(hashed_pin_salt);
		goto cleanup3;
	}

	free(hashed_pin_salt);

	if (new_hashed_pin == NULL) {
		goto cleanup3;
	}

	new_hashed_pin_len = strlen(new_hashed_pin);

	/* write new hashed pin length to file */
	swaped_val = SWAP64(new_hashed_pin_len);
	if (writen_nointr(tmp_ks_fd, (void *)&swaped_val,
	    KS_HASHED_PINLEN_SIZE) != KS_HASHED_PINLEN_SIZE) {
		goto cleanup3;
	}

	if (writen_nointr(tmp_ks_fd, (void *)new_hashed_pin,
	    new_hashed_pin_len) != (ssize_t)new_hashed_pin_len) {
		goto cleanup3;
	}

	if (pin_never_set) {
		/* there was no private object, no need to re-encrypt them */
		goto rename_desc_file;
	}

	/* re-encrypt all the private objects */
	pri_dirp = opendir(get_pri_obj_path(pri_obj_path));
	if (pri_dirp == NULL) {
		/*
		 * this directory should exist, even if it doesn't contain
		 * any objects.  Don't want to update the pin if the
		 * keystore is somehow messed up.
		 */

		goto cleanup3;
	}

	/* if user did not login, need to set the old pin */
	if (!user_logged_in) {
		if (soft_keystore_authpin(oldpin) != 0) {
			goto cleanup3;
		}
	}

	while ((pri_ent = readdir(pri_dirp)) != NULL) {

		if ((strcmp(pri_ent->d_name, ".") == 0) ||
		    (strcmp(pri_ent->d_name, "..") == 0) ||
		    (strncmp(pri_ent->d_name, TMP_OBJ_PREFIX,
		    strlen(TMP_OBJ_PREFIX)) == 0)) {
			continue;
		}

		obj = malloc(sizeof (priobjs_t));
		if (obj == NULL) {
			goto cleanup2;
		}
		(void) snprintf(obj->orig_name, MAXPATHLEN,
		    "%s/%s", pri_obj_path, pri_ent->d_name);
		(void) snprintf(obj->tmp_name, MAXPATHLEN, "%s/%s%s",
		    pri_obj_path, TMP_OBJ_PREFIX,
		    (pri_ent->d_name) + OBJ_PREFIX_LEN);
		if (reencrypt_obj(new_crypt_key, new_hmac_key,
		    obj->orig_name, obj->tmp_name) != 0) {
			free(obj);
			goto cleanup2;
		}

		/* insert into list of file to be renamed */
		if (pri_objs == NULL) {
			obj->next = NULL;
			pri_objs = obj;
		} else {
			obj->next = pri_objs;
			pri_objs = obj;
		}
	}

	/* rename all the private objects */
	tmp = pri_objs;
	while (tmp) {
		(void) rename(tmp->tmp_name, tmp->orig_name);
		tmp = tmp->next;
	}

rename_desc_file:

	/* destroy the old encryption key, and hmac key */
	if ((!pin_never_set) && (user_logged_in)) {
		(void) soft_cleanup_object(enc_key);
		(void) soft_cleanup_object(hmac_key);
	}

	if (user_logged_in) {
		enc_key = new_crypt_key;
		hmac_key = new_hmac_key;
	}
	(void) rename(tmp_ks_desc_name, ks_desc_file);

	ret_val = 0;

cleanup2:
	if (pri_objs != NULL) {
		priobjs_t *p = pri_objs;
		while (p) {
			tmp = p->next;
			free(p);
			p = tmp;
		}
	}
	if (!pin_never_set) {
		(void) closedir(pri_dirp);
	}

	if ((!user_logged_in) && (!pin_never_set)) {
		(void) soft_cleanup_object(enc_key);
		(void) soft_cleanup_object(hmac_key);
		enc_key = NULL;
		hmac_key = NULL;
	}
cleanup3:
	if ((ret_val != 0) || (!user_logged_in)) {
		(void) soft_cleanup_object(new_crypt_key);
		(void) soft_cleanup_object(new_hmac_key);
	}

cleanup:
	if (!lock_held) {
		if (lock_file(fd, B_FALSE, B_FALSE) < 0) {
			ret_val = 1;
		}
	}
	if (crypt_salt != NULL) {
		free(crypt_salt);
	}
	if (hmac_salt != NULL) {
		free(hmac_salt);
	}
	(void) close(fd);
	(void) close(tmp_ks_fd);
	if (ret_val != 0) {
		(void) remove(tmp_ks_desc_name);
	}
	return (ret_val);
}

/*
 *	FUNCTION: soft_keystore_authpin
 *
 *	ARGUMENTS:
 *		pin: pin specified by the user for logging into
 *		     the keystore.
 *
 *	RETURN VALUE:
 *		0: if no error
 *		-1: if there is any error
 *
 *	DESCRIPTION:
 *
 *		This function takes the pin specified in the argument
 *		and generates an encryption key based on the pin.
 *		The generated encryption key will be used for
 *		all future encryption and decryption for private
 *		objects.  Before this function is called, none
 *		of the keystore related interfaces is able
 *		to decrypt/encrypt any private object.
 */
int
soft_keystore_authpin(uchar_t  *pin)
{
	int fd;
	int ret_val = -1;
	CK_BYTE *crypt_salt = NULL, *hmac_salt;

	/* get the salt from the keystore description file */
	if ((fd = open_and_lock_keystore_desc(O_RDONLY,
	    B_FALSE, B_FALSE)) < 0) {
		return (-1);
	}

	crypt_salt = malloc(KS_KEY_SALT_SIZE);
	if (crypt_salt == NULL) {
		goto cleanup;
	}

	if (lseek(fd, KS_KEY_SALT_OFFSET, SEEK_SET) != KS_KEY_SALT_OFFSET) {
		goto cleanup;
	}

	if (readn_nointr(fd, (char *)crypt_salt, KS_KEY_SALT_SIZE)
	    != KS_KEY_SALT_SIZE) {
		goto cleanup;
	}

	if (soft_gen_crypt_key(pin, &enc_key, (CK_BYTE **)&crypt_salt)
	    != CKR_OK) {
		goto cleanup;
	}

	hmac_salt = malloc(KS_HMAC_SALT_SIZE);
	if (hmac_salt == NULL) {
		goto cleanup;
	}

	if (lseek(fd, KS_HMAC_SALT_OFFSET, SEEK_SET) != KS_HMAC_SALT_OFFSET) {
		goto cleanup;
	}

	if (readn_nointr(fd, (char *)hmac_salt, KS_HMAC_SALT_SIZE)
	    != KS_HMAC_SALT_SIZE) {
		goto cleanup;
	}

	if (soft_gen_hmac_key(pin, &hmac_key, (CK_BYTE **)&hmac_salt)
	    != CKR_OK) {
		goto cleanup;
	}

	ret_val = 0;

cleanup:
	/* unlock the file */
	(void) lock_file(fd, B_TRUE, B_FALSE);
	(void) close(fd);
	if (crypt_salt != NULL) {
		free(crypt_salt);
	}
	if (hmac_salt != NULL) {
		free(hmac_salt);
	}
	return (ret_val);
}

/*
 * 	FUNCTION: soft_keystore_get_objs
 *
 *	ARGUMENTS:
 *
 *		search_type: Specify type of objects to return.
 *		lock_held: TRUE if the lock is held by caller.
 *
 *
 *	RETURN VALUE:
 *
 *		NULL: if there are no object in the database.
 *
 *		Otherwise, linked list of objects as requested
 *		in search type.
 *
 *		The linked list returned will need to be freed
 *		by the caller.
 *
 *	DESCRIPTION:
 *
 *		Returns objects as requested.
 *
 *		If private objects is requested, and the caller
 *		has not previously passed in the pin or if the pin
 *		passed in is wrong, private objects will not
 *		be returned.
 *
 *		The buffers returned for private objects are already
 *		decrypted.
 */
CK_RV
soft_keystore_get_objs(ks_search_type_t search_type,
    ks_obj_t **result_obj_list, boolean_t lock_held)
{
	DIR *dirp;
	ks_obj_handle_t ks_handle;
	CK_RV rv;
	ks_obj_t *tmp;
	int ks_fd;

	*result_obj_list = NULL;

	/*
	 * lock the keystore description file in "read" mode so that
	 * objects won't get added/deleted/modified while we are
	 * doing the search
	 */
	if ((ks_fd = open_and_lock_keystore_desc(O_RDONLY, B_TRUE,
	    B_FALSE)) < 0) {
		return (CKR_FUNCTION_FAILED);
	}

	if ((search_type == ALL_TOKENOBJS) || (search_type == PUB_TOKENOBJS)) {

		char pub_obj_path[MAXPATHLEN];

		ks_handle.public = B_TRUE;

		if ((dirp = opendir(get_pub_obj_path(pub_obj_path))) == NULL) {
			(void) lock_file(ks_fd, B_TRUE, B_FALSE);
			(void) close(ks_fd);
			return (CKR_FUNCTION_FAILED);
		}
		rv = get_all_objs_in_dir(dirp, &ks_handle, result_obj_list,
		    lock_held);
		if (rv != CKR_OK) {
			(void) closedir(dirp);
			goto cleanup;
		}

		(void) closedir(dirp);
	}

	if ((search_type == ALL_TOKENOBJS) || (search_type == PRI_TOKENOBJS)) {

		char pri_obj_path[MAXPATHLEN];

		if ((enc_key == NULL) ||
		    (enc_key->magic_marker != SOFTTOKEN_OBJECT_MAGIC)) {
			/* has not login - no need to go any further */
			(void) lock_file(ks_fd, B_TRUE, B_FALSE);
			(void) close(ks_fd);
			return (CKR_OK);
		}

		ks_handle.public = B_FALSE;

		if ((dirp = opendir(get_pri_obj_path(pri_obj_path))) == NULL) {
			(void) lock_file(ks_fd, B_TRUE, B_FALSE);
			(void) close(ks_fd);
			return (CKR_OK);
		}
		rv = get_all_objs_in_dir(dirp, &ks_handle, result_obj_list,
		    lock_held);
		if (rv != CKR_OK) {
			(void) closedir(dirp);
			goto cleanup;
		}

		(void) closedir(dirp);
	}
	/* close the keystore description file */
	(void) lock_file(ks_fd, B_TRUE, B_FALSE);
	(void) close(ks_fd);
	return (CKR_OK);
cleanup:

	/* close the keystore description file */
	(void) lock_file(ks_fd, B_TRUE, B_FALSE);
	(void) close(ks_fd);

	/* free all the objects found before hitting the error */
	tmp = *result_obj_list;
	while (tmp) {
		*result_obj_list = tmp->next;
		free(tmp->buf);
		free(tmp);
		tmp = *result_obj_list;
	}
	*result_obj_list = NULL;
	return (rv);
}


/*
 *	FUNCTION: soft_keystore_get_single_obj
 *
 *	ARGUMENTS:
 *		ks_handle: handle of the key store object to be accessed
 *		lock_held: TRUE if the lock is held by caller.
 *
 *	RETURN VALUE:
 *
 *		NULL: if handle doesn't match any object
 *
 *		Otherwise, the object is returned in
 *		the same structure used in soft_keystore_get_objs().
 *		The structure need to be freed by the caller.
 *
 *	DESCRIPTION:
 *
 *		Retrieves the object specified by the object
 *		handle to the caller.
 *
 *		If a private object is requested, and the caller
 *		has not previously passed in the pin or if the pin
 *		passed in is wrong, the requested private object will not
 *		be returned.
 *
 *		The buffer returned for the requested private object
 *		is already decrypted.
 */
CK_RV
soft_keystore_get_single_obj(ks_obj_handle_t *ks_handle,
    ks_obj_t **return_obj, boolean_t lock_held)
{

	ks_obj_t *obj;
	uchar_t iv[OBJ_IV_SIZE], obj_hmac[OBJ_HMAC_SIZE];
	uchar_t *buf, *decrypted_buf;
	int fd;
	ssize_t nread;
	CK_RV rv = CKR_FUNCTION_FAILED;

	if (!(ks_handle->public)) {
		if ((enc_key == NULL) ||
		    (enc_key->magic_marker != SOFTTOKEN_OBJECT_MAGIC)) {
			return (CKR_FUNCTION_FAILED);
		}
	}

	if ((fd = open_and_lock_object_file(ks_handle, O_RDONLY,
	    lock_held)) < 0) {
		return (CKR_FUNCTION_FAILED);
	}

	obj = malloc(sizeof (ks_obj_t));
	if (obj == NULL) {
		return (CKR_HOST_MEMORY);
	}

	obj->next = NULL;

	(void) strcpy((char *)((obj->ks_handle).name),
	    (char *)ks_handle->name);
	(obj->ks_handle).public = ks_handle->public;

	/* 1st get the version */
	if (readn_nointr(fd, &(obj->obj_version), OBJ_VER_SIZE)
	    != OBJ_VER_SIZE) {
		goto cleanup;
	}
	obj->obj_version = SWAP32(obj->obj_version);

	/* Then, read the IV */
	if (readn_nointr(fd, iv, OBJ_IV_SIZE) != OBJ_IV_SIZE) {
		goto cleanup;
	}

	/* Then, read the HMAC */
	if (readn_nointr(fd, obj_hmac, OBJ_HMAC_SIZE) != OBJ_HMAC_SIZE) {
		goto cleanup;
	}

	/* read the object */
	rv = read_obj_data(fd, (char **)&buf, &nread);
	if (rv != CKR_OK) {
		goto cleanup;
	}

	if (ks_handle->public) {
		obj->size = nread;
		obj->buf = buf;
		*return_obj = obj;
	} else {

		CK_ULONG out_len = 0, hmac_size;

		/* verify HMAC of the object, make sure it matches */
		hmac_size = OBJ_HMAC_SIZE;
		if (soft_keystore_hmac(hmac_key, B_FALSE, buf,
		    nread, obj_hmac, &hmac_size) != CKR_OK) {
			free(buf);
			rv = CKR_FUNCTION_FAILED;
			goto cleanup;
		}

		/* decrypt object */
		if (soft_keystore_crypt(enc_key, iv, B_FALSE, buf, nread,
		    NULL, &out_len) != CKR_OK) {
			free(buf);
			rv = CKR_FUNCTION_FAILED;
			goto cleanup;
		}

		decrypted_buf = malloc(sizeof (uchar_t) * out_len);
		if (decrypted_buf == NULL) {
			free(buf);
			rv = CKR_HOST_MEMORY;
			goto cleanup;
		}

		if (soft_keystore_crypt(enc_key, iv, B_FALSE, buf, nread,
		    decrypted_buf, &out_len) != CKR_OK) {
			free(decrypted_buf);
			free(buf);
			rv = CKR_FUNCTION_FAILED;
			goto cleanup;
		}

		obj->size = out_len - MAXPATHLEN;

		/*
		 * decrypted buf here actually contains full path name of
		 * object plus the actual data.  so, need to skip the
		 * full pathname.
		 * See prepare_data_for_encrypt() function in the file
		 * to understand how and why the pathname is added.
		 */
		obj->buf = malloc(sizeof (uchar_t) * (out_len - MAXPATHLEN));
		if (obj->buf == NULL) {
			free(decrypted_buf);
			free(buf);
			rv = CKR_HOST_MEMORY;
			goto cleanup;
		}
		(void) memcpy(obj->buf, decrypted_buf + MAXPATHLEN, obj->size);
		free(decrypted_buf);
		free(buf);
		*return_obj = obj;
	}

cleanup:

	if (rv != CKR_OK) {
		free(obj);
	}

	/* unlock the file after reading */
	if (!lock_held) {
		(void) lock_file(fd, B_TRUE, B_FALSE);
	}

	(void) close(fd);

	return (rv);
}


/*
 * 	FUNCTION: soft_keystore_put_new_obj
 *
 *	ARGUMENTS:
 *		buf: buffer containing un-encrypted data
 *		     to be stored in keystore.
 *		len: length of data
 *		public:  TRUE if it is a public object,
 *			 FALSE if it is private obj
 *		lock_held: TRUE if the lock is held by caller.
 *		keyhandle: pointer to object handle to
 *			   receive keyhandle for new object
 *
 *	RETURN VALUE:
 *		0: object successfully stored in file
 *		-1: some error occurred, object is not stored in file.
 *
 *	DESCRIPTION:
 *		This API is used to write a newly created token object
 *		to keystore.
 *
 *		This function does the following:
 *
 *		1) Creates a token object file based on "public" parameter.
 *		2) Generates a new IV and stores it in obj_meta_data_t if it is
 *		   private object.
 *		3) Set object version number to 1.
 *		4) If it is a private object, it will be encrypted before
 *		   being written to the newly created keystore token object
 *		   file.
 *		5) Calculates the obj_chksum in obj_meta_data_t.
 *		6) Calculates the pin_chksum in obj_meta_data_t.
 *		7) Increments the keystore version number.
 */
int
soft_keystore_put_new_obj(uchar_t *buf, size_t len, boolean_t public,
    boolean_t lock_held, ks_obj_handle_t *keyhandle)
{

	int fd, tmp_ks_fd, obj_fd;
	unsigned int counter, version;
	uchar_t obj_hmac[OBJ_HMAC_SIZE];
	CK_BYTE iv[OBJ_IV_SIZE];
	char obj_name[MAXPATHLEN], tmp_ks_desc_name[MAXPATHLEN];
	char filebuf[BUFSIZ];
	char pub_obj_path[MAXPATHLEN], pri_obj_path[MAXPATHLEN],
	    ks_desc_file[MAXPATHLEN];
	CK_ULONG hmac_size;
	ssize_t nread;

	if (keyhandle == NULL) {
		return (-1);
	}

	/* if it is private object, make sure we have the key */
	if (!public) {
		if ((enc_key == NULL) ||
		    (enc_key->magic_marker != SOFTTOKEN_OBJECT_MAGIC)) {
			return (-1);
		}
	}

	/* open keystore, and set write lock */
	if ((fd = open_and_lock_keystore_desc(O_RDWR, B_TRUE,
	    lock_held)) < 0) {
		return (-1);
	}

	(void) get_desc_file_path(ks_desc_file);
	(void) get_tmp_desc_file_path(tmp_ks_desc_name);

	/*
	 * create a tempoary file for the keystore description
	 * file for updating version and counter information
	 */
	tmp_ks_fd = open_nointr(tmp_ks_desc_name,
	    O_RDWR|O_CREAT|O_EXCL|O_NONBLOCK, S_IRUSR|S_IWUSR);
	if (tmp_ks_fd < 0) {
		(void) close(fd);
		return (-1);
	}

	/* read and write pkcs11 version */
	if (readn_nointr(fd, filebuf, KS_PKCS11_VER_SIZE)
	    != KS_PKCS11_VER_SIZE) {
		goto cleanup;
	}

	if (writen_nointr(tmp_ks_fd, filebuf, KS_PKCS11_VER_SIZE)
	    != KS_PKCS11_VER_SIZE) {
		goto cleanup;
	}

	/* get version number, and write updated number to temp file */
	if (readn_nointr(fd, &version, KS_VER_SIZE) != KS_VER_SIZE) {
		goto cleanup;
	}

	version = SWAP32(version);
	version++;
	version = SWAP32(version);

	if (writen_nointr(tmp_ks_fd, (void *)&version,
	    KS_VER_SIZE) != KS_VER_SIZE) {
		goto cleanup;
	}

	/* get object count value */
	if (readn_nointr(fd, &counter, KS_COUNTER_SIZE) != KS_COUNTER_SIZE) {
		goto cleanup;
	}
	counter = SWAP32(counter);

	bzero(obj_name, sizeof (obj_name));
	if (public) {
		(void) snprintf(obj_name, MAXPATHLEN,  "%s/%s%d",
		    get_pub_obj_path(pub_obj_path), OBJ_PREFIX, counter);
	} else {
		(void) snprintf(obj_name, MAXPATHLEN,  "%s/%s%d",
		    get_pri_obj_path(pri_obj_path), OBJ_PREFIX, counter);
	}

	/* create object file */
	obj_fd = open_nointr(obj_name,
	    O_WRONLY|O_CREAT|O_EXCL|O_NONBLOCK, S_IRUSR|S_IWUSR);
	if (obj_fd < 0) {
		/* can't create object file */
		goto cleanup;
	}

	/* lock object file for writing */
	if (lock_file(obj_fd, B_FALSE, B_TRUE) != 0) {
		(void) close(obj_fd);
		goto cleanup2;
	}

	/* write object meta data */
	version = SWAP32(1);
	if (writen_nointr(obj_fd, (void *)&version, sizeof (version))
	    != sizeof (version)) {
		goto cleanup2;
	}

	if (public) {
		bzero(iv, sizeof (iv));
	} else {
		/* generate an IV */
		if (soft_gen_iv(iv) != CKR_OK) {
			goto cleanup2;
		}

	}

	if (writen_nointr(obj_fd, (void *)iv, sizeof (iv)) != sizeof (iv)) {
		goto cleanup2;
	}

	if (public) {

		bzero(obj_hmac, sizeof (obj_hmac));
		if (writen_nointr(obj_fd, (void *)obj_hmac,
		    sizeof (obj_hmac)) != sizeof (obj_hmac)) {
			goto cleanup2;
		}

		if (writen_nointr(obj_fd, (char *)buf, len) != len) {
			goto cleanup2;
		}

	} else {

		uchar_t *encrypted_buf, *prepared_buf;
		CK_ULONG out_len = 0, prepared_len;

		if (prepare_data_for_encrypt(obj_name, buf, len,
		    &prepared_buf, &prepared_len) != 0) {
			goto cleanup2;
		}

		if (soft_keystore_crypt(enc_key, iv,
		    B_TRUE, prepared_buf, prepared_len,
		    NULL, &out_len) != CKR_OK) {
			free(prepared_buf);
			goto cleanup2;
		}

		encrypted_buf = malloc(out_len * sizeof (char));
		if (encrypted_buf == NULL) {
			free(prepared_buf);
			goto cleanup2;
		}

		if (soft_keystore_crypt(enc_key, iv,
		    B_TRUE, prepared_buf, prepared_len,
		    encrypted_buf, &out_len) != CKR_OK) {
			free(encrypted_buf);
			free(prepared_buf);
			goto cleanup2;
		}
		free(prepared_buf);

		/* calculate HMAC of encrypted object */
		hmac_size = OBJ_HMAC_SIZE;
		if (soft_keystore_hmac(hmac_key, B_TRUE, encrypted_buf,
		    out_len, obj_hmac, &hmac_size) != CKR_OK) {
			free(encrypted_buf);
			goto cleanup2;
		}

		if (hmac_size != OBJ_HMAC_SIZE) {
			free(encrypted_buf);
			goto cleanup2;
		}

		/* write hmac */
		if (writen_nointr(obj_fd, (void *)obj_hmac,
		    sizeof (obj_hmac)) != sizeof (obj_hmac)) {
			free(encrypted_buf);
			goto cleanup2;
		}

		/* write encrypted object */
		if (writen_nointr(obj_fd, (void *)encrypted_buf, out_len)
		    != out_len) {
			free(encrypted_buf);
			goto cleanup2;
		}

		free(encrypted_buf);
	}


	(void) close(obj_fd);
	(void) snprintf((char *)keyhandle->name, sizeof (keyhandle->name),
	    "obj%d", counter);
	keyhandle->public = public;

	/*
	 * store new counter to temp keystore description file.
	 */
	counter++;
	counter = SWAP32(counter);
	if (writen_nointr(tmp_ks_fd, (void *)&counter,
	    sizeof (counter)) != sizeof (counter)) {
		goto cleanup2;
	}

	/* read rest of keystore description file and store into temp file */
	nread = readn_nointr(fd, filebuf, sizeof (filebuf));
	while (nread > 0) {
		if (writen_nointr(tmp_ks_fd, filebuf, nread) != nread) {
			goto cleanup2;
		}
		nread = readn_nointr(fd, filebuf, sizeof (filebuf));
	}

	(void) close(tmp_ks_fd);
	(void) rename(tmp_ks_desc_name, ks_desc_file);

	if (!lock_held) {
		/* release lock on description file */
		if (lock_file(fd, B_FALSE, B_FALSE) != 0) {
			(void) close(fd);
			return (-1);
		}
	}
	(void) close(fd);
	return (0);

cleanup2:

	/* remove object file.  No need to remove lock first */
	(void) unlink(obj_name);

cleanup:

	(void) close(tmp_ks_fd);
	(void) remove(tmp_ks_desc_name);
	if (!lock_held) {
		/* release lock on description file */
		(void) lock_file(fd, B_FALSE, B_FALSE);
	}

	(void) close(fd);
	return (-1);
}

/*
 *	FUNCTION: soft_keystore_modify_obj
 *
 *	ARGUMENTS:
 *		ks_handle: handle of the key store object to be modified
 *		buf: buffer containing un-encrypted data
 *		     to be modified in keystore.
 *		len: length of data
 *		lock_held: TRUE if the lock is held by caller.
 *
 *	RETURN VALUE:
 *		-1: if any error occurred.
 *		Otherwise, 0 is returned.
 *
 *	DESCRIPTION:
 *
 *		This API is used to write a modified token object back
 *		to keystore.   This function will do the following:
 *
 *		1) If it is a private object, it will be encrypted before
 *		   being written to the corresponding keystore token
 *		   object file.
 *		2) Record incremented object version number.
 *		3) Record incremented keystore version number.
 */
int
soft_keystore_modify_obj(ks_obj_handle_t *ks_handle, uchar_t *buf,
    size_t len, boolean_t lock_held)
{
	int fd, ks_fd, tmp_fd, version;
	char orig_name[MAXPATHLEN], tmp_name[MAXPATHLEN],
	    tmp_ks_name[MAXPATHLEN];
	uchar_t iv[OBJ_IV_SIZE], obj_hmac[OBJ_HMAC_SIZE];
	char pub_obj_path[MAXPATHLEN], pri_obj_path[MAXPATHLEN],
	    ks_desc_file[MAXPATHLEN];
	CK_ULONG hmac_size;

	/* if it is private object, make sure we have the key */
	if (!(ks_handle->public)) {
		if ((enc_key == NULL) ||
		    (enc_key->magic_marker != SOFTTOKEN_OBJECT_MAGIC)) {
			return (-1);
		}
	}

	/* open and lock keystore description file */
	if ((ks_fd = open_and_lock_keystore_desc(O_RDWR, B_FALSE,
	    B_FALSE)) < 0) {
		return (-1);
	}

	(void) get_desc_file_path(ks_desc_file);

	/* update the version of for keystore file in tempoary file */
	(void) get_tmp_desc_file_path(tmp_ks_name);
	if (create_updated_keystore_version(ks_fd, tmp_ks_name) != 0) {
		/* unlock keystore description file */
		(void) lock_file(ks_fd, B_FALSE, B_FALSE);
		(void) close(ks_fd);
		return (-1);
	}

	/* open object file */
	if ((fd = open_and_lock_object_file(ks_handle, O_RDWR,
	    lock_held)) < 0) {
		goto cleanup;
	}

	/*
	 * make the change in a temporary file.  Create the temp
	 * file in the same directory as the token object.  That
	 * way, the "rename" later will be an atomic operation
	 */
	if (ks_handle->public) {
		(void) snprintf(orig_name, MAXPATHLEN, "%s/%s",
		    get_pub_obj_path(pub_obj_path), ks_handle->name);
		(void) snprintf(tmp_name, MAXPATHLEN, "%s/%s%s",
		    pub_obj_path, TMP_OBJ_PREFIX,
		    (ks_handle->name) + OBJ_PREFIX_LEN);
	} else {
		(void) snprintf(orig_name, MAXPATHLEN, "%s/%s",
		    get_pri_obj_path(pri_obj_path), ks_handle->name);
		(void) snprintf(tmp_name, MAXPATHLEN, "%s/%s%s",
		    pri_obj_path, TMP_OBJ_PREFIX,
		    (ks_handle->name) + OBJ_PREFIX_LEN);
	}

	tmp_fd = open_nointr(tmp_name,
	    O_WRONLY|O_CREAT|O_EXCL|O_NONBLOCK, S_IRUSR|S_IWUSR);
	if (tmp_fd < 0) {
		/* can't create tmp object file */
		goto cleanup1;
	}

	/* read version, increment, and write to tmp file */
	if (readn_nointr(fd, (char *)&version, OBJ_VER_SIZE) != OBJ_VER_SIZE) {
		goto cleanup2;
	}

	version = SWAP32(version);
	version++;
	version = SWAP32(version);

	if (writen_nointr(tmp_fd, (char *)&version, OBJ_VER_SIZE)
	    != OBJ_VER_SIZE) {
		goto cleanup2;
	}

	/* generate a new IV for the object, old one can be ignored */
	if (soft_gen_iv(iv) != CKR_OK) {
		goto cleanup2;
	}

	if (writen_nointr(tmp_fd, (char *)iv, OBJ_IV_SIZE) != OBJ_IV_SIZE) {
		goto cleanup2;
	}

	if (ks_handle->public) {

		/* hmac is always NULL for public objects */
		bzero(obj_hmac, sizeof (obj_hmac));
		if (writen_nointr(tmp_fd, (char *)obj_hmac, OBJ_HMAC_SIZE)
		    != OBJ_HMAC_SIZE) {
			goto cleanup2;
		}

		/* write updated object */
		if (writen_nointr(tmp_fd, (char *)buf, len) != len) {
			goto cleanup2;
		}

	} else {

		uchar_t *encrypted_buf, *prepared_buf;
		CK_ULONG out_len = 0, prepared_len;

		if (prepare_data_for_encrypt(orig_name, buf, len,
		    &prepared_buf, &prepared_len) != 0) {
			goto cleanup2;
		}

		/* encrypt the data */
		if (soft_keystore_crypt(enc_key, iv, B_TRUE, prepared_buf,
		    prepared_len, NULL, &out_len) != CKR_OK) {
			free(prepared_buf);
			goto cleanup2;
		}

		encrypted_buf = malloc(out_len * sizeof (char));
		if (encrypted_buf == NULL) {
			free(prepared_buf);
			goto cleanup2;
		}

		if (soft_keystore_crypt(enc_key, iv, B_TRUE, prepared_buf,
		    prepared_len, encrypted_buf, &out_len) != CKR_OK) {
			free(encrypted_buf);
			free(prepared_buf);
			goto cleanup2;
		}

		free(prepared_buf);

		/* calculate hmac on encrypted buf */
		hmac_size = OBJ_HMAC_SIZE;
		if (soft_keystore_hmac(hmac_key, B_TRUE, encrypted_buf,
		    out_len, obj_hmac, &hmac_size) != CKR_OK) {
			free(encrypted_buf);
			goto cleanup2;
		}

		if (hmac_size != OBJ_HMAC_SIZE) {
			free(encrypted_buf);
			goto cleanup2;
		}

		if (writen_nointr(tmp_fd, (char *)obj_hmac, OBJ_HMAC_SIZE)
		    != OBJ_HMAC_SIZE) {
			free(encrypted_buf);
			goto cleanup2;
		}

		if (writen_nointr(tmp_fd, (void *)encrypted_buf, out_len)
		    != out_len) {
			free(encrypted_buf);
			goto cleanup2;
		}
		free(encrypted_buf);
	}
	(void) close(tmp_fd);

	/* rename updated temporary object file */
	if (rename(tmp_name, orig_name) != 0) {
		(void) unlink(tmp_name);
		return (-1);
	}

	/* rename updated keystore description file */
	if (rename(tmp_ks_name, ks_desc_file) != 0) {
		(void) unlink(tmp_name);
		(void) unlink(tmp_ks_name);
		return (-1);
	}

	/* determine need to unlock file or not */
	if (!lock_held) {
		if (lock_file(fd, B_FALSE, B_FALSE) < 0) {
			(void) close(fd);
			(void) unlink(tmp_name);
			return (-1);
		}
	}

	/* unlock keystore description file */
	if (lock_file(ks_fd, B_FALSE, B_FALSE) != 0) {
		(void) close(ks_fd);
		(void) close(fd);
		return (-1);
	}

	(void) close(ks_fd);

	(void) close(fd);

	return (0); /* All operations completed successfully */

cleanup2:
	(void) close(tmp_fd);
	(void) remove(tmp_name);

cleanup1:
	(void) close(fd);

cleanup:
	/* unlock keystore description file */
	(void) lock_file(ks_fd, B_FALSE, B_FALSE);
	(void) close(ks_fd);
	(void) remove(tmp_ks_name);
	return (-1);
}

/*
 *	FUNCTION: soft_keystore_del_obj
 *
 *	ARGUMENTS:
 *		ks_handle: handle of the key store object to be deleted
 *		lock_held: TRUE if the lock is held by caller.
 *
 *	RETURN VALUE:
 *		-1: if any error occurred.
 *		0: object successfully deleted from keystore.
 *
 *	DESCRIPTION:
 *		This API is used to delete a particular token object from
 *		the keystore.  The corresponding token object file will be
 *		removed from the file system.
 *		Any future reference to the deleted file will
 *		return an CKR_OBJECT_HANDLE_INVALID error.
 */
int
soft_keystore_del_obj(ks_obj_handle_t *ks_handle, boolean_t lock_held)
{
	char objname[MAXPATHLEN], tmp_ks_name[MAXPATHLEN];
	int fd;
	char pub_obj_path[MAXPATHLEN], pri_obj_path[MAXPATHLEN],
	    ks_desc_file[MAXPATHLEN];
	int ret_val = -1;
	int obj_fd;

	if ((fd = open_and_lock_keystore_desc(O_RDWR, B_FALSE,
	    lock_held)) < 0) {
		return (-1);
	}

	(void) get_desc_file_path(ks_desc_file);
	(void) get_tmp_desc_file_path(tmp_ks_name);
	if (create_updated_keystore_version(fd, tmp_ks_name) != 0) {
		goto cleanup;
	}

	if (ks_handle->public) {
		(void) snprintf(objname, MAXPATHLEN, "%s/%s",
		    get_pub_obj_path(pub_obj_path), ks_handle->name);
	} else {
		(void) snprintf(objname, MAXPATHLEN, "%s/%s",
		    get_pri_obj_path(pri_obj_path), ks_handle->name);
	}

	/*
	 * make sure no other process is reading/writing the file
	 * by acquiring the lock on the file
	 */
	if ((obj_fd = open_and_lock_object_file(ks_handle, O_WRONLY,
	    B_FALSE)) < 0) {
		return (-1);
	}

	if (unlink(objname) != 0) {
		(void) lock_file(obj_fd, B_FALSE, B_FALSE);
		(void) close(obj_fd);
		goto cleanup;
	}

	(void) lock_file(obj_fd, B_FALSE, B_FALSE);
	(void) close(obj_fd);

	if (rename(tmp_ks_name, ks_desc_file) != 0) {
		goto cleanup;
	}
	ret_val = 0;

cleanup:
	/* unlock keystore description file */
	if (!lock_held) {
		if (lock_file(fd, B_FALSE, B_FALSE) != 0) {
			(void) close(fd);
			return (-1);
		}
	}

	(void) close(fd);
	return (ret_val);
}

/*
 * Get the salt used for generating hashed pin from the
 * keystore description file.
 *
 * The result will be stored in the provided buffer "salt" passed
 * in as an argument.
 *
 * Return 0 if no error, return -1 if there's any error.
 */
int
soft_keystore_get_pin_salt(char **salt)
{
	int fd, ret_val = -1;
	uint64_t hashed_pin_salt_size;

	if ((fd = open_and_lock_keystore_desc(O_RDONLY, B_TRUE,
	    B_FALSE)) < 0) {
		return (-1);
	}

	if (lseek(fd, KS_HASHED_PIN_SALT_LEN_OFFSET, SEEK_SET)
	    != KS_HASHED_PIN_SALT_LEN_OFFSET) {
		goto cleanup;
	}

	if (readn_nointr(fd, (char *)&hashed_pin_salt_size,
	    KS_HASHED_PIN_SALT_LEN_SIZE) != KS_HASHED_PIN_SALT_LEN_SIZE) {
		goto cleanup;
	}
	hashed_pin_salt_size = SWAP64(hashed_pin_salt_size);

	*salt = malloc(hashed_pin_salt_size + 1);
	if (*salt == NULL) {
		goto cleanup;
	}

	if ((readn_nointr(fd, *salt, hashed_pin_salt_size))
	    != (ssize_t)hashed_pin_salt_size) {
		free(*salt);
		goto cleanup;
	}
	(*salt)[hashed_pin_salt_size] = '\0';

	ret_val = 0;

cleanup:
	if (lock_file(fd, B_TRUE, B_FALSE) < 0) {
		ret_val = -1;
	}

	(void) close(fd);
	return (ret_val);
}

/*
 *	FUNCTION: soft_keystore_pin_initialized
 *
 *	ARGUMENTS:
 *		initialized: This value will be set to true if keystore is
 *			     initialized, and false otherwise.
 *		hashed_pin: If the keystore is initialized, this will contain
 *			    the hashed pin.  It will be NULL if the keystore
 *			    pin is not initialized.  Memory allocated
 *			    for the hashed pin needs to be freed by
 *			    the caller.
 *		lock_held: TRUE if the lock is held by caller.
 *
 *	RETURN VALUE:
 *		CKR_OK: No error
 *		any other appropriate CKR_value
 *
 *	DESCRIPTION:
 *		This API is used to determine if the PIN in the keystore
 *		has been initialized or not.
 *		It makes the determination using the salt for generating the
 *		encryption key.  The salt is stored in the keystore
 *		descryption file.  The salt should be all zero if
 *		the keystore pin has not been initialized.
 *		If the pin has been initialized, it is returned in the
 *		hashed_pin argument.
 */
CK_RV
soft_keystore_pin_initialized(boolean_t *initialized, char **hashed_pin,
    boolean_t lock_held)
{
	int fd;
	CK_BYTE crypt_salt[KS_KEY_SALT_SIZE], tmp_buf[KS_KEY_SALT_SIZE];
	CK_RV ret_val = CKR_OK;

	if ((fd = open_and_lock_keystore_desc(O_RDONLY, B_TRUE,
	    lock_held)) < 0) {
		return (CKR_FUNCTION_FAILED);
	}

	if (lseek(fd, KS_KEY_SALT_OFFSET, SEEK_SET) != KS_KEY_SALT_OFFSET) {
		ret_val = CKR_FUNCTION_FAILED;
		goto cleanup;
	}

	if (readn_nointr(fd, (char *)crypt_salt, KS_KEY_SALT_SIZE)
	    != KS_KEY_SALT_SIZE) {
		ret_val = CKR_FUNCTION_FAILED;
		goto cleanup;
	}

	(void) bzero(tmp_buf, KS_KEY_SALT_SIZE);

	if (memcmp(crypt_salt, tmp_buf, KS_KEY_SALT_SIZE) == 0) {
		*initialized = B_FALSE;
		hashed_pin = NULL;
	} else {
		*initialized = B_TRUE;
		ret_val = get_hashed_pin(fd, hashed_pin);
	}

cleanup:

	if (!lock_held) {
		if (lock_file(fd, B_TRUE, B_FALSE) < 0) {
			ret_val = CKR_FUNCTION_FAILED;
		}
	}

	(void) close(fd);
	return (ret_val);
}

/*
 * This checks if the keystore file exists
 */

static int
soft_keystore_exists()
{
	int ret;
	struct stat fn_stat;
	char *fname, ks_desc_file[MAXPATHLEN];

	fname = get_desc_file_path(ks_desc_file);
	ret = stat(fname, &fn_stat);
	if (ret == 0)
		return (0);
	return (errno);
}

/*
 *	FUNCTION: soft_keystore_init
 *
 *	ARGUMENTS:
 *		desired_state:  The keystore state the caller would like
 *				it to be.
 *
 *	RETURN VALUE:
 *		Returns the state the function is in.  If it succeeded, it
 *		will be the same as the desired, if not it will be
 *		KEYSTORE_UNAVAILABLE.
 *
 *	DESCRIPTION:
 *		This function will only load as much keystore data as is
 *		requested at that time. This is for performace by delaying the
 *		reading of token objects until they are needed or never at
 *		all if they are not used.
 *
 *		It is only called by soft_keystore_status() when the
 *		"desired_state" is not the the current load state of keystore.
 *
 */
int
soft_keystore_init(int desired_state)
{
	int ret;

	(void) pthread_mutex_lock(&soft_slot.keystore_mutex);

	/*
	 * If more than one session tries to initialize the keystore, the
	 * second and other following sessions that were waiting for the lock
	 * will quickly exit if their requirements are satisfied.
	 */
	if (desired_state <= soft_slot.keystore_load_status) {
		(void) pthread_mutex_unlock(&soft_slot.keystore_mutex);
		return (soft_slot.keystore_load_status);
	}

	/*
	 * With 'keystore_load_status' giving the current state of the
	 * process, this switch will bring it up to the desired state if
	 * possible.
	 */

	switch (soft_slot.keystore_load_status) {
	case KEYSTORE_UNINITIALIZED:
		ret = soft_keystore_exists();
		if (ret == 0)
			soft_slot.keystore_load_status = KEYSTORE_PRESENT;
		else if (ret == ENOENT)
			if (create_keystore() == 0)
				soft_slot.keystore_load_status =
				    KEYSTORE_PRESENT;
			else {
				soft_slot.keystore_load_status =
				    KEYSTORE_UNAVAILABLE;
				cryptoerror(LOG_DEBUG,
				    "pkcs11_softtoken: "
				    "Cannot create keystore.");
				break;
			}

		if (desired_state <= KEYSTORE_PRESENT)
			break;

	/* FALLTHRU */
	case KEYSTORE_PRESENT:
		if (soft_keystore_get_version(&soft_slot.ks_version, B_FALSE)
		    != 0) {
			soft_slot.keystore_load_status = KEYSTORE_UNAVAILABLE;
			cryptoerror(LOG_DEBUG,
			    "pkcs11_softtoken: Keystore access failed.");
			break;
		}

		soft_slot.keystore_load_status = KEYSTORE_VERSION_OK;
		if (desired_state <= KEYSTORE_VERSION_OK)
			break;

	/* FALLTHRU */
	case KEYSTORE_VERSION_OK:
		/* Load all the public token objects from keystore */
		if (soft_get_token_objects_from_keystore(PUB_TOKENOBJS)
		    != CKR_OK) {
			(void) soft_destroy_token_session();
			soft_slot.keystore_load_status = KEYSTORE_UNAVAILABLE;
			cryptoerror(LOG_DEBUG,
			    "pkcs11_softtoken: Cannot initialize keystore.");
			break;
		}

		soft_slot.keystore_load_status = KEYSTORE_INITIALIZED;
	};

	(void) pthread_mutex_unlock(&soft_slot.keystore_mutex);
	return (soft_slot.keystore_load_status);
}

/*
 *	FUNCTION: soft_keystore_status
 *
 *	ARGUMENTS:
 *		desired_state:  The keystore state the caller would like
 *				it to be.
 *
 *	RETURN VALUE:
 *		B_TRUE if keystore is ready and at the desired state.
 *		B_FALSE if keystore had an error and is not available.
 *
 *	DESCRIPTION:
 *		The calling function wants to make sure the keystore load
 *		status to in a state it requires.  If it is not at that
 *		state it will call the load function.
 *		If keystore is at the desired state or has just been
 *		loaded to that state, it will return TRUE.  If there has been
 *		load failure, it will return FALSE.
 *
 */
boolean_t
soft_keystore_status(int desired_state)
{

	if (soft_slot.keystore_load_status == KEYSTORE_UNAVAILABLE)
		return (B_FALSE);

	return ((desired_state <= soft_slot.keystore_load_status) ||
	    (soft_keystore_init(desired_state) == desired_state));
}