You've already forked opencryptoki
mirror of
https://github.com/opencryptoki/opencryptoki.git
synced 2026-10-24 05:39:17 +03:00
As per PKCS#7, the pad bytes must all equal the padding value, i.e. the number of bytes to pad. Every byte that could be padding is examined, so the time taken does not depend on the padding. Also make sure that the padding value is not larger than the block size. Suggested-by: Alexandre Laroche <larochealexandre@hotmail.com> Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
3247 lines
94 KiB
C
3247 lines
94 KiB
C
/*
|
|
* COPYRIGHT (c) International Business Machines Corp. 2001-2017
|
|
*
|
|
* This program is provided under the terms of the Common Public License,
|
|
* version 1.0 (CPL-1.0). Any use, reproduction or distribution for this
|
|
* software constitutes recipient's acceptance of CPL-1.0 terms which can be
|
|
* found in the file LICENSE file or at
|
|
* https://opensource.org/licenses/cpl1.0.php
|
|
*/
|
|
|
|
// loadsave.c
|
|
//
|
|
// routines associated with loading/saving files
|
|
//
|
|
//
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <unistd.h>
|
|
#include <alloca.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ipc.h>
|
|
#include <sys/file.h>
|
|
#include <errno.h>
|
|
#include <syslog.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#include <openssl/evp.h>
|
|
|
|
#include "platform.h"
|
|
#include "pkcs11types.h"
|
|
#include "defs.h"
|
|
#include "host_defs.h"
|
|
#include "h_extern.h"
|
|
#include "tok_spec_struct.h"
|
|
#include "pkcs32.h"
|
|
#include "sw_crypt.h"
|
|
#include "trace.h"
|
|
#include "ock_syslog.h"
|
|
#include "slotmgr.h" // for ock_snprintf
|
|
|
|
#define OCK_MAX_TOKEN_OBJ_SIZE 0x80000000
|
|
|
|
CK_RV set_perm(int, const char *group);
|
|
|
|
CK_RV restore_private_token_object_old(STDLL_TokData_t *tokdata, CK_BYTE *data,
|
|
CK_ULONG len, OBJECT *pObj,
|
|
const char *fname);
|
|
CK_RV reload_token_object_old(STDLL_TokData_t *tokdata, OBJECT *obj);
|
|
CK_RV save_public_token_object_old(STDLL_TokData_t *tokdata, OBJECT *obj);
|
|
CK_RV load_public_token_objects_old(STDLL_TokData_t *tokdata);
|
|
|
|
static int get_token_object_path(char *buf, size_t buflen,
|
|
STDLL_TokData_t *tokdata, const char *path,
|
|
const char *postfix)
|
|
{
|
|
if (ock_snprintf(buf, buflen, "%s/" PK_LITE_OBJ_DIR "/%.8s%s",
|
|
tokdata->data_store, path,
|
|
postfix != NULL ? postfix : "") != 0) {
|
|
TRACE_ERROR("buffer overflow for object path %s", path);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static FILE *open_token_object_path(char *buf, size_t buflen,
|
|
STDLL_TokData_t *tokdata, const char *path,
|
|
const char *mode)
|
|
{
|
|
FILE *fp;
|
|
|
|
if (get_token_object_path(buf, buflen, tokdata, path, NULL) < 0)
|
|
return NULL;
|
|
|
|
/* CWE-59 fix: Use fopen_nofollow to prevent symlink attacks */
|
|
fp = fopen_nofollow(buf, mode);
|
|
if (fp == NULL && errno == ELOOP)
|
|
TRACE_ERROR("Refusing to follow symlink: %s\n", buf);
|
|
|
|
return fp;
|
|
}
|
|
|
|
static FILE *open_token_object_path_new(char *newbuf, size_t newbuflen,
|
|
char *basebuf, size_t basebuflen,
|
|
STDLL_TokData_t *tokdata,
|
|
const char *path, const char *mode)
|
|
{
|
|
FILE *fp;
|
|
|
|
if (get_token_object_path(newbuf, newbuflen, tokdata, path, ".TMP") < 0)
|
|
return NULL;
|
|
if (get_token_object_path(basebuf, basebuflen, tokdata, path, NULL) < 0)
|
|
return NULL;
|
|
|
|
/* CWE-59 fix: Use fopen_nofollow to prevent symlink attacks */
|
|
fp = fopen_nofollow(newbuf, mode);
|
|
if (fp == NULL && errno == ELOOP)
|
|
TRACE_ERROR("Refusing to follow symlink: %s\n", newbuf);
|
|
|
|
return fp;
|
|
}
|
|
|
|
static int get_token_data_store_path(char *buf, size_t buflen,
|
|
STDLL_TokData_t *tokdata, const char *path,
|
|
const char *postfix)
|
|
{
|
|
if (ock_snprintf(buf, buflen, "%s/%s%s", tokdata->data_store, path,
|
|
postfix != NULL ? postfix : "")) {
|
|
TRACE_ERROR("buffer overflow for path %s", path);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static FILE *open_token_data_store_path(char *buf, size_t buflen,
|
|
STDLL_TokData_t *tokdata,
|
|
const char *path, const char *mode)
|
|
{
|
|
FILE *fp;
|
|
|
|
if (get_token_data_store_path(buf, buflen, tokdata, path, NULL) < 0)
|
|
return NULL;
|
|
|
|
/* CWE-59 fix: Use fopen_nofollow to prevent symlink attacks */
|
|
fp = fopen_nofollow(buf, mode);
|
|
if (fp == NULL && errno == ELOOP)
|
|
TRACE_ERROR("Refusing to follow symlink: %s\n", buf);
|
|
|
|
return fp;
|
|
}
|
|
|
|
static FILE *open_token_data_store_path_new(char *newbuf, size_t newbuflen,
|
|
char *basebuf, size_t basebuflen,
|
|
STDLL_TokData_t *tokdata,
|
|
const char *path, const char *mode)
|
|
{
|
|
FILE *fp;
|
|
|
|
if (get_token_data_store_path(newbuf, newbuflen, tokdata, path, ".TMP") < 0)
|
|
return NULL;
|
|
if (get_token_data_store_path(basebuf, basebuflen, tokdata, path, NULL) < 0)
|
|
return NULL;
|
|
|
|
/* CWE-59 fix: Use fopen_nofollow to prevent symlink attacks */
|
|
fp = fopen_nofollow(newbuf, mode);
|
|
if (fp == NULL && errno == ELOOP)
|
|
TRACE_ERROR("Refusing to follow symlink: %s\n", newbuf);
|
|
|
|
return fp;
|
|
}
|
|
|
|
static FILE *open_token_object_index(char *buf, size_t buflen,
|
|
STDLL_TokData_t *tokdata, const char *mode)
|
|
{
|
|
return open_token_object_path(buf, buflen, tokdata, PK_LITE_OBJ_IDX, mode);
|
|
}
|
|
|
|
static FILE *open_token_nvdat(char *buf, size_t buflen,
|
|
STDLL_TokData_t *tokdata, const char *mode)
|
|
{
|
|
FILE *fp;
|
|
|
|
if (ock_snprintf(buf, buflen, "%s/" PK_LITE_NV, tokdata->data_store)) {
|
|
TRACE_ERROR("NVDAT.TOK file name buffer overflow\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* CWE-59 fix: Use fopen_nofollow to prevent symlink attacks */
|
|
fp = fopen_nofollow(buf, mode);
|
|
if (fp == NULL && errno == ELOOP)
|
|
TRACE_ERROR("Refusing to follow symlink: %s\n", buf);
|
|
|
|
return fp;
|
|
}
|
|
|
|
static FILE *open_token_nvdat_new(char *newbuf, size_t newbuflen,
|
|
char *basebuf, size_t basebuflen,
|
|
STDLL_TokData_t *tokdata, const char *mode)
|
|
{
|
|
FILE *fp;
|
|
|
|
if (ock_snprintf(newbuf, newbuflen, "%s/" PK_LITE_NV ".TMP",
|
|
tokdata->data_store)) {
|
|
TRACE_ERROR("NVDAT.TOK file name buffer overflow\n");
|
|
return NULL;
|
|
}
|
|
if (ock_snprintf(basebuf, basebuflen, "%s/" PK_LITE_NV,
|
|
tokdata->data_store)) {
|
|
TRACE_ERROR("NVDAT.TOK file name buffer overflow\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* CWE-59 fix: Use fopen_nofollow to prevent symlink attacks */
|
|
fp = fopen_nofollow(newbuf, mode);
|
|
if (fp == NULL && errno == ELOOP)
|
|
TRACE_ERROR("Refusing to follow symlink: %s\n", newbuf);
|
|
|
|
return fp;
|
|
}
|
|
|
|
static CK_RV close_token_file_new(FILE * fp, CK_RV rc,
|
|
const char *newname, const char *basename)
|
|
{
|
|
if (fp != NULL) {
|
|
fclose(fp);
|
|
|
|
if (rc == CKR_OK) {
|
|
/* Rename will replace already existing basename file */
|
|
if (rename(newname, basename) != 0) {
|
|
TRACE_ERROR("rename(%s, %s): %s\n", newname, basename,
|
|
strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
}
|
|
} else {
|
|
remove((newname));
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
char *get_pk_dir(STDLL_TokData_t *tokdata, char *fname, size_t len)
|
|
{
|
|
int snres;
|
|
struct passwd *pw = NULL;
|
|
|
|
if (token_specific.data_store.per_user && (pw = getpwuid(geteuid())) != NULL)
|
|
snres = ock_snprintf(fname, len, "%s/%s", tokdata->pk_dir, pw->pw_name);
|
|
else
|
|
snres = ock_snprintf(fname, len, "%s", tokdata->pk_dir);
|
|
return snres != 0 ? NULL : fname;
|
|
}
|
|
|
|
CK_RV set_perm(int file, const char *group)
|
|
{
|
|
struct stat sb;
|
|
struct group *grp;
|
|
mode_t mode;
|
|
|
|
if (group == NULL || group[0] == '\0')
|
|
group = PKCS_GROUP;
|
|
|
|
if (fstat(file, &sb) != 0) {
|
|
TRACE_DEVEL("fstat failed: %s\n", strerror(errno));
|
|
return CKR_FUNCTION_FAILED;
|
|
}
|
|
|
|
grp = getgrnam(group);
|
|
if (grp == NULL) {
|
|
TRACE_DEVEL("getgrnam(%s) failed: %s\n", group, strerror(errno));
|
|
return CKR_FUNCTION_FAILED;
|
|
}
|
|
|
|
if (token_specific.data_store.per_user) {
|
|
/*
|
|
* In the TPM token, with per user data stores, we don't share
|
|
* the token object amongst a group. In fact, we want to
|
|
* restrict access to a single user.
|
|
* Only change the file mode if its not already as expected.
|
|
*/
|
|
if (S_ISDIR(sb.st_mode))
|
|
mode = S_IRUSR | S_IWUSR | S_IXUSR;
|
|
else
|
|
mode = S_IRUSR | S_IWUSR;
|
|
|
|
if ((sb.st_mode & ~S_IFMT) != mode) {
|
|
if (fchmod(file, mode) != 0) {
|
|
TRACE_DEVEL("fchmod(rw-------) failed: %s\n", strerror(errno));
|
|
return CKR_FUNCTION_FAILED;
|
|
}
|
|
}
|
|
} else {
|
|
/* Set absolute permissions or rw-rw----, if not already as expected */
|
|
if (S_ISDIR(sb.st_mode))
|
|
mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP;
|
|
else
|
|
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
|
|
|
|
if ((sb.st_mode & ~S_IFMT) != mode) {
|
|
if (fchmod(file, mode) != 0) {
|
|
TRACE_DEVEL("fchmod(rw-rw----) failed: %s\n", strerror(errno));
|
|
return CKR_FUNCTION_FAILED;
|
|
}
|
|
}
|
|
|
|
/* set ownership to pkcs11 group, if not already as expected */
|
|
if (sb.st_gid != grp->gr_gid) {
|
|
if (fchown(file, -1, grp->gr_gid) != 0) {
|
|
TRACE_DEVEL("fchown(-1, %s) failed: %s\n", group,
|
|
strerror(errno));
|
|
return CKR_FUNCTION_FAILED;
|
|
}
|
|
}
|
|
}
|
|
|
|
return CKR_OK;
|
|
}
|
|
|
|
//
|
|
// Note: The token lock (XProcLock) must be held when calling this function.
|
|
// The object must hold the READ lock when this function is called.
|
|
//
|
|
CK_RV save_token_object(STDLL_TokData_t *tokdata, OBJECT *obj)
|
|
{
|
|
FILE *fp = NULL;
|
|
char line[256];
|
|
char fname[PATH_MAX];
|
|
CK_RV rc;
|
|
|
|
// write token object
|
|
if (object_is_private(obj) == TRUE)
|
|
rc = save_private_token_object(tokdata, obj);
|
|
else
|
|
rc = save_public_token_object(tokdata, obj);
|
|
if (rc != CKR_OK)
|
|
return rc;
|
|
|
|
// update the index file if it exists
|
|
fp = open_token_object_index(fname, sizeof(fname), tokdata, "r");
|
|
if (fp) {
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK) {
|
|
fclose(fp);
|
|
return rc;
|
|
}
|
|
while (fgets(line, 50, fp)) {
|
|
line[strlen(line) - 1] = 0;
|
|
if (strcmp(line, (char *)obj->name) == 0) {
|
|
fclose(fp);
|
|
// object is already in the list
|
|
return CKR_OK;
|
|
}
|
|
}
|
|
fclose(fp);
|
|
}
|
|
// we didn't find it...either the index file doesn't exist or this
|
|
// is a new object...
|
|
//
|
|
fp = fopen_nofollow(fname, "a");
|
|
if (!fp) {
|
|
if (errno == ELOOP)
|
|
TRACE_ERROR("Refusing to follow symlink: %s\n", fname);
|
|
else
|
|
TRACE_ERROR("fopen(%s): %s\n", fname, strerror(errno));
|
|
return CKR_FUNCTION_FAILED;
|
|
}
|
|
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK) {
|
|
fclose(fp);
|
|
return rc;
|
|
}
|
|
|
|
fprintf(fp, "%s\n", obj->name);
|
|
fclose(fp);
|
|
|
|
return CKR_OK;
|
|
}
|
|
|
|
//
|
|
// Note: The token lock (XProcLock) must be held when calling this function.
|
|
//
|
|
CK_RV delete_token_object(STDLL_TokData_t *tokdata, OBJECT *obj)
|
|
{
|
|
FILE *fp1, *fp2;
|
|
char objidx[PATH_MAX], idxtmp[PATH_MAX], fname[PATH_MAX], line[256];
|
|
CK_RV rc;
|
|
|
|
// FIXME: on UNIX, we need to make sure these guys aren't symlinks
|
|
// before we blindly write to these files...
|
|
//
|
|
|
|
// remove the object from the index file
|
|
//
|
|
|
|
fp1 = open_token_object_index(objidx, sizeof(objidx), tokdata, "r");
|
|
fp2 = open_token_object_path(idxtmp, sizeof(idxtmp),
|
|
tokdata, "IDX.TMP", "w");
|
|
if (!fp1 || !fp2) {
|
|
if (fp1)
|
|
fclose(fp1);
|
|
if (fp2)
|
|
fclose(fp2);
|
|
TRACE_ERROR("fopen failed\n");
|
|
return CKR_FUNCTION_FAILED;
|
|
}
|
|
|
|
rc = set_perm(fileno(fp2), tokdata->tokgroup);
|
|
if (rc != CKR_OK) {
|
|
fclose(fp1);
|
|
fclose(fp2);
|
|
return rc;
|
|
}
|
|
|
|
while (fgets(line, 50, fp1)) {
|
|
line[strlen(line) - 1] = 0;
|
|
if (strcmp(line, (char *)obj->name) == 0)
|
|
continue;
|
|
else
|
|
fprintf(fp2, "%s\n", line);
|
|
}
|
|
|
|
fclose(fp1);
|
|
fclose(fp2);
|
|
|
|
if (rename(idxtmp, objidx) != 0) {
|
|
TRACE_ERROR("rename failed\n");
|
|
return CKR_FUNCTION_FAILED;
|
|
}
|
|
|
|
if (get_token_object_path(fname, sizeof(fname), tokdata,
|
|
(char *)obj->name, NULL) < 0)
|
|
TRACE_DEVEL("file name buffer overflow in obj unlink\n");
|
|
else
|
|
unlink(fname);
|
|
|
|
return CKR_OK;
|
|
}
|
|
|
|
CK_RV remove_files_in_directory(const char *directory, CK_BBOOL ignore_errors)
|
|
{
|
|
CK_RV rc = CKR_OK;
|
|
DIR *dir;
|
|
struct dirent *entry;
|
|
int dfd;
|
|
|
|
dir = opendir_nofollow(directory);
|
|
if (dir == NULL) {
|
|
TRACE_DEVEL("Failed to open directory '%s': %s\n", directory,
|
|
strerror(errno));
|
|
return ignore_errors ? CKR_OK : CKR_FUNCTION_FAILED;
|
|
}
|
|
|
|
dfd = dirfd(dir);
|
|
|
|
while ((entry = readdir(dir)) != NULL) {
|
|
if (strncmp(entry->d_name, ".", 1) == 0)
|
|
continue;
|
|
|
|
if (unlinkat(dfd, entry->d_name, 0) != 0) {
|
|
TRACE_DEVEL("Failed to remove file '%s/%s': %s\n",
|
|
directory, entry->d_name, strerror(errno));
|
|
|
|
if (!ignore_errors) {
|
|
rc = CKR_FUNCTION_FAILED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
closedir(dir);
|
|
|
|
return rc;
|
|
}
|
|
|
|
CK_RV delete_token_data(STDLL_TokData_t *tokdata)
|
|
{
|
|
char directory[PATH_MAX];
|
|
|
|
if (get_token_data_store_path(directory, sizeof(directory), tokdata,
|
|
PK_LITE_OBJ_DIR, NULL) != 0)
|
|
return CKR_FUNCTION_FAILED;
|
|
|
|
return remove_files_in_directory(directory, CK_TRUE);
|
|
}
|
|
|
|
CK_RV init_data_store(STDLL_TokData_t *tokdata, char *directory,
|
|
char *data_store, size_t len)
|
|
{
|
|
char *pkdir;
|
|
int pklen;
|
|
struct stat statbuf;
|
|
struct group *grp;
|
|
const char *group;
|
|
|
|
if (tokdata->pk_dir != NULL) {
|
|
free(tokdata->pk_dir);
|
|
tokdata->pk_dir = NULL;
|
|
}
|
|
|
|
if ((pkdir = secure_getenv("PKCS_APP_STORE")) != NULL) {
|
|
pklen = strlen(pkdir) + 1024;
|
|
tokdata->pk_dir = (char *) calloc(pklen, 1);
|
|
if (!(tokdata->pk_dir))
|
|
return CKR_HOST_MEMORY;
|
|
if (ock_snprintf(tokdata->pk_dir, pklen, "%s/%s", pkdir, SUB_DIR) != 0)
|
|
return CKR_FUNCTION_FAILED;
|
|
if (get_pk_dir(tokdata, data_store, len) == NULL)
|
|
return CKR_FUNCTION_FAILED;
|
|
goto check;
|
|
}
|
|
|
|
if (directory) {
|
|
pklen = strlen(directory) + 1;
|
|
tokdata->pk_dir = (char *) calloc(pklen, 1);
|
|
if (!(tokdata->pk_dir))
|
|
return CKR_HOST_MEMORY;
|
|
if (ock_snprintf(tokdata->pk_dir, pklen, "%s", directory) != 0)
|
|
return CKR_FUNCTION_FAILED;
|
|
} else {
|
|
pklen = strlen(PK_DIR) + 1;
|
|
tokdata->pk_dir = (char *) calloc(pklen, 1);
|
|
if (!(tokdata->pk_dir))
|
|
return CKR_HOST_MEMORY;
|
|
if (ock_snprintf(tokdata->pk_dir, pklen, "%s", PK_DIR) != 0)
|
|
return CKR_FUNCTION_FAILED;
|
|
}
|
|
if (get_pk_dir(tokdata, data_store, len) == NULL)
|
|
return CKR_FUNCTION_FAILED;
|
|
|
|
check:
|
|
group = tokdata->tokgroup;
|
|
if (group == NULL || group[0] == '\0')
|
|
group = PKCS_GROUP;
|
|
|
|
grp = getgrnam(group);
|
|
if (grp == NULL) {
|
|
OCK_SYSLOG(LOG_ERR, "getgrname(%s): %s\n", group, strerror(errno));
|
|
TRACE_ERROR("getgrname(%s): %s\n", group, strerror(errno));
|
|
return CKR_FUNCTION_FAILED;
|
|
}
|
|
|
|
if (stat(tokdata->pk_dir, &statbuf) != 0) {
|
|
OCK_SYSLOG(LOG_ERR, "Could not stat directory '%s': %s\n",
|
|
tokdata->pk_dir, strerror(errno));
|
|
TRACE_ERROR("Could not stat directory '%s': %s\n", tokdata->pk_dir,
|
|
strerror(errno));
|
|
return CKR_FUNCTION_FAILED;
|
|
}
|
|
|
|
if (statbuf.st_gid != grp->gr_gid) {
|
|
OCK_SYSLOG(LOG_ERR, "Directory '%s' is not owned by token group '%s'\n",
|
|
tokdata->pk_dir, group);
|
|
TRACE_ERROR("Directory '%s' is not owned by token group '%s'\n",
|
|
tokdata->pk_dir, group);
|
|
return CKR_FUNCTION_FAILED;
|
|
}
|
|
|
|
return CKR_OK;
|
|
}
|
|
|
|
void final_data_store(STDLL_TokData_t * tokdata)
|
|
{
|
|
if (tokdata->pk_dir != NULL) {
|
|
free(tokdata->pk_dir);
|
|
tokdata->pk_dir = NULL;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************
|
|
* tokversion < 3.12 object store
|
|
*/
|
|
|
|
static CK_RV get_encryption_info(CK_ULONG *p_key_len, CK_ULONG *p_block_size)
|
|
{
|
|
CK_ULONG key_len = 0L;
|
|
CK_ULONG block_size = 0L;
|
|
|
|
switch (token_specific.data_store.encryption_algorithm) {
|
|
case CKM_DES3_CBC:
|
|
key_len = 3 * DES_KEY_SIZE;
|
|
block_size = DES_BLOCK_SIZE;
|
|
break;
|
|
case CKM_AES_CBC:
|
|
key_len = AES_KEY_SIZE_256;
|
|
block_size = AES_BLOCK_SIZE;
|
|
break;
|
|
default:
|
|
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
|
|
return CKR_MECHANISM_INVALID;
|
|
}
|
|
|
|
if (p_key_len)
|
|
*p_key_len = key_len;
|
|
if (p_block_size)
|
|
*p_block_size = block_size;
|
|
|
|
return CKR_OK;
|
|
}
|
|
|
|
static CK_BYTE *duplicate_initial_vector(const CK_BYTE *iv)
|
|
{
|
|
CK_ULONG block_size = 0L;
|
|
CK_BYTE *initial_vector = NULL;
|
|
|
|
if (iv == NULL)
|
|
goto done;
|
|
|
|
if (get_encryption_info(NULL, &block_size) != CKR_OK)
|
|
goto done;
|
|
|
|
initial_vector = malloc(block_size);
|
|
if (initial_vector == NULL) {
|
|
goto done;
|
|
}
|
|
memcpy(initial_vector, iv, block_size);
|
|
|
|
done:
|
|
return initial_vector;
|
|
}
|
|
|
|
static CK_RV encrypt_data_with_clear_key(STDLL_TokData_t *tokdata,
|
|
CK_BYTE *key, CK_ULONG keylen,
|
|
const CK_BYTE *iv,
|
|
CK_BYTE *clear, CK_ULONG clear_len,
|
|
CK_BYTE *cipher,
|
|
CK_ULONG *p_cipher_len,
|
|
CK_BBOOL mk_crypt)
|
|
{
|
|
#ifndef CLEARTEXT
|
|
CK_RV rc = CKR_OK;
|
|
CK_BYTE *initial_vector = NULL;
|
|
|
|
initial_vector = duplicate_initial_vector(iv);
|
|
if (initial_vector == NULL) {
|
|
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
|
|
return CKR_HOST_MEMORY;
|
|
}
|
|
|
|
switch (token_specific.data_store.encryption_algorithm) {
|
|
case CKM_DES3_CBC:
|
|
rc = sw_des3_cbc_encrypt(clear, clear_len,
|
|
cipher, p_cipher_len, initial_vector, key);
|
|
break;
|
|
case CKM_AES_CBC:
|
|
rc = sw_aes_cbc_encrypt(clear, clear_len, cipher, p_cipher_len,
|
|
initial_vector, key, keylen);
|
|
break;
|
|
default:
|
|
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
|
|
rc = CKR_MECHANISM_INVALID;
|
|
}
|
|
|
|
if (initial_vector)
|
|
free(initial_vector);
|
|
|
|
if (rc == CKR_OK &&
|
|
(tokdata->statistics->flags & STATISTICS_FLAG_COUNT_INTERNAL) != 0) {
|
|
if (mk_crypt)
|
|
tokdata->statistics->increment_func(tokdata->statistics,
|
|
tokdata->slot_id,
|
|
&tokdata->store_strength.mk_crypt,
|
|
tokdata->store_strength.mk_strength);
|
|
else
|
|
tokdata->statistics->increment_func(tokdata->statistics,
|
|
tokdata->slot_id,
|
|
&tokdata->store_strength.wrap_crypt,
|
|
tokdata->store_strength.wrap_strength);
|
|
}
|
|
|
|
return rc;
|
|
|
|
#else
|
|
memcpy(cipher, clear, clear_len);
|
|
return CKR_OK;
|
|
#endif
|
|
}
|
|
|
|
static CK_RV decrypt_data_with_clear_key(STDLL_TokData_t *tokdata,
|
|
CK_BYTE *key, CK_ULONG keylen,
|
|
const CK_BYTE *iv,
|
|
CK_BYTE *cipher, CK_ULONG cipher_len,
|
|
CK_BYTE *clear,
|
|
CK_ULONG *p_clear_len,
|
|
CK_BBOOL mk_crypt)
|
|
{
|
|
#ifndef CLEARTEXT
|
|
CK_RV rc = CKR_OK;
|
|
CK_BYTE *initial_vector = NULL;
|
|
|
|
initial_vector = duplicate_initial_vector(iv);
|
|
if (initial_vector == NULL) {
|
|
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
|
|
return CKR_HOST_MEMORY;
|
|
}
|
|
|
|
switch (token_specific.data_store.encryption_algorithm) {
|
|
case CKM_DES3_CBC:
|
|
rc = sw_des3_cbc_decrypt(cipher, cipher_len, clear, p_clear_len,
|
|
initial_vector, key);
|
|
break;
|
|
case CKM_AES_CBC:
|
|
rc = sw_aes_cbc_decrypt(cipher, cipher_len, clear, p_clear_len,
|
|
initial_vector, key, keylen);
|
|
break;
|
|
default:
|
|
TRACE_ERROR("%s\n", ock_err(ERR_MECHANISM_INVALID));
|
|
rc = CKR_MECHANISM_INVALID;
|
|
}
|
|
|
|
if (initial_vector)
|
|
free(initial_vector);
|
|
|
|
if (rc == CKR_OK &&
|
|
(tokdata->statistics->flags & STATISTICS_FLAG_COUNT_INTERNAL) != 0) {
|
|
if (mk_crypt)
|
|
tokdata->statistics->increment_func(tokdata->statistics,
|
|
tokdata->slot_id,
|
|
&tokdata->store_strength.mk_crypt,
|
|
tokdata->store_strength.mk_strength);
|
|
else
|
|
tokdata->statistics->increment_func(tokdata->statistics,
|
|
tokdata->slot_id,
|
|
&tokdata->store_strength.wrap_crypt,
|
|
tokdata->store_strength.wrap_strength);
|
|
}
|
|
|
|
return rc;
|
|
|
|
#else
|
|
memcpy(clear, cipher, cipher_len);
|
|
return CKR_OK;
|
|
#endif
|
|
}
|
|
|
|
//
|
|
//
|
|
CK_RV load_token_data_old(STDLL_TokData_t *tokdata, CK_SLOT_ID slot_id)
|
|
{
|
|
FILE *fp = NULL;
|
|
char fname[PATH_MAX];
|
|
TOKEN_DATA td;
|
|
CK_RV rc;
|
|
|
|
rc = XProcLock(tokdata);
|
|
if (rc != CKR_OK) {
|
|
TRACE_ERROR("Failed to get Process Lock.\n");
|
|
goto out_nolock;
|
|
}
|
|
|
|
fp = open_token_nvdat(fname, sizeof(fname), tokdata, "r");
|
|
if (!fp) {
|
|
/* Better error checking added */
|
|
if (errno == ENOENT) {
|
|
init_token_data(tokdata, slot_id);
|
|
|
|
fp = fopen_nofollow(fname, "r");
|
|
if (!fp) {
|
|
// were really hosed here since the created
|
|
// did not occur
|
|
if (errno == ELOOP)
|
|
TRACE_ERROR("Refusing to follow symlink: %s\n", fname);
|
|
else
|
|
TRACE_ERROR("fopen(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto out_unlock;
|
|
}
|
|
} else {
|
|
/* Could not open file for some unknown reason */
|
|
TRACE_ERROR("fopen(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto out_unlock;
|
|
}
|
|
}
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto out_unlock;
|
|
|
|
/* Load generic token data */
|
|
if (fread(&td, sizeof(TOKEN_DATA_OLD), 1, fp) != 1) {
|
|
TRACE_ERROR("fread(%s): %s\n", fname,
|
|
ferror(fp) ? strerror(errno) : "failed");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto out_unlock;
|
|
}
|
|
memcpy(tokdata->nv_token_data, &td, sizeof(TOKEN_DATA_OLD));
|
|
|
|
/* Load token-specific data */
|
|
if (token_specific.t_load_token_data) {
|
|
rc = token_specific.t_load_token_data(tokdata, slot_id, fp);
|
|
if (rc)
|
|
goto out_unlock;
|
|
}
|
|
|
|
rc = CKR_OK;
|
|
|
|
out_unlock:
|
|
if (fp)
|
|
fclose(fp);
|
|
|
|
if (rc == CKR_OK) {
|
|
rc = XProcUnLock(tokdata);
|
|
if (rc != CKR_OK)
|
|
TRACE_ERROR("Failed to release Process Lock.\n");
|
|
} else {
|
|
/* return error that occurred first */
|
|
XProcUnLock(tokdata);
|
|
}
|
|
|
|
out_nolock:
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
//
|
|
CK_RV save_token_data_old(STDLL_TokData_t *tokdata, CK_SLOT_ID slot_id)
|
|
{
|
|
FILE *fp = NULL;
|
|
TOKEN_DATA td;
|
|
CK_RV rc;
|
|
char fname[PATH_MAX];
|
|
char basename[PATH_MAX];
|
|
|
|
rc = XProcLock(tokdata);
|
|
if (rc != CKR_OK) {
|
|
TRACE_ERROR("Failed to get Process Lock.\n");
|
|
goto out_nolock;
|
|
}
|
|
|
|
fp = open_token_nvdat_new(fname, sizeof(fname), basename, sizeof(basename),
|
|
tokdata, "w");
|
|
if (!fp) {
|
|
TRACE_ERROR("fopen(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
/* Write generic token data */
|
|
memcpy(&td, tokdata->nv_token_data, sizeof(TOKEN_DATA_OLD));
|
|
if (!fwrite(&td, sizeof(TOKEN_DATA_OLD), 1, fp)) {
|
|
TRACE_ERROR("fwrite(%s): %s\n", fname,
|
|
ferror(fp) ? strerror(errno) : "failed");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
/* Write token-specific data */
|
|
if (token_specific.t_save_token_data) {
|
|
rc = token_specific.t_save_token_data(tokdata, slot_id, fp);
|
|
if (rc)
|
|
goto done;
|
|
}
|
|
|
|
rc = CKR_OK;
|
|
|
|
done:
|
|
rc = close_token_file_new(fp, rc, fname, basename);
|
|
|
|
if (rc == CKR_OK) {
|
|
rc = XProcUnLock(tokdata);
|
|
if (rc != CKR_OK)
|
|
TRACE_ERROR("Failed to release Process Lock.\n");
|
|
} else {
|
|
/* return error that occurred first */
|
|
XProcUnLock(tokdata);
|
|
}
|
|
|
|
out_nolock:
|
|
return rc;
|
|
}
|
|
|
|
|
|
//
|
|
// Note: The token lock (XProcLock) must be held when calling this function.
|
|
//
|
|
static CK_RV save_private_token_object_old(STDLL_TokData_t *tokdata, OBJECT *obj)
|
|
{
|
|
FILE *fp = NULL;
|
|
CK_BYTE *obj_data = NULL;
|
|
CK_BYTE *clear = NULL;
|
|
CK_BYTE *cipher = NULL;
|
|
CK_BYTE *ptr = NULL;
|
|
char fname[PATH_MAX];
|
|
char basename[PATH_MAX];
|
|
CK_BYTE hash_sha[SHA1_HASH_SIZE];
|
|
CK_BYTE *key = NULL;
|
|
CK_ULONG key_len = 0L;
|
|
CK_ULONG block_size = 0L;
|
|
CK_ULONG obj_data_len, clear_len, cipher_len;
|
|
CK_ULONG padded_len;
|
|
CK_BBOOL flag;
|
|
CK_RV rc;
|
|
CK_ULONG_32 obj_data_len_32;
|
|
CK_ULONG_32 total_len;
|
|
|
|
rc = object_flatten(obj, &obj_data, &obj_data_len);
|
|
obj_data_len_32 = obj_data_len;
|
|
if (rc != CKR_OK) {
|
|
goto error;
|
|
}
|
|
//
|
|
// format for the object file:
|
|
// private flag
|
|
// ---- begin encrypted part <--+
|
|
// length of object data |
|
|
// object data +---- sensitive part
|
|
// SHA of (object data) |
|
|
// ---- end encrypted part <--+
|
|
//
|
|
rc = compute_sha1(tokdata, obj_data, obj_data_len, hash_sha);
|
|
if (rc != CKR_OK)
|
|
goto error;
|
|
|
|
// encrypt the sensitive object data. need to be careful.
|
|
// if I use the normal high-level encryption routines I'll need to
|
|
// create a tepmorary key object containing the master key, perform the
|
|
// encryption, then destroy the key object. There is a race condition
|
|
// here if the application is multithreaded (if a thread-switch occurs,
|
|
// the other application thread could do a FindObject and be able to
|
|
// access the master key object.
|
|
//
|
|
// So I have to use the low-level encryption routines.
|
|
//
|
|
|
|
if ((rc = get_encryption_info(&key_len, &block_size)) != CKR_OK)
|
|
goto error;
|
|
|
|
// Duplicate key
|
|
key = malloc(key_len);
|
|
if (!key)
|
|
goto oom_error;
|
|
memcpy(key, tokdata->master_key, key_len);
|
|
|
|
|
|
clear_len = sizeof(CK_ULONG_32) + obj_data_len_32 + SHA1_HASH_SIZE;
|
|
cipher_len = padded_len = block_size * (clear_len / block_size + 1);
|
|
|
|
clear = malloc(padded_len);
|
|
cipher = malloc(padded_len);
|
|
if (!clear || !cipher)
|
|
goto oom_error;
|
|
|
|
// Build data that will be encrypted
|
|
ptr = clear;
|
|
memcpy(ptr, &obj_data_len_32, sizeof(CK_ULONG_32));
|
|
ptr += sizeof(CK_ULONG_32);
|
|
memcpy(ptr, obj_data, obj_data_len_32);
|
|
ptr += obj_data_len_32;
|
|
memcpy(ptr, hash_sha, SHA1_HASH_SIZE);
|
|
|
|
add_pkcs_padding(clear + clear_len, block_size, clear_len, padded_len);
|
|
|
|
rc = encrypt_data_with_clear_key(tokdata, key, key_len,
|
|
token_specific.data_store.
|
|
obj_initial_vector, clear, padded_len,
|
|
cipher, &cipher_len, CK_FALSE);
|
|
if (rc != CKR_OK) {
|
|
goto error;
|
|
}
|
|
|
|
fp = open_token_object_path_new(fname, sizeof(fname),
|
|
basename, sizeof(basename),
|
|
tokdata, (char *)obj->name, "w");
|
|
if (!fp) {
|
|
TRACE_ERROR("fopen(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto error;
|
|
}
|
|
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto error;
|
|
|
|
if (cipher_len > UINT32_MAX - sizeof(CK_ULONG_32) - sizeof(CK_BBOOL)) {
|
|
TRACE_ERROR("Object too large to store in old format (%lu bytes)\n",
|
|
cipher_len);
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto error;
|
|
}
|
|
|
|
total_len = sizeof(CK_ULONG_32) + sizeof(CK_BBOOL) + cipher_len;
|
|
|
|
flag = TRUE;
|
|
|
|
if (fwrite(&total_len, sizeof(CK_ULONG_32), 1, fp) != 1 ||
|
|
fwrite(&flag, sizeof(CK_BBOOL), 1, fp) != 1 ||
|
|
fwrite(cipher, cipher_len, 1, fp) != 1) {
|
|
TRACE_ERROR("fwrite(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
}
|
|
|
|
rc = close_token_file_new(fp, rc, fname, basename);
|
|
OPENSSL_cleanse(obj_data, obj_data_len);
|
|
free(obj_data);
|
|
OPENSSL_cleanse(clear, padded_len);
|
|
free(clear);
|
|
OPENSSL_cleanse(cipher, cipher_len);
|
|
free(cipher);
|
|
OPENSSL_cleanse(key, key_len);
|
|
free(key);
|
|
|
|
return rc;
|
|
|
|
oom_error:
|
|
rc = CKR_HOST_MEMORY;
|
|
|
|
error:
|
|
if (obj_data) {
|
|
OPENSSL_cleanse(obj_data, obj_data_len);
|
|
free(obj_data);
|
|
}
|
|
if (clear) {
|
|
OPENSSL_cleanse(clear, padded_len);
|
|
free(clear);
|
|
}
|
|
if (cipher) {
|
|
OPENSSL_cleanse(cipher, cipher_len);
|
|
free(cipher);
|
|
}
|
|
if (key) {
|
|
OPENSSL_cleanse(key, key_len);
|
|
free(key);
|
|
}
|
|
rc = close_token_file_new(fp, rc, fname, basename);
|
|
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
// Note: The token lock (XProcLock) must be held when calling this function.
|
|
//
|
|
CK_RV load_private_token_objects_old(STDLL_TokData_t *tokdata)
|
|
{
|
|
FILE *fp1 = NULL, *fp2 = NULL;
|
|
CK_BYTE *buf = NULL;
|
|
char tmp[PATH_MAX];
|
|
char iname[PATH_MAX];
|
|
char fname[PATH_MAX];
|
|
CK_BBOOL priv;
|
|
CK_ULONG_32 size;
|
|
CK_RV rc;
|
|
size_t read_size, slen;
|
|
|
|
fp1 = open_token_object_index(iname, sizeof(iname), tokdata, "r");
|
|
if (!fp1)
|
|
return CKR_OK; // no token objects
|
|
|
|
while (fgets(tmp, 50, fp1)) {
|
|
slen = strlen(tmp);
|
|
if (slen > 0 && tmp[slen - 1] == '\n')
|
|
tmp[slen - 1] = '\0';
|
|
|
|
fp2 = open_token_object_path(fname, sizeof(fname), tokdata, tmp, "r");
|
|
if (!fp2)
|
|
continue;
|
|
|
|
if (fread(&size, sizeof(CK_ULONG_32), 1, fp2) != 1) {
|
|
fclose(fp2);
|
|
OCK_SYSLOG(LOG_ERR, "Cannot read size\n");
|
|
continue;
|
|
}
|
|
if (fread(&priv, sizeof(CK_BBOOL), 1, fp2) != 1) {
|
|
fclose(fp2);
|
|
OCK_SYSLOG(LOG_ERR, "Cannot read boolean\n");
|
|
continue;
|
|
}
|
|
if (priv == FALSE) {
|
|
fclose(fp2);
|
|
continue;
|
|
}
|
|
if (size <= sizeof(CK_ULONG_32) + sizeof(CK_BBOOL) ||
|
|
size >= OCK_MAX_TOKEN_OBJ_SIZE) {
|
|
fclose(fp2);
|
|
OCK_SYSLOG(LOG_ERR, "Improper size of object %s (ignoring it)\n",
|
|
fname);
|
|
continue;
|
|
}
|
|
|
|
size -= sizeof(CK_ULONG_32) + sizeof(CK_BBOOL);
|
|
buf = (CK_BYTE *) malloc(size);
|
|
if (!buf) {
|
|
fclose(fp2);
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"Cannot malloc %u bytes to read in "
|
|
"token object %s (ignoring it)", size, fname);
|
|
continue;
|
|
}
|
|
|
|
read_size = fread(buf, 1, size, fp2);
|
|
if (read_size != size) {
|
|
OPENSSL_cleanse(buf, size);
|
|
free(buf);
|
|
fclose(fp2);
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"Cannot read token object %s " "(ignoring it)", fname);
|
|
continue;
|
|
}
|
|
|
|
rc = restore_private_token_object_old(tokdata, buf, size, NULL, fname);
|
|
if (rc != CKR_OK)
|
|
goto error;
|
|
|
|
OPENSSL_cleanse(buf, size);
|
|
free(buf);
|
|
fclose(fp2);
|
|
}
|
|
|
|
fclose(fp1);
|
|
|
|
return CKR_OK;
|
|
|
|
error:
|
|
if (buf) {
|
|
OPENSSL_cleanse(buf, size);
|
|
free(buf);
|
|
}
|
|
if (fp1)
|
|
fclose(fp1);
|
|
if (fp2)
|
|
fclose(fp2);
|
|
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
//
|
|
CK_RV load_masterkey_so_old(STDLL_TokData_t *tokdata)
|
|
{
|
|
FILE *fp = NULL;
|
|
CK_BYTE hash_sha[SHA1_HASH_SIZE];
|
|
CK_BYTE *cipher = NULL;
|
|
CK_BYTE *clear = NULL;
|
|
CK_BYTE *key = NULL;
|
|
CK_ULONG data_len;
|
|
CK_ULONG cipher_len, clear_len;
|
|
CK_RV rc;
|
|
char fname[PATH_MAX];
|
|
CK_ULONG key_len = 0L;
|
|
CK_ULONG master_key_len = 0L;
|
|
CK_ULONG block_size = 0L;
|
|
struct stat sb;
|
|
|
|
if ((rc = get_encryption_info(&key_len, &block_size)) != CKR_OK)
|
|
goto done;
|
|
|
|
master_key_len = key_len;
|
|
memset(tokdata->master_key, 0x0, master_key_len);
|
|
|
|
data_len = master_key_len + SHA1_HASH_SIZE;
|
|
clear_len = cipher_len = (data_len + block_size - 1)
|
|
& ~(block_size - 1);
|
|
|
|
if (ock_snprintf(fname, sizeof(fname), "%s/MK_SO",
|
|
tokdata->data_store) != 0) {
|
|
TRACE_ERROR("path name for MK_SO too long\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
if (stat(fname, &sb) != 0) {
|
|
TRACE_ERROR("stat(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
if ((CK_ULONG)sb.st_size > cipher_len &&
|
|
token_specific.secure_key_token &&
|
|
strcmp(token_specific.token_subdir, "ccatok") == 0) {
|
|
/*
|
|
* The CCA token used to have a secure master key length of 64, although
|
|
* it uses clear keys for the master key in the meantime. The master key
|
|
* length has an influence on the file size of the MK_SO and MK_USER
|
|
* files when using the old pin encryption format. Use special handling
|
|
* for such larger MK_SO files, and accept the larger length. Newly
|
|
* written MK_SO files will use the clear key master key length, but we
|
|
* need to be able to read larger files for backwards compatibility.
|
|
*/
|
|
master_key_len = 64;
|
|
|
|
data_len = master_key_len + SHA1_HASH_SIZE;
|
|
clear_len = cipher_len = (data_len + block_size - 1)
|
|
& ~(block_size - 1);
|
|
}
|
|
|
|
key = malloc(key_len);
|
|
cipher = malloc(cipher_len);
|
|
clear = malloc(clear_len);
|
|
if (key == NULL || cipher == NULL || clear == NULL) {
|
|
rc = CKR_HOST_MEMORY;
|
|
goto done;
|
|
}
|
|
// this file gets created on C_InitToken so we can assume that it always
|
|
// exists
|
|
//
|
|
fp = open_token_data_store_path(fname, sizeof(fname), tokdata, "MK_SO", "r");
|
|
if (!fp) {
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
rc = fread(cipher, cipher_len, 1, fp);
|
|
if (rc != 1) {
|
|
TRACE_ERROR("fread() failed.\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
// decrypt the master key data using the MD5 of the SO key
|
|
// (we can't use the SHA of the SO key since the SHA of the key is
|
|
// stored in the token data file).
|
|
memcpy(key, tokdata->so_pin_md5, MD5_HASH_SIZE);
|
|
memcpy(key + MD5_HASH_SIZE, tokdata->so_pin_md5, key_len - MD5_HASH_SIZE);
|
|
|
|
rc = decrypt_data_with_clear_key(tokdata, key, key_len,
|
|
token_specific.data_store.
|
|
pin_initial_vector, cipher, cipher_len,
|
|
clear, &clear_len, CK_TRUE);
|
|
if (rc != CKR_OK) {
|
|
TRACE_DEVEL("decrypt_data_with_clear_key failed.\n");
|
|
goto done;
|
|
}
|
|
//
|
|
// technically should strip PKCS padding here but since I already know
|
|
// what the length should be, I don't bother.
|
|
//
|
|
|
|
// compare the hashes
|
|
//
|
|
rc = compute_sha1(tokdata, clear, master_key_len, hash_sha);
|
|
if (rc != CKR_OK) {
|
|
goto done;
|
|
}
|
|
|
|
if (memcmp(hash_sha, clear + master_key_len, SHA1_HASH_SIZE) != 0) {
|
|
TRACE_ERROR("masterkey hashes do not match\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
memcpy(tokdata->master_key, clear, master_key_len);
|
|
rc = CKR_OK;
|
|
|
|
done:
|
|
if (fp)
|
|
fclose(fp);
|
|
if (clear) {
|
|
OPENSSL_cleanse(clear, clear_len);
|
|
free(clear);
|
|
}
|
|
if (cipher)
|
|
free(cipher);
|
|
if (key) {
|
|
OPENSSL_cleanse(key, key_len);
|
|
free(key);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
//
|
|
CK_RV load_masterkey_user_old(STDLL_TokData_t *tokdata)
|
|
{
|
|
FILE *fp = NULL;
|
|
CK_BYTE hash_sha[SHA1_HASH_SIZE];
|
|
CK_BYTE *cipher = NULL;
|
|
CK_BYTE *clear = NULL;
|
|
CK_BYTE *key = NULL;
|
|
CK_ULONG data_len;
|
|
CK_ULONG cipher_len, clear_len;
|
|
CK_RV rc;
|
|
char fname[PATH_MAX];
|
|
CK_ULONG key_len = 0L;
|
|
CK_ULONG master_key_len = 0L;
|
|
CK_ULONG block_size = 0L;
|
|
struct stat sb;
|
|
|
|
if ((rc = get_encryption_info(&key_len, &block_size)) != CKR_OK)
|
|
goto done;
|
|
|
|
master_key_len = key_len;
|
|
memset(tokdata->master_key, 0x0, master_key_len);
|
|
|
|
data_len = master_key_len + SHA1_HASH_SIZE;
|
|
clear_len = cipher_len = (data_len + block_size - 1)
|
|
& ~(block_size - 1);
|
|
|
|
if (ock_snprintf(fname, sizeof(fname), "%s/MK_USER",
|
|
tokdata->data_store) != 0) {
|
|
TRACE_ERROR("path name for MK_USER too long\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
if (stat(fname, &sb) != 0) {
|
|
TRACE_ERROR("stat(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
if ((CK_ULONG)sb.st_size > cipher_len &&
|
|
token_specific.secure_key_token &&
|
|
strcmp(token_specific.token_subdir, "ccatok") == 0) {
|
|
/*
|
|
* The CCA token used to have a secure master key length of 64, although
|
|
* it uses clear keys for the master key in the meantime. The master key
|
|
* length has an influence on the file size of the MK_SO and MK_USER
|
|
* files when using the old pin encryption format. Use special handling
|
|
* for such larger MK_USER files, and accept the larger length. Newly
|
|
* written MK_USER files will use the clear key master key length, but
|
|
* we need to be able to read larger files for backwards compatibility.
|
|
*/
|
|
master_key_len = 64;
|
|
|
|
data_len = master_key_len + SHA1_HASH_SIZE;
|
|
clear_len = cipher_len = (data_len + block_size - 1)
|
|
& ~(block_size - 1);
|
|
}
|
|
|
|
key = malloc(key_len);
|
|
cipher = malloc(cipher_len);
|
|
clear = malloc(clear_len);
|
|
if (key == NULL || cipher == NULL || clear == NULL) {
|
|
rc = CKR_HOST_MEMORY;
|
|
goto done;
|
|
}
|
|
// this file gets created on C_InitToken so we can assume that it always
|
|
// exists
|
|
//
|
|
fp = open_token_data_store_path(fname, sizeof(fname), tokdata, "MK_USER", "r");
|
|
if (!fp) {
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
rc = fread(cipher, cipher_len, 1, fp);
|
|
if (rc != 1) {
|
|
TRACE_ERROR("fread failed.\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
// decrypt the master key data using the MD5 of the SO key
|
|
// (we can't use the SHA of the SO key since the SHA of the key is
|
|
// stored in the token data file).
|
|
memcpy(key, tokdata->user_pin_md5, MD5_HASH_SIZE);
|
|
memcpy(key + MD5_HASH_SIZE, tokdata->user_pin_md5, key_len - MD5_HASH_SIZE);
|
|
|
|
rc = decrypt_data_with_clear_key(tokdata, key, key_len,
|
|
token_specific.data_store.
|
|
pin_initial_vector, cipher, cipher_len,
|
|
clear, &clear_len, CK_TRUE);
|
|
if (rc != CKR_OK) {
|
|
TRACE_DEVEL("decrypt_data_with_clear_key failed.\n");
|
|
goto done;
|
|
}
|
|
//
|
|
// technically should strip PKCS padding here but since I already know
|
|
// what the length should be, I don't bother.
|
|
//
|
|
|
|
// compare the hashes
|
|
//
|
|
rc = compute_sha1(tokdata, clear, master_key_len, hash_sha);
|
|
if (rc != CKR_OK) {
|
|
goto done;
|
|
}
|
|
|
|
if (memcmp(hash_sha, clear + master_key_len, SHA1_HASH_SIZE) != 0) {
|
|
TRACE_ERROR("User's masterkey hashes do not match.\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
memcpy(tokdata->master_key, clear, master_key_len);
|
|
rc = CKR_OK;
|
|
|
|
done:
|
|
if (fp)
|
|
fclose(fp);
|
|
if (key) {
|
|
OPENSSL_cleanse(key, key_len);
|
|
free(key);
|
|
}
|
|
if (clear) {
|
|
OPENSSL_cleanse(clear, clear_len);
|
|
free(clear);
|
|
}
|
|
if (cipher)
|
|
free(cipher);
|
|
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
//
|
|
CK_RV save_masterkey_so_old(STDLL_TokData_t *tokdata)
|
|
{
|
|
FILE *fp = NULL;
|
|
CK_BYTE *clear = NULL;
|
|
CK_ULONG clear_len = 0L;
|
|
CK_BYTE *cipher = NULL;
|
|
CK_ULONG cipher_len = 0L;
|
|
CK_BYTE *key = NULL;
|
|
CK_ULONG key_len = 0L;
|
|
CK_ULONG block_size = 0L;
|
|
CK_ULONG data_len = 0L;
|
|
char fname[PATH_MAX];
|
|
char basename[PATH_MAX];
|
|
CK_RV rc;
|
|
|
|
/* Skip it if master key is not needed. */
|
|
if (!token_specific.data_store.use_master_key)
|
|
return CKR_OK;
|
|
|
|
if ((rc = get_encryption_info(&key_len, &block_size)) != CKR_OK)
|
|
goto done;
|
|
|
|
data_len = key_len + SHA1_HASH_SIZE;
|
|
cipher_len = clear_len = block_size * (data_len / block_size + 1);
|
|
|
|
key = malloc(key_len);
|
|
clear = malloc(clear_len);
|
|
cipher = malloc(cipher_len);
|
|
if (key == NULL || clear == NULL || cipher == NULL) {
|
|
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
|
|
rc = CKR_HOST_MEMORY;
|
|
goto done;
|
|
}
|
|
// Copy data to buffer (key+hash)
|
|
memcpy(clear, tokdata->master_key, key_len);
|
|
if ((rc = compute_sha1(tokdata, tokdata->master_key,
|
|
key_len, clear + key_len)) != CKR_OK)
|
|
goto done;
|
|
add_pkcs_padding(clear + data_len, block_size, data_len, clear_len);
|
|
|
|
// encrypt the key data
|
|
memcpy(key, tokdata->so_pin_md5, MD5_HASH_SIZE);
|
|
memcpy(key + MD5_HASH_SIZE, tokdata->so_pin_md5, key_len - MD5_HASH_SIZE);
|
|
|
|
rc = encrypt_data_with_clear_key(tokdata, key, key_len,
|
|
token_specific.data_store.
|
|
pin_initial_vector, clear, clear_len,
|
|
cipher, &cipher_len, CK_TRUE);
|
|
if (rc != CKR_OK) {
|
|
goto done;
|
|
}
|
|
// write the file
|
|
//
|
|
// probably ought to ensure the permissions are correct
|
|
//
|
|
fp = open_token_data_store_path_new(fname, sizeof(fname),
|
|
basename, sizeof(basename),
|
|
tokdata, "MK_SO", "w");
|
|
if (!fp) {
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
rc = fwrite(cipher, cipher_len, 1, fp);
|
|
if (rc != 1) {
|
|
TRACE_ERROR("fwrite failed.\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
rc = CKR_OK;
|
|
|
|
done:
|
|
rc = close_token_file_new(fp, rc, fname, basename);
|
|
if (key) {
|
|
OPENSSL_cleanse(key, key_len);
|
|
free(key);
|
|
}
|
|
if (clear) {
|
|
OPENSSL_cleanse(clear, clear_len);
|
|
free(clear);
|
|
}
|
|
if (cipher)
|
|
free(cipher);
|
|
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
//
|
|
CK_RV save_masterkey_user_old(STDLL_TokData_t *tokdata)
|
|
{
|
|
FILE *fp = NULL;
|
|
CK_BYTE *clear = NULL;
|
|
CK_ULONG clear_len = 0L;
|
|
CK_BYTE *cipher = NULL;
|
|
CK_ULONG cipher_len = 0L;
|
|
CK_BYTE *key = NULL;
|
|
CK_ULONG key_len = 0L;
|
|
CK_ULONG block_size = 0L;
|
|
CK_ULONG data_len = 0L;
|
|
char fname[PATH_MAX];
|
|
char basename[PATH_MAX];
|
|
CK_RV rc;
|
|
|
|
if ((rc = get_encryption_info(&key_len, &block_size)) != CKR_OK)
|
|
goto done;
|
|
|
|
data_len = key_len + SHA1_HASH_SIZE;
|
|
cipher_len = clear_len = block_size * (data_len / block_size + 1);
|
|
|
|
key = malloc(key_len);
|
|
clear = malloc(clear_len);
|
|
cipher = malloc(cipher_len);
|
|
if (key == NULL || clear == NULL || cipher == NULL) {
|
|
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
|
|
rc = CKR_HOST_MEMORY;
|
|
goto done;
|
|
}
|
|
// Copy data to buffer (key+hash)
|
|
memcpy(clear, tokdata->master_key, key_len);
|
|
if ((rc = compute_sha1(tokdata, tokdata->master_key,
|
|
key_len, clear + key_len)) != CKR_OK)
|
|
goto done;
|
|
add_pkcs_padding(clear + data_len, block_size, data_len, clear_len);
|
|
|
|
// encrypt the key data
|
|
memcpy(key, tokdata->user_pin_md5, MD5_HASH_SIZE);
|
|
memcpy(key + MD5_HASH_SIZE, tokdata->user_pin_md5, key_len - MD5_HASH_SIZE);
|
|
|
|
rc = encrypt_data_with_clear_key(tokdata, key, key_len,
|
|
token_specific.data_store.
|
|
pin_initial_vector, clear, clear_len,
|
|
cipher, &cipher_len, CK_TRUE);
|
|
if (rc != CKR_OK) {
|
|
goto done;
|
|
}
|
|
// write the file
|
|
//
|
|
// probably ought to ensure the permissions are correct
|
|
//
|
|
fp = open_token_data_store_path_new(fname, sizeof(fname),
|
|
basename, sizeof(basename),
|
|
tokdata, "MK_USER", "w");
|
|
if (!fp) {
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
rc = fwrite(cipher, cipher_len, 1, fp);
|
|
if (rc != 1) {
|
|
TRACE_ERROR("fwrite failed.\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
rc = CKR_OK;
|
|
|
|
done:
|
|
rc = close_token_file_new(fp, rc, fname, basename);
|
|
if (key) {
|
|
OPENSSL_cleanse(key, key_len);
|
|
free(key);
|
|
}
|
|
if (clear) {
|
|
OPENSSL_cleanse(clear, clear_len);
|
|
free(clear);
|
|
}
|
|
if (cipher)
|
|
free(cipher);
|
|
|
|
return rc;
|
|
}
|
|
|
|
CK_RV generate_master_key_old(STDLL_TokData_t *tokdata, CK_BYTE *key)
|
|
{
|
|
CK_RV rc = CKR_OK;
|
|
CK_ULONG key_len = 0L;
|
|
CK_ULONG master_key_len;
|
|
CK_BYTE *master_key = NULL;
|
|
CK_BBOOL is_opaque = FALSE;
|
|
TEMPLATE *tmpl = NULL;
|
|
|
|
/* Skip it if master key is not needed. */
|
|
if (!token_specific.data_store.use_master_key)
|
|
return CKR_OK;
|
|
|
|
if (get_encryption_info(&key_len, NULL) != CKR_OK)
|
|
return CKR_FUNCTION_FAILED;
|
|
|
|
/* For secure key tokens, object encrypt/decrypt uses
|
|
* software(openssl), not token. So generate masterkey via RNG.
|
|
*/
|
|
if (token_specific.secure_key_token) {
|
|
rc = rng_generate(tokdata, key, key_len);
|
|
|
|
if (rc == CKR_OK &&
|
|
(tokdata->statistics->flags & STATISTICS_FLAG_COUNT_INTERNAL) != 0)
|
|
tokdata->statistics->increment_func(tokdata->statistics,
|
|
tokdata->slot_id,
|
|
&tokdata->store_strength.mk_keygen,
|
|
tokdata->store_strength.mk_strength);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* For clear key tokens, let token generate masterkey
|
|
* since token will also encrypt/decrypt the objects.
|
|
*/
|
|
tmpl = (TEMPLATE *)calloc(1, sizeof(TEMPLATE));
|
|
if (tmpl == NULL) {
|
|
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
|
|
return CKR_HOST_MEMORY;
|
|
}
|
|
|
|
switch (token_specific.data_store.encryption_algorithm) {
|
|
case CKM_DES3_CBC:
|
|
rc = token_specific.t_des_key_gen(tokdata, tmpl, &master_key,
|
|
&master_key_len, key_len,
|
|
&is_opaque);
|
|
break;
|
|
case CKM_AES_CBC:
|
|
rc = token_specific.t_aes_key_gen(tokdata, tmpl, &master_key,
|
|
&master_key_len, key_len,
|
|
&is_opaque);
|
|
break;
|
|
default:
|
|
template_free(tmpl);
|
|
return CKR_MECHANISM_INVALID;
|
|
}
|
|
|
|
template_free(tmpl);
|
|
|
|
if (rc != CKR_OK)
|
|
return rc;
|
|
|
|
if (master_key_len != key_len) {
|
|
TRACE_ERROR("Invalid master key size: %lu\n", master_key_len);
|
|
OPENSSL_cleanse(master_key, master_key_len);
|
|
free(master_key);
|
|
return CKR_FUNCTION_FAILED;
|
|
}
|
|
|
|
memcpy(key, master_key, master_key_len);
|
|
OPENSSL_cleanse(master_key, master_key_len);
|
|
free(master_key);
|
|
|
|
if ((tokdata->statistics->flags & STATISTICS_FLAG_COUNT_INTERNAL) != 0)
|
|
tokdata->statistics->increment_func(tokdata->statistics,
|
|
tokdata->slot_id,
|
|
&tokdata->store_strength.mk_keygen,
|
|
tokdata->store_strength.mk_strength);
|
|
|
|
return CKR_OK;
|
|
}
|
|
|
|
CK_RV restore_private_token_object_old(STDLL_TokData_t *tokdata, CK_BYTE *data,
|
|
CK_ULONG len, OBJECT *pObj,
|
|
const char *fname)
|
|
{
|
|
CK_BYTE *clear = NULL;
|
|
CK_BYTE *obj_data = NULL;
|
|
CK_BYTE *ptr = NULL;
|
|
CK_BYTE *key = NULL;
|
|
CK_ULONG key_len = 0;
|
|
CK_ULONG block_size;
|
|
CK_BYTE hash_sha[SHA1_HASH_SIZE];
|
|
CK_ULONG clear_len, obj_data_len;
|
|
CK_RV rc;
|
|
|
|
// format for the object data:
|
|
// (private flag has already been read at this point)
|
|
// ---- begin encrypted part
|
|
// length of object data
|
|
// object data
|
|
// SHA of object data
|
|
// ---- end encrypted part
|
|
//
|
|
|
|
clear_len = len;
|
|
|
|
clear = (CK_BYTE *) malloc(len);
|
|
if (!clear) {
|
|
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
|
|
rc = CKR_HOST_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
if ((rc = get_encryption_info(&key_len, &block_size)) != CKR_OK)
|
|
goto done;
|
|
|
|
// decrypt the encrypted chunk
|
|
key = malloc(key_len);
|
|
if (!key) {
|
|
rc = CKR_HOST_MEMORY;
|
|
goto done;
|
|
}
|
|
memcpy(key, tokdata->master_key, key_len);
|
|
|
|
rc = decrypt_data_with_clear_key(tokdata, key, key_len,
|
|
token_specific.data_store.
|
|
obj_initial_vector, data, len, clear,
|
|
&clear_len, CK_FALSE);
|
|
if (rc != CKR_OK) {
|
|
goto done;
|
|
}
|
|
|
|
rc = strip_pkcs_padding(clear, block_size, len, &clear_len);
|
|
|
|
// if the padding extraction didn't work it means the object was
|
|
// tampered with or the key was incorrect
|
|
//
|
|
if (rc != CKR_OK || (clear_len > len)) {
|
|
TRACE_DEVEL("strip_pkcs_padding failed.\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
ptr = clear;
|
|
|
|
obj_data_len = *(CK_ULONG_32 *) ptr;
|
|
|
|
// prevent buffer overflow in sha_update and memcmp
|
|
if (obj_data_len > clear_len ||
|
|
sizeof(CK_ULONG_32) + obj_data_len + SHA1_HASH_SIZE > clear_len) {
|
|
TRACE_ERROR("stripped length is greater than clear length\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
ptr += sizeof(CK_ULONG_32);
|
|
obj_data = ptr;
|
|
|
|
// check the hash
|
|
//
|
|
rc = compute_sha1(tokdata, ptr, obj_data_len, hash_sha);
|
|
if (rc != CKR_OK) {
|
|
goto done;
|
|
}
|
|
ptr += obj_data_len;
|
|
|
|
if (memcmp(ptr, hash_sha, SHA1_HASH_SIZE) != 0) {
|
|
TRACE_ERROR("stored hash does not match restored data hash.\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
// okay. at this point, we're satisfied that nobody has tampered with
|
|
// the token object...
|
|
//
|
|
|
|
rc = object_mgr_restore_obj_withSize(tokdata, obj_data, pObj, obj_data_len,
|
|
fname);
|
|
if (rc != CKR_OK) {
|
|
goto done;
|
|
}
|
|
rc = CKR_OK;
|
|
|
|
done:
|
|
if (clear) {
|
|
OPENSSL_cleanse(clear, clear_len);
|
|
free(clear);
|
|
}
|
|
if (key) {
|
|
OPENSSL_cleanse(key, key_len);
|
|
free(key);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
//
|
|
CK_RV reload_token_object_old(STDLL_TokData_t *tokdata, OBJECT *obj)
|
|
{
|
|
FILE *fp = NULL;
|
|
CK_BYTE *buf = NULL;
|
|
char fname[PATH_MAX];
|
|
CK_BBOOL priv;
|
|
CK_ULONG_32 size;
|
|
CK_ULONG size_64;
|
|
CK_RV rc;
|
|
size_t read_size;
|
|
|
|
fp = open_token_object_path(fname, sizeof(fname), tokdata,
|
|
(char *)obj->name, "r");
|
|
if (!fp) {
|
|
TRACE_ERROR("fopen(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
if (fread(&size, sizeof(CK_ULONG_32), 1, fp) != 1) {
|
|
OCK_SYSLOG(LOG_ERR, "Cannot read size\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
if (fread(&priv, sizeof(CK_BBOOL), 1, fp) != 1) {
|
|
OCK_SYSLOG(LOG_ERR, "Cannot read boolean\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
if (size <= sizeof(CK_ULONG_32) + sizeof(CK_BBOOL) ||
|
|
size >= OCK_MAX_TOKEN_OBJ_SIZE) {
|
|
rc = CKR_FUNCTION_FAILED;
|
|
OCK_SYSLOG(LOG_ERR, "Improper size of object %s (ignoring it)\n",
|
|
fname);
|
|
goto done;
|
|
}
|
|
|
|
size -= sizeof(CK_ULONG_32) + sizeof(CK_BBOOL);
|
|
|
|
buf = (CK_BYTE *) malloc(size);
|
|
if (!buf) {
|
|
rc = CKR_HOST_MEMORY;
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"Cannot malloc %u bytes to read in token object %s "
|
|
"(ignoring it)", size, fname);
|
|
goto done;
|
|
}
|
|
|
|
read_size = fread(buf, 1, size, fp);
|
|
if (read_size != size) {
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"Token object %s appears corrupted (ignoring it)", fname);
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
size_64 = size;
|
|
|
|
if (priv)
|
|
rc = restore_private_token_object_old(tokdata, buf, size_64, obj,
|
|
fname);
|
|
else
|
|
rc = object_mgr_restore_obj_withSize(tokdata, buf, obj, size_64, fname);
|
|
|
|
done:
|
|
if (fp)
|
|
fclose(fp);
|
|
if (buf)
|
|
free(buf);
|
|
|
|
return rc;
|
|
}
|
|
|
|
// this is the same as the old version. public token objects are stored in the
|
|
// clear
|
|
// Note: The token lock (XProcLock) must be held when calling this function.
|
|
//
|
|
CK_RV save_public_token_object_old(STDLL_TokData_t *tokdata, OBJECT * obj)
|
|
{
|
|
FILE *fp = NULL;
|
|
CK_BYTE *clear = NULL;
|
|
char fname[PATH_MAX];
|
|
char basename[PATH_MAX];
|
|
CK_ULONG clear_len;
|
|
CK_BBOOL flag = FALSE;
|
|
CK_RV rc;
|
|
CK_ULONG_32 total_len;
|
|
|
|
rc = object_flatten(obj, &clear, &clear_len);
|
|
if (rc != CKR_OK) {
|
|
goto error;
|
|
}
|
|
|
|
fp = open_token_object_path_new(fname, sizeof(fname),
|
|
basename, sizeof(basename),
|
|
tokdata, (char *)obj->name, "w");
|
|
if (!fp) {
|
|
TRACE_ERROR("fopen(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto error;
|
|
}
|
|
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto error;
|
|
|
|
if (clear_len > UINT32_MAX - sizeof(CK_ULONG_32) - sizeof(CK_BBOOL)) {
|
|
TRACE_ERROR("Object too large to store in old format (%lu bytes)\n",
|
|
clear_len);
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto error;
|
|
}
|
|
|
|
total_len = clear_len + sizeof(CK_ULONG_32) + sizeof(CK_BBOOL);
|
|
|
|
if (fwrite(&total_len, sizeof(CK_ULONG_32), 1, fp) != 1 ||
|
|
fwrite(&flag, sizeof(CK_BBOOL), 1, fp) != 1 ||
|
|
fwrite(clear, clear_len, 1, fp) != 1) {
|
|
TRACE_ERROR("fwrite(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
}
|
|
|
|
rc = close_token_file_new(fp, rc, fname, basename);
|
|
free(clear);
|
|
|
|
return rc;
|
|
|
|
error:
|
|
if (clear)
|
|
free(clear);
|
|
rc = close_token_file_new(fp, rc, fname, basename);
|
|
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
// Note: The token lock (XProcLock) must be held when calling this function.
|
|
//
|
|
CK_RV load_public_token_objects_old(STDLL_TokData_t *tokdata)
|
|
{
|
|
FILE *fp1 = NULL, *fp2 = NULL;
|
|
CK_BYTE *buf = NULL;
|
|
char tmp[PATH_MAX];
|
|
char iname[PATH_MAX];
|
|
char fname[PATH_MAX];
|
|
CK_BBOOL priv;
|
|
CK_ULONG_32 size;
|
|
size_t read_size, slen;
|
|
|
|
fp1 = open_token_object_index(iname, sizeof(iname), tokdata, "r");
|
|
if (!fp1)
|
|
return CKR_OK; // no token objects
|
|
|
|
while (fgets(tmp, 50, fp1)) {
|
|
slen = strlen(tmp);
|
|
if (slen > 0 && tmp[slen - 1] == '\n')
|
|
tmp[slen - 1] = '\0';
|
|
|
|
fp2 = open_token_object_path(fname, sizeof(fname), tokdata, tmp, "r");
|
|
if (!fp2)
|
|
continue;
|
|
|
|
if (fread(&size, sizeof(CK_ULONG_32), 1, fp2) != 1) {
|
|
fclose(fp2);
|
|
OCK_SYSLOG(LOG_ERR, "Cannot read size\n");
|
|
continue;
|
|
}
|
|
if (fread(&priv, sizeof(CK_BBOOL), 1, fp2) != 1) {
|
|
fclose(fp2);
|
|
OCK_SYSLOG(LOG_ERR, "Cannot read boolean\n");
|
|
continue;
|
|
}
|
|
if (priv == TRUE) {
|
|
fclose(fp2);
|
|
continue;
|
|
}
|
|
|
|
if (size <= sizeof(CK_ULONG_32) + sizeof(CK_BBOOL)) {
|
|
fclose(fp2);
|
|
OCK_SYSLOG(LOG_ERR, "Improper size of object %s (ignoring it)\n",
|
|
fname);
|
|
continue;
|
|
}
|
|
|
|
size -= sizeof(CK_ULONG_32) + sizeof(CK_BBOOL);
|
|
buf = (CK_BYTE *) malloc(size);
|
|
if (!buf) {
|
|
fclose(fp2);
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"Cannot malloc %u bytes to read in "
|
|
"token object %s (ignoring it)", size, fname);
|
|
continue;
|
|
}
|
|
|
|
read_size = fread(buf, 1, size, fp2);
|
|
if (read_size != size) {
|
|
fclose(fp2);
|
|
free(buf);
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"Cannot read token object %s " "(ignoring it)", fname);
|
|
continue;
|
|
}
|
|
// ... grab object mutex here.
|
|
if (object_mgr_restore_obj_withSize(tokdata,
|
|
buf, NULL, size, fname) != CKR_OK) {
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"Cannot restore token object %s "
|
|
"(ignoring it)", fname);
|
|
}
|
|
free(buf);
|
|
fclose(fp2);
|
|
}
|
|
|
|
fclose(fp1);
|
|
|
|
return CKR_OK;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* tokversion >= 3.12 object store
|
|
*/
|
|
|
|
static CK_RV aes_256_gcm_seal(STDLL_TokData_t *tokdata,
|
|
unsigned char *out,
|
|
unsigned char tag[16],
|
|
const unsigned char *aad,
|
|
size_t aadlen,
|
|
const unsigned char *in,
|
|
size_t inlen,
|
|
const unsigned char key[32],
|
|
const unsigned char iv[12])
|
|
{
|
|
CK_RV rc;
|
|
int outlen;
|
|
|
|
EVP_CIPHER_CTX *ctx = NULL;
|
|
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
if (ctx == NULL) {
|
|
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
|
|
rc = CKR_HOST_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
if (EVP_CipherInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL, -1) != 1
|
|
|| EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL) != 1
|
|
|| EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, 1) != 1
|
|
|| EVP_CipherUpdate(ctx, NULL, &outlen, aad, aadlen) != 1
|
|
|| EVP_CipherUpdate(ctx, out, &outlen, in, inlen) != 1
|
|
|| EVP_CipherFinal_ex(ctx, out + outlen, &outlen) != 1
|
|
|| EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag) != 1) {
|
|
TRACE_ERROR("%s\n", ock_err(ERR_GENERAL_ERROR));
|
|
rc = CKR_GENERAL_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
rc = CKR_OK;
|
|
|
|
if ((tokdata->statistics->flags & STATISTICS_FLAG_COUNT_INTERNAL) != 0)
|
|
tokdata->statistics->increment_func(tokdata->statistics,
|
|
tokdata->slot_id,
|
|
&tokdata->store_strength.mk_crypt,
|
|
tokdata->store_strength.mk_strength);
|
|
|
|
done:
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
return rc;
|
|
}
|
|
|
|
static CK_RV aes_256_gcm_unseal(STDLL_TokData_t *tokdata,
|
|
unsigned char *out,
|
|
const unsigned char *aad,
|
|
size_t aadlen,
|
|
const unsigned char *in,
|
|
size_t inlen,
|
|
const unsigned char tag[16],
|
|
const unsigned char key[32],
|
|
const unsigned char iv[12])
|
|
{
|
|
CK_RV rc;
|
|
int outlen;
|
|
|
|
EVP_CIPHER_CTX *ctx = NULL;
|
|
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
if (ctx == NULL) {
|
|
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
|
|
rc = CKR_HOST_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
if (EVP_CipherInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL, -1) != 1
|
|
|| EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (unsigned char *)tag) != 1
|
|
|| EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 12, NULL) != 1
|
|
|| EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, 0) != 1
|
|
|| EVP_CipherUpdate(ctx, NULL, &outlen, aad, aadlen) != 1
|
|
|| EVP_CipherUpdate(ctx, out, &outlen, in, inlen) != 1
|
|
|| EVP_CipherFinal_ex(ctx, out + outlen, &outlen) != 1) {
|
|
TRACE_ERROR("%s\n", ock_err(ERR_GENERAL_ERROR));
|
|
rc = CKR_GENERAL_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
rc = CKR_OK;
|
|
|
|
if ((tokdata->statistics->flags & STATISTICS_FLAG_COUNT_INTERNAL) != 0)
|
|
tokdata->statistics->increment_func(tokdata->statistics,
|
|
tokdata->slot_id,
|
|
&tokdata->store_strength.mk_crypt,
|
|
tokdata->store_strength.mk_strength);
|
|
|
|
done:
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
return rc;
|
|
}
|
|
|
|
static CK_RV aes_256_wrap(STDLL_TokData_t *tokdata,
|
|
unsigned char out[40],
|
|
const unsigned char in[32],
|
|
const unsigned char kek[32])
|
|
{
|
|
CK_RV rc;
|
|
int outlen;
|
|
unsigned char buffer[40 + EVP_MAX_BLOCK_LENGTH];
|
|
|
|
EVP_CIPHER_CTX *ctx = NULL;
|
|
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
if (ctx == NULL) {
|
|
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
|
|
rc = CKR_HOST_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
|
|
|
|
if (EVP_CipherInit_ex(ctx, EVP_aes_256_wrap(), NULL, kek, NULL, 1) != 1
|
|
|| EVP_CipherUpdate(ctx, buffer, &outlen, in, 32) != 1
|
|
|| EVP_CipherFinal_ex(ctx, buffer + outlen, &outlen) != 1) {
|
|
TRACE_ERROR("%s\n", ock_err(ERR_GENERAL_ERROR));
|
|
rc = CKR_GENERAL_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
memcpy(out, buffer, 40);
|
|
rc = CKR_OK;
|
|
|
|
if ((tokdata->statistics->flags & STATISTICS_FLAG_COUNT_INTERNAL) != 0)
|
|
tokdata->statistics->increment_func(tokdata->statistics,
|
|
tokdata->slot_id,
|
|
&tokdata->store_strength.wrap_crypt,
|
|
tokdata->store_strength.wrap_strength);
|
|
|
|
done:
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
return rc;
|
|
}
|
|
|
|
static CK_RV aes_256_unwrap(STDLL_TokData_t *tokdata,
|
|
unsigned char key[32],
|
|
const unsigned char in[40],
|
|
const unsigned char kek[32])
|
|
{
|
|
CK_RV rc;
|
|
int outlen;
|
|
unsigned char buffer[32 + EVP_MAX_BLOCK_LENGTH];
|
|
|
|
EVP_CIPHER_CTX *ctx = NULL;
|
|
|
|
ctx = EVP_CIPHER_CTX_new();
|
|
if (ctx == NULL) {
|
|
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
|
|
rc = CKR_HOST_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW);
|
|
|
|
if (EVP_CipherInit_ex(ctx, EVP_aes_256_wrap(), NULL, kek, NULL, 0) != 1
|
|
|| EVP_CipherUpdate(ctx, buffer, &outlen, in, 40) != 1
|
|
|| EVP_CipherFinal_ex(ctx, buffer + outlen, &outlen) != 1) {
|
|
TRACE_ERROR("%s\n", ock_err(ERR_GENERAL_ERROR));
|
|
rc = CKR_GENERAL_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
memcpy(key, buffer, 32);
|
|
rc = CKR_OK;
|
|
|
|
if ((tokdata->statistics->flags & STATISTICS_FLAG_COUNT_INTERNAL) != 0)
|
|
tokdata->statistics->increment_func(tokdata->statistics,
|
|
tokdata->slot_id,
|
|
&tokdata->store_strength.wrap_crypt,
|
|
tokdata->store_strength.wrap_strength);
|
|
|
|
done:
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
return rc;
|
|
}
|
|
|
|
CK_RV generate_master_key(STDLL_TokData_t *tokdata, CK_BYTE *key)
|
|
{
|
|
CK_RV rc;
|
|
|
|
if (tokdata->version < TOK_NEW_DATA_STORE)
|
|
return generate_master_key_old(tokdata, key);
|
|
|
|
/* generate a 256-bit AES key */
|
|
rc = rng_generate(tokdata, key, 32);
|
|
|
|
if (rc == CKR_OK &&
|
|
(tokdata->statistics->flags & STATISTICS_FLAG_COUNT_INTERNAL) != 0)
|
|
tokdata->statistics->increment_func(tokdata->statistics,
|
|
tokdata->slot_id,
|
|
&tokdata->store_strength.mk_keygen,
|
|
tokdata->store_strength.mk_strength);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
/**
|
|
* Wrap 256-bit AES master key by 256-bit AES SO wrap key
|
|
* using AES-KW (RFC 3394). The resulting 40-bytes cipher-text
|
|
* is stored in the MK_SO file in the token's data store.
|
|
*/
|
|
CK_RV save_masterkey_so(STDLL_TokData_t *tokdata)
|
|
{
|
|
FILE *fp = NULL;
|
|
char fname[PATH_MAX];
|
|
char basename[PATH_MAX];
|
|
CK_RV rc;
|
|
unsigned char outbuf[40];
|
|
|
|
if (tokdata->version < TOK_NEW_DATA_STORE)
|
|
return save_masterkey_so_old(tokdata);
|
|
|
|
/* Skip it if master key is not needed. */
|
|
if (!token_specific.data_store.use_master_key)
|
|
return CKR_OK;
|
|
|
|
/* wrap master key with so_wrap_key */
|
|
rc = aes_256_wrap(tokdata, outbuf, tokdata->master_key,
|
|
tokdata->so_wrap_key);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
// write the file
|
|
//
|
|
// probably ought to ensure the permissions are correct
|
|
//
|
|
fp = open_token_data_store_path_new(fname, sizeof(fname),
|
|
basename, sizeof(basename),
|
|
tokdata, "MK_SO", "w");
|
|
if (!fp) {
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
rc = fwrite(outbuf, sizeof(outbuf), 1, fp);
|
|
if (rc != 1) {
|
|
TRACE_ERROR("fwrite failed.\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
rc = CKR_OK;
|
|
done:
|
|
rc = close_token_file_new(fp, rc, fname, basename);
|
|
OPENSSL_cleanse(outbuf, sizeof(outbuf));
|
|
return rc;
|
|
}
|
|
|
|
CK_RV load_masterkey_so(STDLL_TokData_t *tokdata)
|
|
{
|
|
FILE *fp = NULL;
|
|
CK_RV rc;
|
|
char fname[PATH_MAX];
|
|
unsigned char inbuf[40];
|
|
|
|
if (tokdata->version < TOK_NEW_DATA_STORE)
|
|
return load_masterkey_so_old(tokdata);
|
|
|
|
memset(tokdata->master_key, 0, sizeof(tokdata->master_key));
|
|
|
|
// this file gets created on C_InitToken so we can assume that it always
|
|
// exists
|
|
//
|
|
fp = open_token_data_store_path(fname, sizeof(fname), tokdata, "MK_SO",
|
|
"r");
|
|
if (!fp) {
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
rc = fread(inbuf, sizeof(inbuf), 1, fp);
|
|
if (rc != 1) {
|
|
TRACE_ERROR("fread() failed.\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
/* unwrap master key with so_wrap_key */
|
|
rc = aes_256_unwrap(tokdata, tokdata->master_key, inbuf,
|
|
tokdata->so_wrap_key);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
rc = CKR_OK;
|
|
done:
|
|
if (fp)
|
|
fclose(fp);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Wrap 256-bit AES master key by 256-bit AES User wrap key
|
|
* using AES-KW (RFC 3394). The resulting 40-bytes cipher-text
|
|
* is stored in the MK_SO file in the token's data store.
|
|
*/
|
|
CK_RV save_masterkey_user(STDLL_TokData_t *tokdata)
|
|
{
|
|
FILE *fp = NULL;
|
|
char fname[PATH_MAX];
|
|
char basename[PATH_MAX];
|
|
CK_RV rc;
|
|
unsigned char outbuf[40];
|
|
|
|
if (tokdata->version < TOK_NEW_DATA_STORE)
|
|
return save_masterkey_user_old(tokdata);
|
|
|
|
/* wrap master key with so_wrap_key */
|
|
rc = aes_256_wrap(tokdata, outbuf, tokdata->master_key,
|
|
tokdata->user_wrap_key);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
// write the file
|
|
//
|
|
// probably ought to ensure the permissions are correct
|
|
//
|
|
fp = open_token_data_store_path_new(fname, sizeof(fname),
|
|
basename, sizeof(basename),
|
|
tokdata, "MK_USER", "w");
|
|
if (!fp) {
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
rc = fwrite(outbuf, sizeof(outbuf), 1, fp);
|
|
if (rc != 1) {
|
|
TRACE_ERROR("fwrite failed.\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
rc = CKR_OK;
|
|
done:
|
|
rc = close_token_file_new(fp, rc, fname, basename);
|
|
OPENSSL_cleanse(outbuf, sizeof(outbuf));
|
|
return rc;
|
|
}
|
|
|
|
CK_RV load_masterkey_user(STDLL_TokData_t *tokdata)
|
|
{
|
|
FILE *fp = NULL;
|
|
CK_RV rc;
|
|
char fname[PATH_MAX];
|
|
EVP_CIPHER_CTX *ctx = NULL;
|
|
unsigned char inbuf[40];
|
|
|
|
if (tokdata->version < TOK_NEW_DATA_STORE)
|
|
return load_masterkey_user_old(tokdata);
|
|
|
|
memset(tokdata->master_key, 0, sizeof(tokdata->master_key));
|
|
|
|
// this file gets created on C_InitToken so we can assume that it always
|
|
// exists
|
|
//
|
|
fp = open_token_data_store_path(fname, sizeof(fname), tokdata, "MK_USER",
|
|
"r");
|
|
if (!fp) {
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
rc = fread(inbuf, sizeof(inbuf), 1, fp);
|
|
if (rc != 1) {
|
|
TRACE_ERROR("fread failed.\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
/* unwrap master key with user_wrap_key */
|
|
rc = aes_256_unwrap(tokdata, tokdata->master_key, inbuf,
|
|
tokdata->user_wrap_key);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
rc = CKR_OK;
|
|
done:
|
|
if (fp)
|
|
fclose(fp);
|
|
EVP_CIPHER_CTX_free(ctx);
|
|
return rc;
|
|
}
|
|
|
|
CK_RV save_token_data(STDLL_TokData_t *tokdata, CK_SLOT_ID slot_id)
|
|
{
|
|
FILE *fp = NULL;
|
|
TOKEN_DATA td;
|
|
CK_RV rc;
|
|
char fname[PATH_MAX];
|
|
char basename[PATH_MAX];
|
|
|
|
if (tokdata->version < TOK_NEW_DATA_STORE)
|
|
return save_token_data_old(tokdata, slot_id);
|
|
|
|
rc = XProcLock(tokdata);
|
|
if (rc != CKR_OK) {
|
|
TRACE_ERROR("Failed to get Process Lock.\n");
|
|
goto out_nolock;
|
|
}
|
|
|
|
fp = open_token_nvdat_new(fname, sizeof(fname), basename, sizeof(basename),
|
|
tokdata, "w");
|
|
if (!fp) {
|
|
TRACE_ERROR("fopen(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
/* Write generic token data */
|
|
memcpy(&td, tokdata->nv_token_data, sizeof(TOKEN_DATA));
|
|
|
|
td.token_info.flags = htobe32(td.token_info.flags);
|
|
td.token_info.ulMaxSessionCount = htobe32(td.token_info.ulMaxSessionCount);
|
|
td.token_info.ulSessionCount = htobe32(td.token_info.ulSessionCount);
|
|
td.token_info.ulMaxRwSessionCount
|
|
= htobe32(td.token_info.ulMaxRwSessionCount);
|
|
td.token_info.ulRwSessionCount = htobe32(td.token_info.ulRwSessionCount);
|
|
td.token_info.ulMaxPinLen = htobe32(td.token_info.ulMaxPinLen);
|
|
td.token_info.ulMinPinLen = htobe32(td.token_info.ulMinPinLen);
|
|
td.token_info.ulTotalPublicMemory
|
|
= htobe32(td.token_info.ulTotalPublicMemory);
|
|
td.token_info.ulFreePublicMemory
|
|
= htobe32(td.token_info.ulFreePublicMemory);
|
|
td.token_info.ulTotalPrivateMemory
|
|
= htobe32(td.token_info.ulTotalPrivateMemory);
|
|
td.token_info.ulFreePrivateMemory
|
|
= htobe32(td.token_info.ulFreePrivateMemory);
|
|
td.tweak_vector.allow_weak_des = htobe32(td.tweak_vector.allow_weak_des);
|
|
td.tweak_vector.check_des_parity
|
|
= htobe32(td.tweak_vector.check_des_parity);
|
|
td.tweak_vector.allow_key_mods = htobe32(td.tweak_vector.allow_key_mods);
|
|
td.tweak_vector.netscape_mods = htobe32(td.tweak_vector.netscape_mods);
|
|
td.dat.version = htobe32(td.dat.version);
|
|
td.dat.so_login_it = htobe64(td.dat.so_login_it);
|
|
td.dat.user_login_it = htobe64(td.dat.user_login_it);
|
|
td.dat.so_wrap_it = htobe64(td.dat.so_wrap_it);
|
|
td.dat.user_wrap_it = htobe64(td.dat.user_wrap_it);
|
|
|
|
if (!fwrite(&td, sizeof(TOKEN_DATA), 1, fp)) {
|
|
TRACE_ERROR("fwrite(%s): %s\n", fname,
|
|
ferror(fp) ? strerror(errno) : "failed");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
/* Write token-specific data */
|
|
if (token_specific.t_save_token_data) {
|
|
rc = token_specific.t_save_token_data(tokdata, slot_id, fp);
|
|
if (rc)
|
|
goto done;
|
|
}
|
|
|
|
rc = CKR_OK;
|
|
|
|
done:
|
|
rc = close_token_file_new(fp, rc, fname, basename);
|
|
|
|
if (rc == CKR_OK) {
|
|
rc = XProcUnLock(tokdata);
|
|
if (rc != CKR_OK)
|
|
TRACE_ERROR("Failed to release Process Lock.\n");
|
|
} else {
|
|
/* return error that occurred first */
|
|
XProcUnLock(tokdata);
|
|
}
|
|
|
|
out_nolock:
|
|
return rc;
|
|
}
|
|
|
|
CK_RV load_token_data(STDLL_TokData_t *tokdata, CK_SLOT_ID slot_id)
|
|
{
|
|
FILE *fp = NULL;
|
|
char fname[PATH_MAX];
|
|
TOKEN_DATA td;
|
|
CK_RV rc;
|
|
|
|
if (tokdata->version < TOK_NEW_DATA_STORE)
|
|
return load_token_data_old(tokdata, slot_id);
|
|
|
|
rc = XProcLock(tokdata);
|
|
if (rc != CKR_OK) {
|
|
TRACE_ERROR("Failed to get Process Lock.\n");
|
|
goto out_nolock;
|
|
}
|
|
|
|
fp = open_token_nvdat(fname, sizeof(fname), tokdata, "r");
|
|
if (!fp) {
|
|
/* Better error checking added */
|
|
if (errno == ENOENT) {
|
|
init_token_data(tokdata, slot_id);
|
|
|
|
fp = fopen_nofollow(fname, "r");
|
|
if (!fp) {
|
|
// were really hosed here since the created
|
|
// did not occur
|
|
if (errno == ELOOP)
|
|
TRACE_ERROR("Refusing to follow symlink: %s\n", fname);
|
|
else
|
|
TRACE_ERROR("fopen(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto out_unlock;
|
|
}
|
|
} else {
|
|
/* Could not open file for some unknown reason */
|
|
TRACE_ERROR("fopen(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto out_unlock;
|
|
}
|
|
}
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto out_unlock;
|
|
|
|
/* Load generic token data */
|
|
if (fread(&td, sizeof(TOKEN_DATA), 1, fp) != 1) {
|
|
TRACE_ERROR("fread(%s): %s\n", fname,
|
|
ferror(fp) ? strerror(errno) : "failed");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto out_unlock;
|
|
}
|
|
/* data marshalling */
|
|
td.token_info.flags = be32toh(td.token_info.flags);
|
|
td.token_info.ulMaxSessionCount = be32toh(td.token_info.ulMaxSessionCount);
|
|
td.token_info.ulSessionCount = be32toh(td.token_info.ulSessionCount);
|
|
td.token_info.ulMaxRwSessionCount
|
|
= be32toh(td.token_info.ulMaxRwSessionCount);
|
|
td.token_info.ulRwSessionCount = be32toh(td.token_info.ulRwSessionCount);
|
|
td.token_info.ulMaxPinLen = be32toh(td.token_info.ulMaxPinLen);
|
|
td.token_info.ulMinPinLen = be32toh(td.token_info.ulMinPinLen);
|
|
td.token_info.ulTotalPublicMemory
|
|
= be32toh(td.token_info.ulTotalPublicMemory);
|
|
td.token_info.ulFreePublicMemory
|
|
= be32toh(td.token_info.ulFreePublicMemory);
|
|
td.token_info.ulTotalPrivateMemory
|
|
= be32toh(td.token_info.ulTotalPrivateMemory);
|
|
td.token_info.ulFreePrivateMemory
|
|
= be32toh(td.token_info.ulFreePrivateMemory);
|
|
td.tweak_vector.allow_weak_des = be32toh(td.tweak_vector.allow_weak_des);
|
|
td.tweak_vector.check_des_parity
|
|
= be32toh(td.tweak_vector.check_des_parity);
|
|
td.tweak_vector.allow_key_mods = be32toh(td.tweak_vector.allow_key_mods);
|
|
td.tweak_vector.netscape_mods = be32toh(td.tweak_vector.netscape_mods);
|
|
td.dat.version = be32toh(td.dat.version);
|
|
td.dat.so_login_it = be64toh(td.dat.so_login_it);
|
|
td.dat.user_login_it = be64toh(td.dat.user_login_it);
|
|
td.dat.so_wrap_it = be64toh(td.dat.so_wrap_it);
|
|
td.dat.user_wrap_it = be64toh(td.dat.user_wrap_it);
|
|
|
|
memcpy(tokdata->nv_token_data, &td, sizeof(TOKEN_DATA));
|
|
|
|
/* Load token-specific data */
|
|
if (token_specific.t_load_token_data) {
|
|
rc = token_specific.t_load_token_data(tokdata, slot_id, fp);
|
|
if (rc)
|
|
goto out_unlock;
|
|
}
|
|
|
|
rc = CKR_OK;
|
|
|
|
out_unlock:
|
|
if (fp)
|
|
fclose(fp);
|
|
|
|
if (rc == CKR_OK) {
|
|
rc = XProcUnLock(tokdata);
|
|
if (rc != CKR_OK)
|
|
TRACE_ERROR("Failed to release Process Lock.\n");
|
|
} else {
|
|
/* return error that occurred first */
|
|
XProcUnLock(tokdata);
|
|
}
|
|
|
|
out_nolock:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* Big-endian increment. Return carry.
|
|
*/
|
|
static inline int inc32(unsigned char ctr[4])
|
|
{
|
|
unsigned int c = 1;
|
|
|
|
c += (unsigned int)ctr[3];
|
|
ctr[3] = (unsigned char)c;
|
|
c >>= 8;
|
|
c += (unsigned int)ctr[2];
|
|
ctr[2] = (unsigned char)c;
|
|
c >>= 8;
|
|
c += (unsigned int)ctr[1];
|
|
ctr[1] = (unsigned char)c;
|
|
c >>= 8;
|
|
c += (unsigned int)ctr[0];
|
|
ctr[0] = (unsigned char)c;
|
|
c >>= 8;
|
|
|
|
return c;
|
|
}
|
|
|
|
/**
|
|
* private tok obj layout
|
|
*
|
|
* --- auth ------- <--+
|
|
* u32 tokversion | 64-byte header
|
|
* u8 private_flag |
|
|
* u8 reserved[3] |
|
|
* u8 key_wrapped[40] |
|
|
* u8 iv[12] |
|
|
* u32 object_len |
|
|
* --- auth+enc --- <--+
|
|
* u8 object[object_len] | body
|
|
* ---------------- <--+
|
|
* u8 tag[16] | 16-byte footer
|
|
* ---------------- <--+
|
|
*/
|
|
#define HEADER_LEN 64
|
|
#define FOOTER_LEN 16
|
|
|
|
/**
|
|
* public tok obj layout
|
|
*
|
|
* ---------------- <--+
|
|
* u32 tokversion | 16-byte header
|
|
* u8 private_flag |
|
|
* u8 reserved[7] |
|
|
* u32 object_len |
|
|
* ---------------- <--+
|
|
* u8 object[object_len] | body
|
|
* ---------------- <--+
|
|
*/
|
|
#define PUB_HEADER_LEN 16
|
|
#define HEADER_COMMON_LEN 5
|
|
|
|
//
|
|
// Note: The token lock (XProcLock) must be held when calling this function.
|
|
//
|
|
CK_RV save_private_token_object(STDLL_TokData_t *tokdata, OBJECT *obj)
|
|
{
|
|
FILE *fp = NULL;
|
|
CK_BYTE *obj_data = NULL;
|
|
char fname[PATH_MAX];
|
|
char basename[PATH_MAX];
|
|
struct stat sb;
|
|
CK_ULONG obj_data_len;
|
|
CK_RV rc;
|
|
CK_ULONG_32 obj_data_len_32;
|
|
CK_ULONG_32 total_len;
|
|
CK_BBOOL flag = CK_TRUE;
|
|
unsigned char obj_key[256 / 8], obj_iv[96 / 8], obj_key_wrapped[40];
|
|
unsigned char *data = NULL;
|
|
uint32_t tmp;
|
|
int new = 0;
|
|
|
|
if (tokdata->version < TOK_NEW_DATA_STORE)
|
|
return save_private_token_object_old(tokdata, obj);
|
|
|
|
rc = object_flatten(obj, &obj_data, &obj_data_len);
|
|
obj_data_len_32 = obj_data_len;
|
|
if (rc != CKR_OK) {
|
|
goto done;
|
|
}
|
|
|
|
total_len = HEADER_LEN + obj_data_len_32 + FOOTER_LEN;
|
|
|
|
data = malloc(total_len);
|
|
if (data == NULL) {
|
|
rc = CKR_HOST_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
fp = open_token_object_path(fname, sizeof(fname), tokdata,
|
|
(char *)obj->name, "r");
|
|
if (fp == NULL) {
|
|
/* create new token object */
|
|
new = 1;
|
|
} else {
|
|
if (fstat(fileno(fp), &sb) != 0) {
|
|
TRACE_ERROR("fstat(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
/* New token objects files created by mkstemp have a size of zero */
|
|
if (sb.st_size == 0) {
|
|
new = 1;
|
|
fclose(fp);
|
|
fp = NULL;
|
|
goto do_work;
|
|
}
|
|
|
|
/* update existing token object */
|
|
if (fread(data, HEADER_LEN, 1, fp) != 1) {
|
|
TRACE_ERROR("fread(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
fclose(fp);
|
|
fp = NULL;
|
|
|
|
/* iv */
|
|
memcpy(obj_iv, data + 48, 12);
|
|
|
|
/* increment iv counter field */
|
|
if (inc32(obj_iv + 8)) {
|
|
/* counter overflow: generate new key */
|
|
new = 1;
|
|
} else {
|
|
/* get wrapped key key */
|
|
memcpy(obj_key_wrapped, data + 8, 40);
|
|
|
|
/* get key */
|
|
rc = aes_256_unwrap(tokdata, obj_key, obj_key_wrapped,
|
|
tokdata->master_key);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
}
|
|
}
|
|
do_work:
|
|
if (new) {
|
|
/* get key */
|
|
rng_generate(tokdata, obj_key, 32);
|
|
|
|
/* iv = [obj.-name|counter] */
|
|
memcpy(obj_iv, obj->name, 8);
|
|
obj_iv[8] = 0;
|
|
obj_iv[9] = 0;
|
|
obj_iv[10] = 0;
|
|
obj_iv[11] = 1;
|
|
|
|
/* get wrapped key */
|
|
rc = aes_256_wrap(tokdata, obj_key_wrapped, obj_key,
|
|
tokdata->master_key);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
}
|
|
|
|
/* version */
|
|
tmp = htobe32(tokdata->version);
|
|
memcpy(data, &tmp, 4);
|
|
/* flags */
|
|
memcpy(data + 4, &flag, 1);
|
|
tmp = 0;
|
|
memcpy(data + 5, &tmp, 3);
|
|
/* wrapped key */
|
|
memcpy(data + 8, obj_key_wrapped, 40);
|
|
/* iv */
|
|
memcpy(data + 48, obj_iv, 12);
|
|
/* object len */
|
|
tmp = htobe32(obj_data_len_32);
|
|
memcpy(data + 60, &tmp, 4);
|
|
|
|
rc = aes_256_gcm_seal(tokdata,
|
|
/* ciphertext */
|
|
data + HEADER_LEN,
|
|
/* tag */
|
|
data + HEADER_LEN
|
|
+ obj_data_len_32,
|
|
/* aad */
|
|
data, HEADER_LEN,
|
|
/* plaintext */
|
|
obj_data, obj_data_len_32,
|
|
/* key */
|
|
obj_key,
|
|
/* iv */
|
|
obj_iv);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
fp = open_token_object_path_new(fname, sizeof(fname),
|
|
basename, sizeof(basename),
|
|
tokdata, (char *)obj->name, "w");
|
|
if (!fp) {
|
|
TRACE_ERROR("fopen(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
if (fwrite(data, total_len, 1, fp) != 1) {
|
|
TRACE_ERROR("fwrite(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
rc = CKR_OK;
|
|
done:
|
|
rc = close_token_file_new(fp, rc, fname, basename);
|
|
if (obj_data) {
|
|
OPENSSL_cleanse(obj_data, obj_data_len);
|
|
free(obj_data);
|
|
}
|
|
if (data) {
|
|
OPENSSL_cleanse(data, total_len);
|
|
free(data);
|
|
}
|
|
OPENSSL_cleanse(obj_key, sizeof(obj_key));
|
|
OPENSSL_cleanse(obj_key_wrapped, sizeof(obj_key_wrapped));
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
// Note: The token lock (XProcLock) must be held when calling this function.
|
|
//
|
|
CK_RV load_private_token_objects(STDLL_TokData_t *tokdata)
|
|
{
|
|
FILE *fp1 = NULL, *fp2 = NULL;
|
|
CK_BYTE *buf = NULL;
|
|
char tmp[PATH_MAX];
|
|
char iname[PATH_MAX];
|
|
char fname[PATH_MAX];
|
|
CK_BBOOL priv;
|
|
CK_ULONG_32 size;
|
|
CK_RV rc;
|
|
unsigned char header[HEADER_LEN], footer[FOOTER_LEN];
|
|
uint32_t len;
|
|
size_t slen;
|
|
|
|
if (tokdata->version < TOK_NEW_DATA_STORE)
|
|
return load_private_token_objects_old(tokdata);
|
|
|
|
fp1 = open_token_object_index(iname, sizeof(iname), tokdata, "r");
|
|
if (!fp1)
|
|
return CKR_OK; // no token objects
|
|
|
|
while (fgets(tmp, 50, fp1)) {
|
|
slen = strlen(tmp);
|
|
if (slen > 0 && tmp[slen - 1] == '\n')
|
|
tmp[slen - 1] = '\0';
|
|
|
|
fp2 = open_token_object_path(fname, sizeof(fname), tokdata, tmp, "r");
|
|
if (!fp2)
|
|
continue;
|
|
|
|
if (fread(header, HEADER_LEN, 1, fp2) != 1) {
|
|
fclose(fp2);
|
|
continue;
|
|
}
|
|
|
|
memcpy(&priv, header + 4, 1);
|
|
if (priv == FALSE) {
|
|
fclose(fp2);
|
|
continue;
|
|
}
|
|
|
|
memcpy(&len, header + 60, 4);
|
|
size = be32toh(len);
|
|
|
|
if (size == 0 || size >= OCK_MAX_TOKEN_OBJ_SIZE) {
|
|
fclose(fp2);
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"token object %s has an invalid size %u (ignoring it)",
|
|
fname, size);
|
|
continue;
|
|
}
|
|
|
|
buf = (CK_BYTE *)malloc(size);
|
|
if (!buf) {
|
|
fclose(fp2);
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"Cannot malloc %u bytes to read in "
|
|
"token object %s (ignoring it)", size, fname);
|
|
continue;
|
|
}
|
|
|
|
if (fread(buf, size, 1, fp2) != 1) {
|
|
free(buf);
|
|
fclose(fp2);
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"Cannot read token object %s " "(ignoring it)", fname);
|
|
continue;
|
|
}
|
|
if (fread(footer, FOOTER_LEN, 1, fp2) != 1) {
|
|
free(buf);
|
|
fclose(fp2);
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"Cannot read token object %s " "(ignoring it)", fname);
|
|
continue;
|
|
}
|
|
|
|
rc = restore_private_token_object(tokdata, header,
|
|
buf, size,
|
|
footer, NULL, fname);
|
|
if (rc != CKR_OK)
|
|
goto error;
|
|
|
|
OPENSSL_cleanse(buf, size);
|
|
free(buf);
|
|
fclose(fp2);
|
|
}
|
|
|
|
fclose(fp1);
|
|
return CKR_OK;
|
|
error:
|
|
if (buf) {
|
|
OPENSSL_cleanse(buf, size);
|
|
free(buf);
|
|
}
|
|
if (fp1)
|
|
fclose(fp1);
|
|
if (fp2)
|
|
fclose(fp2);
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
//
|
|
CK_RV restore_private_token_object(STDLL_TokData_t *tokdata,
|
|
CK_BYTE *header,
|
|
CK_BYTE *data, CK_ULONG len,
|
|
CK_BYTE *footer,
|
|
OBJECT *pObj,
|
|
const char *fname)
|
|
{
|
|
unsigned char obj_iv[12], obj_key[32], obj_key_wrapped[40];
|
|
CK_BYTE *buff = NULL;
|
|
CK_RV rc;
|
|
|
|
if (tokdata->version < TOK_NEW_DATA_STORE)
|
|
return restore_private_token_object_old(tokdata, data, len, pObj,
|
|
fname);
|
|
|
|
/* wrapped key */
|
|
memcpy(obj_key_wrapped, header + 8, 40);
|
|
/* iv */
|
|
memcpy(obj_iv, header + 48, 12);
|
|
|
|
rc = aes_256_unwrap(tokdata, obj_key, obj_key_wrapped, tokdata->master_key);
|
|
if (rc != CKR_OK) {
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
buff = (CK_BYTE *)malloc(len);
|
|
if (buff == NULL) {
|
|
TRACE_ERROR("%s\n", ock_err(ERR_HOST_MEMORY));
|
|
rc = CKR_HOST_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
rc = aes_256_gcm_unseal(tokdata,
|
|
buff, /* plain-text */
|
|
header, HEADER_LEN, /* aad */
|
|
data, len, /* cipher-text*/
|
|
footer, /* tag */
|
|
obj_key, obj_iv);
|
|
if (rc != CKR_OK) {
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
rc = object_mgr_restore_obj_withSize(tokdata, buff, pObj, len, fname);
|
|
if (rc != CKR_OK) {
|
|
goto done;
|
|
}
|
|
|
|
rc = CKR_OK;
|
|
done:
|
|
if (buff) {
|
|
OPENSSL_cleanse(buff, len);
|
|
free(buff);
|
|
}
|
|
OPENSSL_cleanse(obj_key, sizeof(obj_key));
|
|
OPENSSL_cleanse(obj_key_wrapped, sizeof(obj_key_wrapped));
|
|
return rc;
|
|
}
|
|
|
|
CK_RV reload_token_object(STDLL_TokData_t *tokdata, OBJECT *obj)
|
|
{
|
|
unsigned char header[HEADER_LEN], footer[FOOTER_LEN];
|
|
FILE *fp = NULL;
|
|
CK_BYTE *buf = NULL;
|
|
char fname[PATH_MAX];
|
|
CK_BBOOL priv;
|
|
CK_ULONG_32 size;
|
|
CK_ULONG size_64;
|
|
CK_RV rc;
|
|
uint32_t len;
|
|
uint32_t ver;
|
|
|
|
if (tokdata->version < TOK_NEW_DATA_STORE)
|
|
return reload_token_object_old(tokdata, obj);
|
|
|
|
fp = open_token_object_path(fname, sizeof(fname), tokdata,
|
|
(char *)obj->name, "r");
|
|
if (!fp) {
|
|
TRACE_ERROR("fopen(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
if (fread(header, HEADER_COMMON_LEN, 1, fp) != 1) {
|
|
OCK_SYSLOG(LOG_ERR, "Cannot read header\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
memcpy(&ver, header, 4);
|
|
memcpy(&priv, header + 4, 1);
|
|
if (priv) {
|
|
if (fread(header + HEADER_COMMON_LEN,
|
|
HEADER_LEN - HEADER_COMMON_LEN, 1, fp) != 1) {
|
|
OCK_SYSLOG(LOG_ERR, "Cannot read header\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
memcpy(&len, header + 60, 4);
|
|
} else {
|
|
if (fread(header + HEADER_COMMON_LEN,
|
|
PUB_HEADER_LEN - HEADER_COMMON_LEN, 1, fp) != 1) {
|
|
OCK_SYSLOG(LOG_ERR, "Cannot read header\n");
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
memcpy(&len, header + 12, 4);
|
|
}
|
|
|
|
/*
|
|
* In OCK 3.12 - 3.14 the version and size was not stored in BE. So if
|
|
* version field is in platform endianness, keep size as is also.
|
|
*/
|
|
if (ver == TOK_NEW_DATA_STORE)
|
|
size = len;
|
|
else
|
|
size = be32toh(len);
|
|
|
|
if (size == 0 || size >= OCK_MAX_TOKEN_OBJ_SIZE) {
|
|
rc = CKR_FUNCTION_FAILED;
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"token object %s has an invalid size %u (ignoring it)",
|
|
fname, size);
|
|
goto done;
|
|
}
|
|
|
|
buf = (CK_BYTE *) malloc(size);
|
|
if (buf == NULL) {
|
|
rc = CKR_HOST_MEMORY;
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"Cannot malloc %u bytes to read in token object %s "
|
|
"(ignoring it)", size, fname);
|
|
goto done;
|
|
}
|
|
|
|
if (fread(buf, size, 1, fp) != 1) {
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"Token object %s appears corrupted (ignoring it)", fname);
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
if (priv) {
|
|
if (fread(footer, FOOTER_LEN, 1, fp) != 1) {
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"Token object %s appears corrupted (ignoring it)", fname);
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
size_64 = size;
|
|
|
|
if (priv) {
|
|
rc = restore_private_token_object(tokdata, header, buf, size_64,
|
|
footer, obj, fname);
|
|
} else {
|
|
rc = object_mgr_restore_obj_withSize(tokdata, buf, obj, size_64, fname);
|
|
}
|
|
done:
|
|
if (fp)
|
|
fclose(fp);
|
|
if (buf)
|
|
free(buf);
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
// Note: The token lock (XProcLock) must be held when calling this function.
|
|
//
|
|
CK_RV save_public_token_object(STDLL_TokData_t *tokdata, OBJECT *obj)
|
|
{
|
|
FILE *fp = NULL;
|
|
CK_BYTE *clear = NULL;
|
|
char fname[PATH_MAX];
|
|
char basename[PATH_MAX];
|
|
CK_ULONG clear_len;
|
|
CK_BBOOL flag = FALSE;
|
|
CK_RV rc;
|
|
CK_ULONG_32 len, be_len;
|
|
unsigned char reserved[7] = {0};
|
|
uint32_t tmp;
|
|
|
|
if (tokdata->version < TOK_NEW_DATA_STORE)
|
|
return save_public_token_object_old(tokdata, obj);
|
|
|
|
rc = object_flatten(obj, &clear, &clear_len);
|
|
if (rc != CKR_OK) {
|
|
goto done;
|
|
}
|
|
len = (CK_ULONG_32)clear_len;
|
|
|
|
fp = open_token_object_path_new(fname, sizeof(fname),
|
|
basename, sizeof(basename),
|
|
tokdata, (char *)obj->name, "w");
|
|
if (!fp) {
|
|
TRACE_ERROR("fopen(%s): %s\n", fname, strerror(errno));
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
tmp = htobe32(tokdata->version);
|
|
be_len = htobe32(len);
|
|
|
|
rc = set_perm(fileno(fp), tokdata->tokgroup);
|
|
if (rc != CKR_OK)
|
|
goto done;
|
|
|
|
if (fwrite(&tmp, 4, 1, fp) != 1
|
|
|| fwrite(&flag, 1, 1, fp) != 1
|
|
|| fwrite(reserved, 7, 1, fp) != 1
|
|
|| fwrite(&be_len, 4, 1, fp) != 1
|
|
|| fwrite(clear, len, 1, fp) != 1) {
|
|
rc = CKR_FUNCTION_FAILED;
|
|
goto done;
|
|
}
|
|
|
|
rc = CKR_OK;
|
|
done:
|
|
rc = close_token_file_new(fp, rc, fname, basename);
|
|
if (clear)
|
|
free(clear);
|
|
return rc;
|
|
}
|
|
|
|
//
|
|
// Note: The token lock (XProcLock) must be held when calling this function.
|
|
//
|
|
CK_RV load_public_token_objects(STDLL_TokData_t *tokdata)
|
|
{
|
|
FILE *fp1 = NULL, *fp2 = NULL;
|
|
CK_BYTE *buf = NULL;
|
|
char tmp[PATH_MAX];
|
|
char iname[PATH_MAX];
|
|
char fname[PATH_MAX];
|
|
CK_BBOOL priv;
|
|
CK_ULONG_32 size;
|
|
unsigned char header[PUB_HEADER_LEN];
|
|
uint32_t ver;
|
|
size_t slen;
|
|
|
|
if (tokdata->version < TOK_NEW_DATA_STORE)
|
|
return load_public_token_objects_old(tokdata);
|
|
|
|
fp1 = open_token_object_index(iname, sizeof(iname), tokdata, "r");
|
|
if (!fp1)
|
|
return CKR_OK; // no token objects
|
|
|
|
while (fgets(tmp, 50, fp1)) {
|
|
slen = strlen(tmp);
|
|
if (slen > 0 && tmp[slen - 1] == '\n')
|
|
tmp[slen - 1] = '\0';
|
|
|
|
fp2 = open_token_object_path(fname, sizeof(fname), tokdata, tmp, "r");
|
|
if (!fp2)
|
|
continue;
|
|
|
|
if (fread(header, PUB_HEADER_LEN, 1, fp2) != 1) {
|
|
fclose(fp2);
|
|
OCK_SYSLOG(LOG_ERR, "Cannot read header\n");
|
|
continue;
|
|
}
|
|
|
|
memcpy(&ver, header, 4);
|
|
memcpy(&priv, header + 4, 1);
|
|
memcpy(&size, header + 12, 4);
|
|
|
|
/*
|
|
* In OCK 3.12 - 3.14 the version and size was not stored in BE. So if
|
|
* version field is in platform endianness, keep size as is also
|
|
*/
|
|
if (ver != TOK_NEW_DATA_STORE)
|
|
size = be32toh(size);
|
|
|
|
if (priv == TRUE) {
|
|
fclose(fp2);
|
|
continue;
|
|
}
|
|
|
|
/* size can not be negative if treated as signed int */
|
|
if (size >= OCK_MAX_TOKEN_OBJ_SIZE) {
|
|
fclose(fp2);
|
|
OCK_SYSLOG(LOG_ERR, "Size is invalid in header of token object %s "
|
|
"(ignoring it)\n", fname);
|
|
continue;
|
|
}
|
|
|
|
buf = (CK_BYTE *) malloc(size);
|
|
if (!buf) {
|
|
fclose(fp2);
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"Cannot malloc %u bytes to read in "
|
|
"token object %s (ignoring it)", size, fname);
|
|
continue;
|
|
}
|
|
|
|
if (fread(buf, size, 1, fp2) != 1) {
|
|
fclose(fp2);
|
|
free(buf);
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"Cannot read token object %s " "(ignoring it)", fname);
|
|
continue;
|
|
}
|
|
// ... grab object mutex here.
|
|
if (object_mgr_restore_obj_withSize(tokdata,
|
|
buf, NULL, size, fname) != CKR_OK) {
|
|
OCK_SYSLOG(LOG_ERR,
|
|
"Cannot restore token object %s "
|
|
"(ignoring it)", fname);
|
|
}
|
|
free(buf);
|
|
fclose(fp2);
|
|
}
|
|
|
|
fclose(fp1);
|
|
return CKR_OK;
|
|
}
|