OLD | NEW |
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 |
11 #include "tlcl.h" | 11 #include "tlcl.h" |
12 #include "tss_constants.h" | 12 #include "tss_constants.h" |
13 #include "utility.h" | 13 #include "utility.h" |
14 | 14 |
15 uint16_t g_firmware_key_version = 0; | 15 static int g_rollback_recovery_mode = 0; |
16 uint16_t g_firmware_version = 0; | |
17 uint16_t g_kernel_key_version = 0; | |
18 uint16_t g_kernel_version = 0; | |
19 | 16 |
20 /* disable MSVC warning on const logical expression (as in } while(0);) */ | 17 /* disable MSVC warning on const logical expression (as in } while(0);) */ |
21 __pragma(warning (disable: 4127)) | 18 __pragma(warning (disable: 4127)) |
22 | 19 |
23 #define RETURN_ON_FAILURE(tpm_command) do { \ | 20 #define RETURN_ON_FAILURE(tpm_command) do { \ |
24 uint32_t result; \ | 21 uint32_t result; \ |
25 if ((result = (tpm_command)) != TPM_SUCCESS) { \ | 22 if ((result = (tpm_command)) != TPM_SUCCESS) { \ |
26 return result; \ | 23 return result; \ |
27 } \ | 24 } \ |
28 } while (0) | 25 } while (0) |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
126 uint32_t must_use_backup; | 123 uint32_t must_use_backup; |
127 RETURN_ON_FAILURE(TlclRead(KERNEL_MUST_USE_BACKUP_NV_INDEX, | 124 RETURN_ON_FAILURE(TlclRead(KERNEL_MUST_USE_BACKUP_NV_INDEX, |
128 (uint8_t*) &must_use_backup, sizeof(uint32_t))); | 125 (uint8_t*) &must_use_backup, sizeof(uint32_t))); |
129 if (must_use_backup != distrust) { | 126 if (must_use_backup != distrust) { |
130 RETURN_ON_FAILURE(SafeWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX, | 127 RETURN_ON_FAILURE(SafeWrite(KERNEL_MUST_USE_BACKUP_NV_INDEX, |
131 (uint8_t*) &distrust, sizeof(uint32_t))); | 128 (uint8_t*) &distrust, sizeof(uint32_t))); |
132 } | 129 } |
133 return TPM_SUCCESS; | 130 return TPM_SUCCESS; |
134 } | 131 } |
135 | 132 |
136 static uint32_t GetTPMRollbackIndices(int type) { | |
137 uint32_t firmware_versions; | |
138 uint32_t kernel_versions; | |
139 | |
140 /* We perform the reads, making sure they succeed. A failure means that the | |
141 * rollback index locations are missing or somehow messed up. We let the | |
142 * caller deal with that. | |
143 */ | |
144 switch (type) { | |
145 case FIRMWARE_VERSIONS: | |
146 RETURN_ON_FAILURE(TlclRead(FIRMWARE_VERSIONS_NV_INDEX, | |
147 (uint8_t*) &firmware_versions, | |
148 sizeof(firmware_versions))); | |
149 g_firmware_key_version = (uint16_t) (firmware_versions >> 16); | |
150 g_firmware_version = (uint16_t) (firmware_versions & 0xffff); | |
151 break; | |
152 case KERNEL_VERSIONS: | |
153 RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_NV_INDEX, | |
154 (uint8_t*) &kernel_versions, | |
155 sizeof(kernel_versions))); | |
156 g_kernel_key_version = (uint16_t) (kernel_versions >> 16); | |
157 g_kernel_version = (uint16_t) (kernel_versions & 0xffff); | |
158 break; | |
159 } | |
160 | |
161 return TPM_SUCCESS; | |
162 } | |
163 | |
164 /* Checks if the kernel version space has been mucked with. If it has, | 133 /* Checks if the kernel version space has been mucked with. If it has, |
165 * reconstructs it using the backup value. | 134 * reconstructs it using the backup value. |
166 */ | 135 */ |
167 uint32_t RecoverKernelSpace(void) { | 136 uint32_t RecoverKernelSpace(void) { |
168 uint32_t perms = 0; | 137 uint32_t perms = 0; |
169 uint8_t buffer[KERNEL_SPACE_SIZE]; | 138 uint8_t buffer[KERNEL_SPACE_SIZE]; |
170 int read_OK = 0; | 139 int read_OK = 0; |
171 int perms_OK = 0; | 140 int perms_OK = 0; |
172 uint32_t backup_combined_versions; | 141 uint32_t backup_combined_versions; |
173 uint32_t must_use_backup; | 142 uint32_t must_use_backup; |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
237 sizeof(past_developer))); | 206 sizeof(past_developer))); |
238 if (past_developer != current_developer) { | 207 if (past_developer != current_developer) { |
239 RETURN_ON_FAILURE(TPMClearAndReenable()); | 208 RETURN_ON_FAILURE(TPMClearAndReenable()); |
240 RETURN_ON_FAILURE(SafeWrite(DEVELOPER_MODE_NV_INDEX, | 209 RETURN_ON_FAILURE(SafeWrite(DEVELOPER_MODE_NV_INDEX, |
241 (uint8_t*) ¤t_developer, | 210 (uint8_t*) ¤t_developer, |
242 sizeof(current_developer))); | 211 sizeof(current_developer))); |
243 } | 212 } |
244 return TPM_SUCCESS; | 213 return TPM_SUCCESS; |
245 } | 214 } |
246 | 215 |
247 static uint32_t SetupTPM_(int mode, int developer_flag) { | 216 /* SetupTPM starts the TPM and establishes the root of trust for the |
| 217 * anti-rollback mechanism. SetupTPM can fail for three reasons. 1 A bug. 2 a |
| 218 * TPM hardware failure. 3 An unexpected TPM state due to some attack. In |
| 219 * general we cannot easily distinguish the kind of failure, so our strategy is |
| 220 * to reboot in recovery mode in all cases. The recovery mode calls SetupTPM |
| 221 * again, which executes (almost) the same sequence of operations. There is a |
| 222 * good chance that, if recovery mode was entered because of a TPM failure, the |
| 223 * failure will repeat itself. (In general this is impossible to guarantee |
| 224 * because we have no way of creating the exact TPM initial state at the |
| 225 * previous boot.) In recovery mode, we ignore the failure and continue, thus |
| 226 * giving the recovery kernel a chance to fix things (that's why we don't set |
| 227 * bGlobalLock). The choice is between a knowingly insecure device and a |
| 228 * bricked device. |
| 229 * |
| 230 * As a side note, observe that we go through considerable hoops to avoid using |
| 231 * the STCLEAR permissions for the index spaces. We do this to avoid writing |
| 232 * to the TPM flashram at every reboot or wake-up, because of concerns about |
| 233 * the durability of the NVRAM. |
| 234 */ |
| 235 static uint32_t SetupTPM(int recovery_mode, |
| 236 int developer_mode) { |
248 uint8_t disable; | 237 uint8_t disable; |
249 uint8_t deactivated; | 238 uint8_t deactivated; |
| 239 |
250 TlclLibInit(); | 240 TlclLibInit(); |
251 RETURN_ON_FAILURE(TlclStartup()); | 241 RETURN_ON_FAILURE(TlclStartup()); |
252 RETURN_ON_FAILURE(TlclContinueSelfTest()); | 242 RETURN_ON_FAILURE(TlclContinueSelfTest()); |
253 RETURN_ON_FAILURE(TlclAssertPhysicalPresence()); | 243 RETURN_ON_FAILURE(TlclAssertPhysicalPresence()); |
254 /* Checks that the TPM is enabled and activated. */ | 244 /* Checks that the TPM is enabled and activated. */ |
255 RETURN_ON_FAILURE(TlclGetFlags(&disable, &deactivated)); | 245 RETURN_ON_FAILURE(TlclGetFlags(&disable, &deactivated)); |
256 if (disable || deactivated) { | 246 if (disable || deactivated) { |
257 RETURN_ON_FAILURE(TlclSetEnable()); | 247 RETURN_ON_FAILURE(TlclSetEnable()); |
258 RETURN_ON_FAILURE(TlclSetDeactivated(0)); | 248 RETURN_ON_FAILURE(TlclSetDeactivated(0)); |
259 return TPM_E_MUST_REBOOT; | 249 return TPM_E_MUST_REBOOT; |
260 } | 250 } |
261 /* We expect this to fail the first time we run on a device, because the TPM | 251 /* We expect this to fail the first time we run on a device, because the TPM |
262 * has not been initialized yet. | 252 * has not been initialized yet. |
263 */ | 253 */ |
264 if (RecoverKernelSpace() != TPM_SUCCESS) { | 254 if (RecoverKernelSpace() != TPM_SUCCESS) { |
265 int initialized = 0; | 255 int initialized = 0; |
266 RETURN_ON_FAILURE(GetSpacesInitialized(&initialized)); | 256 RETURN_ON_FAILURE(GetSpacesInitialized(&initialized)); |
267 if (initialized) { | 257 if (initialized) { |
268 return TPM_E_ALREADY_INITIALIZED; | 258 return TPM_E_ALREADY_INITIALIZED; |
269 } else { | 259 } else { |
270 RETURN_ON_FAILURE(InitializeSpaces()); | 260 RETURN_ON_FAILURE(InitializeSpaces()); |
271 RETURN_ON_FAILURE(RecoverKernelSpace()); | 261 RETURN_ON_FAILURE(RecoverKernelSpace()); |
272 } | 262 } |
273 } | 263 } |
274 RETURN_ON_FAILURE(BackupKernelSpace()); | 264 RETURN_ON_FAILURE(BackupKernelSpace()); |
275 RETURN_ON_FAILURE(SetDistrustKernelSpaceAtNextBoot(mode == RO_RECOVERY_MODE)); | 265 RETURN_ON_FAILURE(SetDistrustKernelSpaceAtNextBoot(recovery_mode)); |
276 RETURN_ON_FAILURE(GetTPMRollbackIndices(FIRMWARE_VERSIONS)); | 266 RETURN_ON_FAILURE(CheckDeveloperModeTransition(developer_mode)); |
277 RETURN_ON_FAILURE(GetTPMRollbackIndices(KERNEL_VERSIONS)); | |
278 | 267 |
279 RETURN_ON_FAILURE(CheckDeveloperModeTransition(developer_flag)); | 268 if (recovery_mode) { |
280 | 269 /* In recovery mode global variables are usable. */ |
281 /* As a courtesy (I hope) to the caller, lock the firmware versions if we are | 270 g_rollback_recovery_mode = 1; |
282 * in recovery mode. The normal mode may need to update the firmware | |
283 * versions, so they cannot be locked here. | |
284 */ | |
285 if (mode == RO_RECOVERY_MODE) { | |
286 RETURN_ON_FAILURE(LockFirmwareVersions()); | |
287 } | 271 } |
288 return TPM_SUCCESS; | 272 return TPM_SUCCESS; |
289 } | 273 } |
290 | 274 |
291 /* SetupTPM starts the TPM and establishes the root of trust for the | |
292 * anti-rollback mechanism. SetupTPM can fail for three reasons. 1 A bug. 2 a | |
293 * TPM hardware failure. 3 An unexpected TPM state due to some attack. In | |
294 * general we cannot easily distinguish the kind of failure, so our strategy is | |
295 * to reboot in recovery mode in all cases. The recovery mode calls SetupTPM | |
296 * again, which executes (almost) the same sequence of operations. There is a | |
297 * good chance that, if recovery mode was entered because of a TPM failure, the | |
298 * failure will repeat itself. (In general this is impossible to guarantee | |
299 * because we have no way of creating the exact TPM initial state at the | |
300 * previous boot.) In recovery mode, we ignore the failure and continue, thus | |
301 * giving the recovery kernel a chance to fix things (that's why we don't set | |
302 * bGlobalLock). The choice is between a knowingly insecure device and a | |
303 * bricked device. | |
304 * | |
305 * As a side note, observe that we go through considerable hoops to avoid using | |
306 * the STCLEAR permissions for the index spaces. We do this to avoid writing | |
307 * to the TPM flashram at every reboot or wake-up, because of concerns about | |
308 * the durability of the NVRAM. | |
309 */ | |
310 uint32_t SetupTPM(int mode, int developer_flag) { | |
311 switch (mode) { | |
312 case RO_RECOVERY_MODE: | |
313 case RO_NORMAL_MODE: { | |
314 uint32_t result = SetupTPM_(mode, developer_flag); | |
315 if (mode == RO_NORMAL_MODE) { | |
316 return result; | |
317 } else { | |
318 /* In recovery mode we want to keep going even if there are errors. */ | |
319 return TPM_SUCCESS; | |
320 } | |
321 } | |
322 case RW_NORMAL_MODE: | |
323 RETURN_ON_FAILURE(GetTPMRollbackIndices(KERNEL_VERSIONS)); | |
324 default: | |
325 return TPM_E_INTERNAL_INCONSISTENCY; | |
326 } | |
327 } | |
328 | |
329 uint32_t GetStoredVersions(int type, uint16_t* key_version, uint16_t* version) { | |
330 /* TODO: should verify that SetupTPM() has been called. | |
331 * | |
332 * Note that SetupTPM() does hardware setup AND sets global variables. When | |
333 * we get down into kernel verification, the hardware setup persists, but we | |
334 * lose the global variables. | |
335 */ | |
336 switch (type) { | |
337 case FIRMWARE_VERSIONS: | |
338 *key_version = g_firmware_key_version; | |
339 *version = g_firmware_version; | |
340 break; | |
341 case KERNEL_VERSIONS: | |
342 *key_version = g_kernel_key_version; | |
343 *version = g_kernel_version; | |
344 break; | |
345 } | |
346 | |
347 return TPM_SUCCESS; | |
348 } | |
349 | |
350 uint32_t WriteStoredVersions(int type, uint16_t key_version, uint16_t version) { | |
351 uint32_t combined_version = (key_version << 16) & version; | |
352 switch (type) { | |
353 case FIRMWARE_VERSIONS: | |
354 RETURN_ON_FAILURE(SafeWrite(FIRMWARE_VERSIONS_NV_INDEX, | |
355 (uint8_t*) &combined_version, | |
356 sizeof(uint32_t))); | |
357 break; | |
358 | |
359 case KERNEL_VERSIONS: | |
360 RETURN_ON_FAILURE(SafeWrite(KERNEL_VERSIONS_NV_INDEX, | |
361 (uint8_t*) &combined_version, | |
362 sizeof(uint32_t))); | |
363 } | |
364 return TPM_SUCCESS; | |
365 } | |
366 | |
367 uint32_t LockFirmwareVersions() { | |
368 return TlclSetGlobalLock(); | |
369 } | |
370 | |
371 uint32_t LockKernelVersionsByLockingPP() { | |
372 return TlclLockPhysicalPresence(); | |
373 } | |
374 | |
375 /* disable MSVC warnings on unused arguments */ | 275 /* disable MSVC warnings on unused arguments */ |
376 __pragma(warning (disable: 4100)) | 276 __pragma(warning (disable: 4100)) |
377 | 277 |
378 /* NEW APIS! HELP ME LUIGI, YOU'RE MY ONLY HOPE! */ | 278 uint32_t RollbackFirmwareSetup(int developer_mode) { |
| 279 return SetupTPM(0, developer_mode); |
| 280 } |
379 | 281 |
380 uint32_t RollbackFirmwareSetup(int developer_mode, | 282 uint32_t RollbackFirmwareRead(uint16_t* key_version, uint16_t* version) { |
381 uint16_t* key_version, uint16_t* version) { | 283 uint32_t firmware_versions; |
| 284 /* Gets firmware versions. */ |
| 285 RETURN_ON_FAILURE(TlclRead(FIRMWARE_VERSIONS_NV_INDEX, |
| 286 (uint8_t*) &firmware_versions, |
| 287 sizeof(firmware_versions))); |
| 288 *key_version = (uint16_t) (firmware_versions >> 16); |
| 289 *version = (uint16_t) (firmware_versions & 0xffff); |
382 return TPM_SUCCESS; | 290 return TPM_SUCCESS; |
383 } | 291 } |
384 | 292 |
385 uint32_t RollbackFirmwareWrite(uint16_t key_version, uint16_t version) { | 293 uint32_t RollbackFirmwareWrite(uint16_t key_version, uint16_t version) { |
386 return TPM_SUCCESS; | 294 uint32_t combined_version = (key_version << 16) & version; |
| 295 return SafeWrite(FIRMWARE_VERSIONS_NV_INDEX, |
| 296 (uint8_t*) &combined_version, |
| 297 sizeof(uint32_t)); |
387 } | 298 } |
388 | 299 |
389 uint32_t RollbackFirmwareLock(void) { | 300 uint32_t RollbackFirmwareLock(void) { |
390 return TPM_SUCCESS; | 301 return TlclSetGlobalLock(); |
391 } | 302 } |
392 | 303 |
393 uint32_t RollbackKernelRecovery(int developer_mode) { | 304 uint32_t RollbackKernelRecovery(int developer_mode) { |
| 305 uint32_t result = SetupTPM(1, developer_mode); |
| 306 if (result == TPM_SUCCESS) { |
| 307 RETURN_ON_FAILURE(TlclSetGlobalLock()); |
| 308 } |
394 return TPM_SUCCESS; | 309 return TPM_SUCCESS; |
395 } | 310 } |
396 | 311 |
397 uint32_t RollbackKernelRead(uint16_t* key_version, uint16_t* version) { | 312 uint32_t RollbackKernelRead(uint16_t* key_version, uint16_t* version) { |
| 313 uint32_t kernel_versions; |
| 314 if (g_rollback_recovery_mode) { |
| 315 *key_version = 0; |
| 316 *version = 0; |
| 317 } else { |
| 318 /* Reads kernel versions from TPM. */ |
| 319 RETURN_ON_FAILURE(TlclRead(KERNEL_VERSIONS_NV_INDEX, |
| 320 (uint8_t*) &kernel_versions, |
| 321 sizeof(kernel_versions))); |
| 322 *key_version = (uint16_t) (kernel_versions >> 16); |
| 323 *version = (uint16_t) (kernel_versions & 0xffff); |
| 324 } |
398 return TPM_SUCCESS; | 325 return TPM_SUCCESS; |
399 } | 326 } |
400 | 327 |
401 uint32_t RollbackKernelWrite(uint16_t key_version, uint16_t version) { | 328 uint32_t RollbackKernelWrite(uint16_t key_version, uint16_t version) { |
| 329 if (!g_rollback_recovery_mode) { |
| 330 uint32_t combined_version = (key_version << 16) & version; |
| 331 return SafeWrite(KERNEL_VERSIONS_NV_INDEX, |
| 332 (uint8_t*) &combined_version, |
| 333 sizeof(uint32_t)); |
| 334 } |
402 return TPM_SUCCESS; | 335 return TPM_SUCCESS; |
403 } | 336 } |
404 | 337 |
405 uint32_t RollbackKernelLock(void) { | 338 uint32_t RollbackKernelLock(void) { |
406 return TPM_SUCCESS; | 339 if (!g_rollback_recovery_mode) { |
| 340 return TlclLockPhysicalPresence(); |
| 341 } else { |
| 342 return TPM_SUCCESS; |
| 343 } |
407 } | 344 } |
OLD | NEW |