Vadim Bendebury | 5679752 | 2015-05-20 10:32:25 -0700 | [diff] [blame] | 1 | // This file was extracted from the TCG Published |
| 2 | // Trusted Platform Module Library |
| 3 | // Part 4: Supporting Routines |
| 4 | // Family "2.0" |
| 5 | // Level 00 Revision 01.16 |
| 6 | // October 30, 2014 |
| 7 | |
Vadim Bendebury | f5cc58b | 2015-05-31 14:09:19 -0700 | [diff] [blame] | 8 | #include "InternalRoutines.h" |
Jocelyn Bohr | 764a7d6 | 2015-07-30 12:57:09 -0700 | [diff] [blame] | 9 | #include "ExecCommand_fp.h" |
Vadim Bendebury | f5cc58b | 2015-05-31 14:09:19 -0700 | [diff] [blame] | 10 | #include "HandleProcess_fp.h" |
| 11 | #include "SessionProcess_fp.h" |
| 12 | #include "CommandDispatcher_fp.h" |
| 13 | // |
| 14 | // Uncomment this next #include if doing static command/response buffer sizing |
| 15 | // |
| 16 | // #include "CommandResponseSizes_fp.h" |
| 17 | // |
| 18 | // |
| 19 | // ExecuteCommand() |
| 20 | // |
| 21 | // The function performs the following steps. |
| 22 | // a) Parses the command header from input buffer. |
| 23 | // b) Calls ParseHandleBuffer() to parse the handle area of the command. |
| 24 | // c) Validates that each of the handles references a loaded entity. |
| 25 | // |
| 26 | // d) Calls ParseSessionBuffer() () to: |
| 27 | // 1) unmarshal and parse the session area; |
| 28 | // 2) check the authorizations; and |
| 29 | // 3) when necessary, decrypt a parameter. |
| 30 | // e) Calls CommandDispatcher() to: |
| 31 | // 1) unmarshal the command parameters from the command buffer; |
| 32 | // 2) call the routine that performs the command actions; and |
| 33 | // 3) marshal the responses into the response buffer. |
| 34 | // f) If any error occurs in any of the steps above create the error response and return. |
| 35 | // g) Calls BuildResponseSession() to: |
| 36 | // 1) when necessary, encrypt a parameter |
| 37 | // 2) build the response authorization sessions |
| 38 | // 3) update the audit sessions and nonces |
| 39 | // h) Assembles handle, parameter and session buffers for response and return. |
| 40 | // |
| 41 | LIB_EXPORT void |
| 42 | ExecuteCommand( |
| 43 | unsigned int requestSize, // IN: command buffer size |
| 44 | unsigned char *request, // IN: command buffer |
| 45 | unsigned int *responseSize, // OUT: response buffer size |
| 46 | unsigned char **response // OUT: response buffer |
| 47 | ) |
| 48 | { |
| 49 | // Command local variables |
| 50 | TPM_ST tag; // these first three variables are the |
| 51 | UINT32 commandSize; |
| 52 | TPM_CC commandCode = 0; |
| 53 | BYTE *parmBufferStart; // pointer to the first byte of an |
| 54 | // optional parameter buffer |
| 55 | UINT32 parmBufferSize = 0;// number of bytes in parameter area |
| 56 | UINT32 handleNum = 0; // number of handles unmarshaled into |
| 57 | // the handles array |
| 58 | TPM_HANDLE handles[MAX_HANDLE_NUM];// array to hold handles in the |
| 59 | // command. Only handles in the handle |
| 60 | // area are stored here, not handles |
| 61 | // passed as parameters. |
| 62 | // Response local variables |
| 63 | TPM_RC result; // return code for the command |
| 64 | TPM_ST resTag; // tag for the response |
| 65 | UINT32 resHandleSize = 0; // size of the handle area in the |
| 66 | // response. This is needed so that the |
| 67 | // handle area can be skipped when |
| 68 | // generating the rpHash. |
| 69 | UINT32 resParmSize = 0; // the size of the response parameters |
| 70 | // These values go in the rpHash. |
| 71 | UINT32 resAuthSize = 0; // size of authorization area in the |
| 72 | // |
| 73 | // response |
| 74 | INT32 size; // remaining data to be unmarshaled |
| 75 | // or remaining space in the marshaling |
| 76 | // buffer |
| 77 | BYTE *buffer; // pointer into the buffer being used |
| 78 | // for marshaling or unmarshaling |
Jocelyn Bohr | 32be404 | 2015-07-29 15:14:01 -0700 | [diff] [blame] | 79 | INT32 bufferSize; // size of buffer being used for |
| 80 | // marshaling or unmarshaling |
Vadim Bendebury | f5cc58b | 2015-05-31 14:09:19 -0700 | [diff] [blame] | 81 | UINT32 i; // local temp |
| 82 | // This next function call is used in development to size the command and response |
| 83 | // buffers. The values printed are the sizes of the internal structures and |
| 84 | // not the sizes of the canonical forms of the command response structures. Also, |
| 85 | // the sizes do not include the tag, commandCode, requestSize, or the authorization |
| 86 | // fields. |
| 87 | //CommandResponseSizes(); |
| 88 | // Set flags for NV access state. This should happen before any other |
| 89 | // operation that may require a NV write. Note, that this needs to be done |
| 90 | // even when in failure mode. Otherwise, g_updateNV would stay SET while in |
| 91 | // Failure mode and the NB would be written on each call. |
| 92 | g_updateNV = FALSE; |
| 93 | g_clearOrderly = FALSE; |
| 94 | // As of Sept 25, 2013, the failure mode handling has been incorporated in the |
| 95 | // reference code. This implementation requires that the system support |
| 96 | // setjmp/longjmp. This code is put here because of the complexity being |
| 97 | // added to the platform and simulator code to deal with all the variations |
| 98 | // of errors. |
| 99 | if(g_inFailureMode) |
| 100 | { |
| 101 | // Do failure mode processing |
| 102 | TpmFailureMode (requestSize, request, responseSize, response); |
| 103 | return; |
| 104 | } |
Vadim Bendebury | 7878aef | 2015-08-12 12:57:26 -0700 | [diff] [blame] | 105 | #ifndef EMBEDDED_MODE |
Vadim Bendebury | f5cc58b | 2015-05-31 14:09:19 -0700 | [diff] [blame] | 106 | if(setjmp(g_jumpBuffer) != 0) |
| 107 | { |
| 108 | // Get here if we got a longjump putting us into failure mode |
| 109 | g_inFailureMode = TRUE; |
| 110 | result = TPM_RC_FAILURE; |
| 111 | goto Fail; |
| 112 | } |
Vadim Bendebury | 7878aef | 2015-08-12 12:57:26 -0700 | [diff] [blame] | 113 | #endif // EMBEDDED_MODE ^^^ not defined |
Vadim Bendebury | f5cc58b | 2015-05-31 14:09:19 -0700 | [diff] [blame] | 114 | // Assume that everything is going to work. |
| 115 | result = TPM_RC_SUCCESS; |
| 116 | // Query platform to get the NV state. The result state is saved internally |
| 117 | // and will be reported by NvIsAvailable(). The reference code requires that |
| 118 | // accessibility of NV does not change during the execution of a command. |
| 119 | // Specifically, if NV is available when the command execution starts and then |
| 120 | // is not available later when it is necessary to write to NV, then the TPM |
| 121 | // will go into failure mode. |
| 122 | NvCheckState(); |
| 123 | // Due to the limitations of the simulation, TPM clock must be explicitly |
| 124 | // synchronized with the system clock whenever a command is received. |
| 125 | // This function call is not necessary in a hardware TPM. However, taking |
| 126 | // a snapshot of the hardware timer at the beginning of the command allows |
| 127 | // the time value to be consistent for the duration of the command execution. |
| 128 | TimeUpdateToCurrent(); |
| 129 | // Any command through this function will unceremoniously end the |
| 130 | // _TPM_Hash_Data/_TPM_Hash_End sequence. |
| 131 | if(g_DRTMHandle != TPM_RH_UNASSIGNED) |
| 132 | ObjectTerminateEvent(); |
| 133 | // Get command buffer size and command buffer. |
| 134 | size = requestSize; |
| 135 | buffer = request; |
| 136 | // Parse command header: tag, commandSize and commandCode. |
| 137 | // First parse the tag. The unmarshaling routine will validate |
| 138 | // that it is either TPM_ST_SESSIONS or TPM_ST_NO_SESSIONS. |
| 139 | result = TPMI_ST_COMMAND_TAG_Unmarshal(&tag, &buffer, &size); |
| 140 | if(result != TPM_RC_SUCCESS) |
| 141 | goto Cleanup; |
| 142 | // Unmarshal the commandSize indicator. |
| 143 | result = UINT32_Unmarshal(&commandSize, &buffer, &size); |
| 144 | if(result != TPM_RC_SUCCESS) |
| 145 | goto Cleanup; |
| 146 | // On a TPM that receives bytes on a port, the number of bytes that were |
| 147 | // received on that port is requestSize it must be identical to commandSize. |
| 148 | // In addition, commandSize must not be larger than MAX_COMMAND_SIZE allowed |
| 149 | // by the implementation. The check against MAX_COMMAND_SIZE may be redundant |
| 150 | // as the input processing (the function that receives the command bytes and |
| 151 | // places them in the input buffer) would likely have the input truncated when |
| 152 | // it reaches MAX_COMMAND_SIZE, and requestSize would not equal commandSize. |
| 153 | if(commandSize != requestSize || commandSize > MAX_COMMAND_SIZE) |
| 154 | { |
| 155 | result = TPM_RC_COMMAND_SIZE; |
| 156 | goto Cleanup; |
| 157 | } |
| 158 | // Unmarshal the command code. |
| 159 | result = TPM_CC_Unmarshal(&commandCode, &buffer, &size); |
| 160 | if(result != TPM_RC_SUCCESS) |
| 161 | goto Cleanup; |
| 162 | // Check to see if the command is implemented. |
| 163 | if(!CommandIsImplemented(commandCode)) |
| 164 | { |
| 165 | result = TPM_RC_COMMAND_CODE; |
| 166 | goto Cleanup; |
| 167 | } |
| 168 | #if FIELD_UPGRADE_IMPLEMENTED == YES |
| 169 | // If the TPM is in FUM, then the only allowed command is |
| 170 | // TPM_CC_FieldUpgradeData. |
| 171 | if(IsFieldUgradeMode() && (commandCode != TPM_CC_FieldUpgradeData)) |
| 172 | { |
| 173 | result = TPM_RC_UPGRADE; |
| 174 | goto Cleanup; |
| 175 | } |
| 176 | else |
| 177 | #endif |
| 178 | // Excepting FUM, the TPM only accepts TPM2_Startup() after |
| 179 | // _TPM_Init. After getting a TPM2_Startup(), TPM2_Startup() |
| 180 | // is no longer allowed. |
| 181 | if(( !TPMIsStarted() && commandCode != TPM_CC_Startup) |
| 182 | || (TPMIsStarted() && commandCode == TPM_CC_Startup)) |
| 183 | { |
| 184 | result = TPM_RC_INITIALIZE; |
| 185 | goto Cleanup; |
| 186 | } |
| 187 | // Start regular command process. |
| 188 | // Parse Handle buffer. |
| 189 | result = ParseHandleBuffer(commandCode, &buffer, &size, handles, &handleNum); |
| 190 | if(result != TPM_RC_SUCCESS) |
| 191 | goto Cleanup; |
| 192 | // Number of handles retrieved from handle area should be less than |
| 193 | // MAX_HANDLE_NUM. |
| 194 | pAssert(handleNum <= MAX_HANDLE_NUM); |
| 195 | // All handles in the handle area are required to reference TPM-resident |
| 196 | // entities. |
| 197 | for(i = 0; i < handleNum; i++) |
| 198 | { |
| 199 | result = EntityGetLoadStatus(&handles[i], commandCode); |
| 200 | if(result != TPM_RC_SUCCESS) |
| 201 | { |
| 202 | if(result == TPM_RC_REFERENCE_H0) |
| 203 | result = result + i; |
| 204 | else |
| 205 | result = RcSafeAddToResult(result, TPM_RC_H + g_rcIndex[i]); |
| 206 | goto Cleanup; |
| 207 | } |
| 208 | } |
| 209 | // Authorization session handling for the command. |
| 210 | if(tag == TPM_ST_SESSIONS) |
| 211 | { |
| 212 | BYTE *sessionBufferStart;// address of the session area first byte |
| 213 | // in the input buffer |
| 214 | UINT32 authorizationSize; // number of bytes in the session area |
| 215 | // Find out session buffer size. |
| 216 | result = UINT32_Unmarshal(&authorizationSize, &buffer, &size); |
| 217 | if(result != TPM_RC_SUCCESS) |
| 218 | goto Cleanup; |
| 219 | // Perform sanity check on the unmarshaled value. If it is smaller than |
| 220 | // the smallest possible session or larger than the remaining size of |
| 221 | // the command, then it is an error. NOTE: This check could pass but the |
| 222 | // session size could still be wrong. That will be determined after the |
| 223 | // sessions are unmarshaled. |
| 224 | if( authorizationSize < 9 |
| 225 | || authorizationSize > (UINT32) size) |
| 226 | { |
| 227 | result = TPM_RC_SIZE; |
| 228 | goto Cleanup; |
| 229 | } |
| 230 | // The sessions, if any, follows authorizationSize. |
| 231 | sessionBufferStart = buffer; |
| 232 | // The parameters follow the session area. |
| 233 | parmBufferStart = sessionBufferStart + authorizationSize; |
| 234 | // Any data left over after removing the authorization sessions is |
| 235 | // parameter data. If the command does not have parameters, then an |
| 236 | // error will be returned if the remaining size is not zero. This is |
| 237 | // checked later. |
| 238 | parmBufferSize = size - authorizationSize; |
| 239 | // The actions of ParseSessionBuffer() are described in the introduction. |
| 240 | result = ParseSessionBuffer(commandCode, |
| 241 | handleNum, |
| 242 | handles, |
| 243 | sessionBufferStart, |
| 244 | authorizationSize, |
| 245 | parmBufferStart, |
| 246 | parmBufferSize); |
| 247 | if(result != TPM_RC_SUCCESS) |
| 248 | goto Cleanup; |
| 249 | } |
| 250 | else |
| 251 | { |
| 252 | // Whatever remains in the input buffer is used for the parameters of the |
| 253 | // command. |
| 254 | parmBufferStart = buffer; |
| 255 | parmBufferSize = size; |
| 256 | // The command has no authorization sessions. |
| 257 | // If the command requires authorizations, then CheckAuthNoSession() will |
| 258 | // return an error. |
| 259 | result = CheckAuthNoSession(commandCode, handleNum, handles, |
| 260 | parmBufferStart, parmBufferSize); |
| 261 | if(result != TPM_RC_SUCCESS) |
| 262 | goto Cleanup; |
| 263 | } |
| 264 | // CommandDispatcher returns a response handle buffer and a response parameter |
| 265 | // buffer if it succeeds. It will also set the parameterSize field in the |
| 266 | // buffer if the tag is TPM_RC_SESSIONS. |
| 267 | result = CommandDispatcher(tag, |
| 268 | commandCode, |
| 269 | (INT32 *) &parmBufferSize, |
| 270 | parmBufferStart, |
| 271 | handles, |
| 272 | &resHandleSize, |
| 273 | &resParmSize); |
| 274 | if(result != TPM_RC_SUCCESS) |
| 275 | goto Cleanup; |
| 276 | // Build the session area at the end of the parameter area. |
| 277 | BuildResponseSession(tag, |
| 278 | commandCode, |
| 279 | resHandleSize, |
| 280 | resParmSize, |
| 281 | &resAuthSize); |
| 282 | Cleanup: |
| 283 | // This implementation loads an "evict" object to a transient object slot in |
| 284 | // RAM whenever an "evict" object handle is used in a command so that the |
| 285 | // access to any object is the same. These temporary objects need to be |
| 286 | // cleared from RAM whether the command succeeds or fails. |
| 287 | ObjectCleanupEvict(); |
Vadim Bendebury | 7878aef | 2015-08-12 12:57:26 -0700 | [diff] [blame] | 288 | #ifndef EMBEDDED_MODE |
Vadim Bendebury | f5cc58b | 2015-05-31 14:09:19 -0700 | [diff] [blame] | 289 | Fail: |
Vadim Bendebury | 7878aef | 2015-08-12 12:57:26 -0700 | [diff] [blame] | 290 | #endif // EMBEDDED_MODE ^^^ not defined |
Vadim Bendebury | f5cc58b | 2015-05-31 14:09:19 -0700 | [diff] [blame] | 291 | // The response will contain at least a response header. |
| 292 | *responseSize = sizeof(TPM_ST) + sizeof(UINT32) + sizeof(TPM_RC); |
| 293 | // If the command completed successfully, then build the rest of the response. |
| 294 | if(result == TPM_RC_SUCCESS) |
| 295 | { |
| 296 | // Outgoing tag will be the same as the incoming tag. |
| 297 | resTag = tag; |
| 298 | // The overall response will include the handles, parameters, |
| 299 | // and authorizations. |
| 300 | *responseSize += resHandleSize + resParmSize + resAuthSize; |
| 301 | // Adding parameter size field. |
| 302 | if(tag == TPM_ST_SESSIONS) |
| 303 | *responseSize += sizeof(UINT32); |
| 304 | if( g_clearOrderly == TRUE |
| 305 | && gp.orderlyState != SHUTDOWN_NONE) |
| 306 | { |
| 307 | gp.orderlyState = SHUTDOWN_NONE; |
| 308 | NvWriteReserved(NV_ORDERLY, &gp.orderlyState); |
| 309 | g_updateNV = TRUE; |
| 310 | } |
| 311 | } |
| 312 | else |
| 313 | { |
| 314 | // The command failed. |
| 315 | // If this was a failure due to a bad command tag, then need to return |
| 316 | // a TPM 1.2 compatible response |
| 317 | if(result == TPM_RC_BAD_TAG) |
| 318 | resTag = TPM_ST_RSP_COMMAND; |
| 319 | else |
| 320 | // return 2.0 compatible response |
| 321 | resTag = TPM_ST_NO_SESSIONS; |
| 322 | } |
| 323 | // Try to commit all the writes to NV if any NV write happened during this |
| 324 | // command execution. This check should be made for both succeeded and failed |
| 325 | // commands, because a failed one may trigger a NV write in DA logic as well. |
| 326 | // This is the only place in the command execution path that may call the NV |
| 327 | // commit. If the NV commit fails, the TPM should be put in failure mode. |
| 328 | if(g_updateNV && !g_inFailureMode) |
| 329 | { |
| 330 | g_updateNV = FALSE; |
Bill Richardson | 70ac07b | 2016-09-28 11:17:18 -0700 | [diff] [blame] | 331 | if(!NvCommit()) { |
Vadim Bendebury | f5cc58b | 2015-05-31 14:09:19 -0700 | [diff] [blame] | 332 | FAIL(FATAL_ERROR_INTERNAL); |
Bill Richardson | 70ac07b | 2016-09-28 11:17:18 -0700 | [diff] [blame] | 333 | #ifdef EMBEDDED_MODE |
| 334 | // Make sure we pass errors along |
| 335 | result = TPM_RC_FAILURE; |
| 336 | resTag = TPM_ST_NO_SESSIONS; |
| 337 | #endif |
| 338 | } |
Vadim Bendebury | f5cc58b | 2015-05-31 14:09:19 -0700 | [diff] [blame] | 339 | } |
| 340 | // Marshal the response header. |
| 341 | buffer = MemoryGetResponseBuffer(commandCode); |
Jocelyn Bohr | 32be404 | 2015-07-29 15:14:01 -0700 | [diff] [blame] | 342 | bufferSize = 10; |
| 343 | TPM_ST_Marshal(&resTag, &buffer, &bufferSize); |
| 344 | UINT32_Marshal((UINT32 *)responseSize, &buffer, &bufferSize); |
Vadim Bendebury | f5cc58b | 2015-05-31 14:09:19 -0700 | [diff] [blame] | 345 | pAssert(*responseSize <= MAX_RESPONSE_SIZE); |
Jocelyn Bohr | 32be404 | 2015-07-29 15:14:01 -0700 | [diff] [blame] | 346 | TPM_RC_Marshal(&result, &buffer, &bufferSize); |
Vadim Bendebury | f5cc58b | 2015-05-31 14:09:19 -0700 | [diff] [blame] | 347 | *response = MemoryGetResponseBuffer(commandCode); |
| 348 | // Clear unused bit in response buffer. |
| 349 | MemorySet(*response + *responseSize, 0, MAX_RESPONSE_SIZE - *responseSize); |
| 350 | return; |
| 351 | } |