// 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; }