Coverage Report

Created: 2025-07-09 06:07

/src/tpm2/ExecCommand.c
Line
Count
Source (jump to first uncovered line)
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
8
#include     "InternalRoutines.h"
9
#include     "ExecCommand_fp.h"
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
888
{
49
    // Command local variables
50
888
    TPM_ST               tag;                         // these first three variables are the
51
888
    UINT32               commandSize;
52
888
    TPM_CC               commandCode = 0;
53
888
    BYTE                     *parmBufferStart;        // pointer to the first byte of an
54
                                                      // optional parameter buffer
55
888
    UINT32                    parmBufferSize = 0;// number of bytes in parameter area
56
888
    UINT32                    handleNum = 0;          // number of handles unmarshaled into
57
                                                      // the handles array
58
888
    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
888
    TPM_RC               result;                      // return code for the command
64
888
    TPM_ST                    resTag;                 // tag for the response
65
888
    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
888
    UINT32                    resParmSize = 0;        // the size of the response parameters
70
                                                      // These values go in the rpHash.
71
888
    UINT32                    resAuthSize = 0;        // size of authorization area in the
72
//
73
                                                   // response
74
888
   INT32                      size;                // remaining data to be unmarshaled
75
                                                   // or remaining space in the marshaling
76
                                                   // buffer
77
888
   BYTE                      *buffer;              // pointer into the buffer being used
78
                                                   // for marshaling or unmarshaling
79
888
   INT32                      bufferSize;          // size of buffer being used for
80
                                                   // marshaling or unmarshaling
81
888
   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
888
   g_updateNV = FALSE;
93
888
   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
888
   if(g_inFailureMode)
100
0
   {
101
       // Do failure mode processing
102
0
       TpmFailureMode (requestSize, request, responseSize, response);
103
0
       return;
104
0
   }
105
888
#ifndef EMBEDDED_MODE
106
888
   if(setjmp(g_jumpBuffer) != 0)
107
0
   {
108
       // Get here if we got a longjump putting us into failure mode
109
0
       g_inFailureMode = TRUE;
110
0
       result = TPM_RC_FAILURE;
111
0
       goto Fail;
112
0
   }
113
888
#endif  // EMBEDDED_MODE   ^^^ not defined
114
   // Assume that everything is going to work.
115
888
   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
888
   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
888
   TimeUpdateToCurrent();
129
   // Any command through this function will unceremoniously end the
130
   // _TPM_Hash_Data/_TPM_Hash_End sequence.
131
888
   if(g_DRTMHandle != TPM_RH_UNASSIGNED)
132
0
       ObjectTerminateEvent();
133
     // Get command buffer size and command buffer.
134
888
     size = requestSize;
135
888
     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
888
     result = TPMI_ST_COMMAND_TAG_Unmarshal(&tag, &buffer, &size);
140
888
     if(result != TPM_RC_SUCCESS)
141
5
         goto Cleanup;
142
     // Unmarshal the commandSize indicator.
143
883
     result = UINT32_Unmarshal(&commandSize, &buffer, &size);
144
883
     if(result != TPM_RC_SUCCESS)
145
0
         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
883
     if(commandSize != requestSize || commandSize > MAX_COMMAND_SIZE)
154
13
     {
155
13
         result = TPM_RC_COMMAND_SIZE;
156
13
         goto Cleanup;
157
13
     }
158
     // Unmarshal the command code.
159
870
     result = TPM_CC_Unmarshal(&commandCode, &buffer, &size);
160
870
     if(result != TPM_RC_SUCCESS)
161
4
         goto Cleanup;
162
     // Check to see if the command is implemented.
163
866
     if(!CommandIsImplemented(commandCode))
164
0
     {
165
0
         result = TPM_RC_COMMAND_CODE;
166
0
         goto Cleanup;
167
0
     }
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
866
        if((    !TPMIsStarted() && commandCode != TPM_CC_Startup)
182
866
             || (TPMIsStarted() && commandCode == TPM_CC_Startup))
183
0
        {
184
0
             result = TPM_RC_INITIALIZE;
185
0
             goto Cleanup;
186
0
        }
187
     // Start regular command process.
188
     // Parse Handle buffer.
189
866
     result = ParseHandleBuffer(commandCode, &buffer, &size, handles, &handleNum);
190
866
     if(result != TPM_RC_SUCCESS)
191
18
        goto Cleanup;
192
   // Number of handles retrieved from handle area should be less than
193
   // MAX_HANDLE_NUM.
194
848
   pAssert(handleNum <= MAX_HANDLE_NUM);
195
   // All handles in the handle area are required to reference TPM-resident
196
   // entities.
197
1.08k
   for(i = 0; i < handleNum; i++)
198
240
   {
199
240
       result = EntityGetLoadStatus(&handles[i], commandCode);
200
240
       if(result != TPM_RC_SUCCESS)
201
1
       {
202
1
           if(result == TPM_RC_REFERENCE_H0)
203
0
               result = result + i;
204
1
           else
205
1
               result = RcSafeAddToResult(result, TPM_RC_H + g_rcIndex[i]);
206
1
           goto Cleanup;
207
1
       }
208
240
   }
209
   // Authorization session handling for the command.
210
847
   if(tag == TPM_ST_SESSIONS)
211
221
   {
212
221
       BYTE        *sessionBufferStart;// address of the session area first byte
213
                                       // in the input buffer
214
221
        UINT32        authorizationSize;   // number of bytes in the session area
215
        // Find out session buffer size.
216
221
        result = UINT32_Unmarshal(&authorizationSize, &buffer, &size);
217
221
        if(result != TPM_RC_SUCCESS)
218
0
            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
221
        if(    authorizationSize < 9
225
221
            || authorizationSize > (UINT32) size)
226
0
        {
227
0
             result = TPM_RC_SIZE;
228
0
             goto Cleanup;
229
0
        }
230
        // The sessions, if any, follows authorizationSize.
231
221
        sessionBufferStart = buffer;
232
        // The parameters follow the session area.
233
221
        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
221
        parmBufferSize = size - authorizationSize;
239
        // The actions of ParseSessionBuffer() are described in the introduction.
240
221
        result = ParseSessionBuffer(commandCode,
241
221
                                    handleNum,
242
221
                                    handles,
243
221
                                    sessionBufferStart,
244
221
                                    authorizationSize,
245
221
                                    parmBufferStart,
246
221
                                    parmBufferSize);
247
221
         if(result != TPM_RC_SUCCESS)
248
17
             goto Cleanup;
249
221
   }
250
626
   else
251
626
   {
252
       // Whatever remains in the input buffer is used for the parameters of the
253
       // command.
254
626
       parmBufferStart = buffer;
255
626
       parmBufferSize = size;
256
         // The command has no authorization sessions.
257
         // If the command requires authorizations, then CheckAuthNoSession() will
258
         // return an error.
259
626
         result = CheckAuthNoSession(commandCode, handleNum, handles,
260
626
                                      parmBufferStart, parmBufferSize);
261
626
         if(result != TPM_RC_SUCCESS)
262
0
             goto Cleanup;
263
626
   }
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
830
   result = CommandDispatcher(tag,
268
830
                              commandCode,
269
830
                              (INT32 *) &parmBufferSize,
270
830
                              parmBufferStart,
271
830
                              handles,
272
830
                              &resHandleSize,
273
830
                              &resParmSize);
274
830
   if(result != TPM_RC_SUCCESS)
275
279
       goto Cleanup;
276
   // Build the session area at the end of the parameter area.
277
551
   BuildResponseSession(tag,
278
551
                        commandCode,
279
551
                        resHandleSize,
280
551
                        resParmSize,
281
551
                        &resAuthSize);
282
888
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
888
   ObjectCleanupEvict();
288
888
#ifndef EMBEDDED_MODE
289
888
Fail:
290
888
#endif  // EMBEDDED_MODE  ^^^ not defined
291
   // The response will contain at least a response header.
292
888
   *responseSize = sizeof(TPM_ST) + sizeof(UINT32) + sizeof(TPM_RC);
293
   // If the command completed successfully, then build the rest of the response.
294
888
   if(result == TPM_RC_SUCCESS)
295
551
   {
296
       // Outgoing tag will be the same as the incoming tag.
297
551
       resTag = tag;
298
       // The overall response will include the handles, parameters,
299
       // and authorizations.
300
551
       *responseSize += resHandleSize + resParmSize + resAuthSize;
301
         // Adding parameter size field.
302
551
         if(tag == TPM_ST_SESSIONS)
303
101
             *responseSize += sizeof(UINT32);
304
551
         if(      g_clearOrderly == TRUE
305
551
               && gp.orderlyState != SHUTDOWN_NONE)
306
0
         {
307
0
                gp.orderlyState = SHUTDOWN_NONE;
308
0
                NvWriteReserved(NV_ORDERLY, &gp.orderlyState);
309
0
                g_updateNV = TRUE;
310
0
         }
311
551
     }
312
337
     else
313
337
     {
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
337
         if(result == TPM_RC_BAD_TAG)
318
5
              resTag = TPM_ST_RSP_COMMAND;
319
332
         else
320
              // return 2.0 compatible response
321
332
              resTag = TPM_ST_NO_SESSIONS;
322
337
     }
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
888
     if(g_updateNV && !g_inFailureMode)
329
446
     {
330
446
         g_updateNV = FALSE;
331
446
         if(!NvCommit()) {
332
0
              FAIL(FATAL_ERROR_INTERNAL);
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
0
   }
339
446
     }
340
     // Marshal the response header.
341
888
     buffer = MemoryGetResponseBuffer(commandCode);
342
888
     bufferSize = 10;
343
888
     TPM_ST_Marshal(&resTag, &buffer, &bufferSize);
344
888
     UINT32_Marshal((UINT32 *)responseSize, &buffer, &bufferSize);
345
888
     pAssert(*responseSize <= MAX_RESPONSE_SIZE);
346
888
     TPM_RC_Marshal(&result, &buffer, &bufferSize);
347
888
     *response = MemoryGetResponseBuffer(commandCode);
348
     // Clear unused bit in response buffer.
349
888
     MemorySet(*response + *responseSize, 0, MAX_RESPONSE_SIZE - *responseSize);
350
888
     return;
351
888
}