1
0
mirror of https://github.com/opencryptoki/opencryptoki.git synced 2026-10-24 05:39:17 +03:00
Files
opencryptoki/usr/lib/api/statistics.c
Ingo Franzki d51d728862 STATISTICS/PKCSSTATS: Prevent buffer overrun when building SHM name
For incredibly long token directory names the buffer could overflow
when building the SHM name.

Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
2026-08-19 08:27:43 +02:00

527 lines
20 KiB
C

/*
* COPYRIGHT (c) International Business Machines Corp. 2021
*
* 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
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "statistics.h"
#include "trace.h"
#include "h_extern.h"
#include "ock_syslog.h"
#define COMPARE_SYMMETRIC 2
static CK_RV statistics_increment(struct statistics *statistics,
CK_SLOT_ID slot,
const CK_MECHANISM *mech,
CK_ULONG strength_idx)
{
CK_ULONG ofs;
counter_t *counter, old, next, prev;
int mech_idx;
CK_MECHANISM implicit_mech = { 0, NULL, 0 };
CK_ULONG idx, impl_strength_idx;
CK_ECDH1_DERIVE_PARAMS ecdh_params = { 0 };
CK_RV rc;
if (slot >= NUMBER_SLOTS_MANAGED || strength_idx > POLICY_STRENGTH_IDX_0 ||
mech == NULL)
return CKR_ARGUMENTS_BAD;
ofs = statistics->slot_shm_offsets[slot];
if (ofs > statistics->shm_size)
return CKR_SLOT_ID_INVALID;
mech_idx = mechtable_idx_from_numeric(mech->mechanism);
if (mech_idx < 0)
return CKR_MECHANISM_INVALID;
ofs += mech_idx * (NUM_SUPPORTED_STRENGTHS + 1) * sizeof(counter_t);
idx = NUM_SUPPORTED_STRENGTHS - strength_idx;
ofs += idx * sizeof(counter_t);
if (ofs > statistics->shm_size)
return CKR_FUNCTION_FAILED;
counter = (counter_t*)(statistics->shm_data + ofs);
old = *counter;
do {
prev = next = old;
if (next < ULONG_MAX)
next++;
} while ((old = __sync_val_compare_and_swap(counter, prev, next)) != prev);
if ((statistics->flags & STATISTICS_FLAG_COUNT_IMPLICIT) == 0)
return CKR_OK;
/* deep inspect certain mechanism params for implicit mechanism use */
switch (mech->mechanism) {
case CKM_RSA_PKCS_PSS:
case CKM_SHA1_RSA_PKCS_PSS:
case CKM_SHA224_RSA_PKCS_PSS:
case CKM_SHA256_RSA_PKCS_PSS:
case CKM_SHA384_RSA_PKCS_PSS:
case CKM_SHA512_RSA_PKCS_PSS:
case CKM_SHA3_224_RSA_PKCS_PSS:
case CKM_SHA3_256_RSA_PKCS_PSS:
case CKM_SHA3_384_RSA_PKCS_PSS:
case CKM_SHA3_512_RSA_PKCS_PSS:
if (mech->pParameter == NULL ||
mech->ulParameterLen != sizeof(CK_RSA_PKCS_PSS_PARAMS))
return CKR_MECHANISM_PARAM_INVALID;
implicit_mech.mechanism =
((CK_RSA_PKCS_PSS_PARAMS *)mech->pParameter)->hashAlg;
rc = statistics_increment(statistics, slot, &implicit_mech,
POLICY_STRENGTH_IDX_0);
if (rc != CKR_OK)
return rc;
rc = get_mgf_mech(((CK_RSA_PKCS_PSS_PARAMS *)mech->pParameter)->mgf,
&implicit_mech.mechanism);
if (rc != CKR_OK)
return rc;
rc = statistics_increment(statistics, slot, &implicit_mech,
POLICY_STRENGTH_IDX_0);
if (rc != CKR_OK)
return rc;
break;
case CKM_RSA_PKCS_OAEP:
if (mech->pParameter == NULL ||
mech->ulParameterLen != sizeof(CK_RSA_PKCS_OAEP_PARAMS))
return CKR_MECHANISM_PARAM_INVALID;
implicit_mech.mechanism =
((CK_RSA_PKCS_OAEP_PARAMS *)mech->pParameter)->hashAlg;
rc = statistics_increment(statistics, slot, &implicit_mech,
POLICY_STRENGTH_IDX_0);
if (rc != CKR_OK)
return rc;
rc = get_mgf_mech(((CK_RSA_PKCS_OAEP_PARAMS *)mech->pParameter)->mgf,
&implicit_mech.mechanism);
if (rc != CKR_OK)
return rc;
rc = statistics_increment(statistics, slot, &implicit_mech,
POLICY_STRENGTH_IDX_0);
if (rc != CKR_OK)
return rc;
break;
case CKM_RSA_AES_KEY_WRAP:
if (mech->pParameter == NULL ||
mech->ulParameterLen != sizeof(CK_RSA_AES_KEY_WRAP_PARAMS))
return CKR_MECHANISM_PARAM_INVALID;
if (((CK_RSA_AES_KEY_WRAP_PARAMS *)
mech->pParameter)->ulAESKeyBits > 0) {
impl_strength_idx = statistics->policy->get_sym_key_strength(
statistics->policy,
((CK_RSA_AES_KEY_WRAP_PARAMS *)
mech->pParameter)->ulAESKeyBits);
implicit_mech.mechanism = CKM_AES_KEY_WRAP;
rc = statistics_increment(statistics, slot, &implicit_mech,
impl_strength_idx);
if (rc != CKR_OK)
return rc;
}
implicit_mech.mechanism = CKM_RSA_PKCS_OAEP;
implicit_mech.pParameter =
((CK_RSA_AES_KEY_WRAP_PARAMS *)mech->pParameter)->pOAEPParams;
implicit_mech.ulParameterLen = sizeof(CK_RSA_PKCS_OAEP_PARAMS);
rc = statistics_increment(statistics, slot, &implicit_mech,
strength_idx);
if (rc != CKR_OK)
return rc;
break;
case CKM_ECDH1_DERIVE:
case CKM_ECDH1_COFACTOR_DERIVE:
if (mech->pParameter == NULL ||
mech->ulParameterLen != sizeof(CK_ECDH1_DERIVE_PARAMS))
return CKR_MECHANISM_PARAM_INVALID;
if (((CK_ECDH1_DERIVE_PARAMS *)mech->pParameter)->kdf == CKD_NULL ||
((CK_ECDH1_DERIVE_PARAMS *)mech->pParameter)->kdf == CKD_IBM_HYBRID_NULL)
break;
rc = digest_from_kdf(((CK_ECDH1_DERIVE_PARAMS *)mech->pParameter)->kdf,
&implicit_mech.mechanism);
if (rc != CKR_OK)
return rc;
rc = statistics_increment(statistics, slot, &implicit_mech,
POLICY_STRENGTH_IDX_0);
if (rc != CKR_OK)
return rc;
break;
case CKM_ECDH_AES_KEY_WRAP:
case CKM_ECDH_COF_AES_KEY_WRAP:
case CKM_ECDH_X_AES_KEY_WRAP:
if (mech->pParameter == NULL ||
mech->ulParameterLen != sizeof(CK_ECDH_AES_KEY_WRAP_PARAMS))
return CKR_MECHANISM_PARAM_INVALID;
if (((CK_ECDH_AES_KEY_WRAP_PARAMS *)
mech->pParameter)->ulAESKeyBits > 0) {
impl_strength_idx = statistics->policy->get_sym_key_strength(
statistics->policy,
((CK_ECDH_AES_KEY_WRAP_PARAMS *)
mech->pParameter)->ulAESKeyBits);
implicit_mech.mechanism = CKM_AES_KEY_WRAP;
rc = statistics_increment(statistics, slot, &implicit_mech,
impl_strength_idx);
if (rc != CKR_OK)
return rc;
}
ecdh_params.kdf =
((CK_ECDH_AES_KEY_WRAP_PARAMS *)mech->pParameter)->kdf;
implicit_mech.mechanism = mech->mechanism == CKM_ECDH_COF_AES_KEY_WRAP ?
CKM_ECDH1_COFACTOR_DERIVE : CKM_ECDH1_DERIVE;
implicit_mech.pParameter = &ecdh_params;
implicit_mech.ulParameterLen = sizeof(ecdh_params);
rc = statistics_increment(statistics, slot, &implicit_mech,
strength_idx);
if (rc != CKR_OK)
return rc;
break;
case CKM_IBM_ECDSA_OTHER:
if (mech->pParameter == NULL ||
mech->ulParameterLen != sizeof(CK_IBM_ECDSA_OTHER_PARAMS))
return CKR_MECHANISM_PARAM_INVALID;
switch (((CK_IBM_ECDSA_OTHER_PARAMS *)mech->pParameter)->submechanism) {
case CKM_IBM_ECSDSA_RAND:
case CKM_IBM_ECSDSA_COMPR_MULTI:
/* Uses SHA-256 internally */
implicit_mech.mechanism = CKM_SHA256;
rc = statistics_increment(statistics, slot, &implicit_mech,
POLICY_STRENGTH_IDX_0);
if (rc != CKR_OK)
return rc;
break;
case CKM_IBM_BLS:
break;
}
break;
case CKM_IBM_BTC_DERIVE:
if (mech->pParameter == NULL ||
mech->ulParameterLen != sizeof(CK_IBM_BTC_DERIVE_PARAMS))
return CKR_MECHANISM_PARAM_INVALID;
if (((CK_IBM_BTC_DERIVE_PARAMS *)mech->pParameter)->version !=
CK_IBM_BTC_DERIVE_PARAMS_VERSION_1)
break;
switch (((CK_IBM_BTC_DERIVE_PARAMS *)mech->pParameter)->type) {
case CK_IBM_BTC_BIP0032_PRV2PRV:
case CK_IBM_BTC_BIP0032_PRV2PUB:
case CK_IBM_BTC_BIP0032_PUB2PUB:
case CK_IBM_BTC_BIP0032_MASTERK:
case CK_IBM_BTC_SLIP0010_PRV2PRV:
case CK_IBM_BTC_SLIP0010_PRV2PUB:
case CK_IBM_BTC_SLIP0010_PUB2PUB:
case CK_IBM_BTC_SLIP0010_MASTERK:
/* Uses SHA-512 internally */
implicit_mech.mechanism = CKM_SHA512_HMAC;
rc = statistics_increment(statistics, slot, &implicit_mech,
POLICY_STRENGTH_IDX_0);
if (rc != CKR_OK)
return rc;
break;
}
break;
case CKM_IBM_ETH_DERIVE:
if (mech->pParameter == NULL ||
mech->ulParameterLen != sizeof(CK_IBM_ETH_DERIVE_PARAMS))
return CKR_MECHANISM_PARAM_INVALID;
if (((CK_IBM_ETH_DERIVE_PARAMS *)mech->pParameter)->version !=
CK_IBM_ETH_DERIVE_PARAMS_VERSION_1)
break;
switch (((CK_IBM_ETH_DERIVE_PARAMS *)mech->pParameter)->type) {
case CK_IBM_ETH_EIP2333_PRV2PRV:
case CK_IBM_ETH_EIP2333_PRV2PUB:
case CK_IBM_ETH_EIP2333_MASTERK:
/* Uses SHA-256 internally */
implicit_mech.mechanism = CKM_SHA256;
rc = statistics_increment(statistics, slot, &implicit_mech,
POLICY_STRENGTH_IDX_0);
if (rc != CKR_OK)
return rc;
break;
}
break;
case CKM_IBM_KYBER:
/* Only KEM uses a parameter, KeyGen, Encrypt/Decrypt don't */
if (mech->ulParameterLen != sizeof(CK_IBM_KYBER_PARAMS) &&
mech->ulParameterLen != 0)
return CKR_MECHANISM_PARAM_INVALID;
if (mech->ulParameterLen != sizeof(CK_IBM_KYBER_PARAMS))
break;
if (mech->pParameter == NULL)
return CKR_MECHANISM_PARAM_INVALID;
if (((CK_IBM_KYBER_PARAMS *)mech->pParameter)->kdf == CKD_NULL ||
((CK_IBM_KYBER_PARAMS *)mech->pParameter)->kdf == CKD_IBM_HYBRID_NULL)
break;
rc = digest_from_kdf(((CK_IBM_KYBER_PARAMS *)mech->pParameter)->kdf,
&implicit_mech.mechanism);
if (rc != CKR_OK)
return rc;
rc = statistics_increment(statistics, slot, &implicit_mech,
POLICY_STRENGTH_IDX_0);
if (rc != CKR_OK)
return rc;
break;
case CKM_IBM_ML_KEM:
if (mech->ulParameterLen != sizeof(CK_IBM_ML_KEM_PARAMS))
return CKR_MECHANISM_PARAM_INVALID;
if (mech->pParameter == NULL)
return CKR_MECHANISM_PARAM_INVALID;
if (((CK_IBM_ML_KEM_PARAMS *)mech->pParameter)->kdf == CKD_NULL ||
((CK_IBM_ML_KEM_PARAMS *)mech->pParameter)->kdf == CKD_IBM_HYBRID_NULL)
break;
rc = digest_from_kdf(((CK_IBM_ML_KEM_PARAMS *)mech->pParameter)->kdf,
&implicit_mech.mechanism);
if (rc != CKR_OK)
return rc;
rc = statistics_increment(statistics, slot, &implicit_mech,
POLICY_STRENGTH_IDX_0);
if (rc != CKR_OK)
return rc;
break;
case CKM_IBM_ML_KEM_WITH_ECDH:
if (mech->ulParameterLen != sizeof(CK_IBM_ML_KEM_WITH_ECDH_PARAMS))
return CKR_MECHANISM_PARAM_INVALID;
if (mech->pParameter == NULL)
return CKR_MECHANISM_PARAM_INVALID;
if (((CK_IBM_ML_KEM_WITH_ECDH_PARAMS *)mech->pParameter)->kdf == CKD_NULL ||
((CK_IBM_ML_KEM_WITH_ECDH_PARAMS *)mech->pParameter)->kdf == CKD_IBM_HYBRID_NULL)
break;
rc = digest_from_kdf(((CK_IBM_ML_KEM_WITH_ECDH_PARAMS *)mech->pParameter)->kdf,
&implicit_mech.mechanism);
if (rc != CKR_OK)
return rc;
rc = statistics_increment(statistics, slot, &implicit_mech,
POLICY_STRENGTH_IDX_0);
if (rc != CKR_OK)
return rc;
break;
default:
break;
}
return CKR_OK;
}
/*
* Open the statistics shared memory segment for the specified user.
* If user is -1, then it is opened for the current user.
* If create is TRUE then the shared memory segment is created if it is not
* already existent.
*/
static CK_RV statistics_open_shm(struct statistics *statistics, uid_t user,
CK_BBOOL create)
{
int i, err, clear = 0, fd;
struct stat stat_buf;
snprintf(statistics->shm_name, sizeof(statistics->shm_name),
"%s_stats_%u", CONFIG_PATH, user == (uid_t)-1 ? geteuid() : user);
for (i = 1; statistics->shm_name[i] != '\0'; i++) {
if (statistics->shm_name[i] == '/')
statistics->shm_name[i] = '.';
}
if (statistics->shm_name[0] != '/') {
size_t len = strlen(statistics->shm_name);
if (len + 1 >= sizeof(statistics->shm_name)) {
TRACE_ERROR("SHM name too long for CONFIG_PATH '%s'\n", CONFIG_PATH);
OCK_SYSLOG(LOG_ERR, "SHM name too long for CONFIG_PATH '%s'\n",
CONFIG_PATH);
return CKR_FUNCTION_FAILED;
}
memmove(&statistics->shm_name[1], &statistics->shm_name[0], len + 1);
statistics->shm_name[0] = '/';
}
TRACE_INFO("Statistics SHM name: '%s'\n", statistics->shm_name);
fd = shm_open(statistics->shm_name, O_RDWR, S_IRUSR | S_IWUSR);
if (fd == -1) {
if (create) {
/* try to create it */
fd = shm_open(statistics->shm_name, O_CREAT | O_RDWR,
S_IRUSR | S_IWUSR);
if (fd == -1) {
err = errno;
TRACE_ERROR("Failed to create SHM '%s': %s\n",
statistics->shm_name, strerror(err));
OCK_SYSLOG(LOG_ERR, "Failed to create SHM '%s': %s\n",
statistics->shm_name, strerror(err));
return CKR_FUNCTION_FAILED;
}
if (fchmod(fd, S_IRUSR | S_IWUSR) == -1) {
err = errno;
TRACE_ERROR("Failed to change mode of SHM '%s': %s\n",
statistics->shm_name, strerror(err));
OCK_SYSLOG(LOG_ERR, "Failed to change mode of SHM '%s': %s\n",
statistics->shm_name, strerror(err));
close(fd);
shm_unlink(statistics->shm_name);
return CKR_FUNCTION_FAILED;
}
} else {
err = errno;
TRACE_ERROR("Failed to open SHM '%s': %s\n",
statistics->shm_name, strerror(err));
OCK_SYSLOG(LOG_ERR, "Failed to open SHM '%s': %s\n",
statistics->shm_name, strerror(err));
return CKR_FUNCTION_FAILED;
}
}
if (fstat(fd, &stat_buf)) {
err = errno;
TRACE_ERROR("Failed to stat SHM '%s': %s\n",
statistics->shm_name, strerror(err));
OCK_SYSLOG(LOG_ERR, "Failed to stat SHM '%s': %s\n",
statistics->shm_name, strerror(err));
close(fd);
return CKR_FUNCTION_FAILED;
}
/*
* If the shared memory segment does not belong to the current user or does
* not have correct permissions, do not use it.
*/
if (stat_buf.st_uid != geteuid() ||
(stat_buf.st_mode & ~S_IFMT) != (S_IRUSR | S_IWUSR)) {
TRACE_ERROR("SHM '%s' has wrong mode/owner\n", statistics->shm_name);
OCK_SYSLOG(LOG_ERR, "SHM '%s' has wrong mode/owner\n",
statistics->shm_name);
close(fd);
return CKR_FUNCTION_FAILED;
}
if ((CK_ULONG)stat_buf.st_size != statistics->shm_size) {
if (create) {
if (ftruncate(fd, statistics->shm_size) < 0) {
err = errno;
TRACE_ERROR("Failed to set size of SHM '%s': %s\n",
statistics->shm_name, strerror(err));
OCK_SYSLOG(LOG_ERR, "Failed to set size of SHM '%s': %s\n",
statistics->shm_name, strerror(err));
close(fd);
return CKR_FUNCTION_FAILED;
}
clear = 1;
} else {
TRACE_ERROR("SHM '%s' has wrong size\n", statistics->shm_name);
OCK_SYSLOG(LOG_ERR, "SHM '%s' has wrong size\n",
statistics->shm_name);
close(fd);
return CKR_FUNCTION_FAILED;
}
}
statistics->shm_data = (CK_BYTE *)mmap(NULL, statistics->shm_size,
PROT_READ | PROT_WRITE, MAP_SHARED,
fd, 0);
close(fd);
if (statistics->shm_data == MAP_FAILED) {
err = errno;
TRACE_ERROR("Failed to memory-map SHM '%s': %s\n",
statistics->shm_name, strerror(err));
OCK_SYSLOG(LOG_ERR, "Failed to memory-map SHM '%s': %s\n",
statistics->shm_name, strerror(err));
statistics->shm_data = NULL;
return CKR_FUNCTION_FAILED;
}
if (clear)
memset(statistics->shm_data, 0, statistics->shm_size);
return CKR_OK;
}
static CK_RV statistics_close_shm(struct statistics *statistics,
CK_BBOOL destroy)
{
CK_RV rc;
if (statistics->shm_data == NULL)
return CKR_ARGUMENTS_BAD;
munmap(statistics->shm_data, statistics->shm_size);
if (destroy) {
rc = shm_unlink(statistics->shm_name);
if (rc != 0) {
TRACE_ERROR("Failed to unlink SHM '%s': %s\n",
statistics->shm_name, strerror(errno));
return CKR_FUNCTION_FAILED;
}
}
statistics->shm_data = NULL;
statistics->shm_size = -1;
return CKR_OK;
}
CK_RV statistics_init(struct statistics *statistics,
Slot_Mgr_Socket_t *slots_infos, CK_ULONG flags,
uid_t uid, struct policy *policy)
{
CK_ULONG i;
CK_RV rc;
statistics->flags = flags;
statistics->shm_data = NULL;
/* Count number of configured slots and calculate slot offsets. */
statistics->num_slots = 0;
for (i = 0; i < NUMBER_SLOTS_MANAGED; i++) {
if (slots_infos->slot_info[i].present) {
statistics->slot_shm_offsets[i] =
statistics->num_slots * STAT_SLOT_SIZE;
statistics->num_slots++;
} else {
statistics->slot_shm_offsets[i] = (CK_ULONG)-1;
}
}
if (statistics->num_slots > NUMBER_SLOTS_MANAGED) {
TRACE_ERROR("Slot count %lu exceeds NUMBER_SLOTS_MANAGED (%d)\n",
statistics->num_slots, NUMBER_SLOTS_MANAGED);
return CKR_GENERAL_ERROR;
}
statistics->shm_size = statistics->num_slots * STAT_SLOT_SIZE;
TRACE_INFO("%lu slots defined\n", statistics->num_slots);
TRACE_INFO("Statistics SHM size: %lu\n", statistics->shm_size);
rc = statistics_open_shm(statistics, uid, CK_TRUE);
if (rc != CKR_OK)
goto error;
statistics->increment_func = statistics_increment;
statistics->policy = policy;
return CKR_OK;
error:
statistics_term(statistics);
return rc;
}
void statistics_term(struct statistics *statistics)
{
statistics_close_shm(statistics, CK_FALSE);
}