blob: b09ea64a6d30c7b3cb4f18baa0657238e987e792 [file] [log] [blame]
Gaurav Shahce0cc302010-03-24 13:48:55 -07001/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 *
5 * Functions for querying, manipulating and locking rollback indices
6 * stored in the TPM NVRAM.
7 */
8
9#include "rollback_index.h"
10
11#include <stdint.h>
Gaurav Shahce0cc302010-03-24 13:48:55 -070012
Gaurav Shahf3dd1a62010-04-05 15:50:00 -070013#include "utility.h"
Gaurav Shahce0cc302010-03-24 13:48:55 -070014#include "tlcl.h"
Gaurav Shah887e3d42010-04-27 16:26:48 -070015#include "tss_constants.h"
Gaurav Shahce0cc302010-03-24 13:48:55 -070016
17uint16_t g_firmware_key_version = 0;
18uint16_t g_firmware_version = 0;
19uint16_t g_kernel_key_version = 0;
20uint16_t g_kernel_version = 0;
21
Luigi Semenzato4f11c362010-06-10 11:01:04 -070022#define RETURN_ON_FAILURE(tpm_command) do { \
23 uint32_t result; \
24 if ((result = (tpm_command)) != TPM_SUCCESS) { \
25 return result; \
26 } \
Luigi Semenzato59204c52010-06-09 13:37:15 -070027 } while (0)
28
Luigi Semenzato4f11c362010-06-10 11:01:04 -070029static uint32_t InitializeKernelVersionsSpaces(void) {
30 RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX,
31 TPM_NV_PER_PPWRITE, KERNEL_SPACE_SIZE));
32 RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX, KERNEL_SPACE_INIT_DATA,
33 KERNEL_SPACE_SIZE));
34 return TPM_SUCCESS;
35}
36
Luigi Semenzato2666f102010-06-15 08:12:32 -070037/* When the return value is TPM_SUCCESS, this function sets *|initialized| to 1
38 * if the spaces have been fully initialized, to 0 if not. Otherwise
39 * *|initialized| is not changed.
40 */
Luigi Semenzato4f11c362010-06-10 11:01:04 -070041static uint32_t GetSpacesInitialized(int* initialized) {
42 uint32_t space_holder;
43 uint32_t result;
44 result = TlclRead(TPM_IS_INITIALIZED_NV_INDEX,
45 (uint8_t*) &space_holder, sizeof(space_holder));
46 switch (result) {
47 case TPM_SUCCESS:
48 *initialized = 1;
49 break;
50 case TPM_E_BADINDEX:
51 *initialized = 0;
52 result = TPM_SUCCESS;
53 break;
54 }
55 return result;
56}
57
Luigi Semenzato2666f102010-06-15 08:12:32 -070058/* Creates the NVRAM spaces, and sets their initial values as needed.
59 */
Luigi Semenzato59204c52010-06-09 13:37:15 -070060static uint32_t InitializeSpaces(void) {
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -070061 uint32_t zero = 0;
Luigi Semenzato596b6402010-05-27 14:04:52 -070062 uint32_t firmware_perm = TPM_NV_PER_GLOBALLOCK | TPM_NV_PER_PPWRITE;
Gaurav Shahce0cc302010-03-24 13:48:55 -070063
Gaurav Shahf3dd1a62010-04-05 15:50:00 -070064 debug("Initializing spaces\n");
Gaurav Shahce0cc302010-03-24 13:48:55 -070065
Luigi Semenzato59204c52010-06-09 13:37:15 -070066 RETURN_ON_FAILURE(TlclSetNvLocked());
Gaurav Shahce0cc302010-03-24 13:48:55 -070067
Luigi Semenzato59204c52010-06-09 13:37:15 -070068 RETURN_ON_FAILURE(TlclDefineSpace(FIRMWARE_VERSIONS_NV_INDEX,
69 firmware_perm, sizeof(uint32_t)));
70 RETURN_ON_FAILURE(TlclWrite(FIRMWARE_VERSIONS_NV_INDEX,
71 (uint8_t*) &zero, sizeof(uint32_t)));
Gaurav Shahce0cc302010-03-24 13:48:55 -070072
Luigi Semenzato4f11c362010-06-10 11:01:04 -070073 RETURN_ON_FAILURE(InitializeKernelVersionsSpaces());
Gaurav Shahce0cc302010-03-24 13:48:55 -070074
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -070075 /* The space KERNEL_VERSIONS_BACKUP_NV_INDEX is used to protect the kernel
Luigi Semenzato2666f102010-06-15 08:12:32 -070076 * versions. The content of space KERNEL_MUST_USE_BACKUP determines whether
77 * only the backup value should be trusted.
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -070078 */
Luigi Semenzato59204c52010-06-09 13:37:15 -070079 RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_BACKUP_NV_INDEX,
80 firmware_perm, sizeof(uint32_t)));
81 RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX,
82 (uint8_t*) &zero, sizeof(uint32_t)));
Luigi Semenzato4f11c362010-06-10 11:01:04 -070083 RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_MUST_USE_BACKUP_NV_INDEX,
Luigi Semenzato59204c52010-06-09 13:37:15 -070084 firmware_perm, sizeof(uint32_t)));
Luigi Semenzato4f11c362010-06-10 11:01:04 -070085 RETURN_ON_FAILURE(TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX,
Luigi Semenzato59204c52010-06-09 13:37:15 -070086 (uint8_t*) &zero, sizeof(uint32_t)));
Luigi Semenzato2666f102010-06-15 08:12:32 -070087 RETURN_ON_FAILURE(TlclDefineSpace(DEVELOPER_MODE_NV_INDEX,
88 firmware_perm, sizeof(uint32_t)));
89 RETURN_ON_FAILURE(TlclWrite(DEVELOPER_MODE_NV_INDEX,
90 (uint8_t*) &zero, sizeof(uint32_t)));
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -070091
92 /* The space TPM_IS_INITIALIZED_NV_INDEX is used to indicate that the TPM
93 * initialization has completed. Without it we cannot be sure that the last
94 * space to be created was also initialized (power could have been lost right
95 * after its creation).
96 */
Luigi Semenzato59204c52010-06-09 13:37:15 -070097 RETURN_ON_FAILURE(TlclDefineSpace(TPM_IS_INITIALIZED_NV_INDEX,
98 firmware_perm, sizeof(uint32_t)));
99 return TPM_SUCCESS;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700100}
101
Luigi Semenzato2666f102010-06-15 08:12:32 -0700102static uint32_t SetDistrustKernelSpaceAtNextBoot(uint32_t distrust) {
Luigi Semenzato4f11c362010-06-10 11:01:04 -0700103 uint32_t must_use_backup;
Luigi Semenzato2666f102010-06-15 08:12:32 -0700104 RETURN_ON_FAILURE(TlclRead(KERNEL_MUST_USE_BACKUP_NV_INDEX,
105 (uint8_t*) &must_use_backup, sizeof(uint32_t)));
106 if (must_use_backup != distrust) {
107 RETURN_ON_FAILURE(TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX,
108 (uint8_t*) &distrust, sizeof(uint32_t)));
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700109 }
Luigi Semenzato2666f102010-06-15 08:12:32 -0700110 return TPM_SUCCESS;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700111}
112
Luigi Semenzato2666f102010-06-15 08:12:32 -0700113static uint32_t GetTPMRollbackIndices(int type) {
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700114 uint32_t firmware_versions;
115 uint32_t kernel_versions;
116
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700117 /* We perform the reads, making sure they succeed. A failure means that the
118 * rollback index locations are missing or somehow messed up. We let the
119 * caller deal with that.
120 */
Luigi Semenzato2666f102010-06-15 08:12:32 -0700121 switch (type) {
122 case FIRMWARE_VERSIONS:
123 RETURN_ON_FAILURE(TlclRead(FIRMWARE_VERSIONS_NV_INDEX,
124 (uint8_t*) &firmware_versions,
125 sizeof(firmware_versions)));
126 g_firmware_key_version = firmware_versions >> 16;
127 g_firmware_version = firmware_versions && 0xffff;
128 break;
129 case KERNEL_VERSIONS:
130 RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_NV_INDEX,
131 (uint8_t*) &kernel_versions,
132 sizeof(kernel_versions)));
133 g_kernel_key_version = kernel_versions >> 16;
134 g_kernel_version = kernel_versions && 0xffff;
135 break;
136 }
Luigi Semenzato0a486812010-06-04 13:34:43 -0700137
Luigi Semenzato59204c52010-06-09 13:37:15 -0700138 return TPM_SUCCESS;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700139}
140
Luigi Semenzato4f11c362010-06-10 11:01:04 -0700141/* Checks if the kernel version space has been mucked with. If it has,
142 * reconstructs it using the backup value.
143 */
144uint32_t RecoverKernelSpace(void) {
145 uint32_t perms = 0;
146 uint8_t buffer[KERNEL_SPACE_SIZE];
147 int read_OK = 0;
148 int perms_OK = 0;
149 uint32_t backup_combined_versions;
150 uint32_t must_use_backup;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700151
Luigi Semenzato4f11c362010-06-10 11:01:04 -0700152 RETURN_ON_FAILURE(TlclRead(KERNEL_MUST_USE_BACKUP_NV_INDEX,
153 (uint8_t*) &must_use_backup, sizeof(uint32_t)));
154 /* must_use_backup is true if the previous boot entered recovery mode. */
155
156 read_OK = TlclRead(KERNEL_VERSIONS_NV_INDEX, (uint8_t*) &buffer,
157 KERNEL_SPACE_SIZE) == TPM_SUCCESS;
158 if (read_OK) {
159 RETURN_ON_FAILURE(TlclGetPermissions(KERNEL_VERSIONS_NV_INDEX, &perms));
160 perms_OK = perms == TPM_NV_PER_PPWRITE;
161 }
162 if (!must_use_backup && read_OK && perms_OK &&
163 !Memcmp(buffer + sizeof(uint32_t), KERNEL_SPACE_UID,
164 KERNEL_SPACE_UID_SIZE)) {
165 /* Everything is fine. This is the normal, frequent path. */
166 return TPM_SUCCESS;
167 }
168
169 /* Either we detected that something went wrong, or we cannot trust the
170 * PP-protected kernel space. Attempts to fix. It is not always necessary
171 * to redefine the space, but we might as well, since this path should be
172 * taken quite seldom (after recovery mode and after an attack).
173 */
174 RETURN_ON_FAILURE(InitializeKernelVersionsSpaces());
175 RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_BACKUP_NV_INDEX,
176 (uint8_t*) &backup_combined_versions,
177 sizeof(uint32_t)));
178 RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX,
179 (uint8_t*) &backup_combined_versions,
180 sizeof(uint32_t)));
181 if (must_use_backup) {
182 uint32_t zero = 0;
183 RETURN_ON_FAILURE(TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX,
184 (uint8_t*) &zero, 0));
185
186 }
187 return TPM_SUCCESS;
188}
189
190static uint32_t BackupKernelSpace(void) {
191 uint32_t kernel_versions;
192 uint32_t backup_versions;
193 RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_NV_INDEX,
194 (uint8_t*) &kernel_versions, sizeof(uint32_t)));
195 RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_BACKUP_NV_INDEX,
196 (uint8_t*) &backup_versions, sizeof(uint32_t)));
197 if (kernel_versions == backup_versions) {
198 return TPM_SUCCESS;
199 } else if (kernel_versions < backup_versions) {
200 /* This cannot happen. We're screwed. */
201 return TPM_E_INTERNAL_INCONSISTENCY;
202 }
203 RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX,
204 (uint8_t*) &kernel_versions, sizeof(uint32_t)));
205 return TPM_SUCCESS;
206}
207
Luigi Semenzato2666f102010-06-15 08:12:32 -0700208/* Checks for transitions between protected mode to developer mode. When going
209 * into developer mode, clear the TPM.
210 */
211static uint32_t CheckDeveloperModeTransition(uint32_t current_developer) {
212 uint32_t past_developer;
213 int must_clear;
214 RETURN_ON_FAILURE(TlclRead(DEVELOPER_MODE_NV_INDEX,
215 (uint8_t*) &past_developer,
216 sizeof(past_developer)));
217 must_clear = current_developer && !past_developer;
218 if (must_clear) {
219 RETURN_ON_FAILURE(TlclForceClear());
220 }
221 if (past_developer != current_developer) {
222 /* (Unauthorized) writes to the TPM succeed even when the TPM is disabled
223 * and deactivated.
224 */
225 RETURN_ON_FAILURE(TlclWrite(DEVELOPER_MODE_NV_INDEX,
226 (uint8_t*) &current_developer,
227 sizeof(current_developer)));
228 }
229 return must_clear ? TPM_E_MUST_REBOOT : TPM_SUCCESS;
230}
231
232static uint32_t SetupTPM_(int mode, int developer_flag) {
Luigi Semenzato596b6402010-05-27 14:04:52 -0700233 uint8_t disable;
234 uint8_t deactivated;
Luigi Semenzato59204c52010-06-09 13:37:15 -0700235 TlclLibInit();
236 RETURN_ON_FAILURE(TlclStartup());
237 RETURN_ON_FAILURE(TlclContinueSelfTest());
238 RETURN_ON_FAILURE(TlclAssertPhysicalPresence());
Luigi Semenzato4f11c362010-06-10 11:01:04 -0700239 /* Checks that the TPM is enabled and activated. */
Luigi Semenzato59204c52010-06-09 13:37:15 -0700240 RETURN_ON_FAILURE(TlclGetFlags(&disable, &deactivated));
Luigi Semenzato596b6402010-05-27 14:04:52 -0700241 if (disable || deactivated) {
Luigi Semenzato59204c52010-06-09 13:37:15 -0700242 RETURN_ON_FAILURE(TlclSetEnable());
243 RETURN_ON_FAILURE(TlclSetDeactivated(0));
Luigi Semenzato2666f102010-06-15 08:12:32 -0700244 return TPM_E_MUST_REBOOT;
Luigi Semenzato596b6402010-05-27 14:04:52 -0700245 }
Luigi Semenzato2666f102010-06-15 08:12:32 -0700246 /* We expect this to fail the first time we run on a device, because the TPM
247 * has not been initialized yet.
248 */
Luigi Semenzato4f11c362010-06-10 11:01:04 -0700249 if (RecoverKernelSpace() != TPM_SUCCESS) {
250 int initialized = 0;
251 RETURN_ON_FAILURE(GetSpacesInitialized(&initialized));
252 if (initialized) {
253 return TPM_E_ALREADY_INITIALIZED;
254 } else {
255 RETURN_ON_FAILURE(InitializeSpaces());
256 RETURN_ON_FAILURE(RecoverKernelSpace());
257 }
Gaurav Shahce0cc302010-03-24 13:48:55 -0700258 }
Luigi Semenzato4f11c362010-06-10 11:01:04 -0700259 RETURN_ON_FAILURE(BackupKernelSpace());
Luigi Semenzato2666f102010-06-15 08:12:32 -0700260 RETURN_ON_FAILURE(SetDistrustKernelSpaceAtNextBoot(mode == RO_RECOVERY_MODE));
261 RETURN_ON_FAILURE(GetTPMRollbackIndices(FIRMWARE_VERSIONS));
262 RETURN_ON_FAILURE(GetTPMRollbackIndices(KERNEL_VERSIONS));
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700263
Luigi Semenzato2666f102010-06-15 08:12:32 -0700264 RETURN_ON_FAILURE(CheckDeveloperModeTransition(developer_flag));
265
266 /* As a courtesy (I hope) to the caller, lock the firmware versions if we are
267 * in recovery mode. The normal mode may need to update the firmware
268 * versions, so they cannot be locked here.
269 */
270 if (mode == RO_RECOVERY_MODE) {
271 RETURN_ON_FAILURE(LockFirmwareVersions());
272 }
Luigi Semenzato59204c52010-06-09 13:37:15 -0700273 return TPM_SUCCESS;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700274}
275
Luigi Semenzato2666f102010-06-15 08:12:32 -0700276/* SetupTPM starts the TPM and establishes the root of trust for the
277 * anti-rollback mechanism. SetupTPM can fail for three reasons. 1 A bug. 2 a
278 * TPM hardware failure. 3 An unexpected TPM state due to some attack. In
279 * general we cannot easily distinguish the kind of failure, so our strategy is
280 * to reboot in recovery mode in all cases. The recovery mode calls SetupTPM
281 * again, which executes (almost) the same sequence of operations. There is a
282 * good chance that, if recovery mode was entered because of a TPM failure, the
283 * failure will repeat itself. (In general this is impossible to guarantee
284 * because we have no way of creating the exact TPM initial state at the
285 * previous boot.) In recovery mode, we ignore the failure and continue, thus
286 * giving the recovery kernel a chance to fix things (that's why we don't set
287 * bGlobalLock). The choice is between a knowingly insecure device and a
288 * bricked device.
289 *
290 * As a side note, observe that we go through considerable hoops to avoid using
291 * the STCLEAR permissions for the index spaces. We do this to avoid writing
292 * to the TPM flashram at every reboot or wake-up, because of concerns about
293 * the durability of the NVRAM.
294 */
295uint32_t SetupTPM(int mode, int developer_flag) {
296 switch (mode) {
297 case RO_RECOVERY_MODE:
298 case RO_NORMAL_MODE: {
299 uint32_t result = SetupTPM_(mode, developer_flag);
300 if (result == TPM_E_MAXNVWRITES) {
301 /* ForceClears and reboots */
302 RETURN_ON_FAILURE(TlclForceClear());
303 return TPM_E_MUST_REBOOT;
304 } else if (mode == RO_NORMAL_MODE) {
305 return result;
306 } else {
307 /* In recovery mode we want to keep going even if there are errors. */
308 return TPM_SUCCESS;
309 }
310 }
311 case RW_NORMAL_MODE:
312 /* There are no TPM writes here, so no need to check for write limit errors.
313 */
314 RETURN_ON_FAILURE(GetTPMRollbackIndices(KERNEL_VERSIONS));
315 default:
316 return TPM_E_INTERNAL_INCONSISTENCY;
Luigi Semenzato4f11c362010-06-10 11:01:04 -0700317 }
318}
319
Luigi Semenzato59204c52010-06-09 13:37:15 -0700320uint32_t GetStoredVersions(int type, uint16_t* key_version, uint16_t* version) {
Luigi Semenzato2666f102010-06-15 08:12:32 -0700321 /* TODO: should verify that SetupTPM() has been called.
322 *
323 * Note that SetupTPM() does hardware setup AND sets global variables. When
324 * we get down into kernel verification, the hardware setup persists, but we
325 * lose the global variables.
326 */
Gaurav Shahce0cc302010-03-24 13:48:55 -0700327 switch (type) {
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700328 case FIRMWARE_VERSIONS:
329 *key_version = g_firmware_key_version;
330 *version = g_firmware_version;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700331 break;
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700332 case KERNEL_VERSIONS:
333 *key_version = g_kernel_key_version;
334 *version = g_kernel_version;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700335 break;
336 }
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700337
Luigi Semenzato59204c52010-06-09 13:37:15 -0700338 return TPM_SUCCESS;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700339}
340
Luigi Semenzato59204c52010-06-09 13:37:15 -0700341uint32_t WriteStoredVersions(int type, uint16_t key_version, uint16_t version) {
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700342 uint32_t combined_version = (key_version << 16) & version;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700343 switch (type) {
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700344 case FIRMWARE_VERSIONS:
Luigi Semenzato59204c52010-06-09 13:37:15 -0700345 RETURN_ON_FAILURE(TlclWrite(FIRMWARE_VERSIONS_NV_INDEX,
346 (uint8_t*) &combined_version,
347 sizeof(uint32_t)));
348 break;
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700349
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700350 case KERNEL_VERSIONS:
Luigi Semenzato59204c52010-06-09 13:37:15 -0700351 RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX,
352 (uint8_t*) &combined_version,
353 sizeof(uint32_t)));
Luigi Semenzato59204c52010-06-09 13:37:15 -0700354 }
355 return TPM_SUCCESS;
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700356}
357
Luigi Semenzato59204c52010-06-09 13:37:15 -0700358uint32_t LockFirmwareVersions() {
359 return TlclSetGlobalLock();
Gaurav Shahce0cc302010-03-24 13:48:55 -0700360}
361
Luigi Semenzato59204c52010-06-09 13:37:15 -0700362uint32_t LockKernelVersionsByLockingPP() {
363 return TlclLockPhysicalPresence();
Gaurav Shahce0cc302010-03-24 13:48:55 -0700364}