You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
351 lines
16 KiB
351 lines
16 KiB
// This file was extracted from the TCG Published |
|
// Trusted Platform Module Library |
|
// Part 4: Supporting Routines |
|
// Family "2.0" |
|
// Level 00 Revision 01.16 |
|
// October 30, 2014 |
|
|
|
#include "InternalRoutines.h" |
|
#include "ExecCommand_fp.h" |
|
#include "HandleProcess_fp.h" |
|
#include "SessionProcess_fp.h" |
|
#include "CommandDispatcher_fp.h" |
|
// |
|
// Uncomment this next #include if doing static command/response buffer sizing |
|
// |
|
// #include "CommandResponseSizes_fp.h" |
|
// |
|
// |
|
// ExecuteCommand() |
|
// |
|
// 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) Assembles handle, parameter and session buffers for response and return. |
|
// |
|
LIB_EXPORT void |
|
ExecuteCommand( |
|
unsigned int requestSize, // IN: command buffer size |
|
unsigned char *request, // IN: command buffer |
|
unsigned int *responseSize, // OUT: response buffer size |
|
unsigned char **response // OUT: response buffer |
|
) |
|
{ |
|
// Command local variables |
|
TPM_ST tag; // these first three variables are the |
|
UINT32 commandSize; |
|
TPM_CC commandCode = 0; |
|
BYTE *parmBufferStart; // pointer to the first byte of an |
|
// optional parameter buffer |
|
UINT32 parmBufferSize = 0;// number of bytes in parameter area |
|
UINT32 handleNum = 0; // number of handles unmarshaled into |
|
// the handles array |
|
TPM_HANDLE handles[MAX_HANDLE_NUM];// array to hold handles in the |
|
// command. Only handles in the handle |
|
// area are stored here, not handles |
|
// passed as parameters. |
|
// Response local variables |
|
TPM_RC result; // return code for the command |
|
TPM_ST resTag; // tag for the response |
|
UINT32 resHandleSize = 0; // size of the handle area in the |
|
// response. This is needed so that the |
|
// handle area can be skipped when |
|
// generating the rpHash. |
|
UINT32 resParmSize = 0; // the size of the response parameters |
|
// These values go in the rpHash. |
|
UINT32 resAuthSize = 0; // size of authorization area in the |
|
// |
|
// response |
|
INT32 size; // remaining data to be unmarshaled |
|
// or remaining space in the marshaling |
|
// buffer |
|
BYTE *buffer; // pointer into the buffer being used |
|
// for marshaling or unmarshaling |
|
INT32 bufferSize; // size of buffer being used for |
|
// marshaling or unmarshaling |
|
UINT32 i; // local temp |
|
// 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 the command response structures. Also, |
|
// the sizes do not include the tag, commandCode, 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 NB would be written on each call. |
|
g_updateNV = FALSE; |
|
g_clearOrderly = FALSE; |
|
// As of Sept 25, 2013, the failure mode handling has been incorporated in the |
|
// reference code. This implementation requires that the system support |
|
// setjmp/longjmp. This code is put here because of the complexity being |
|
// added to the platform and simulator code to deal with all the variations |
|
// of errors. |
|
if(g_inFailureMode) |
|
{ |
|
// Do failure mode processing |
|
TpmFailureMode (requestSize, request, responseSize, response); |
|
return; |
|
} |
|
#ifndef EMBEDDED_MODE |
|
if(setjmp(g_jumpBuffer) != 0) |
|
{ |
|
// Get here if we got a longjump putting us into failure mode |
|
g_inFailureMode = TRUE; |
|
result = TPM_RC_FAILURE; |
|
goto Fail; |
|
} |
|
#endif // EMBEDDED_MODE ^^^ not defined |
|
// Assume that everything is going to work. |
|
result = TPM_RC_SUCCESS; |
|
// 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. |
|
size = requestSize; |
|
buffer = request; |
|
// Parse command header: tag, commandSize and commandCode. |
|
// 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(&tag, &buffer, &size); |
|
if(result != TPM_RC_SUCCESS) |
|
goto Cleanup; |
|
// Unmarshal the commandSize indicator. |
|
result = UINT32_Unmarshal(&commandSize, &buffer, &size); |
|
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(&commandCode, &buffer, &size); |
|
if(result != TPM_RC_SUCCESS) |
|
goto Cleanup; |
|
// Check to see if the command is implemented. |
|
if(!CommandIsImplemented(commandCode)) |
|
{ |
|
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() && (commandCode != 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() && commandCode != TPM_CC_Startup) |
|
|| (TPMIsStarted() && commandCode == TPM_CC_Startup)) |
|
{ |
|
result = TPM_RC_INITIALIZE; |
|
goto Cleanup; |
|
} |
|
// Start regular command process. |
|
// Parse Handle buffer. |
|
result = ParseHandleBuffer(commandCode, &buffer, &size, handles, &handleNum); |
|
if(result != TPM_RC_SUCCESS) |
|
goto Cleanup; |
|
// Number of handles retrieved from handle area should be less than |
|
// MAX_HANDLE_NUM. |
|
pAssert(handleNum <= MAX_HANDLE_NUM); |
|
// All handles in the handle area are required to reference TPM-resident |
|
// entities. |
|
for(i = 0; i < handleNum; i++) |
|
{ |
|
result = EntityGetLoadStatus(&handles[i], commandCode); |
|
if(result != TPM_RC_SUCCESS) |
|
{ |
|
if(result == TPM_RC_REFERENCE_H0) |
|
result = result + i; |
|
else |
|
result = RcSafeAddToResult(result, TPM_RC_H + g_rcIndex[i]); |
|
goto Cleanup; |
|
} |
|
} |
|
// Authorization session handling for the command. |
|
if(tag == TPM_ST_SESSIONS) |
|
{ |
|
BYTE *sessionBufferStart;// address of the session area first byte |
|
// in the input buffer |
|
UINT32 authorizationSize; // number of bytes in the session area |
|
// Find out session buffer size. |
|
result = UINT32_Unmarshal(&authorizationSize, &buffer, &size); |
|
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( authorizationSize < 9 |
|
|| authorizationSize > (UINT32) size) |
|
{ |
|
result = TPM_RC_SIZE; |
|
goto Cleanup; |
|
} |
|
// The sessions, if any, follows authorizationSize. |
|
sessionBufferStart = buffer; |
|
// The parameters follow the session area. |
|
parmBufferStart = sessionBufferStart + authorizationSize; |
|
// Any data left over after removing the authorization sessions is |
|
// parameter data. If the command does not have parameters, then an |
|
// error will be returned if the remaining size is not zero. This is |
|
// checked later. |
|
parmBufferSize = size - authorizationSize; |
|
// The actions of ParseSessionBuffer() are described in the introduction. |
|
result = ParseSessionBuffer(commandCode, |
|
handleNum, |
|
handles, |
|
sessionBufferStart, |
|
authorizationSize, |
|
parmBufferStart, |
|
parmBufferSize); |
|
if(result != TPM_RC_SUCCESS) |
|
goto Cleanup; |
|
} |
|
else |
|
{ |
|
// Whatever remains in the input buffer is used for the parameters of the |
|
// command. |
|
parmBufferStart = buffer; |
|
parmBufferSize = size; |
|
// The command has no authorization sessions. |
|
// If the command requires authorizations, then CheckAuthNoSession() will |
|
// return an error. |
|
result = CheckAuthNoSession(commandCode, handleNum, handles, |
|
parmBufferStart, parmBufferSize); |
|
if(result != TPM_RC_SUCCESS) |
|
goto Cleanup; |
|
} |
|
// 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(tag, |
|
commandCode, |
|
(INT32 *) &parmBufferSize, |
|
parmBufferStart, |
|
handles, |
|
&resHandleSize, |
|
&resParmSize); |
|
if(result != TPM_RC_SUCCESS) |
|
goto Cleanup; |
|
// Build the session area at the end of the parameter area. |
|
BuildResponseSession(tag, |
|
commandCode, |
|
resHandleSize, |
|
resParmSize, |
|
&resAuthSize); |
|
Cleanup: |
|
// 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(); |
|
#ifndef EMBEDDED_MODE |
|
Fail: |
|
#endif // EMBEDDED_MODE ^^^ not defined |
|
// The response will contain at least a response header. |
|
*responseSize = sizeof(TPM_ST) + sizeof(UINT32) + sizeof(TPM_RC); |
|
// If the command completed successfully, then build the rest of the response. |
|
if(result == TPM_RC_SUCCESS) |
|
{ |
|
// Outgoing tag will be the same as the incoming tag. |
|
resTag = tag; |
|
// The overall response will include the handles, parameters, |
|
// and authorizations. |
|
*responseSize += resHandleSize + resParmSize + resAuthSize; |
|
// Adding parameter size field. |
|
if(tag == TPM_ST_SESSIONS) |
|
*responseSize += sizeof(UINT32); |
|
if( g_clearOrderly == TRUE |
|
&& gp.orderlyState != SHUTDOWN_NONE) |
|
{ |
|
gp.orderlyState = SHUTDOWN_NONE; |
|
NvWriteReserved(NV_ORDERLY, &gp.orderlyState); |
|
g_updateNV = TRUE; |
|
} |
|
} |
|
else |
|
{ |
|
// The command failed. |
|
// If this was a failure due to a bad command tag, then need to return |
|
// a TPM 1.2 compatible response |
|
if(result == TPM_RC_BAD_TAG) |
|
resTag = TPM_ST_RSP_COMMAND; |
|
else |
|
// return 2.0 compatible response |
|
resTag = TPM_ST_NO_SESSIONS; |
|
} |
|
// 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 && !g_inFailureMode) |
|
{ |
|
g_updateNV = FALSE; |
|
if(!NvCommit()) { |
|
FAIL(FATAL_ERROR_INTERNAL); |
|
#ifdef EMBEDDED_MODE |
|
// Make sure we pass errors along |
|
result = TPM_RC_FAILURE; |
|
resTag = TPM_ST_NO_SESSIONS; |
|
#endif |
|
} |
|
} |
|
// Marshal the response header. |
|
buffer = MemoryGetResponseBuffer(commandCode); |
|
bufferSize = 10; |
|
TPM_ST_Marshal(&resTag, &buffer, &bufferSize); |
|
UINT32_Marshal((UINT32 *)responseSize, &buffer, &bufferSize); |
|
pAssert(*responseSize <= MAX_RESPONSE_SIZE); |
|
TPM_RC_Marshal(&result, &buffer, &bufferSize); |
|
*response = MemoryGetResponseBuffer(commandCode); |
|
// Clear unused bit in response buffer. |
|
MemorySet(*response + *responseSize, 0, MAX_RESPONSE_SIZE - *responseSize); |
|
return; |
|
}
|
|
|