| 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 |