summaryrefslogtreecommitdiffstats
path: root/src/tpm2/ExecCommand.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tpm2/ExecCommand.c')
-rw-r--r--src/tpm2/ExecCommand.c317
1 files changed, 317 insertions, 0 deletions
diff --git a/src/tpm2/ExecCommand.c b/src/tpm2/ExecCommand.c
new file mode 100644
index 0000000..f21934b
--- /dev/null
+++ b/src/tpm2/ExecCommand.c
@@ -0,0 +1,317 @@
+/********************************************************************************/
+/* */
+/* ExecCommand */
+/* Written by Ken Goldman */
+/* IBM Thomas J. Watson Research Center */
+/* $Id: ExecCommand.c 1600 2020-03-30 22:08:01Z kgoldman $ */
+/* */
+/* Licenses and Notices */
+/* */
+/* 1. Copyright Licenses: */
+/* */
+/* - Trusted Computing Group (TCG) grants to the user of the source code in */
+/* this specification (the "Source Code") a worldwide, irrevocable, */
+/* nonexclusive, royalty free, copyright license to reproduce, create */
+/* derivative works, distribute, display and perform the Source Code and */
+/* derivative works thereof, and to grant others the rights granted herein. */
+/* */
+/* - The TCG grants to the user of the other parts of the specification */
+/* (other than the Source Code) the rights to reproduce, distribute, */
+/* display, and perform the specification solely for the purpose of */
+/* developing products based on such documents. */
+/* */
+/* 2. Source Code Distribution Conditions: */
+/* */
+/* - Redistributions of Source Code must retain the above copyright licenses, */
+/* this list of conditions and the following disclaimers. */
+/* */
+/* - Redistributions in binary form must reproduce the above copyright */
+/* licenses, this list of conditions and the following disclaimers in the */
+/* documentation and/or other materials provided with the distribution. */
+/* */
+/* 3. Disclaimers: */
+/* */
+/* - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF */
+/* LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH */
+/* RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES) */
+/* THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE. */
+/* Contact TCG Administration (admin@trustedcomputinggroup.org) for */
+/* information on specification licensing rights available through TCG */
+/* membership agreements. */
+/* */
+/* - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED */
+/* WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR */
+/* FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR */
+/* NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY */
+/* OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE. */
+/* */
+/* - Without limitation, TCG and its members and licensors disclaim all */
+/* liability, including liability for infringement of any proprietary */
+/* rights, relating to use of information in this specification and to the */
+/* implementation of this specification, and TCG disclaims all liability for */
+/* cost of procurement of substitute goods or services, lost profits, loss */
+/* of use, loss of data or any incidental, consequential, direct, indirect, */
+/* or special damages, whether under contract, tort, warranty or otherwise, */
+/* arising in any way out of use or reliance upon this specification or any */
+/* information herein. */
+/* */
+/* (c) Copyright IBM Corp. and others, 2016 - 2020 */
+/* */
+/********************************************************************************/
+
+/* 6.2 ExecCommand.c */
+/* This file contains the entry function ExecuteCommand() which provides the main control flow for
+ TPM command execution. */
+#include "Tpm.h"
+#include "ExecCommand_fp.h"
+
+#define TPM_HAVE_TPM2_DECLARATIONS
+#include "tpm_library_intern.h" // libtpms added
+
+/* Uncomment this next #include if doing static command/response buffer sizing */
+// #include "CommandResponseSizes_fp.h"
+// The function performs the following steps.
+// a) Parses the command header from input buffer.
+// b) Calls ParseHandleBuffer() to parse the handle area of the command.
+// c) Validates that each of the handles references a loaded entity.
+// d) Calls ParseSessionBuffer() () to:
+// 1) unmarshal and parse the session area;
+// 2) check the authorizations; and
+// 3) when necessary, decrypt a parameter.
+// e) Calls CommandDispatcher() to:
+// 1) unmarshal the command parameters from the command buffer;
+// 2) call the routine that performs the command actions; and
+// 3) marshal the responses into the response buffer.
+// f) If any error occurs in any of the steps above create the error response and return.
+// g) Calls BuildResponseSession() to:
+// 1) when necessary, encrypt a parameter
+// 2) build the response authorization sessions
+// 3) update the audit sessions and nonces
+// h) Calls BuildResponseHeader() to complete the construction of the response.
+
+// responseSize is set by the caller to the maximum number of bytes available in the output
+// buffer. ExecuteCommand() will adjust the value and return the number of bytes placed in
+// the buffer.
+// response is also set by the caller to indicate the buffer into which ExecuteCommand() is
+// to place the response.
+// request and response may point to the same buffer
+// NOTE: As of February, 2016, the failure processing has been moved to the platform-specific
+// code. When the TPM code encounters an unrecoverable failure, it will SET g_inFailureMode
+// and call _plat__Fail(). That function should not return but may call ExecuteCommand().
+LIB_EXPORT void
+ExecuteCommand(
+ uint32_t requestSize, // IN: command buffer size
+ unsigned char *request, // IN: command buffer
+ uint32_t *responseSize, // IN/OUT: response buffer size
+ unsigned char **response // IN/OUT: response buffer
+ )
+{
+ // Command local variables
+ UINT32 commandSize;
+ COMMAND command;
+ // Response local variables
+ UINT32 maxResponse = *responseSize;
+ TPM_RC result; // return code for the command
+ // This next function call is used in development to size the command and response
+ // buffers. The values printed are the sizes of the internal structures and
+ // not the sizes of the canonical forms of he command response structures. Also,
+ // the sizes do not include the tag, command.code, requestSize, or the authorization
+ // fields.
+ //CommandResponseSizes();
+ // Set flags for NV access state. This should happen before any other
+ // operation that may require a NV write. Note, that this needs to be done
+ // even when in failure mode. Otherwise, g_updateNV would stay SET while in
+ // Failure mode and the NV would be written on each call.
+ g_updateNV = UT_NONE;
+ g_clearOrderly = FALSE;
+ if(g_inFailureMode)
+ {
+ // Do failure mode processing
+ TpmFailureMode(requestSize, request, responseSize, response);
+ return;
+ }
+ // Query platform to get the NV state. The result state is saved internally
+ // and will be reported by NvIsAvailable(). The reference code requires that
+ // accessibility of NV does not change during the execution of a command.
+ // Specifically, if NV is available when the command execution starts and then
+ // is not available later when it is necessary to write to NV, then the TPM
+ // will go into failure mode.
+ NvCheckState();
+ // Due to the limitations of the simulation, TPM clock must be explicitly
+ // synchronized with the system clock whenever a command is received.
+ // This function call is not necessary in a hardware TPM. However, taking
+ // a snapshot of the hardware timer at the beginning of the command allows
+ // the time value to be consistent for the duration of the command execution.
+ TimeUpdateToCurrent();
+ // Any command through this function will unceremoniously end the
+ // _TPM_Hash_Data/_TPM_Hash_End sequence.
+ if(g_DRTMHandle != TPM_RH_UNASSIGNED)
+ ObjectTerminateEvent();
+ // Get command buffer size and command buffer.
+ command.parameterBuffer = request;
+ command.parameterSize = requestSize;
+ // Parse command header: tag, commandSize and command.code.
+ // First parse the tag. The unmarshaling routine will validate
+ // that it is either TPM_ST_SESSIONS or TPM_ST_NO_SESSIONS.
+ result = TPMI_ST_COMMAND_TAG_Unmarshal(&command.tag,
+ &command.parameterBuffer,
+ &command.parameterSize);
+ if(result != TPM_RC_SUCCESS)
+ goto Cleanup;
+ // Unmarshal the commandSize indicator.
+ result = UINT32_Unmarshal(&commandSize,
+ &command.parameterBuffer,
+ &command.parameterSize);
+ if(result != TPM_RC_SUCCESS)
+ goto Cleanup;
+ // On a TPM that receives bytes on a port, the number of bytes that were
+ // received on that port is requestSize it must be identical to commandSize.
+ // In addition, commandSize must not be larger than MAX_COMMAND_SIZE allowed
+ // by the implementation. The check against MAX_COMMAND_SIZE may be redundant
+ // as the input processing (the function that receives the command bytes and
+ // places them in the input buffer) would likely have the input truncated when
+ // it reaches MAX_COMMAND_SIZE, and requestSize would not equal commandSize.
+ if(commandSize != requestSize || commandSize > MAX_COMMAND_SIZE)
+ {
+ result = TPM_RC_COMMAND_SIZE;
+ goto Cleanup;
+ }
+ // Unmarshal the command code.
+ result = TPM_CC_Unmarshal(&command.code, &command.parameterBuffer,
+ &command.parameterSize);
+ if(result != TPM_RC_SUCCESS)
+ goto Cleanup;
+ // Check to see if the command is implemented.
+ command.index = CommandCodeToCommandIndex(command.code);
+ if(UNIMPLEMENTED_COMMAND_INDEX == command.index)
+ {
+ result = TPM_RC_COMMAND_CODE;
+ goto Cleanup;
+ }
+#if FIELD_UPGRADE_IMPLEMENTED == YES
+ // If the TPM is in FUM, then the only allowed command is
+ // TPM_CC_FieldUpgradeData.
+ if(IsFieldUgradeMode() && (command.code != TPM_CC_FieldUpgradeData))
+ {
+ result = TPM_RC_UPGRADE;
+ goto Cleanup;
+ }
+ else
+#endif
+ // Excepting FUM, the TPM only accepts TPM2_Startup() after
+ // _TPM_Init. After getting a TPM2_Startup(), TPM2_Startup()
+ // is no longer allowed.
+ if((!TPMIsStarted() && command.code != TPM_CC_Startup)
+ || (TPMIsStarted() && command.code == TPM_CC_Startup))
+ {
+ result = TPM_RC_INITIALIZE;
+ goto Cleanup;
+ }
+ // Start regular command process.
+ NvIndexCacheInit();
+ // Parse Handle buffer.
+ result = ParseHandleBuffer(&command);
+ if(result != TPM_RC_SUCCESS)
+ goto Cleanup;
+ // All handles in the handle area are required to reference TPM-resident
+ // entities.
+ result = EntityGetLoadStatus(&command);
+ if(result != TPM_RC_SUCCESS)
+ goto Cleanup;
+ // Authorization session handling for the command.
+ ClearCpRpHashes(&command);
+ if(command.tag == TPM_ST_SESSIONS)
+ {
+ // Find out session buffer size.
+ result = UINT32_Unmarshal((UINT32 *)&command.authSize,
+ &command.parameterBuffer,
+ &command.parameterSize);
+ if(result != TPM_RC_SUCCESS)
+ goto Cleanup;
+ // Perform sanity check on the unmarshaled value. If it is smaller than
+ // the smallest possible session or larger than the remaining size of
+ // the command, then it is an error. NOTE: This check could pass but the
+ // session size could still be wrong. That will be determined after the
+ // sessions are unmarshaled.
+ if(command.authSize < 9
+ || command.authSize > command.parameterSize)
+ {
+ result = TPM_RC_SIZE;
+ goto Cleanup;
+ }
+ command.parameterSize -= command.authSize;
+ // The actions of ParseSessionBuffer() are described in the introduction.
+ // As the sessions are parsed command.parameterBuffer is advanced so, on a
+ // successful return, command.parameterBuffer should be pointing at the
+ // first byte of the parameters.
+ result = ParseSessionBuffer(&command);
+ if(result != TPM_RC_SUCCESS)
+ goto Cleanup;
+ }
+ else
+ {
+ command.authSize = 0;
+ // The command has no authorization sessions.
+ // If the command requires authorizations, then CheckAuthNoSession() will
+ // return an error.
+ result = CheckAuthNoSession(&command);
+ if(result != TPM_RC_SUCCESS)
+ goto Cleanup;
+ }
+ // Set up the response buffer pointers. CommandDispatch will marshal the
+ // response parameters starting at the address in command.responseBuffer.
+ // *response = MemoryGetResponseBuffer(command.index);
+ // leave space for the command header
+ command.responseBuffer = *response + STD_RESPONSE_HEADER;
+ // leave space for the parameter size field if needed
+ if(command.tag == TPM_ST_SESSIONS)
+ command.responseBuffer += sizeof(UINT32);
+ if(IsHandleInResponse(command.index))
+ command.responseBuffer += sizeof(TPM_HANDLE);
+ // CommandDispatcher returns a response handle buffer and a response parameter
+ // buffer if it succeeds. It will also set the parameterSize field in the
+ // buffer if the tag is TPM_RC_SESSIONS.
+ result = CommandDispatcher(&command);
+ if(result != TPM_RC_SUCCESS)
+ goto Cleanup;
+ // Build the session area at the end of the parameter area.
+ BuildResponseSession(&command);
+ Cleanup:
+ if(g_clearOrderly == TRUE
+ && NV_IS_ORDERLY)
+ {
+#if USE_DA_USED
+ gp.orderlyState = g_daUsed ? SU_DA_USED_VALUE : SU_NONE_VALUE;
+#else
+ gp.orderlyState = SU_NONE_VALUE;
+#endif
+ NV_SYNC_PERSISTENT(orderlyState);
+ }
+ // This implementation loads an "evict" object to a transient object slot in
+ // RAM whenever an "evict" object handle is used in a command so that the
+ // access to any object is the same. These temporary objects need to be
+ // cleared from RAM whether the command succeeds or fails.
+ ObjectCleanupEvict();
+ // The parameters and sessions have been marshaled. Now tack on the header and
+ // set the sizes
+ BuildResponseHeader(&command, *response, result);
+ // Try to commit all the writes to NV if any NV write happened during this
+ // command execution. This check should be made for both succeeded and failed
+ // commands, because a failed one may trigger a NV write in DA logic as well.
+ // This is the only place in the command execution path that may call the NV
+ // commit. If the NV commit fails, the TPM should be put in failure mode.
+ if((g_updateNV != UT_NONE) && !g_inFailureMode)
+ {
+ if(g_updateNV == UT_ORDERLY)
+ NvUpdateIndexOrderlyData();
+ if(!NvCommit())
+ FAIL(FATAL_ERROR_INTERNAL);
+ g_updateNV = UT_NONE;
+ }
+ pAssert((UINT32)command.parameterSize <= maxResponse);
+ // Clear unused bits in response buffer.
+ MemorySet(*response + *responseSize, 0, maxResponse - *responseSize);
+ // as a final act, and not before, update the response size.
+ *responseSize = (UINT32)command.parameterSize;
+ return;
+}