libtpms/src/tpm2/RuntimeProfile.c
Stefan Berger d5368c7c41 tpm2: Enable SVN-limited hierarchy: svn-limited-hierarchy (SFL 8)
Implement support for a profile attribute svn-limited-hierarchy that must
be set for SVN-limited hiearchy support to be enabled. Bump up the
StateFormatLevel to 8 and store the SVN base secret starting with
StateFormatLevel 8.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
2024-11-12 08:26:54 -05:00

990 lines
32 KiB
C

/********************************************************************************/
/* */
/* Runtime Profile */
/* Written by Stefan Berger */
/* IBM Thomas J. Watson Research Center */
/* */
/* Licenses and Notices */
/* */
/* (c) Copyright IBM Corporation, 2022 */
/* */
/* All rights reserved. */
/* */
/* Redistribution and use in source and binary forms, with or without */
/* modification, are permitted provided that the following conditions are */
/* met: */
/* */
/* Redistributions of source code must retain the above copyright notice, */
/* this list of conditions and the following disclaimer. */
/* */
/* Redistributions in binary form must reproduce the above copyright */
/* notice, this list of conditions and the following disclaimer in the */
/* documentation and/or other materials provided with the distribution. */
/* */
/* Neither the names of the IBM Corporation nor the names of its */
/* contributors may be used to endorse or promote products derived from */
/* this software without specific prior written permission. */
/* */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR */
/* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
/* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */
/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, */
/* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY */
/* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT */
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE */
/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/* */
/********************************************************************************/
#define _GNU_SOURCE
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <regex.h>
#include <limits.h>
#include "Tpm.h"
#include "tpm_library_intern.h"
struct RuntimeProfile g_RuntimeProfile;
const char defaultCommandsProfile[] =
"0x11f-0x122,0x124-0x12e,0x130-0x140,0x142-0x159,0x15b-0x15e,"
"0x160-0x165,0x167-0x174,0x176-0x178,0x17a-0x193,0x197,0x199-0x19c";
const char defaultAlgorithmsProfile[] =
"rsa,rsa-min-size=1024,tdes,tdes-min-size=128,sha1,hmac,"
"aes,aes-min-size=128,mgf1,keyedhash,xor,sha256,sha384,sha512,"
"null,rsassa,rsaes,rsapss,oaep,ecdsa,ecdh,ecdaa,sm2,ecschnorr,ecmqv,"
"kdf1-sp800-56a,kdf2,kdf1-sp800-108,ecc,ecc-min-size=192,ecc-nist,"
"ecc-bn,ecc-sm2-p256,symcipher,camellia,camellia-min-size=128,cmac,"
"ctr,ofb,cbc,cfb,ecb";
const char defaultAttributesProfile[] =
"svn-limited-hierarchy";
static const struct RuntimeProfileDesc {
const char *name;
#define MAX_PROFILE_NAME_LEN 32
const char *prefix;
size_t prefix_len;
const char *commandsProfile;
const char *algorithmsProfile;
const char *attributesProfile;
/* StateFormatLevel drives the format the TPM's state is written in and
* how it is read.
* Once a version of libtpms is released this field must never change afterwards
* so that backwards compatibility for reading the state can be maintained.
* This basically locks the name of the profile to the stateFormatLevel.
*/
unsigned int stateFormatLevel;
#define STATE_FORMAT_LEVEL_CURRENT 8
#define STATE_FORMAT_LEVEL_UNKNOWN 0 /* JSON didn't provide StateFormatLevel; this is only
allowed for the 'default' profile or when user
passed JSON via SetProfile() */
/* State Format Levels:
* 1 : write the state in format of libtpms v0.9 : only 'null' profile may have this
* 2 : write the state in format of libtpms v0.10: the profile will be written into the state
* 3 : Enabled ECC_Encrypt (0x199) & ECC_Decrypt (0x19a) along with disabling COMPRESSED_LIST.
* PERSISTENT_DATA.ppList and PERSISTENT_DATA.auditCommands became bigger and need to
* be written differently.
* 4 : Camellia-192 & AES-192 enabled
* Session attribute isNameHashDefined was added and existing functions TPM2_PolicyNameHash
* and CheckPolicyAuthSession are using it.
* 5 : Enabled TPM2_PolicyCapability (0x19b) & TPM2_PolicyParameters (0x19c)
* 6 : Only OBJECTs for RSA keys marshal the private exponent; hierachy field is also
* marshalled now
* 7 : Attribute support was added:
* - no-unpadded-encryption
* - no-sha1-signing
* - no-sha1-verification
* - no-sha1-hmac-creation
* - no-sha1-hmac-verification
* - no-sha1-hmac
* - fips-host
* - drbg-continous-test
* - pct
* - no-ecc-key-derivation
* 8 : Attribute 'svn-limited-hierarchy' was added
*/
const char *description;
#define DESCRIPTION_MAX_SIZE 250
bool allowModifications; /* user is allowed to modify algorithms profile */
} RuntimeProfileDescs[] = {
#define PROFILE_DEFAULT_IDX 0
#define PROFILE_NULL_IDX 1
[PROFILE_DEFAULT_IDX] = {
/* Once libtpms v0.10 is done, this profile will become frozen */
#define DEFAULT_PROFILE_NAME "default-v1"
.name = DEFAULT_PROFILE_NAME,
.commandsProfile = defaultCommandsProfile,
.algorithmsProfile = defaultAlgorithmsProfile,
.attributesProfile = defaultAttributesProfile,
.stateFormatLevel = STATE_FORMAT_LEVEL_CURRENT, /* should always be the latest */
.description = "This profile enables all libtpms v0.10-supported commands and "
"algorithms. This profile is compatible with libtpms >= v0.10.",
.allowModifications = false,
},
[PROFILE_NULL_IDX] = {
/* When state has no profile, then the 'null' profile is applied which locks the
* TPM 2 into a set of commands and algorithms that were enable for libtpms v0.9
* NEVER CHANGE ANY OF THESE FIELDS!
*/
.name = "null",
.commandsProfile = "0x11f-0x122,0x124-0x12e,0x130-0x140,0x142-0x159,0x15b-0x15e,"
"0x160-0x165,0x167-0x174,0x176-0x178,0x17a-0x193,0x197",
.algorithmsProfile = "rsa,rsa-min-size=1024,tdes,tdes-min-size=128,sha1,hmac,"
"aes,aes-min-size=128,mgf1,keyedhash,xor,sha256,sha384,sha512,"
"null,rsassa,rsaes,rsapss,oaep,ecdsa,ecdh,ecdaa,sm2,ecschnorr,ecmqv,"
"kdf1-sp800-56a,kdf2,kdf1-sp800-108,ecc,ecc-min-size=192,ecc-nist,"
"ecc-bn,ecc-sm2-p256,symcipher,camellia,camellia-min-size=128,cmac,"
"ctr,ofb,cbc,cfb,ecb",
.stateFormatLevel = 1, /* NEVER change */
.description = "The profile enables the commands and algorithms that were "
"enabled in libtpms v0.9. This profile is automatically used "
"when the state does not have a profile, for example when it was "
"created by libtpms v0.9 or before. This profile enables compatibility "
"with libtpms >= v0.9.",
.allowModifications = false,
}, {
.name = "custom",
.prefix = "custom:",
.prefix_len = 7,
.commandsProfile = defaultCommandsProfile,
.algorithmsProfile = defaultAlgorithmsProfile,
/* no need to set attributes profile ever since user MUST provide it */
.stateFormatLevel = 2, /* minimum is '2', algos+cmds determine higher level */
.description = "This profile allows customization of enabled algorithms and commands. "
"This profile requires at least libtpms v0.10.",
.allowModifications = true,
}
};
LIB_EXPORT TPM_RC
RuntimeProfileInit(struct RuntimeProfile *RuntimeProfile)
{
RuntimeAlgorithmInit(&RuntimeProfile->RuntimeAlgorithm);
RuntimeCommandsInit(&RuntimeProfile->RuntimeCommands);
RuntimeAttributesInit(&RuntimeProfile->RuntimeAttributes);
RuntimeProfile->profileName = NULL;
RuntimeProfile->runtimeProfileJSON = NULL;
RuntimeProfile->stateFormatLevel = STATE_FORMAT_LEVEL_UNKNOWN;
RuntimeProfile->wasNullProfile = FALSE;
return TPM_RC_SUCCESS;
}
void
RuntimeProfileFree(struct RuntimeProfile *RuntimeProfile)
{
RuntimeAlgorithmFree(&RuntimeProfile->RuntimeAlgorithm);
RuntimeCommandsFree(&RuntimeProfile->RuntimeCommands);
RuntimeAttributesFree(&RuntimeProfile->RuntimeAttributes);
free(RuntimeProfile->profileName);
RuntimeProfile->profileName = NULL;
free(RuntimeProfile->runtimeProfileJSON);
RuntimeProfile->runtimeProfileJSON = NULL;
free(RuntimeProfile->profileDescription);
RuntimeProfile->profileDescription = NULL;
}
static TPM_RC
RuntimeProfileSetRuntimeProfile(struct RuntimeProfile *RuntimeProfile,
const char *algorithmsProfile,
const char *commandsProfile,
const char *attributesProfile,
unsigned int *stateFormatLevel, // IN/OUT: required stateFormatLevel
unsigned int maxStateFormatLevel // IN: maximum allowed stateFormatLevel
)
{
TPM_RC retVal;
retVal = RuntimeAttributesSetProfile(&RuntimeProfile->RuntimeAttributes, attributesProfile,
stateFormatLevel, maxStateFormatLevel);
if (retVal != TPM_RC_SUCCESS)
return retVal;
retVal = RuntimeAlgorithmSetProfile(&RuntimeProfile->RuntimeAlgorithm, algorithmsProfile,
stateFormatLevel, maxStateFormatLevel);
if (retVal != TPM_RC_SUCCESS)
return retVal;
return RuntimeCommandsSetProfile(&RuntimeProfile->RuntimeCommands, commandsProfile,
stateFormatLevel, maxStateFormatLevel);
}
static TPM_RC
RuntimeProfileCheckJSON(const char *json)
{
#define MAP_ENTRY_REGEX "[[:space:]]*\"[^\"]+\"[[:space:]]*:[[:space:]]*(\"[^\"]*\"|[[:digit:]]+)[[:space:]]*"
const char *regex = "^\\{[[:space:]]*("MAP_ENTRY_REGEX")?(,"MAP_ENTRY_REGEX")*\\}$";
#undef MAP_ENTRY_REGEX
TPM_RC retVal;
regex_t r;
if (regcomp(&r, regex, REG_EXTENDED|REG_NOSUB) != 0)
return TPM_RC_FAILURE;
if (regexec(&r, json, 0, NULL, 0) == REG_NOMATCH) {
retVal = TPM_RC_NO_RESULT;
goto exit;
}
retVal = TPM_RC_SUCCESS;
exit:
regfree(&r);
return retVal;
}
/*
* RuntimeProfileDedupStrItems does in-place deduplication of comma-separated
* items in a string. If an item contains '=' (rsa-min-size=) then the part
* before the '=' is deduplicated. When deduplicating always the later item is
* kept.
*/
static void
RuntimeProfileDedupStrItems(char *input)
{
size_t len = strlen(input), slen;
char *comma, *equals, *dup, *ncomma;
char *pos = input;
bool found;
char exp;
while (true) {
comma = index(pos, ',');
if (!comma)
return;
/* temporarily terminate string here */
*comma = '\0';
equals = index(pos, '=');
if (equals) {
*equals = '\0';
exp = '=';
slen = equals - pos;
} else {
exp = ',';
slen = comma - pos;
}
found = false;
ncomma = comma;
/* search for string after the comma */
while (true) {
dup = strstr(ncomma + 1, pos);
if (dup) {
/* ensure 'dup' is a prefix of 'pos' with either ',' or '\0' before it */
if ((dup[-1] == ',' || dup[-1] == 0) && dup[slen] == exp) {
memmove(pos, comma + 1, len - slen);
/* keep pos as-is */
found = true;
break;
}
/* only a prefix matched; continue search afer comma */
ncomma = index(dup, ',');
if (!ncomma)
break;
} else {
break;
}
}
if (!found) {
*comma = ',';
if (equals)
*equals = '=';
pos = comma + 1;
}
len -= (slen + 1);
}
}
static TPM_RC
RuntimeProfileGetFromJSON(const char *json,
const char *regex,
char **value,
bool removeDuplicates,
bool allowEmptyResult)
{
regmatch_t match[2];
TPM_RC retVal;
regex_t r;
if (regcomp(&r, regex, REG_EXTENDED) != 0)
return TPM_RC_FAILURE;
if (regexec(&r, json, 2, match, 0) == REG_NOMATCH) {
retVal = TPM_RC_NO_RESULT;
goto exit;
}
if (match[1].rm_eo - match[1].rm_so == 0 && !allowEmptyResult) {
retVal = TPM_RC_SIZE;
goto exit;
}
*value = strndup(&json[match[1].rm_so], match[1].rm_eo - match[1].rm_so);
if (removeDuplicates)
RuntimeProfileDedupStrItems(*value);
if (*value == NULL) {
retVal= TPM_RC_MEMORY;
goto exit;
}
retVal = TPM_RC_SUCCESS;
exit:
regfree(&r);
return retVal;
}
static TPM_RC
RuntimeProfileGetNameFromJSON(const char *json,
char **name)
{
const char *regex = "^\\{.*[[:space:]]*\"Name\"[[:space:]]*:[[:space:]]*\"([^\"]+)\".*\\}$";
TPM_RC retVal;
size_t len;
retVal = RuntimeProfileGetFromJSON(json, regex, name, false, false);
if (!retVal) {
len = strlen(*name);
if (len > MAX_PROFILE_NAME_LEN)
(*name)[MAX_PROFILE_NAME_LEN] = 0;
}
return retVal;
}
static TPM_RC
RuntimeProfileGetDescriptionFromJSON(const char *json,
char **description)
{
const char *regex = "^\\{.*[[:space:]]*\"Description\"[[:space:]]*:[[:space:]]*\"([^\"]+)\".*\\}$";
TPM_RC retVal;
size_t len;
retVal = RuntimeProfileGetFromJSON(json, regex, description, false, false);
if (retVal == TPM_RC_NO_RESULT) {
*description = NULL;
return TPM_RC_SUCCESS;
}
if (retVal == TPM_RC_SUCCESS && *description != NULL) {
len = strlen(*description);
if (len > DESCRIPTION_MAX_SIZE)
(*description)[DESCRIPTION_MAX_SIZE] = 0;
}
return retVal;
}
static TPM_RC
GetStateFormatLevelFromJSON(const char *json,
unsigned int *stateFormatLevel)
{
const char *regex = "^\\{.*[[:space:]]*\"StateFormatLevel\"[[:space:]]*:[[:space:]]*([0-9]+).*\\}$";
char *str = NULL;
unsigned long v;
TPM_RC retVal;
retVal = RuntimeProfileGetFromJSON(json, regex, &str, false, false);
if (retVal)
return retVal;
errno = 0;
v = strtoul(str, NULL, 10);
if (v > UINT_MAX || errno) {
TPMLIB_LogTPM2Error("StateFormatLevel value '%s' is not a valid positive number.\n",
str);
retVal = TPM_RC_VALUE;
} else {
*stateFormatLevel = v;
}
free(str);
return retVal;
}
static TPM_RC
GetAlgorithmsProfileFromJSON(const char *json,
char **algorithmsProfile)
{
const char *regex = "^\\{.*[[:space:]]*\"Algorithms\"[[:space:]]*:[[:space:]]*\"([^\"]+)\".*\\}$";
TPM_RC retVal;
retVal = RuntimeProfileGetFromJSON(json, regex, algorithmsProfile, true, false);
if (retVal == TPM_RC_NO_RESULT) {
*algorithmsProfile = NULL;
retVal = 0;
}
return retVal;
}
static TPM_RC
GetAttributesProfileFromJSON(
const char *json,
char **attributesProfile
)
{
const char *regex = "^\\{.*[[:space:]]*\"Attributes\"[[:space:]]*:[[:space:]]*\"([^\"]*)\".*\\}$";
TPM_RC retVal;
retVal = RuntimeProfileGetFromJSON(json, regex, attributesProfile, true, true);
if (retVal == TPM_RC_NO_RESULT) {
*attributesProfile = NULL;
retVal = 0;
}
return retVal;
}
static TPM_RC
GetCommandsProfileFromJSON(const char *json,
char **commandsProfile)
{
const char *regex = "^\\{.*[[:space:]]*\"Commands\"[[:space:]]*:[[:space:]]*\"([^\"]+)\".*\\}$";
TPM_RC retVal;
retVal = RuntimeProfileGetFromJSON(json, regex, commandsProfile, true, false);
if (retVal == TPM_RC_NO_RESULT) {
*commandsProfile = NULL;
retVal = 0;
}
return retVal;
}
/* Get several parameter from the JSON profile. If jsonProfile is NULL
* then use the null-profile.
*/
static TPM_RC
GetParametersFromJSON(const char *jsonProfile,
bool jsonProfileIsFromUser,
char **profileName,
unsigned int *stateFormatLevel,
char **algorithmsProfile,
char **commandsProfile,
char **attributesProfile,
char **profileDescription)
{
TPM_RC retVal;
if (!jsonProfile) {
/* If no profile is given use the null-profile */
*profileName = strdup("null");
if (*profileName == NULL)
return TPM_RC_MEMORY;
return TPM_RC_SUCCESS;
}
retVal = RuntimeProfileCheckJSON(jsonProfile);
if (retVal != TPM_RC_SUCCESS)
return retVal;
retVal = RuntimeProfileGetNameFromJSON(jsonProfile, profileName);
if (retVal != TPM_RC_SUCCESS)
return retVal;
if (jsonProfileIsFromUser) {
/* StateFormatLevel may be missing */
retVal = GetStateFormatLevelFromJSON(jsonProfile, stateFormatLevel);
switch (retVal) {
case TPM_RC_NO_RESULT:
*stateFormatLevel = STATE_FORMAT_LEVEL_UNKNOWN;
break;
case TPM_RC_SUCCESS:
break;
default:
goto err_free_profilename;
}
} else {
retVal = GetStateFormatLevelFromJSON(jsonProfile, stateFormatLevel);
if (retVal != TPM_RC_SUCCESS)
goto err_free_profilename;
}
if (*stateFormatLevel > STATE_FORMAT_LEVEL_CURRENT) {
TPMLIB_LogTPM2Error("The stateFormatLevel '%u' from the JSON exceeds the maximum supported '%u'\n",
*stateFormatLevel, STATE_FORMAT_LEVEL_CURRENT);
retVal = TPM_RC_VALUE;
goto err_free_profilename;
}
retVal = GetAlgorithmsProfileFromJSON(jsonProfile, algorithmsProfile);
if (retVal != TPM_RC_SUCCESS)
goto err_free_profilename;
retVal = GetCommandsProfileFromJSON(jsonProfile, commandsProfile);
if (retVal != TPM_RC_SUCCESS)
goto err_free_algorithmsprofile;
retVal = GetAttributesProfileFromJSON(jsonProfile, attributesProfile);
if (retVal != TPM_RC_SUCCESS)
goto err_free_commandsprofile;
retVal = RuntimeProfileGetDescriptionFromJSON(jsonProfile, profileDescription);
if (retVal != TPM_RC_SUCCESS)
goto err_free_attributesprofile;
return TPM_RC_SUCCESS;
err_free_attributesprofile:
free(*attributesProfile);
err_free_commandsprofile:
free(*commandsProfile);
err_free_algorithmsprofile:
free(*algorithmsProfile);
err_free_profilename:
free(*profileName);
return retVal;
}
static TPM_RC
RuntimeProfileFormat(char **json,
const char *profileName,
unsigned int stateFormatLevel,
const char *algorithmsProfile,
const char *commandsProfile,
const char *attributesProfile,
const char *profileDescription)
{
char *ret, *nret;
int n;
if (!profileName)
return TPM_RC_FAILURE;
n = asprintf(&ret,
"{\"Name\":\"%s\","
"\"StateFormatLevel\":%d",
profileName, stateFormatLevel);
if (n < 0)
return TPM_RC_MEMORY;
if (commandsProfile) {
n = asprintf(&nret, "%s,\"Commands\":\"%s\"", ret, commandsProfile);
free(ret);
if (n < 0)
return TPM_RC_MEMORY;
ret = nret;
}
if (algorithmsProfile) {
n = asprintf(&nret, "%s,\"Algorithms\":\"%s\"", ret, algorithmsProfile);
free(ret);
if (n < 0)
return TPM_RC_MEMORY;
ret = nret;
}
if (attributesProfile) {
n = asprintf(&nret, "%s,\"Attributes\":\"%s\"", ret, attributesProfile);
free(ret);
if (n < 0)
return TPM_RC_MEMORY;
ret = nret;
}
if (profileDescription) {
n = asprintf(&nret, "%s,\"Description\":\"%s\"", ret, profileDescription);
free(ret);
if (n < 0)
return TPM_RC_MEMORY;
ret = nret;
}
n = asprintf(&nret, "%s}", ret);
free(ret);
if (n < 0)
return TPM_RC_MEMORY;
*json = nret;
return TPM_RC_SUCCESS;
}
LIB_EXPORT TPM_RC
RuntimeProfileFormatJSON(struct RuntimeProfile *RuntimeProfile)
{
char *runtimeProfileJSON = NULL;
TPM_RC retVal;
if (!RuntimeProfile->profileName)
return TPM_RC_FAILURE;
retVal = RuntimeProfileFormat(&runtimeProfileJSON,
RuntimeProfile->profileName,
RuntimeProfile->stateFormatLevel,
RuntimeProfile->RuntimeAlgorithm.algorithmProfile,
RuntimeProfile->RuntimeCommands.commandsProfile,
RuntimeProfile->RuntimeAttributes.attributesProfile,
RuntimeProfile->profileDescription);
if (retVal != TPM_RC_SUCCESS)
return retVal;
free(RuntimeProfile->runtimeProfileJSON);
RuntimeProfile->runtimeProfileJSON = runtimeProfileJSON;
return TPM_RC_SUCCESS;
}
static int
RuntimeProfileNameMatch(const struct RuntimeProfileDesc *rp,
const char *profileName)
{
if (!strcmp(rp->name, profileName))
return true;
if (rp->prefix &&
!strncmp(rp->prefix, profileName, rp->prefix_len)) {
return true;
}
return false;
}
static const struct RuntimeProfileDesc *
RuntimeProfileFindByName(const char *profileName,
bool jsonProfileIsFromUser,
unsigned int stateFormatLevel,
const char *commandsProfile,
const char *algorithmsProfile,
const char *attributesProfile,
const char *profileDescription)
{
const struct RuntimeProfileDesc *rp = NULL;
size_t i;
for (i = 0; i < ARRAY_SIZE(RuntimeProfileDescs); i++) {
if (RuntimeProfileNameMatch(&RuntimeProfileDescs[i], profileName)) {
rp = &RuntimeProfileDescs[i];
if (!rp->allowModifications) {
/* user cannot set command or algorithms profile */
if (jsonProfileIsFromUser &&
(stateFormatLevel != STATE_FORMAT_LEVEL_UNKNOWN ||
commandsProfile || algorithmsProfile || attributesProfile || profileDescription)) {
TPMLIB_LogTPM2Error("The '%s' profile does not allow any customization\n",
rp->name);
return NULL;
}
}
return rp;
}
}
return NULL;
}
/*
* Set the given RuntimeProfile to the profile in JSON format. The profile may
* be set by the user and in this case the jsonProfileIsFromUser is set to
* true. Otherwise, it may originate from the TPM 2's state file and in this
* case jsonProfileIsFromUser is false.
* If jsonProfileIsFromUser is 'true' then the the default profile will get
* the latest StateFormatLevel version number, otherwise it will get the
* StateFormatLevel '1' if no stateFormatLevel field is found in the JSON
* profile.
* @RuntimeProfile: the RuntimeProfile to assign values to
* @jsonProfile: optional JSON-formatted profile; if NULL then null-profile
* will be used
* @jsonProfileIsFromUser: whether the user provided the profile (TRUE) or it
* was read from state (FALSE)
*/
LIB_EXPORT TPM_RC
RuntimeProfileSet(struct RuntimeProfile *RuntimeProfile,
const char *jsonProfile,
bool jsonProfileIsFromUser)
{
unsigned int stateFormatLevelJSON = STATE_FORMAT_LEVEL_UNKNOWN;
const struct RuntimeProfileDesc *rp = NULL;
unsigned int maxStateFormatLevel;
char *runtimeProfileJSON = NULL;
char *profileDescription = NULL;
char *algorithmsProfile = NULL;
char *attributesProfile = NULL;
char *commandsProfile = NULL;
char *profileName = NULL;
TPM_RC retVal;
retVal = GetParametersFromJSON(jsonProfile, jsonProfileIsFromUser,
&profileName, &stateFormatLevelJSON,
&algorithmsProfile, &commandsProfile,
&attributesProfile,
&profileDescription);
if (retVal != TPM_RC_SUCCESS)
return retVal;
/* profiles read from state must also have an existing profile */
rp = RuntimeProfileFindByName(profileName,
jsonProfileIsFromUser,
stateFormatLevelJSON,
commandsProfile,
algorithmsProfile,
attributesProfile,
profileDescription);
if (!rp) {
retVal = TPM_RC_VALUE;
goto error;
}
retVal = TPM_RC_MEMORY;
if (!attributesProfile && rp->attributesProfile && !rp->allowModifications) {
/* only use default if no modications are allowed; use NULL otherwise */
if (!(attributesProfile = strdup(rp->attributesProfile)))
goto error;
}
if (!algorithmsProfile && rp->algorithmsProfile) {
if (!(algorithmsProfile = strdup(rp->algorithmsProfile)))
goto error;
}
if (!commandsProfile && rp->commandsProfile) {
if (!(commandsProfile = strdup(rp->commandsProfile)))
goto error;
}
if (!profileDescription && rp->description) {
if (!(profileDescription = strdup(rp->description)))
goto error;
}
if (jsonProfileIsFromUser || stateFormatLevelJSON == STATE_FORMAT_LEVEL_UNKNOWN) {
if (!rp->allowModifications) {
/* StateFormatLevels are controlled by internal profile */
maxStateFormatLevel = rp->stateFormatLevel;
RuntimeProfile->stateFormatLevel = rp->stateFormatLevel;
} else {
if (stateFormatLevelJSON != STATE_FORMAT_LEVEL_UNKNOWN) {
if (stateFormatLevelJSON < 2) {
TPMLIB_LogTPM2Error("The minimum required StateFormatLevel for '%s' profile is '2'\n",
profileName);
goto error;
}
maxStateFormatLevel = stateFormatLevelJSON;
} else {
maxStateFormatLevel = ~0;
}
/* User has some control over StateFormatLevel */
RuntimeProfile->stateFormatLevel = stateFormatLevelJSON;
}
} else {
/* JSON was from TPM 2 state */
maxStateFormatLevel = stateFormatLevelJSON;
RuntimeProfile->stateFormatLevel = stateFormatLevelJSON;
}
retVal = RuntimeProfileSetRuntimeProfile(RuntimeProfile,
algorithmsProfile,
commandsProfile,
attributesProfile,
&RuntimeProfile->stateFormatLevel,
maxStateFormatLevel);
if (retVal != TPM_RC_SUCCESS)
goto error;
assert(maxStateFormatLevel >= RuntimeProfile->stateFormatLevel);
retVal = RuntimeProfileFormat(&runtimeProfileJSON, profileName,
RuntimeProfile->stateFormatLevel, algorithmsProfile,
commandsProfile, attributesProfile, profileDescription);
if (retVal != TPM_RC_SUCCESS)
goto error;
TPMLIB_LogPrintf("%s @ %u: runtimeProfile: %s\n", __func__, __LINE__, runtimeProfileJSON);
free(RuntimeProfile->runtimeProfileJSON);
RuntimeProfile->runtimeProfileJSON = runtimeProfileJSON;
free(RuntimeProfile->RuntimeAlgorithm.algorithmProfile);
RuntimeProfile->RuntimeAlgorithm.algorithmProfile = algorithmsProfile;
free(RuntimeProfile->RuntimeCommands.commandsProfile);
RuntimeProfile->RuntimeCommands.commandsProfile = commandsProfile;
free(RuntimeProfile->RuntimeAttributes.attributesProfile);
RuntimeProfile->RuntimeAttributes.attributesProfile = attributesProfile;
free(RuntimeProfile->profileName);
RuntimeProfile->profileName = profileName;
free(RuntimeProfile->profileDescription);
RuntimeProfile->profileDescription = profileDescription;
/* Indicate whether the profile was mapped to the default profile due to
* a NULL pointer read from the state.
*/
RuntimeProfile->wasNullProfile = (jsonProfile == NULL) && (jsonProfileIsFromUser == FALSE);
/* Another way is if the user passed in the null profile */
if (jsonProfileIsFromUser && !strcmp("null", profileName))
RuntimeProfile->wasNullProfile = true;
return TPM_RC_SUCCESS;
error:
free(profileDescription);
free(attributesProfile);
free(commandsProfile);
free(algorithmsProfile);
free(profileName);
return retVal;
}
LIB_EXPORT const char *
RuntimeProfileGetJSON(struct RuntimeProfile *RuntimeProfile)
{
return RuntimeProfile->runtimeProfileJSON;
}
/*
* Test whether the given jsonProfile is valid.
*
* @RuntimeProfile: the RuntimeProfile to assign values to
* @jsonProfile: optional JSON-formatted profile; if NULL then null-profile
* will be used
* @jsonProfileIsFromUser: whether the user provided the profile (TRUE) or it
* was read from state (FALSE)
*/
LIB_EXPORT TPM_RC
RuntimeProfileTest(struct RuntimeProfile *RuntimeProfile,
const char *jsonProfile,
bool jsonProfileIsFromUser)
{
unsigned int stateFormatLevelJSON = STATE_FORMAT_LEVEL_UNKNOWN;
unsigned int stateFormatLevel = STATE_FORMAT_LEVEL_UNKNOWN;
const struct RuntimeProfileDesc *rp = NULL;
unsigned int maxStateFormatLevel = ~0;
char *profileDescription = NULL;
char *algorithmsProfile = NULL;
char *attributesProfile = NULL;
char *commandsProfile = NULL;
char *profileName = NULL;
char *oldProfile = NULL;
TPM_RC retVal;
retVal = GetParametersFromJSON(jsonProfile, jsonProfileIsFromUser,
&profileName, &stateFormatLevelJSON,
&algorithmsProfile, &commandsProfile,
&attributesProfile,
&profileDescription);
if (retVal != TPM_RC_SUCCESS)
return retVal;
rp = RuntimeProfileFindByName(profileName,
jsonProfileIsFromUser,
stateFormatLevelJSON,
commandsProfile,
algorithmsProfile,
attributesProfile,
profileDescription);
if (!rp) {
retVal = TPM_RC_VALUE;
goto error;
}
if (stateFormatLevelJSON != STATE_FORMAT_LEVEL_UNKNOWN)
maxStateFormatLevel = stateFormatLevelJSON;
if (attributesProfile) {
/* Test the attributes profile if one was given */
retVal = RuntimeAttributesSwitchProfile(&RuntimeProfile->RuntimeAttributes,
attributesProfile, maxStateFormatLevel,
&oldProfile);
if (retVal == TPM_RC_SUCCESS)
retVal = RuntimeAttributesSetProfile(&RuntimeProfile->RuntimeAttributes,
oldProfile, &stateFormatLevel,
~0);
}
if (algorithmsProfile) {
/* Test the algorithms profile if one was given */
retVal = RuntimeAlgorithmSwitchProfile(&RuntimeProfile->RuntimeAlgorithm,
algorithmsProfile, maxStateFormatLevel,
&oldProfile);
if (retVal == TPM_RC_SUCCESS)
retVal = RuntimeAlgorithmSetProfile(&RuntimeProfile->RuntimeAlgorithm,
oldProfile, &stateFormatLevel,
~0);
}
if (commandsProfile) {
/* Test the commands profile if one was given */
retVal = RuntimeCommandsSwitchProfile(&RuntimeProfile->RuntimeCommands,
commandsProfile, maxStateFormatLevel,
&oldProfile);
if (retVal == TPM_RC_SUCCESS)
retVal = RuntimeCommandsSetProfile(&RuntimeProfile->RuntimeCommands,
oldProfile, &stateFormatLevel,
~0);
}
error:
free(profileDescription);
free(attributesProfile);
free(commandsProfile);
free(algorithmsProfile);
free(profileName);
return retVal;
}
LIB_EXPORT BOOL
RuntimeProfileWasNullProfile(struct RuntimeProfile *RuntimeProfile)
{
return RuntimeProfile->wasNullProfile;
}
LIB_EXPORT TPM_RC
RuntimeProfileGetByIndex(size_t idx,
char **runtimeProfileJSON)
{
if (idx >= ARRAY_SIZE(RuntimeProfileDescs))
return TPM_RC_VALUE;
return RuntimeProfileFormat(runtimeProfileJSON,
RuntimeProfileDescs[idx].name,
RuntimeProfileDescs[idx].stateFormatLevel,
RuntimeProfileDescs[idx].algorithmsProfile,
RuntimeProfileDescs[idx].commandsProfile,
RuntimeProfileDescs[idx].attributesProfile,
RuntimeProfileDescs[idx].description);
}
/*
* Determine the SEED_COMPAT_LEVEL that a profile can support. The
* SEED_COMPAT_LEVEL must be available on the earliest version of libtpms
* where the profile can run. If a profile for example can run on libtpms v0.9
* then this function must return only this SEED_COMPAT_LEVEL that was
* available in v0.9, which was SEED_COMPAT_LEVEL_RSA_PRIME_ADJUST_FIX.
* The SEED_COMPAT_LEVEL depends on the stateFormatLevel that in turn depends
* on the libtpms version.
*/
LIB_EXPORT SEED_COMPAT_LEVEL
RuntimeProfileGetSeedCompatLevel(void)
{
MUST_BE(SEED_COMPAT_LEVEL_LAST == 1); // force update when this changes
switch (g_RuntimeProfile.stateFormatLevel) {
case 1: /* profile runs on v0.9 */
return SEED_COMPAT_LEVEL_RSA_PRIME_ADJUST_FIX;
case 2 ... 8: /* profile runs on v0.10 */ {
MUST_BE(STATE_FORMAT_LEVEL_CURRENT == 8); // force update when this changes
return SEED_COMPAT_LEVEL_LAST;
}
default:
FAIL(FATAL_ERROR_INTERNAL);
}
}
LIB_EXPORT BOOL
RuntimeProfileRequiresAttributeFlags(struct RuntimeProfile *RuntimeProfile,
unsigned int attributeFlags)
{
return RuntimeAttributeCheckRequired(&RuntimeProfile->RuntimeAttributes,
attributeFlags);
}