blob: 7e7063200cf90bfb982c57b151e070127d8fb91c [file] [log] [blame]
/* Microsoft Reference Implementation for TPM 2.0
*
* The copyright in this software is being made available under the BSD License,
* included below. This software may be subject to other third party and
* contributor rights, including patent rights, and no such rights are granted
* under this license.
*
* Copyright (c) Microsoft Corporation
*
* All rights reserved.
*
* BSD License
*
* 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.
*
* 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.
*/
//** Introduction
//
// This file contains the entry function ExecuteCommand() which provides the main
// control flow for TPM command execution.
//** Includes
#include "Tpm.h"
#include "ExecCommand_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) 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 the 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;
}