1/* Copyright (c) 2012 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 * TPM command utility. Runs simple TPM commands. Mostly useful when physical 6 * presence has not been locked. 7 * 8 * The exit code is 0 for success, the TPM error code for TPM errors, and 255 9 * for other errors. 10 */ 11 12#include <stdint.h> 13#include <stdlib.h> 14#include <stdio.h> 15#include <string.h> 16#include <syslog.h> 17 18#include "tlcl.h" 19#include "tpm_error_messages.h" 20#include "tss_constants.h" 21 22#define OTHER_ERROR 255 /* OTHER_ERROR must be the largest uint8_t value. */ 23 24typedef struct command_record { 25 const char* name; 26 const char* abbr; 27 const char* description; 28 uint32_t (*handler)(void); 29} command_record; 30 31/* Set in main, consumed by handler functions below. We use global variables 32 * so we can also choose to call Tlcl*() functions directly; they don't take 33 * argv/argc. 34 */ 35int nargs; 36char** args; 37 38/* Converts a string in the form 0x[0-9a-f]+ to a 32-bit value. Returns 0 for 39 * success, non-zero for failure. 40 */ 41int HexStringToUint32(const char* string, uint32_t* value) { 42 char tail[1]; 43 /* strtoul is not as good because it overflows silently */ 44 char* format = strncmp(string, "0x", 2) ? "%8x%s" : "0x%8x%s"; 45 int n = sscanf(string, format, value, tail); 46 return n != 1; 47} 48 49/* Converts a string in the form [0-9a-f]+ to an 8-bit value. Returns 0 for 50 * success, non-zero for failure. 51 */ 52int HexStringToUint8(const char* string, uint8_t* value) { 53 char* end; 54 uint32_t large_value = strtoul(string, &end, 16); 55 if (*end != '\0' || large_value > 0xff) { 56 return 1; 57 } 58 *value = large_value; 59 return 0; 60} 61 62/* TPM error check and reporting. Returns 0 if |result| is 0 (TPM_SUCCESS). 63 * Otherwise looks up a TPM error in the error table and prints the error if 64 * found. Then returns min(result, OTHER_ERROR) since some error codes, such 65 * as TPM_E_RETRY, do not fit in a byte. 66 */ 67uint8_t ErrorCheck(uint32_t result, const char* cmd) { 68 uint8_t exit_code = result > OTHER_ERROR ? OTHER_ERROR : result; 69 if (result == 0) { 70 return 0; 71 } else { 72 int i; 73 int n = sizeof(tpm_error_table) / sizeof(tpm_error_table[0]); 74 fprintf(stderr, "command \"%s\" failed with code 0x%x\n", cmd, result); 75 for (i = 0; i < n; i++) { 76 if (tpm_error_table[i].code == result) { 77 fprintf(stderr, "%s\n%s\n", tpm_error_table[i].name, 78 tpm_error_table[i].description); 79 return exit_code; 80 } 81 } 82 fprintf(stderr, "the TPM error code is unknown to this program\n"); 83 return exit_code; 84 } 85} 86 87/* Handler functions. These wouldn't exist if C had closures. 88 */ 89static uint32_t HandlerGetFlags(void) { 90 uint8_t disabled; 91 uint8_t deactivated; 92 uint8_t nvlocked; 93 uint32_t result = TlclGetFlags(&disabled, &deactivated, &nvlocked); 94 if (result == 0) { 95 printf("disabled: %d\ndeactivated: %d\nnvlocked: %d\n", 96 disabled, deactivated, nvlocked); 97 } 98 return result; 99} 100 101static uint32_t HandlerActivate(void) { 102 return TlclSetDeactivated(0); 103} 104 105static uint32_t HandlerDeactivate(void) { 106 return TlclSetDeactivated(1); 107} 108 109static uint32_t HandlerDefineSpace(void) { 110 uint32_t index, size, perm; 111 if (nargs != 5) { 112 fprintf(stderr, "usage: tpmc def <index> <size> <perm>\n"); 113 exit(OTHER_ERROR); 114 } 115 if (HexStringToUint32(args[2], &index) != 0 || 116 HexStringToUint32(args[3], &size) != 0 || 117 HexStringToUint32(args[4], &perm) != 0) { 118 fprintf(stderr, "<index>, <size>, and <perm> must be " 119 "32-bit hex (0x[0-9a-f]+)\n"); 120 exit(OTHER_ERROR); 121 } 122 return TlclDefineSpace(index, perm, size); 123} 124 125static uint32_t HandlerWrite(void) { 126 uint32_t index, size; 127 uint8_t value[TPM_MAX_COMMAND_SIZE]; 128 char** byteargs; 129 int i; 130 if (nargs < 3) { 131 fprintf(stderr, "usage: tpmc write <index> [<byte0> <byte1> ...]\n"); 132 exit(OTHER_ERROR); 133 } 134 if (HexStringToUint32(args[2], &index) != 0) { 135 fprintf(stderr, "<index> must be 32-bit hex (0x[0-9a-f]+)\n"); 136 exit(OTHER_ERROR); 137 } 138 size = nargs - 3; 139 if (size > sizeof(value)) { 140 fprintf(stderr, "byte array too large\n"); 141 exit(OTHER_ERROR); 142 } 143 144 byteargs = args + 3; 145 for (i = 0; i < size; i++) { 146 if (HexStringToUint8(byteargs[i], &value[i]) != 0) { 147 fprintf(stderr, "invalid byte %s, should be [0-9a-f][0-9a-f]?\n", 148 byteargs[i]); 149 exit(OTHER_ERROR); 150 } 151 } 152 153 if (size == 0) { 154 if (index == TPM_NV_INDEX_LOCK) { 155 fprintf(stderr, "This would set the nvLocked bit. " 156 "Use \"tpmc setnv\" instead.\n"); 157 exit(OTHER_ERROR); 158 } 159 printf("warning: zero-length write\n"); 160 } else { 161 printf("writing %d byte%s\n", size, size > 1 ? "s" : ""); 162 } 163 164 return TlclWrite(index, value, size); 165} 166 167static uint32_t HandlerPCRRead(void) { 168 uint32_t index; 169 uint8_t value[TPM_PCR_DIGEST]; 170 uint32_t result; 171 int i; 172 if (nargs != 3) { 173 fprintf(stderr, "usage: tpmc pcrread <index>\n"); 174 exit(OTHER_ERROR); 175 } 176 if (HexStringToUint32(args[2], &index) != 0) { 177 fprintf(stderr, "<index> must be 32-bit hex (0x[0-9a-f]+)\n"); 178 exit(OTHER_ERROR); 179 } 180 result = TlclPCRRead(index, value, sizeof(value)); 181 if (result == 0) { 182 for (i = 0; i < TPM_PCR_DIGEST; i++) { 183 printf("%02x", value[i]); 184 } 185 printf("\n"); 186 } 187 return result; 188} 189 190static uint32_t HandlerRead(void) { 191 uint32_t index, size; 192 uint8_t value[4096]; 193 uint32_t result; 194 int i; 195 if (nargs != 4) { 196 fprintf(stderr, "usage: tpmc read <index> <size>\n"); 197 exit(OTHER_ERROR); 198 } 199 if (HexStringToUint32(args[2], &index) != 0 || 200 HexStringToUint32(args[3], &size) != 0) { 201 fprintf(stderr, "<index> and <size> must be 32-bit hex (0x[0-9a-f]+)\n"); 202 exit(OTHER_ERROR); 203 } 204 if (size > sizeof(value)) { 205 fprintf(stderr, "size of read (0x%x) is too big\n", size); 206 exit(OTHER_ERROR); 207 } 208 result = TlclRead(index, value, size); 209 if (result == 0 && size > 0) { 210 for (i = 0; i < size - 1; i++) { 211 printf("%x ", value[i]); 212 } 213 printf("%x\n", value[i]); 214 } 215 return result; 216} 217 218static uint32_t HandlerGetPermissions(void) { 219 uint32_t index, permissions, result; 220 if (nargs != 3) { 221 fprintf(stderr, "usage: tpmc getp <index>\n"); 222 exit(OTHER_ERROR); 223 } 224 if (HexStringToUint32(args[2], &index) != 0) { 225 fprintf(stderr, "<index> must be 32-bit hex (0x[0-9a-f]+)\n"); 226 exit(OTHER_ERROR); 227 } 228 result = TlclGetPermissions(index, &permissions); 229 if (result == 0) { 230 printf("space 0x%x has permissions 0x%x\n", index, permissions); 231 } 232 return result; 233} 234 235static uint32_t HandlerGetOwnership(void) { 236 uint8_t owned = 0; 237 uint32_t result; 238 if (nargs != 2) { 239 fprintf(stderr, "usage: tpmc getownership\n"); 240 exit(OTHER_ERROR); 241 } 242 result = TlclGetOwnership(&owned); 243 if (result == 0) { 244 printf("Owned: %s\n", owned ? "yes" : "no"); 245 } 246 return result; 247} 248 249static uint32_t HandlerGetRandom(void) { 250 uint32_t length, size; 251 uint8_t* bytes; 252 uint32_t result; 253 int i; 254 if (nargs != 3) { 255 fprintf(stderr, "usage: tpmc getrandom <size>\n"); 256 exit(OTHER_ERROR); 257 } 258 if (HexStringToUint32(args[2], &length) != 0) { 259 fprintf(stderr, "<size> must be 32-bit hex (0x[0-9a-f]+)\n"); 260 exit(OTHER_ERROR); 261 } 262 bytes = calloc(1, length); 263 if (bytes == NULL) { 264 perror("calloc"); 265 exit(OTHER_ERROR); 266 } 267 result = TlclGetRandom(bytes, length, &size); 268 if (result == 0 && size > 0) { 269 for (i = 0; i < size; i++) { 270 printf("%02x", bytes[i]); 271 } 272 printf("\n"); 273 } 274 free(bytes); 275 return result; 276} 277 278static uint32_t HandlerGetPermanentFlags(void) { 279 TPM_PERMANENT_FLAGS pflags; 280 uint32_t result = TlclGetPermanentFlags(&pflags); 281 if (result == 0) { 282#define P(name) printf("%s %d\n", #name, pflags.name) 283 P(disable); 284 P(ownership); 285 P(deactivated); 286 P(readPubek); 287 P(disableOwnerClear); 288 P(allowMaintenance); 289 P(physicalPresenceLifetimeLock); 290 P(physicalPresenceHWEnable); 291 P(physicalPresenceCMDEnable); 292 P(CEKPUsed); 293 P(TPMpost); 294 P(TPMpostLock); 295 P(FIPS); 296 P(Operator); 297 P(enableRevokeEK); 298 P(nvLocked); 299 P(readSRKPub); 300 P(tpmEstablished); 301 P(maintenanceDone); 302 P(disableFullDALogicInfo); 303#undef P 304 } 305 return result; 306} 307 308static uint32_t HandlerGetSTClearFlags(void) { 309 TPM_STCLEAR_FLAGS vflags; 310 uint32_t result = TlclGetSTClearFlags(&vflags); 311 if (result == 0) { 312#define P(name) printf("%s %d\n", #name, vflags.name) 313 P(deactivated); 314 P(disableForceClear); 315 P(physicalPresence); 316 P(physicalPresenceLock); 317 P(bGlobalLock); 318#undef P 319 } 320 return result; 321} 322 323 324static uint32_t HandlerSendRaw(void) { 325 uint8_t request[4096]; 326 uint8_t response[4096]; 327 uint32_t result; 328 int size; 329 int i; 330 if (nargs == 2) { 331 fprintf(stderr, "usage: tpmc sendraw <hex byte 0> ... <hex byte N>\n"); 332 exit(OTHER_ERROR); 333 } 334 for (i = 0; i < nargs - 2 && i < sizeof(request); i++) { 335 if (HexStringToUint8(args[2 + i], &request[i]) != 0) { 336 fprintf(stderr, "bad byte value \"%s\"\n", args[2 + i]); 337 exit(OTHER_ERROR); 338 } 339 } 340 size = TlclPacketSize(request); 341 if (size != i) { 342 fprintf(stderr, "bad request: size field is %d, but packet has %d bytes\n", 343 size, i); 344 exit(OTHER_ERROR); 345 } 346 bzero(response, sizeof(response)); 347 result = TlclSendReceive(request, response, sizeof(response)); 348 if (result != 0) { 349 fprintf(stderr, "request failed with code %d\n", result); 350 } 351 size = TlclPacketSize(response); 352 if (size < 10 || size > sizeof(response)) { 353 fprintf(stderr, "unexpected response size %d\n", size); 354 exit(OTHER_ERROR); 355 } 356 for (i = 0; i < size; i++) { 357 printf("0x%02x ", response[i]); 358 if (i == size - 1 || (i + 1) % 8 == 0) { 359 printf("\n"); 360 } 361 } 362 return result; 363} 364 365 366/* Table of TPM commands. 367 */ 368command_record command_table[] = { 369 { "getflags", "getf", "read and print the value of selected flags", 370 HandlerGetFlags }, 371 { "startup", "sta", "issue a Startup command", TlclStartup }, 372 { "selftestfull", "test", "issue a SelfTestFull command", TlclSelfTestFull }, 373 { "continueselftest", "ctest", "issue a ContinueSelfTest command", 374 TlclContinueSelfTest }, 375 { "assertphysicalpresence", "ppon", "assert Physical Presence", 376 TlclAssertPhysicalPresence }, 377 { "physicalpresencecmdenable", "ppcmd", "turn on software PP", 378 TlclPhysicalPresenceCMDEnable }, 379 { "enable", "ena", "enable the TPM (needs PP)", TlclSetEnable }, 380 { "disable", "dis", "disable the TPM (needs PP)", TlclClearEnable }, 381 { "activate", "act", "activate the TPM (needs PP, maybe reboot)", 382 HandlerActivate }, 383 { "deactivate", "deact", "deactivate the TPM (needs PP, maybe reboot)", 384 HandlerDeactivate }, 385 { "clear", "clr", "clear the TPM owner (needs PP)", TlclForceClear }, 386 { "setnvlocked", "setnv", "set the nvLocked flag permanently (IRREVERSIBLE!)", 387 TlclSetNvLocked }, 388 { "lockphysicalpresence", "pplock", "lock (turn off) PP until reboot", 389 TlclLockPhysicalPresence }, 390 { "setbgloballock", "block", "set the bGlobalLock until reboot", 391 TlclSetGlobalLock }, 392 { "definespace", "def", "define a space (def <index> <size> <perm>)", 393 HandlerDefineSpace }, 394 { "write", "write", "write to a space (write <index> [<byte0> <byte1> ...])", 395 HandlerWrite }, 396 { "read", "read", "read from a space (read <index> <size>)", 397 HandlerRead }, 398 { "pcrread", "pcr", "read from a PCR (pcrread <index>)", 399 HandlerPCRRead }, 400 { "getownership", "geto", "print state of TPM ownership", 401 HandlerGetOwnership }, 402 { "getpermissions", "getp", "print space permissions (getp <index>)", 403 HandlerGetPermissions }, 404 { "getpermanentflags", "getpf", "print all permanent flags", 405 HandlerGetPermanentFlags }, 406 { "getrandom", "rand", "read bytes from RNG (rand <size>)", 407 HandlerGetRandom }, 408 { "getstclearflags", "getvf", "print all volatile (ST_CLEAR) flags", 409 HandlerGetSTClearFlags }, 410 { "resume", "res", "execute TPM_Startup(ST_STATE)", TlclResume }, 411 { "savestate", "save", "execute TPM_SaveState", TlclSaveState }, 412 { "sendraw", "raw", "send a raw request and print raw response", 413 HandlerSendRaw }, 414}; 415 416static int n_commands = sizeof(command_table) / sizeof(command_table[0]); 417 418int main(int argc, char* argv[]) { 419 char *progname; 420 progname = strrchr(argv[0], '/'); 421 if (progname) 422 progname++; 423 else 424 progname = argv[0]; 425 426 if (argc < 2) { 427 fprintf(stderr, "usage: %s <TPM command> [args]\n or: %s help\n", 428 progname, progname); 429 return OTHER_ERROR; 430 } else { 431 command_record* c; 432 const char* cmd = argv[1]; 433 nargs = argc; 434 args = argv; 435 436 if (strcmp(cmd, "help") == 0) { 437 printf("%26s %7s %s\n\n", "command", "abbr.", "description"); 438 for (c = command_table; c < command_table + n_commands; c++) { 439 printf("%26s %7s %s\n", c->name, c->abbr, c->description); 440 } 441 return 0; 442 } 443 444 TlclLibInit(); 445 446 for (c = command_table; c < command_table + n_commands; c++) { 447 if (strcmp(cmd, c->name) == 0 || strcmp(cmd, c->abbr) == 0) { 448 return ErrorCheck(c->handler(), cmd); 449 } 450 } 451 452 /* No command matched. */ 453 fprintf(stderr, "%s: unknown command: %s\n", progname, cmd); 454 return OTHER_ERROR; 455 } 456} 457