Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(289)

Side by Side Diff: vboot_firmware/lib/rollback_index.c

Issue 2792009: Fix normal/recovery mode, and RO firmware vs. RW firmware behavior. (Closed) Base URL: ssh://git@chromiumos-git/vboot_reference.git
Patch Set: add a comment Created 10 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 1 /* 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 2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file. 3 * found in the LICENSE file.
4 * 4 *
5 * Functions for querying, manipulating and locking rollback indices 5 * Functions for querying, manipulating and locking rollback indices
6 * stored in the TPM NVRAM. 6 * stored in the TPM NVRAM.
7 */ 7 */
8 8
9 #include "rollback_index.h" 9 #include "rollback_index.h"
10 10
(...skipping 16 matching lines...) Expand all
27 } while (0) 27 } while (0)
28 28
29 static uint32_t InitializeKernelVersionsSpaces(void) { 29 static uint32_t InitializeKernelVersionsSpaces(void) {
30 RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX, 30 RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX,
31 TPM_NV_PER_PPWRITE, KERNEL_SPACE_SIZE)); 31 TPM_NV_PER_PPWRITE, KERNEL_SPACE_SIZE));
32 RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX, KERNEL_SPACE_INIT_DATA, 32 RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_NV_INDEX, KERNEL_SPACE_INIT_DATA,
33 KERNEL_SPACE_SIZE)); 33 KERNEL_SPACE_SIZE));
34 return TPM_SUCCESS; 34 return TPM_SUCCESS;
35 } 35 }
36 36
37 /* 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 */
37 static uint32_t GetSpacesInitialized(int* initialized) { 41 static uint32_t GetSpacesInitialized(int* initialized) {
38 uint32_t space_holder; 42 uint32_t space_holder;
39 uint32_t result; 43 uint32_t result;
40 result = TlclRead(TPM_IS_INITIALIZED_NV_INDEX, 44 result = TlclRead(TPM_IS_INITIALIZED_NV_INDEX,
41 (uint8_t*) &space_holder, sizeof(space_holder)); 45 (uint8_t*) &space_holder, sizeof(space_holder));
42 switch (result) { 46 switch (result) {
43 case TPM_SUCCESS: 47 case TPM_SUCCESS:
44 *initialized = 1; 48 *initialized = 1;
45 break; 49 break;
46 case TPM_E_BADINDEX: 50 case TPM_E_BADINDEX:
47 *initialized = 0; 51 *initialized = 0;
48 result = TPM_SUCCESS; 52 result = TPM_SUCCESS;
49 break; 53 break;
50 } 54 }
51 return result; 55 return result;
52 } 56 }
53 57
58 /* Creates the NVRAM spaces, and sets their initial values as needed.
59 */
54 static uint32_t InitializeSpaces(void) { 60 static uint32_t InitializeSpaces(void) {
55 uint32_t zero = 0; 61 uint32_t zero = 0;
56 uint32_t firmware_perm = TPM_NV_PER_GLOBALLOCK | TPM_NV_PER_PPWRITE; 62 uint32_t firmware_perm = TPM_NV_PER_GLOBALLOCK | TPM_NV_PER_PPWRITE;
57 63
58 debug("Initializing spaces\n"); 64 debug("Initializing spaces\n");
59 65
60 RETURN_ON_FAILURE(TlclSetNvLocked()); 66 RETURN_ON_FAILURE(TlclSetNvLocked());
61 67
62 RETURN_ON_FAILURE(TlclDefineSpace(FIRMWARE_VERSIONS_NV_INDEX, 68 RETURN_ON_FAILURE(TlclDefineSpace(FIRMWARE_VERSIONS_NV_INDEX,
63 firmware_perm, sizeof(uint32_t))); 69 firmware_perm, sizeof(uint32_t)));
64 RETURN_ON_FAILURE(TlclWrite(FIRMWARE_VERSIONS_NV_INDEX, 70 RETURN_ON_FAILURE(TlclWrite(FIRMWARE_VERSIONS_NV_INDEX,
65 (uint8_t*) &zero, sizeof(uint32_t))); 71 (uint8_t*) &zero, sizeof(uint32_t)));
66 72
67 RETURN_ON_FAILURE(InitializeKernelVersionsSpaces()); 73 RETURN_ON_FAILURE(InitializeKernelVersionsSpaces());
68 74
69 /* The space KERNEL_VERSIONS_BACKUP_NV_INDEX is used to protect the kernel 75 /* The space KERNEL_VERSIONS_BACKUP_NV_INDEX is used to protect the kernel
70 * versions when entering recovery mode. The content of space 76 * versions. The content of space KERNEL_MUST_USE_BACKUP determines whether
71 * KERNEL_MUST_USE_BACKUP determines whether the backup value (1) or the 77 * only the backup value should be trusted.
72 * regular value (0) should be trusted.
73 */ 78 */
74 RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_BACKUP_NV_INDEX, 79 RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_VERSIONS_BACKUP_NV_INDEX,
75 firmware_perm, sizeof(uint32_t))); 80 firmware_perm, sizeof(uint32_t)));
76 RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX, 81 RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX,
77 (uint8_t*) &zero, sizeof(uint32_t))); 82 (uint8_t*) &zero, sizeof(uint32_t)));
78 RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_MUST_USE_BACKUP_NV_INDEX, 83 RETURN_ON_FAILURE(TlclDefineSpace(KERNEL_MUST_USE_BACKUP_NV_INDEX,
79 firmware_perm, sizeof(uint32_t))); 84 firmware_perm, sizeof(uint32_t)));
80 RETURN_ON_FAILURE(TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX, 85 RETURN_ON_FAILURE(TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX,
81 (uint8_t*) &zero, sizeof(uint32_t))); 86 (uint8_t*) &zero, sizeof(uint32_t)));
87 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)));
82 91
83 /* The space TPM_IS_INITIALIZED_NV_INDEX is used to indicate that the TPM 92 /* The space TPM_IS_INITIALIZED_NV_INDEX is used to indicate that the TPM
84 * initialization has completed. Without it we cannot be sure that the last 93 * initialization has completed. Without it we cannot be sure that the last
85 * space to be created was also initialized (power could have been lost right 94 * space to be created was also initialized (power could have been lost right
86 * after its creation). 95 * after its creation).
87 */ 96 */
88 RETURN_ON_FAILURE(TlclDefineSpace(TPM_IS_INITIALIZED_NV_INDEX, 97 RETURN_ON_FAILURE(TlclDefineSpace(TPM_IS_INITIALIZED_NV_INDEX,
89 firmware_perm, sizeof(uint32_t))); 98 firmware_perm, sizeof(uint32_t)));
90 return TPM_SUCCESS; 99 return TPM_SUCCESS;
91 } 100 }
92 101
93 /* Enters the recovery mode. If |unlocked| is true, there is some problem with 102 static uint32_t SetDistrustKernelSpaceAtNextBoot(uint32_t distrust) {
94 * the TPM, so do not attempt to do any more TPM operations, and particularly
95 * do not set bGlobalLock.
96 */
97 void EnterRecovery(int unlocked) {
98 uint32_t combined_versions;
99 uint32_t backup_versions;
100 uint32_t must_use_backup; 103 uint32_t must_use_backup;
101 uint32_t result; 104 RETURN_ON_FAILURE(TlclRead(KERNEL_MUST_USE_BACKUP_NV_INDEX,
102 105 (uint8_t*) &must_use_backup, sizeof(uint32_t)));
103 if (!unlocked) { 106 if (must_use_backup != distrust) {
104 /* Saves the kernel versions and indicates that we should trust the saved 107 RETURN_ON_FAILURE(TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX,
105 * ones. 108 (uint8_t*) &distrust, sizeof(uint32_t)));
106 */
107 if (TlclRead(KERNEL_VERSIONS_NV_INDEX, (uint8_t*) &combined_versions,
108 sizeof(uint32_t)) != TPM_SUCCESS)
109 goto recovery_mode;
110 if (TlclRead(KERNEL_VERSIONS_BACKUP_NV_INDEX, (uint8_t*) &backup_versions,
111 sizeof(uint32_t)) != TPM_SUCCESS)
112 goto recovery_mode;
113 /* Avoids idempotent writes. */
114 if (combined_versions != backup_versions) {
115 result = TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX,
116 (uint8_t*) &combined_versions, sizeof(uint32_t));
117 if (result == TPM_E_MAXNVWRITES) {
118 goto forceclear_and_reboot;
119 } else if (result != TPM_SUCCESS) {
120 goto recovery_mode;
121 }
122 }
123
124 if (TlclRead(KERNEL_MUST_USE_BACKUP_NV_INDEX, (uint8_t*) &must_use_backup,
125 sizeof(uint32_t)) != TPM_SUCCESS)
126 goto recovery_mode;
127 if (must_use_backup != 1) {
128 must_use_backup = 1;
129 result = TlclWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX,
130 (uint8_t*) &must_use_backup, sizeof(uint32_t));
131 if (result == TPM_E_MAXNVWRITES) {
132 goto forceclear_and_reboot;
133 } else if (result != TPM_SUCCESS) {
134 goto recovery_mode;
135 }
136 }
137 /* Protects the firmware and backup kernel versions. */
138 if (LockFirmwareVersions() != TPM_SUCCESS)
139 goto recovery_mode;
140 } 109 }
141 110 return TPM_SUCCESS;
142 recovery_mode:
143 debug("entering recovery mode");
144
145 /* TODO(nelson): code for entering recovery mode. */
146
147 forceclear_and_reboot:
148 if (TlclForceClear() != TPM_SUCCESS) {
149 goto recovery_mode;
150 }
151 /* TODO: reboot */
152 } 111 }
153 112
154 static uint32_t GetTPMRollbackIndices(void) { 113 static uint32_t GetTPMRollbackIndices(int type) {
155 uint32_t firmware_versions; 114 uint32_t firmware_versions;
156 uint32_t kernel_versions; 115 uint32_t kernel_versions;
157 116
158 /* We perform the reads, making sure they succeed. A failure means that the 117 /* We perform the reads, making sure they succeed. A failure means that the
159 * rollback index locations are missing or somehow messed up. We let the 118 * rollback index locations are missing or somehow messed up. We let the
160 * caller deal with that. 119 * caller deal with that.
161 */ 120 */
162 RETURN_ON_FAILURE(TlclRead(FIRMWARE_VERSIONS_NV_INDEX, 121 switch (type) {
163 (uint8_t*) &firmware_versions, 122 case FIRMWARE_VERSIONS:
164 sizeof(firmware_versions))); 123 RETURN_ON_FAILURE(TlclRead(FIRMWARE_VERSIONS_NV_INDEX,
165 RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_NV_INDEX, 124 (uint8_t*) &firmware_versions,
166 (uint8_t*) &kernel_versions, 125 sizeof(firmware_versions)));
167 sizeof(kernel_versions))); 126 g_firmware_key_version = firmware_versions >> 16;
168 127 g_firmware_version = firmware_versions && 0xffff;
169 g_firmware_key_version = firmware_versions >> 16; 128 break;
170 g_firmware_version = firmware_versions && 0xffff; 129 case KERNEL_VERSIONS:
171 g_kernel_key_version = kernel_versions >> 16; 130 RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_NV_INDEX,
172 g_kernel_version = kernel_versions && 0xffff; 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 }
173 137
174 return TPM_SUCCESS; 138 return TPM_SUCCESS;
175 } 139 }
176 140
177 /* Checks if the kernel version space has been mucked with. If it has, 141 /* Checks if the kernel version space has been mucked with. If it has,
178 * reconstructs it using the backup value. 142 * reconstructs it using the backup value.
179 */ 143 */
180 uint32_t RecoverKernelSpace(void) { 144 uint32_t RecoverKernelSpace(void) {
181 uint32_t perms = 0; 145 uint32_t perms = 0;
182 uint8_t buffer[KERNEL_SPACE_SIZE]; 146 uint8_t buffer[KERNEL_SPACE_SIZE];
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
234 return TPM_SUCCESS; 198 return TPM_SUCCESS;
235 } else if (kernel_versions < backup_versions) { 199 } else if (kernel_versions < backup_versions) {
236 /* This cannot happen. We're screwed. */ 200 /* This cannot happen. We're screwed. */
237 return TPM_E_INTERNAL_INCONSISTENCY; 201 return TPM_E_INTERNAL_INCONSISTENCY;
238 } 202 }
239 RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX, 203 RETURN_ON_FAILURE(TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX,
240 (uint8_t*) &kernel_versions, sizeof(uint32_t))); 204 (uint8_t*) &kernel_versions, sizeof(uint32_t)));
241 return TPM_SUCCESS; 205 return TPM_SUCCESS;
242 } 206 }
243 207
244 static uint32_t SetupTPM_(void) { 208 /* Checks for transitions between protected mode to developer mode. When going
209 * into developer mode, clear the TPM.
210 */
211 static 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
232 static uint32_t SetupTPM_(int mode, int developer_flag) {
245 uint8_t disable; 233 uint8_t disable;
246 uint8_t deactivated; 234 uint8_t deactivated;
247 TlclLibInit(); 235 TlclLibInit();
248 RETURN_ON_FAILURE(TlclStartup()); 236 RETURN_ON_FAILURE(TlclStartup());
249 RETURN_ON_FAILURE(TlclContinueSelfTest()); 237 RETURN_ON_FAILURE(TlclContinueSelfTest());
250 RETURN_ON_FAILURE(TlclAssertPhysicalPresence()); 238 RETURN_ON_FAILURE(TlclAssertPhysicalPresence());
251 /* Checks that the TPM is enabled and activated. */ 239 /* Checks that the TPM is enabled and activated. */
252 RETURN_ON_FAILURE(TlclGetFlags(&disable, &deactivated)); 240 RETURN_ON_FAILURE(TlclGetFlags(&disable, &deactivated));
253 if (disable || deactivated) { 241 if (disable || deactivated) {
254 RETURN_ON_FAILURE(TlclSetEnable()); 242 RETURN_ON_FAILURE(TlclSetEnable());
255 RETURN_ON_FAILURE(TlclSetDeactivated(0)); 243 RETURN_ON_FAILURE(TlclSetDeactivated(0));
256 /* TODO: Reboot */ 244 return TPM_E_MUST_REBOOT;
257 return 9999;
258 } 245 }
259 /* We expect this to fail the first time we run on a device, indicating that 246 /* We expect this to fail the first time we run on a device, because the TPM
260 * the TPM has not been initialized yet. */ 247 * has not been initialized yet.
248 */
261 if (RecoverKernelSpace() != TPM_SUCCESS) { 249 if (RecoverKernelSpace() != TPM_SUCCESS) {
262 int initialized = 0; 250 int initialized = 0;
263 RETURN_ON_FAILURE(GetSpacesInitialized(&initialized)); 251 RETURN_ON_FAILURE(GetSpacesInitialized(&initialized));
264 if (initialized) { 252 if (initialized) {
265 return TPM_E_ALREADY_INITIALIZED; 253 return TPM_E_ALREADY_INITIALIZED;
266 } else { 254 } else {
267 RETURN_ON_FAILURE(InitializeSpaces()); 255 RETURN_ON_FAILURE(InitializeSpaces());
268 RETURN_ON_FAILURE(RecoverKernelSpace()); 256 RETURN_ON_FAILURE(RecoverKernelSpace());
269 } 257 }
270 } 258 }
271 RETURN_ON_FAILURE(BackupKernelSpace()); 259 RETURN_ON_FAILURE(BackupKernelSpace());
272 RETURN_ON_FAILURE(GetTPMRollbackIndices()); 260 RETURN_ON_FAILURE(SetDistrustKernelSpaceAtNextBoot(mode == RO_RECOVERY_MODE));
261 RETURN_ON_FAILURE(GetTPMRollbackIndices(FIRMWARE_VERSIONS));
262 RETURN_ON_FAILURE(GetTPMRollbackIndices(KERNEL_VERSIONS));
273 263
264 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 }
274 return TPM_SUCCESS; 273 return TPM_SUCCESS;
275 } 274 }
276 275
277 uint32_t SetupTPM(void) { 276 /* SetupTPM starts the TPM and establishes the root of trust for the
278 uint32_t result = SetupTPM_(); 277 * anti-rollback mechanism. SetupTPM can fail for three reasons. 1 A bug. 2 a
279 if (result == TPM_E_MAXNVWRITES) { 278 * TPM hardware failure. 3 An unexpected TPM state due to some attack. In
280 /* ForceClears and reboots */ 279 * general we cannot easily distinguish the kind of failure, so our strategy is
281 RETURN_ON_FAILURE(TlclForceClear()); 280 * to reboot in recovery mode in all cases. The recovery mode calls SetupTPM
282 /* TODO: reboot */ 281 * again, which executes (almost) the same sequence of operations. There is a
283 return 9999; 282 * good chance that, if recovery mode was entered because of a TPM failure, the
284 } else { 283 * failure will repeat itself. (In general this is impossible to guarantee
285 return result; 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 */
295 uint32_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;
286 } 317 }
287 } 318 }
288 319
289 uint32_t GetStoredVersions(int type, uint16_t* key_version, uint16_t* version) { 320 uint32_t GetStoredVersions(int type, uint16_t* key_version, uint16_t* version) {
290 321 /* TODO: should verify that SetupTPM() has been called.
291 /* TODO: should verify that SetupTPM() has been called. Note that 322 *
292 * SetupTPM() does hardware setup AND sets global variables. When we 323 * Note that SetupTPM() does hardware setup AND sets global variables. When
293 * get down into kernel verification, the hardware setup persists, but 324 * we get down into kernel verification, the hardware setup persists, but we
294 * we don't have access to the global variables. So I guess we DO need 325 * lose the global variables.
295 * to call SetupTPM() there, and have it be smart enough not to redo the 326 */
296 * hardware init, but it still needs to re-read the flags... */
297
298 switch (type) { 327 switch (type) {
299 case FIRMWARE_VERSIONS: 328 case FIRMWARE_VERSIONS:
300 *key_version = g_firmware_key_version; 329 *key_version = g_firmware_key_version;
301 *version = g_firmware_version; 330 *version = g_firmware_version;
302 break; 331 break;
303 case KERNEL_VERSIONS: 332 case KERNEL_VERSIONS:
304 *key_version = g_kernel_key_version; 333 *key_version = g_kernel_key_version;
305 *version = g_kernel_version; 334 *version = g_kernel_version;
306 break; 335 break;
307 } 336 }
(...skipping 18 matching lines...) Expand all
326 return TPM_SUCCESS; 355 return TPM_SUCCESS;
327 } 356 }
328 357
329 uint32_t LockFirmwareVersions() { 358 uint32_t LockFirmwareVersions() {
330 return TlclSetGlobalLock(); 359 return TlclSetGlobalLock();
331 } 360 }
332 361
333 uint32_t LockKernelVersionsByLockingPP() { 362 uint32_t LockKernelVersionsByLockingPP() {
334 return TlclLockPhysicalPresence(); 363 return TlclLockPhysicalPresence();
335 } 364 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698