/*
* 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
* 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
*/
/*
*/
/*
* Functions used for manipulating the keystore
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <pwd.h>
#include <dirent.h>
#include <limits.h>
#include <libgen.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"
/*
* 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_OFFSET 0
/* Salt for hashed pin */
#define KS_HASHED_PIN_SALT_OFFSET \
/*
* 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()
*/
/* 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
/* End of object metadata pre-defined values */
static int desc_fd = 0;
static char *
{
if (keystore_path_initialized) {
return (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
*/
alt, KEYSTORE_PATH);
/* alternate path not specified, try user's home dir */
}
return (keystore_path);
}
static char *
{
return (name);
}
static char *
{
return (name);
}
static char *
{
get_keystore_path(), DS_FILE);
return (name);
}
static char *
{
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
{
return (-1);
}
return (-1);
}
return (0);
}
/*
*
* read_lock: true for read lock; false for write lock
* set_lock: true to set a lock; false to release a lock
*/
static int
{
int r;
if (read_lock) {
} else {
}
if (set_lock) {
break;
}
if (r == -1) {
return (-1);
}
} else {
break;
}
if (r == -1) {
return (-1);
}
}
return (0);
}
int
{
char *alt;
/* keystore doesn't exist, create keystore directory */
return (0);
}
return (-1);
}
/* can't create keystore directory */
/*
* try to create $HOME/.sunw/pkcs11_softtoken if it
* doesn't exist. If it is a alternate path provided
* by the user, it should have existed. Will not
* create for them.
*/
return (-1);
}
/* create $HOME/.sunw/pkcs11_softtoken */
return (-1);
}
} else {
return (-1);
}
}
}
/* create keystore description file */
if (fd < 0) {
return (0);
} else {
/* can't create keystore description file */
(void) rmdir(get_keystore_path());
return (-1);
}
}
(void) unlink(ks_desc_file);
(void) rmdir(get_keystore_path());
return (-1);
}
/* can't create directory for public objects */
(void) unlink(ks_desc_file);
(void) rmdir(get_keystore_path());
return (-1);
}
/* can't create directory for private objects */
(void) unlink(ks_desc_file);
(void) rmdir(get_keystore_path());
(void) rmdir(pub_obj_path);
return (-1);
}
/* write file format release number */
!= sizeof (ver_buf)) {
goto cleanup;
}
/* write version number, version = 0 since keystore just created */
goto cleanup;
}
/* write monotonic-counter. Counter for keystore objects start at 1 */
!= KS_COUNTER_SIZE) {
goto cleanup;
}
/* initial encryption key salt should be all NULL */
!= KS_KEY_SALT_SIZE) {
goto cleanup;
}
/* initial HMAC key salt should also be all NULL */
!= KS_HMAC_SALT_SIZE) {
goto cleanup;
}
/* generate the hashed pin salt, and MD5 hashed pin of default pin */
&hashed_pin_salt) < 0) {
goto cleanup;
}
goto cleanup;
}
/* write hashed pin salt length */
goto cleanup;
}
goto cleanup;
}
/* write MD5 hashed pin of the default pin */
!= KS_HASHED_PINLEN_SIZE) {
goto cleanup;
}
!= hashed_pin_len) {
goto cleanup;
}
if (hashed_pin_salt)
return (0);
(void) unlink(ks_desc_file);
(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
{
return (-1);
}
return (-1);
}
/* It's the same file if both st_ino and st_dev match */
} else {
}
return (0);
}
static int
}
return (-1);
}
/*
* make sure another process did not modify the file
* while we were trying to get the lock
*/
return (-1);
}
while (!same_inode) {
/*
* need to unlock file, close, re-open the file,
* and re-acquire the lock
*/
/* unlock file */
return (-1);
}
/* re-open */
if (*fd < 0) {
return (-1);
}
/* acquire lock again */
return (-1);
}
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
{
int fd;
/* open the keystore description file in requested mode */
if (fd < 0) {
if (create_keystore() < 0) {
goto done;
}
if (fd < 0) {
goto done;
}
} else {
goto done;
}
}
if (lock_held) {
/* already hold the lock */
return (fd);
}
if (fd > 0) {
}
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
{
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) {
} else {
}
return (-1);
}
} else {
/* make sure we have a valid fd */
if (desc_fd <= 0) {
return (-1);
}
return (-1);
}
desc_fd = 0;
}
return (0);
}
static int
{
int fd;
} else {
}
if (fd < 0) {
return (-1);
}
if (lock_held) {
/* already hold the lock */
return (fd);
}
if (fd > 0) {
}
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
{
/* first, create the tempoary file */
if (tmp_fd < 0) {
return (-1);
}
/*
* copy everything from keystore version to temp file except
* the keystore version. Keystore version is updated
*
*/
/* pkcs11 version */
goto cleanup;
}
goto cleanup;
}
/* version number, it needs to be updated */
/* read the current version number */
goto cleanup;
}
version++;
/* write the updated value to the tmp file */
!= KS_VER_SIZE) {
goto cleanup;
}
/* read rest of information, nothing needs to be updated */
while (nread > 0) {
goto cleanup;
}
}
return (0); /* no error */
return (-1);
}
static CK_RV
{
continue;
return (rv);
}
if (*result_obj_list == NULL) {
*result_obj_list = obj;
} else {
*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
{
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
*/
return (0);
}
/*
* retrieves the hashed pin from the keystore
*/
static CK_RV
{
if (ks_hashed_pinlen_offset == -1) {
if (calculate_hashed_pin_offset(fd) != 0) {
return (CKR_FUNCTION_FAILED);
}
}
/* first, get size of the hashed pin */
!= ks_hashed_pinlen_offset) {
return (CKR_FUNCTION_FAILED);
}
return (CKR_FUNCTION_FAILED);
}
if (*hashed_pin == NULL) {
return (CKR_HOST_MEMORY);
}
!= (ssize_t)hashed_pin_size) {
free(*hashed_pin);
*hashed_pin = NULL;
return (CKR_FUNCTION_FAILED);
}
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
{
}
/*
* 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:
*/
int
{
}
/*
*
* 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
{
int fd;
int oflag;
if (read_lock) {
} else {
}
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:
*/
int
{
return (1);
}
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
{
return (-1);
}
ret_val = -1;
goto cleanup;
}
ret_val = -1;
goto cleanup;
}
if (!lock_held) {
ret_val = -1;
}
}
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
{
lock_held)) < 0) {
return (-1);
}
/*
* read version. Version is always first item in object file
* so, no need to do lseek
*/
ret_val = -1;
goto cleanup;
}
if (!lock_held) {
ret_val = -1;
}
}
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
{
lock_held)) < 0) {
return (-1);
}
ret_val = 0;
}
if (!lock_held) {
ret_val = -1;
}
}
return (ret_val);
}
/*
* Generate a 16-byte Initialization Vector (IV).
*/
{
}
/*
* 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
{
return (CKR_HOST_MEMORY);
}
if (nread < 0) {
return (CKR_FUNCTION_FAILED);
}
loop_count = 1;
loop_count++;
/* more than BUFSIZ of data */
return (CKR_HOST_MEMORY);
}
if (nread_tmp < 0) {
return (CKR_FUNCTION_FAILED);
}
}
*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
char *orig_obj_name, char *new_obj_name) {
if (old_fd < 0) {
return (-1);
}
if (old_fd > 0) {
}
return (-1);
}
if (new_fd < 0) {
return (-1);
}
/* unlock old file */
return (-1);
}
/* read version, increment, and write to tmp file */
!= OBJ_VER_SIZE) {
goto cleanup;
}
version++;
!= OBJ_VER_SIZE) {
goto cleanup;
}
/* read old iv */
goto cleanup;
}
/* generate new IV */
goto cleanup;
}
goto cleanup;
}
/* seek to the original encrypted data, and read all of them */
goto cleanup;
}
goto cleanup;
}
/* decrypt data using old key */
decrypted_len = 0;
goto cleanup;
}
if (decrypted_buf == NULL) {
goto cleanup;
}
goto cleanup;
}
/* re-encrypt with new key */
encrypted_len = 0;
goto cleanup;
}
goto cleanup;
}
goto cleanup;
}
/* calculate hmac on re-encrypted data using new hmac key */
goto cleanup;
}
/* just for sanity check */
if (hmac_len != OBJ_HMAC_SIZE) {
goto cleanup;
}
/* write new hmac */
!= OBJ_HMAC_SIZE) {
goto cleanup;
}
/* write re-encrypted buffer to temp file */
!= encrypted_len) {
goto cleanup;
}
ret_val = 0;
/* unlock the files */
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
{
typedef struct priobjs {
} priobjs_t;
} else {
}
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
*/
if (tmp_ks_fd < 0) {
return (-1);
}
/* read and write PKCS version to temp file */
!= KS_PKCS11_VER_SIZE) {
goto cleanup;
}
!= KS_PKCS11_VER_SIZE) {
goto cleanup;
}
/* get version number, and write updated number to temp file */
goto cleanup;
}
version++;
!= KS_VER_SIZE) {
goto cleanup;
}
/* read and write counter, no modification necessary */
goto cleanup;
}
!= KS_COUNTER_SIZE) {
goto cleanup;
}
/* read old encryption salt */
if (crypt_salt == NULL) {
goto cleanup;
}
!= KS_KEY_SALT_SIZE) {
goto cleanup;
}
/* read old hmac salt */
goto cleanup;
}
!= KS_HMAC_SALT_SIZE) {
goto cleanup;
}
/* just create some empty bytes */
/* PIN as never been set */
!= CKR_OK) {
goto cleanup;
}
KS_KEY_SALT_SIZE) != KS_KEY_SALT_SIZE) {
(void) soft_cleanup_object(new_crypt_key);
goto cleanup;
}
!= CKR_OK) {
(void) soft_cleanup_object(new_crypt_key);
goto cleanup;
}
goto cleanup3;
}
} else {
goto cleanup;
}
/* no change to the encryption salt */
KS_KEY_SALT_SIZE) != KS_KEY_SALT_SIZE) {
(void) soft_cleanup_object(new_crypt_key);
goto cleanup;
}
(void) soft_cleanup_object(new_crypt_key);
goto cleanup;
}
/* no change to the hmac salt */
goto cleanup3;
}
}
/*
* read hashed pin salt, and write to updated keystore description
* file unmodified.
*/
goto cleanup3;
}
goto cleanup3;
}
if (hashed_pin_salt == NULL) {
goto cleanup3;
}
goto cleanup3;
}
!= (ssize_t)hashed_pin_salt_length) {
goto cleanup3;
}
/* old hashed pin length and value can be ignored, generate new one */
&hashed_pin_salt) < 0) {
goto cleanup3;
}
if (new_hashed_pin == NULL) {
goto cleanup3;
}
/* write new hashed pin length to file */
goto cleanup3;
}
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 */
/*
* 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;
}
}
strlen(TMP_OBJ_PREFIX)) == 0)) {
continue;
}
goto cleanup2;
}
goto cleanup2;
}
/* insert into list of file to be renamed */
} else {
}
}
/* rename all the private objects */
while (tmp) {
}
/* 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) {
}
ret_val = 0;
while (p) {
free(p);
p = tmp;
}
}
if (!pin_never_set) {
}
if ((!user_logged_in) && (!pin_never_set)) {
(void) soft_cleanup_object(enc_key);
(void) soft_cleanup_object(hmac_key);
}
if ((ret_val != 0) || (!user_logged_in)) {
(void) soft_cleanup_object(new_crypt_key);
(void) soft_cleanup_object(new_hmac_key);
}
if (!lock_held) {
ret_val = 1;
}
}
if (crypt_salt != NULL) {
}
}
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
*/
int
{
int fd;
/* get the salt from the keystore description file */
return (-1);
}
if (crypt_salt == NULL) {
goto cleanup;
}
goto cleanup;
}
!= KS_KEY_SALT_SIZE) {
goto cleanup;
}
!= CKR_OK) {
goto cleanup;
}
goto cleanup;
}
goto cleanup;
}
!= KS_HMAC_SALT_SIZE) {
goto cleanup;
}
!= CKR_OK) {
goto cleanup;
}
ret_val = 0;
/* unlock the file */
if (crypt_salt != NULL) {
}
}
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.
*/
{
int ks_fd;
*result_obj_list = NULL;
/*
* lock the keystore description file in "read" mode so that
* doing the search
*/
B_FALSE)) < 0) {
return (CKR_FUNCTION_FAILED);
}
return (CKR_FUNCTION_FAILED);
}
goto cleanup;
}
}
/* has not login - no need to go any further */
return (CKR_OK);
}
return (CKR_OK);
}
goto cleanup;
}
}
/* close the keystore description file */
return (CKR_OK);
/* close the keystore description file */
/* free all the objects found before hitting the error */
tmp = *result_obj_list;
while (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.
*/
{
int fd;
return (CKR_FUNCTION_FAILED);
}
}
lock_held)) < 0) {
return (CKR_FUNCTION_FAILED);
}
return (CKR_HOST_MEMORY);
}
/* 1st get the version */
!= OBJ_VER_SIZE) {
goto cleanup;
}
/* Then, read the IV */
goto cleanup;
}
/* Then, read the HMAC */
goto cleanup;
}
/* read the object */
goto cleanup;
}
*return_obj = obj;
} else {
/* verify HMAC of the object, make sure it matches */
goto cleanup;
}
/* decrypt object */
goto cleanup;
}
if (decrypted_buf == NULL) {
goto cleanup;
}
goto cleanup;
}
/*
* 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.
*/
goto cleanup;
}
*return_obj = obj;
}
}
/* unlock the file after reading */
if (!lock_held) {
}
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
{
return (-1);
}
/* if it is private object, make sure we have the key */
if (!public) {
return (-1);
}
}
/* open keystore, and set write lock */
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
*/
if (tmp_ks_fd < 0) {
return (-1);
}
/* read and write pkcs11 version */
!= KS_PKCS11_VER_SIZE) {
goto cleanup;
}
!= KS_PKCS11_VER_SIZE) {
goto cleanup;
}
/* get version number, and write updated number to temp file */
goto cleanup;
}
version++;
KS_VER_SIZE) != KS_VER_SIZE) {
goto cleanup;
}
/* get object count value */
goto cleanup;
}
if (public) {
} else {
}
/* create object file */
if (obj_fd < 0) {
/* can't create object file */
goto cleanup;
}
/* lock object file for writing */
goto cleanup2;
}
/* write object meta data */
!= sizeof (version)) {
goto cleanup2;
}
if (public) {
} else {
/* generate an IV */
goto cleanup2;
}
}
goto cleanup2;
}
if (public) {
goto cleanup2;
}
goto cleanup2;
}
} else {
&prepared_buf, &prepared_len) != 0) {
goto cleanup2;
}
goto cleanup2;
}
if (encrypted_buf == NULL) {
goto cleanup2;
}
goto cleanup2;
}
/* calculate HMAC of encrypted object */
goto cleanup2;
}
if (hmac_size != OBJ_HMAC_SIZE) {
goto cleanup2;
}
/* write hmac */
goto cleanup2;
}
/* write encrypted object */
!= out_len) {
goto cleanup2;
}
}
"obj%d", counter);
/*
* store new counter to temp keystore description file.
*/
counter++;
goto cleanup2;
}
/* read rest of keystore description file and store into temp file */
while (nread > 0) {
goto cleanup2;
}
}
if (!lock_held) {
/* release lock on description file */
return (-1);
}
}
return (0);
/* remove object file. No need to remove lock first */
(void) remove(tmp_ks_desc_name);
if (!lock_held) {
/* release lock on description file */
}
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
{
/* if it is private object, make sure we have the key */
return (-1);
}
}
/* open and lock keystore description file */
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);
/* unlock keystore description file */
return (-1);
}
/* open object file */
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
*/
} else {
}
if (tmp_fd < 0) {
/* can't create tmp object file */
goto cleanup1;
}
/* read version, increment, and write to tmp file */
goto cleanup2;
}
version++;
!= OBJ_VER_SIZE) {
goto cleanup2;
}
/* generate a new IV for the object, old one can be ignored */
goto cleanup2;
}
goto cleanup2;
}
/* hmac is always NULL for public objects */
!= OBJ_HMAC_SIZE) {
goto cleanup2;
}
/* write updated object */
goto cleanup2;
}
} else {
&prepared_buf, &prepared_len) != 0) {
goto cleanup2;
}
/* encrypt the data */
goto cleanup2;
}
if (encrypted_buf == NULL) {
goto cleanup2;
}
goto cleanup2;
}
/* calculate hmac on encrypted buf */
goto cleanup2;
}
if (hmac_size != OBJ_HMAC_SIZE) {
goto cleanup2;
}
!= OBJ_HMAC_SIZE) {
goto cleanup2;
}
!= out_len) {
goto cleanup2;
}
}
/* rename updated temporary object file */
return (-1);
}
/* rename updated keystore description file */
(void) unlink(tmp_ks_name);
return (-1);
}
/* determine need to unlock file or not */
if (!lock_held) {
return (-1);
}
}
/* unlock keystore description file */
return (-1);
}
return (0); /* All operations completed successfully */
/* unlock keystore description file */
(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
{
int fd;
int obj_fd;
lock_held)) < 0) {
return (-1);
}
(void) get_desc_file_path(ks_desc_file);
(void) get_tmp_desc_file_path(tmp_ks_name);
goto cleanup;
}
} else {
}
/*
* by acquiring the lock on the file
*/
B_FALSE)) < 0) {
return (-1);
}
goto cleanup;
}
goto cleanup;
}
ret_val = 0;
/* unlock keystore description file */
if (!lock_held) {
return (-1);
}
}
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
{
B_FALSE)) < 0) {
return (-1);
}
goto cleanup;
}
goto cleanup;
}
goto cleanup;
}
!= (ssize_t)hashed_pin_salt_size) {
goto cleanup;
}
ret_val = 0;
ret_val = -1;
}
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.
*/
{
int fd;
lock_held)) < 0) {
return (CKR_FUNCTION_FAILED);
}
goto cleanup;
}
!= KS_KEY_SALT_SIZE) {
goto cleanup;
}
*initialized = B_FALSE;
hashed_pin = NULL;
} else {
*initialized = B_TRUE;
}
if (!lock_held) {
}
}
return (ret_val);
}
/*
* This checks if the keystore file exists
*/
static int
{
int ret;
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.
*
* Primary use is from C_InitToken().
* It is also called by soft_keystore_status() when the
* "desired_state" is not the the current load state of keystore.
*
*/
int
{
int ret;
/*
* 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.
*/
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)
if (create_keystore() == 0)
else {
"pkcs11_softtoken: "
"Cannot create keystore.");
break;
}
if (desired_state <= KEYSTORE_PRESENT)
break;
/* FALLTHRU */
case KEYSTORE_PRESENT:
!= 0) {
"pkcs11_softtoken: Keystore access failed.");
break;
}
if (desired_state <= KEYSTORE_LOAD)
break;
/* FALLTHRU */
case KEYSTORE_LOAD:
/* Load all the public token objects from keystore */
!= CKR_OK) {
(void) soft_destroy_token_session();
"pkcs11_softtoken: Cannot initialize keystore.");
break;
}
};
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.
*
*/
{
return (B_FALSE);
}