#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 <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"
#define MAXPATHLEN 1024
#define SUNW_PATH ".sunw"
#define KEYSTORE_PATH "pkcs11_softtoken"
#define PUB_OBJ_DIR "public"
#define PRI_OBJ_DIR "private"
#define DS_FILE "objstore_info"
#define TMP_DS_FILE "t_info"
#define OBJ_PREFIX "obj"
#define OBJ_PREFIX_LEN sizeof (OBJ_PREFIX) - 1
#define TMP_OBJ_PREFIX "t_o"
#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
#define KS_COUNTER_OFFSET (KS_VER_OFFSET + KS_VER_SIZE)
#define KS_COUNTER_SIZE 4
#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
#define KS_HASHED_PIN_SALT_LEN_OFFSET (KS_HMAC_SALT_OFFSET + KS_HMAC_SALT_SIZE)
#define KS_HASHED_PIN_SALT_LEN_SIZE 8
#define KS_HASHED_PIN_SALT_OFFSET \
(KS_HASHED_PIN_SALT_LEN_OFFSET + KS_HASHED_PIN_SALT_LEN_SIZE)
static off_t ks_hashed_pinlen_offset = -1;
#define KS_HASHED_PINLEN_SIZE 8
#define OBJ_VER_OFFSET 0
#define OBJ_VER_SIZE 4
#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
#define OBJ_DATA_OFFSET (OBJ_HMAC_OFFSET + OBJ_HMAC_SIZE)
#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_keystore_path()
{
char *home = getenv("HOME");
char *alt = getenv(ALTERNATE_KEYSTORE_PATH);
if (keystore_path_initialized) {
return (keystore_path);
}
bzero(keystore_path, sizeof (keystore_path));
if ((alt != NULL) && (strcmp(alt, "") != 0)) {
(void) snprintf(keystore_path, MAXPATHLEN, "%s/%s",
alt, KEYSTORE_PATH);
keystore_path_initialized = B_TRUE;
} else if ((home != NULL) && (strcmp(home, "") != 0)) {
(void) snprintf(keystore_path, MAXPATHLEN, "%s/%s/%s",
home, SUNW_PATH, 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);
}
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);
}
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;
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);
}
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 *alt;
if (mkdir(get_keystore_path(), S_IRUSR|S_IWUSR|S_IXUSR) < 0) {
if (errno == EEXIST) {
return (0);
}
if (errno == EACCES) {
return (-1);
}
if (errno == ENOENT) {
char keystore[MAXPATHLEN];
alt = getenv(ALTERNATE_KEYSTORE_PATH);
if ((alt == NULL) || (strcmp(alt, "") == 0)) {
char *home = getenv("HOME");
if (home == NULL || strcmp(home, "") == 0) {
return (-1);
}
(void) snprintf(keystore, sizeof (keystore),
"%s/%s/%s", home, SUNW_PATH, KEYSTORE_PATH);
if (mkdirp(keystore,
S_IRUSR|S_IWUSR|S_IXUSR) < 0) {
return (-1);
}
} else {
return (-1);
}
}
}
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 {
(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) {
(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) {
(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);
}
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;
}
buf = SWAP32(0);
if (writen_nointr(fd, (void *)&buf, KS_VER_SIZE) != KS_VER_SIZE) {
goto cleanup;
}
buf = SWAP32(1);
if (writen_nointr(fd, (void *)&buf, KS_COUNTER_SIZE)
!= KS_COUNTER_SIZE) {
goto cleanup;
}
bzero(salt, sizeof (salt));
if (writen_nointr(fd, (void *)salt, KS_KEY_SALT_SIZE)
!= KS_KEY_SALT_SIZE) {
goto cleanup;
}
if (writen_nointr(fd, (void *)salt, KS_HMAC_SALT_SIZE)
!= KS_HMAC_SALT_SIZE) {
goto cleanup;
}
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);
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;
}
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);
freezero(hashed_pin_salt, hashed_pin_salt_len);
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);
}
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);
}
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);
}
if (is_inode_same(*fd, fname, &same_inode) != 0) {
(void) lock_file(*fd, B_TRUE, B_FALSE);
return (-1);
}
while (!same_inode) {
if (lock_file(*fd, B_TRUE, B_FALSE) != 0) {
return (-1);
}
(void) close(*fd);
*fd = open_nointr(fname, mode|O_NONBLOCK);
if (*fd < 0) {
return (-1);
}
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);
return (-1);
}
}
return (0);
}
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];
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) {
return (fd);
}
if (acquire_file_lock(&fd, fname, mode) != 0) {
if (fd > 0) {
(void) close(fd);
}
return (-1);
}
done:
return (fd);
}
static int
lock_desc_file(boolean_t read_lock, boolean_t set_lock)
{
char ks_desc_file[MAXPATHLEN];
if (set_lock) {
int oflag;
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 {
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) {
return (fd);
}
if (acquire_file_lock(&fd, obj_fname, oflag) != 0) {
if (fd > 0) {
(void) close(fd);
}
return (-1);
}
return (fd);
}
static int
create_updated_keystore_version(int fd, char *tmp_fname)
{
int version, tmp_fd;
char buf[BUFSIZ];
size_t nread;
tmp_fd = open_nointr(tmp_fname,
O_WRONLY|O_CREAT|O_EXCL|O_NONBLOCK, S_IRUSR|S_IWUSR);
if (tmp_fd < 0) {
return (-1);
}
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;
}
if (readn_nointr(fd, &version, KS_VER_SIZE) != KS_VER_SIZE) {
goto cleanup;
}
version = SWAP32(version);
version++;
version = SWAP32(version);
if (writen_nointr(tmp_fd, (void *)&version, KS_VER_SIZE)
!= KS_VER_SIZE) {
goto cleanup;
}
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);
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);
}
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);
}
bzero(*prepared_buf, MAXPATHLEN);
(void) memcpy(*prepared_buf, obj_path, strlen(obj_path));
(void) memcpy(*prepared_buf + MAXPATHLEN, buf, buf_len);
return (0);
}
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);
}
}
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) {
freezero(*hashed_pin, hashed_pin_size + 1);
*hashed_pin = NULL;
return (CKR_FUNCTION_FAILED);
}
(*hashed_pin)[hashed_pin_size] = '\0';
return (CKR_OK);
}
int
soft_keystore_readlock(boolean_t set_lock)
{
return (lock_desc_file(B_TRUE, set_lock));
}
int
soft_keystore_writelock(boolean_t set_lock)
{
return (lock_desc_file(B_FALSE, set_lock));
}
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);
}
int
soft_keystore_unlock_object(int fd)
{
if (lock_file(fd, B_TRUE, B_FALSE) != 0) {
return (1);
}
(void) close(fd);
return (0);
}
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_FALSE, 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);
}
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);
}
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);
}
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_FALSE,
lock_held)) < 0) {
return (-1);
}
rv = get_hashed_pin(fd, hashed_pin);
if (rv == CKR_OK) {
ret_val = 0;
}
if (!lock_held) {
if (lock_file(fd, B_TRUE, B_FALSE) < 0) {
ret_val = -1;
}
}
(void) close(fd);
return (ret_val);
}
CK_RV
soft_gen_iv(CK_BYTE *iv)
{
return (pkcs11_get_nzero_urandom(iv, 16) < 0 ?
CKR_DEVICE_ERROR : CKR_OK);
}
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++;
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);
}
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) {
(void) lock_file(old_fd, B_TRUE, B_FALSE);
(void) close(old_fd);
(void) close(new_fd);
return (-1);
}
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;
}
if (readn_nointr(old_fd, (char *)old_iv, OBJ_IV_SIZE) != OBJ_IV_SIZE) {
goto cleanup;
}
if (soft_gen_iv(iv) != CKR_OK) {
goto cleanup;
}
if (writen_nointr(new_fd, (char *)iv, OBJ_IV_SIZE) != OBJ_IV_SIZE) {
goto cleanup;
}
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;
}
decrypted_len = 0;
if (soft_keystore_crypt(enc_key, old_iv, B_FALSE, buf, nread,
NULL, &decrypted_len) != CKR_OK) {
freezero(buf, nread);
goto cleanup;
}
decrypted_buf = malloc(decrypted_len);
if (decrypted_buf == NULL) {
freezero(buf, nread);
goto cleanup;
}
if (soft_keystore_crypt(enc_key, old_iv, B_FALSE, buf, nread,
decrypted_buf, &decrypted_len) != CKR_OK) {
freezero(buf, nread);
freezero(decrypted_buf, decrypted_len);
}
freezero(buf, nread);
encrypted_len = 0;
if (soft_keystore_crypt(new_enc_key, iv, B_TRUE, decrypted_buf,
decrypted_len, NULL, &encrypted_len) != CKR_OK) {
freezero(decrypted_buf, decrypted_len);
goto cleanup;
}
buf = malloc(encrypted_len);
if (buf == NULL) {
freezero(decrypted_buf, decrypted_len);
goto cleanup;
}
if (soft_keystore_crypt(new_enc_key, iv, B_TRUE, decrypted_buf,
decrypted_len, buf, &encrypted_len) != CKR_OK) {
freezero(buf, encrypted_len);
freezero(buf, decrypted_len);
goto cleanup;
}
freezero(decrypted_buf, decrypted_len);
hmac_len = OBJ_HMAC_SIZE;
if (soft_keystore_hmac(new_hmac_key, B_TRUE, buf,
encrypted_len, hmac, &hmac_len) != CKR_OK) {
freezero(buf, encrypted_len);
goto cleanup;
}
if (hmac_len != OBJ_HMAC_SIZE) {
freezero(buf, encrypted_len);
goto cleanup;
}
if (writen_nointr(new_fd, (char *)hmac, OBJ_HMAC_SIZE)
!= OBJ_HMAC_SIZE) {
freezero(buf, encrypted_len);
goto cleanup;
}
if (writen_nointr(new_fd, (void *)buf, encrypted_len)
!= encrypted_len) {
freezero(buf, encrypted_len);
goto cleanup;
}
freezero(buf, encrypted_len);
ret_val = 0;
cleanup:
(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);
}
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_TRUE,
lock_held)) < 0) {
return (-1);
}
(void) get_desc_file_path(ks_desc_file);
(void) get_tmp_desc_file_path(tmp_ks_desc_name);
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);
}
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;
}
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;
}
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;
}
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;
}
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;
}
bzero(filebuf, sizeof (filebuf));
if (memcmp(crypt_salt, filebuf, KS_KEY_SALT_SIZE) == 0) {
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) {
freezero(new_crypt_salt,
KS_KEY_SALT_SIZE);
(void) soft_cleanup_object(new_crypt_key);
goto cleanup;
}
freezero(new_crypt_salt, KS_KEY_SALT_SIZE);
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) {
freezero(new_hmac_salt,
KS_HMAC_SALT_SIZE);
goto cleanup3;
}
freezero(new_hmac_salt, KS_HMAC_SALT_SIZE);
} else {
if (soft_gen_crypt_key(newpin, &new_crypt_key,
(CK_BYTE **)&crypt_salt) != CKR_OK) {
goto cleanup;
}
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;
}
if (writen_nointr(tmp_ks_fd, (void *)hmac_salt,
KS_HMAC_SALT_SIZE) != KS_HMAC_SALT_SIZE) {
goto cleanup3;
}
}
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) {
freezero(hashed_pin_salt,
hashed_pin_salt_length + 1);
goto cleanup3;
}
if ((writen_nointr(tmp_ks_fd, hashed_pin_salt, hashed_pin_salt_length))
!= (ssize_t)hashed_pin_salt_length) {
freezero(hashed_pin_salt,
hashed_pin_salt_length + 1);
goto cleanup3;
}
hashed_pin_salt[hashed_pin_salt_length] = '\0';
if (soft_gen_hashed_pin(newpin, &new_hashed_pin,
&hashed_pin_salt) < 0) {
freezero(hashed_pin_salt,
hashed_pin_salt_length + 1);
goto cleanup3;
}
freezero(hashed_pin_salt, hashed_pin_salt_length + 1);
if (new_hashed_pin == NULL) {
goto cleanup3;
}
new_hashed_pin_len = strlen(new_hashed_pin);
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) {
goto rename_desc_file;
}
pri_dirp = opendir(get_pri_obj_path(pri_obj_path));
if (pri_dirp == NULL) {
goto cleanup3;
}
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;
}
if (pri_objs == NULL) {
obj->next = NULL;
pri_objs = obj;
} else {
obj->next = pri_objs;
pri_objs = obj;
}
}
tmp = pri_objs;
while (tmp) {
(void) rename(tmp->tmp_name, tmp->orig_name);
tmp = tmp->next;
}
rename_desc_file:
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;
}
}
freezero(crypt_salt, KS_KEY_SALT_SIZE);
freezero(hmac_salt, KS_HMAC_SALT_SIZE);
(void) close(fd);
(void) close(tmp_ks_fd);
if (ret_val != 0) {
(void) remove(tmp_ks_desc_name);
}
return (ret_val);
}
int
soft_keystore_authpin(uchar_t *pin)
{
int fd;
int ret_val = -1;
CK_BYTE *crypt_salt = NULL, *hmac_salt;
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:
(void) lock_file(fd, B_TRUE, B_FALSE);
(void) close(fd);
freezero(crypt_salt, KS_KEY_SALT_SIZE);
freezero(hmac_salt, KS_HMAC_SALT_SIZE);
return (ret_val);
}
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;
if ((ks_fd = open_and_lock_keystore_desc(O_RDONLY, B_FALSE,
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)) {
(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);
}
(void) lock_file(ks_fd, B_TRUE, B_FALSE);
(void) close(ks_fd);
return (CKR_OK);
cleanup:
(void) lock_file(ks_fd, B_TRUE, B_FALSE);
(void) close(ks_fd);
tmp = *result_obj_list;
while (tmp) {
*result_obj_list = tmp->next;
freezero(tmp->buf, tmp->size);
free(tmp);
tmp = *result_obj_list;
}
*result_obj_list = NULL;
return (rv);
}
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;
if (readn_nointr(fd, &(obj->obj_version), OBJ_VER_SIZE)
!= OBJ_VER_SIZE) {
goto cleanup;
}
obj->obj_version = SWAP32(obj->obj_version);
if (readn_nointr(fd, iv, OBJ_IV_SIZE) != OBJ_IV_SIZE) {
goto cleanup;
}
if (readn_nointr(fd, obj_hmac, OBJ_HMAC_SIZE) != OBJ_HMAC_SIZE) {
goto cleanup;
}
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;
hmac_size = OBJ_HMAC_SIZE;
if (soft_keystore_hmac(hmac_key, B_FALSE, buf,
nread, obj_hmac, &hmac_size) != CKR_OK) {
freezero(buf, nread);
rv = CKR_FUNCTION_FAILED;
goto cleanup;
}
if (soft_keystore_crypt(enc_key, iv, B_FALSE, buf, nread,
NULL, &out_len) != CKR_OK) {
freezero(buf, nread);
rv = CKR_FUNCTION_FAILED;
goto cleanup;
}
decrypted_buf = malloc(sizeof (uchar_t) * out_len);
if (decrypted_buf == NULL) {
freezero(buf, nread);
rv = CKR_HOST_MEMORY;
goto cleanup;
}
if (soft_keystore_crypt(enc_key, iv, B_FALSE, buf, nread,
decrypted_buf, &out_len) != CKR_OK) {
freezero(buf, nread);
freezero(decrypted_buf, out_len);
rv = CKR_FUNCTION_FAILED;
goto cleanup;
}
obj->size = out_len - MAXPATHLEN;
obj->buf = malloc(sizeof (uchar_t) * (out_len - MAXPATHLEN));
if (obj->buf == NULL) {
freezero(buf, nread);
freezero(decrypted_buf, out_len);
rv = CKR_HOST_MEMORY;
goto cleanup;
}
(void) memcpy(obj->buf, decrypted_buf + MAXPATHLEN, obj->size);
freezero(buf, nread);
freezero(decrypted_buf, out_len);
*return_obj = obj;
}
cleanup:
if (rv != CKR_OK) {
free(obj);
}
if (!lock_held) {
(void) lock_file(fd, B_TRUE, B_FALSE);
}
(void) close(fd);
return (rv);
}
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 (!public) {
if ((enc_key == NULL) ||
(enc_key->magic_marker != SOFTTOKEN_OBJECT_MAGIC)) {
return (-1);
}
}
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);
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);
}
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;
}
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;
}
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);
}
obj_fd = open_nointr(obj_name,
O_WRONLY|O_CREAT|O_EXCL|O_NONBLOCK, S_IRUSR|S_IWUSR);
if (obj_fd < 0) {
goto cleanup;
}
if (lock_file(obj_fd, B_FALSE, B_TRUE) != 0) {
(void) close(obj_fd);
goto cleanup2;
}
version = SWAP32(1);
if (writen_nointr(obj_fd, (void *)&version, sizeof (version))
!= sizeof (version)) {
goto cleanup2;
}
if (public) {
bzero(iv, sizeof (iv));
} else {
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) {
freezero(prepared_buf, prepared_len);
goto cleanup2;
}
encrypted_buf = malloc(out_len * sizeof (char));
if (encrypted_buf == NULL) {
freezero(prepared_buf, prepared_len);
goto cleanup2;
}
if (soft_keystore_crypt(enc_key, iv,
B_TRUE, prepared_buf, prepared_len,
encrypted_buf, &out_len) != CKR_OK) {
freezero(encrypted_buf, out_len);
freezero(prepared_buf, prepared_len);
goto cleanup2;
}
freezero(prepared_buf, prepared_len);
hmac_size = OBJ_HMAC_SIZE;
if (soft_keystore_hmac(hmac_key, B_TRUE, encrypted_buf,
out_len, obj_hmac, &hmac_size) != CKR_OK) {
freezero(encrypted_buf, out_len);
goto cleanup2;
}
if (hmac_size != OBJ_HMAC_SIZE) {
freezero(encrypted_buf, out_len);
goto cleanup2;
}
if (writen_nointr(obj_fd, (void *)obj_hmac,
sizeof (obj_hmac)) != sizeof (obj_hmac)) {
freezero(encrypted_buf, out_len);
goto cleanup2;
}
if (writen_nointr(obj_fd, (void *)encrypted_buf, out_len)
!= out_len) {
freezero(encrypted_buf, out_len);
goto cleanup2;
}
freezero(encrypted_buf, out_len);
}
(void) close(obj_fd);
(void) snprintf((char *)keyhandle->name, sizeof (keyhandle->name),
"obj%d", counter);
keyhandle->public = public;
counter++;
counter = SWAP32(counter);
if (writen_nointr(tmp_ks_fd, (void *)&counter,
sizeof (counter)) != sizeof (counter)) {
goto cleanup2;
}
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) {
if (lock_file(fd, B_FALSE, B_FALSE) != 0) {
(void) close(fd);
return (-1);
}
}
(void) close(fd);
explicit_bzero(obj_hmac, sizeof (obj_hmac));
explicit_bzero(iv, sizeof (iv));
return (0);
cleanup2:
(void) unlink(obj_name);
cleanup:
(void) close(tmp_ks_fd);
(void) remove(tmp_ks_desc_name);
if (!lock_held) {
(void) lock_file(fd, B_FALSE, B_FALSE);
}
(void) close(fd);
explicit_bzero(obj_hmac, sizeof (obj_hmac));
explicit_bzero(iv, sizeof (iv));
return (-1);
}
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 (!(ks_handle->public)) {
if ((enc_key == NULL) ||
(enc_key->magic_marker != SOFTTOKEN_OBJECT_MAGIC)) {
return (-1);
}
}
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);
(void) get_tmp_desc_file_path(tmp_ks_name);
if (create_updated_keystore_version(ks_fd, tmp_ks_name) != 0) {
(void) lock_file(ks_fd, B_FALSE, B_FALSE);
(void) close(ks_fd);
return (-1);
}
if ((fd = open_and_lock_object_file(ks_handle, O_RDWR,
lock_held)) < 0) {
goto cleanup;
}
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) {
goto cleanup1;
}
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;
}
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) {
bzero(obj_hmac, sizeof (obj_hmac));
if (writen_nointr(tmp_fd, (char *)obj_hmac, OBJ_HMAC_SIZE)
!= OBJ_HMAC_SIZE) {
goto cleanup2;
}
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;
}
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) {
freezero(prepared_buf, prepared_len);
goto cleanup2;
}
if (soft_keystore_crypt(enc_key, iv, B_TRUE, prepared_buf,
prepared_len, encrypted_buf, &out_len) != CKR_OK) {
freezero(prepared_buf, prepared_len);
freezero(encrypted_buf, out_len);
goto cleanup2;
}
freezero(prepared_buf, prepared_len);
hmac_size = OBJ_HMAC_SIZE;
if (soft_keystore_hmac(hmac_key, B_TRUE, encrypted_buf,
out_len, obj_hmac, &hmac_size) != CKR_OK) {
freezero(encrypted_buf, out_len);
goto cleanup2;
}
if (hmac_size != OBJ_HMAC_SIZE) {
freezero(encrypted_buf, out_len);
goto cleanup2;
}
if (writen_nointr(tmp_fd, (char *)obj_hmac, OBJ_HMAC_SIZE)
!= OBJ_HMAC_SIZE) {
freezero(encrypted_buf, out_len);
goto cleanup2;
}
if (writen_nointr(tmp_fd, (void *)encrypted_buf, out_len)
!= out_len) {
freezero(encrypted_buf, out_len);
goto cleanup2;
}
freezero(encrypted_buf, out_len);
}
(void) close(tmp_fd);
if (rename(tmp_name, orig_name) != 0) {
(void) unlink(tmp_name);
return (-1);
}
if (rename(tmp_ks_name, ks_desc_file) != 0) {
(void) unlink(tmp_name);
(void) unlink(tmp_ks_name);
return (-1);
}
if (!lock_held) {
if (lock_file(fd, B_FALSE, B_FALSE) < 0) {
(void) close(fd);
(void) unlink(tmp_name);
return (-1);
}
}
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);
explicit_bzero(iv, sizeof (iv));
explicit_bzero(obj_hmac, sizeof (obj_hmac));
return (0);
cleanup2:
(void) close(tmp_fd);
(void) remove(tmp_name);
cleanup1:
(void) close(fd);
cleanup:
(void) lock_file(ks_fd, B_FALSE, B_FALSE);
(void) close(ks_fd);
(void) remove(tmp_ks_name);
explicit_bzero(iv, sizeof (iv));
explicit_bzero(obj_hmac, sizeof (obj_hmac));
return (-1);
}
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);
}
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:
if (!lock_held) {
if (lock_file(fd, B_FALSE, B_FALSE) != 0) {
(void) close(fd);
return (-1);
}
}
(void) close(fd);
return (ret_val);
}
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_FALSE,
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) {
freezero(*salt, hashed_pin_salt_size + 1);
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);
}
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_FALSE,
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);
}
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);
}
int
soft_keystore_init(int desired_state)
{
int ret;
(void) pthread_mutex_lock(&soft_slot.keystore_mutex);
if (desired_state <= soft_slot.keystore_load_status) {
(void) pthread_mutex_unlock(&soft_slot.keystore_mutex);
return (soft_slot.keystore_load_status);
}
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;
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_LOAD;
if (desired_state <= KEYSTORE_LOAD)
break;
case KEYSTORE_LOAD:
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);
}
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));
}