1 2/* 3 * Licensed Materials - Property of IBM 4 * 5 * trousers - An open source TCG Software Stack 6 * 7 * (C) Copyright International Business Machines Corp. 2004-2006 8 * 9 */ 10 11#include <stdio.h> 12#include <string.h> 13#include <stdlib.h> 14#include <limits.h> 15 16#include "trousers/tss.h" 17#include "trousers_types.h" 18#include "tcs_tsp.h" 19#include "tcs_utils.h" 20#include "tcs_int_literals.h" 21#include "capabilities.h" 22#include "tcslog.h" 23#include "tcsd_wrap.h" 24#include "tcsd.h" 25#include "auth_mgr.h" 26#include "req_mgr.h" 27 28 29MUTEX_DECLARE_EXTERN(tcsp_lock); 30 31/* Note: The after taking the auth_mgr_lock in any of the functions below, the 32 * mem_cache_lock cannot be taken without risking a deadlock. So, the auth_mgr 33 * functions must be "self-contained" wrt locking */ 34 35/* no locking done in init since its called by only a single thread */ 36TSS_RESULT 37auth_mgr_init() 38{ 39 memset(&auth_mgr, 0, sizeof(struct _auth_mgr)); 40 41 auth_mgr.max_auth_sessions = tpm_metrics.num_auths; 42 43 auth_mgr.overflow = calloc(TSS_DEFAULT_OVERFLOW_AUTHS, sizeof(COND_VAR *)); 44 if (auth_mgr.overflow == NULL) { 45 LogError("malloc of %zd bytes failed", 46 (TSS_DEFAULT_OVERFLOW_AUTHS * sizeof(COND_VAR *))); 47 return TCSERR(TSS_E_OUTOFMEMORY); 48 } 49 auth_mgr.overflow_size = TSS_DEFAULT_OVERFLOW_AUTHS; 50 51 auth_mgr.auth_mapper = calloc(TSS_DEFAULT_AUTH_TABLE_SIZE, sizeof(struct auth_map)); 52 if (auth_mgr.auth_mapper == NULL) { 53 LogError("malloc of %zd bytes failed", 54 (TSS_DEFAULT_AUTH_TABLE_SIZE * sizeof(struct auth_map))); 55 return TCSERR(TSS_E_OUTOFMEMORY); 56 } 57 auth_mgr.auth_mapper_size = TSS_DEFAULT_AUTH_TABLE_SIZE; 58 59 return TSS_SUCCESS; 60} 61 62TSS_RESULT 63auth_mgr_final() 64{ 65 UINT32 i; 66 67 /* wake up any sleeping threads, so they can be joined */ 68 for (i = 0; i < auth_mgr.overflow_size; i++) { 69 if (auth_mgr.overflow[i] != NULL) 70 COND_SIGNAL(auth_mgr.overflow[i]); 71 } 72 73 free(auth_mgr.overflow); 74 free(auth_mgr.auth_mapper); 75 76 return TSS_SUCCESS; 77} 78 79TSS_RESULT 80auth_mgr_save_ctx(TCS_CONTEXT_HANDLE hContext) 81{ 82 TSS_RESULT result = TSS_SUCCESS; 83 UINT32 i; 84 85 for (i = 0; i < auth_mgr.auth_mapper_size; i++) { 86 if (auth_mgr.auth_mapper[i].full == TRUE && 87 auth_mgr.auth_mapper[i].swap == NULL && 88 auth_mgr.auth_mapper[i].tcs_ctx != hContext) { 89 LogDebug("Calling TPM_SaveAuthContext for TCS CTX %x. Swapping out: TCS %x " 90 "TPM %x", hContext, auth_mgr.auth_mapper[i].tcs_ctx, 91 auth_mgr.auth_mapper[i].tpm_handle); 92 93 if ((result = TPM_SaveAuthContext(auth_mgr.auth_mapper[i].tpm_handle, 94 &auth_mgr.auth_mapper[i].swap_size, 95 &auth_mgr.auth_mapper[i].swap))) { 96 LogDebug("TPM_SaveAuthContext failed: 0x%x", result); 97 return result; 98 } 99 break; 100 } 101 } 102 103 return result; 104} 105 106/* if there's a TCS context waiting to get auth, wake it up or swap it in */ 107void 108auth_mgr_swap_in() 109{ 110 if (auth_mgr.overflow[auth_mgr.of_tail] != NULL) { 111 LogDebug("waking up thread %lddd, auth slot has opened", THREAD_ID); 112 /* wake up the next sleeping thread in order and increment tail */ 113 COND_SIGNAL(auth_mgr.overflow[auth_mgr.of_tail]); 114 auth_mgr.overflow[auth_mgr.of_tail] = NULL; 115 auth_mgr.of_tail = (auth_mgr.of_tail + 1) % auth_mgr.overflow_size; 116 } else { 117 /* else nobody needs to be swapped in, so continue */ 118 LogDebug("no threads need to be signaled."); 119 } 120} 121 122/* we need to swap out an auth context or add a waiting context to the overflow queue */ 123TSS_RESULT 124auth_mgr_swap_out(TCS_CONTEXT_HANDLE hContext) 125{ 126 COND_VAR *cond; 127 128 /* If the TPM can do swapping and it succeeds, return, else cond wait below */ 129 if (tpm_metrics.authctx_swap && !auth_mgr_save_ctx(hContext)) 130 return TSS_SUCCESS; 131 132 if ((cond = ctx_get_cond_var(hContext)) == NULL) { 133 LogError("Auth swap variable not found for TCS context 0x%x", hContext); 134 return TCSERR(TSS_E_INTERNAL_ERROR); 135 } 136 137 /* Test whether we are the last awake thread. If we are, we can't go to sleep 138 * since then there'd be no worker thread to wake the others up. This situation 139 * can arise when we're on a busy system who's TPM doesn't support auth ctx 140 * swapping. 141 */ 142 if (auth_mgr.sleeping_threads == (tcsd_options.num_threads - 1)) { 143 LogError("auth mgr failing: too many threads already waiting"); 144 LogTPMERR(TCPA_E_RESOURCES, __FILE__, __LINE__); 145 return TCPA_E_RESOURCES; 146 } 147 148 if (auth_mgr.overflow[auth_mgr.of_head] == NULL) { 149 auth_mgr.overflow[auth_mgr.of_head] = cond; 150 auth_mgr.of_head = (auth_mgr.of_head + 1) % auth_mgr.overflow_size; 151 /* go to sleep */ 152 LogDebug("thread %lddd going to sleep until auth slot opens", THREAD_ID); 153 auth_mgr.sleeping_threads++; 154 COND_WAIT(cond, &tcsp_lock); 155 auth_mgr.sleeping_threads--; 156 } else if (auth_mgr.overflow_size + TSS_DEFAULT_OVERFLOW_AUTHS < UINT_MAX) { 157 COND_VAR **tmp = auth_mgr.overflow; 158 159 LogDebugFn("Table of sleeping threads is full (%hu), growing to %hu entries", 160 auth_mgr.sleeping_threads, 161 auth_mgr.overflow_size + TSS_DEFAULT_OVERFLOW_AUTHS); 162 163 auth_mgr.overflow = calloc(auth_mgr.overflow_size + TSS_DEFAULT_OVERFLOW_AUTHS, 164 sizeof(COND_VAR *)); 165 if (auth_mgr.overflow == NULL) { 166 LogDebugFn("malloc of %zd bytes failed", 167 (auth_mgr.overflow_size + TSS_DEFAULT_OVERFLOW_AUTHS) * 168 sizeof(COND_VAR *)); 169 auth_mgr.overflow = tmp; 170 return TCSERR(TSS_E_OUTOFMEMORY); 171 } 172 auth_mgr.overflow_size += TSS_DEFAULT_OVERFLOW_AUTHS; 173 174 LogDebugFn("Success."); 175 memcpy(auth_mgr.overflow, tmp, auth_mgr.sleeping_threads * sizeof(COND_VAR *)); 176 free(tmp); 177 178 /* XXX This could temporarily wake threads up out of order */ 179 auth_mgr.of_head = auth_mgr.sleeping_threads; 180 auth_mgr.of_tail = 0; 181 auth_mgr.overflow[auth_mgr.of_head] = cond; 182 auth_mgr.of_head = (auth_mgr.of_head + 1) % auth_mgr.overflow_size; 183 LogDebug("thread %lddd going to sleep until auth slot opens", THREAD_ID); 184 auth_mgr.sleeping_threads++; 185 COND_WAIT(cond, &tcsp_lock); 186 auth_mgr.sleeping_threads--; 187 } else { 188 LogError("Auth table overflow is full (%u entries), some will fail.", 189 auth_mgr.overflow_size); 190 return TCSERR(TSS_E_INTERNAL_ERROR); 191 } 192 193 return TSS_SUCCESS; 194} 195 196/* close all auth contexts associated with this TCS_CONTEXT_HANDLE */ 197TSS_RESULT 198auth_mgr_close_context(TCS_CONTEXT_HANDLE tcs_handle) 199{ 200 UINT32 i; 201 TSS_RESULT result; 202 203 for (i = 0; i < auth_mgr.auth_mapper_size; i++) { 204 if (auth_mgr.auth_mapper[i].full == TRUE && 205 auth_mgr.auth_mapper[i].tcs_ctx == tcs_handle) { 206 if (auth_mgr.auth_mapper[i].swap) { 207 /* This context is swapped out of the TPM, so we can just free the 208 * blob */ 209 free(auth_mgr.auth_mapper[i].swap); 210 auth_mgr.auth_mapper[i].swap = NULL; 211 auth_mgr.auth_mapper[i].swap_size = 0; 212 } else { 213 result = TCSP_FlushSpecific_Common(auth_mgr.auth_mapper[i].tpm_handle, 214 TPM_RT_AUTH); 215 216 /* Ok, probably dealing with a 1.1 TPM */ 217 if (result == TPM_E_BAD_ORDINAL) 218 result = internal_TerminateHandle( 219 auth_mgr.auth_mapper[i].tpm_handle); 220 221 if (result == TCPA_E_INVALID_AUTHHANDLE) { 222 LogDebug("Tried to close an invalid auth handle: %x", 223 auth_mgr.auth_mapper[i].tpm_handle); 224 } else if (result != TCPA_SUCCESS) { 225 LogDebug("TPM_TerminateHandle returned %d", result); 226 } 227 } 228 /* clear the slot */ 229 auth_mgr.open_auth_sessions--; 230 auth_mgr.auth_mapper[i].full = FALSE; 231 auth_mgr.auth_mapper[i].tpm_handle = 0; 232 auth_mgr.auth_mapper[i].tcs_ctx = 0; 233 LogDebug("released auth for TCS %x TPM %x", tcs_handle, 234 auth_mgr.auth_mapper[i].tpm_handle); 235 236 auth_mgr_swap_in(); 237 } 238 } 239 240 return TSS_SUCCESS; 241} 242 243void 244auth_mgr_release_auth(TPM_AUTH *auth0, TPM_AUTH *auth1, TCS_CONTEXT_HANDLE tcs_handle) 245{ 246 if (auth0) 247 auth_mgr_release_auth_handle(auth0->AuthHandle, tcs_handle, 248 auth0->fContinueAuthSession); 249 250 if (auth1) 251 auth_mgr_release_auth_handle(auth1->AuthHandle, tcs_handle, 252 auth1->fContinueAuthSession); 253} 254 255/* unload the auth ctx associated with this auth handle */ 256TSS_RESULT 257auth_mgr_release_auth_handle(TCS_AUTHHANDLE tpm_auth_handle, TCS_CONTEXT_HANDLE tcs_handle, 258 TSS_BOOL cont) 259{ 260 UINT32 i; 261 TSS_RESULT result = TSS_SUCCESS; 262 263 for (i = 0; i < auth_mgr.auth_mapper_size; i++) { 264 if (auth_mgr.auth_mapper[i].full == TRUE && 265 auth_mgr.auth_mapper[i].tpm_handle == tpm_auth_handle && 266 auth_mgr.auth_mapper[i].tcs_ctx == tcs_handle) { 267 if (!cont) { 268 /* 269 * This function should not be necessary, but 270 * if the main operation resulted in an error, 271 * the TPM may still hold the auth handle 272 * and it must be freed. Most of the time 273 * this call will result in TPM_E_INVALID_AUTHHANDLE 274 * error which can be ignored. 275 */ 276 result = TCSP_FlushSpecific_Common( 277 auth_mgr.auth_mapper[i].tpm_handle, 278 TPM_RT_AUTH); 279 280 /* Ok, probably dealing with a 1.1 TPM */ 281 if (result == TPM_E_BAD_ORDINAL) 282 result = internal_TerminateHandle( 283 auth_mgr.auth_mapper[i].tpm_handle); 284 285 if (result == TCPA_E_INVALID_AUTHHANDLE) { 286 LogDebug("Tried to close an invalid auth handle: %x", 287 auth_mgr.auth_mapper[i].tpm_handle); 288 } else if (result != TCPA_SUCCESS) { 289 LogDebug("TPM_TerminateHandle returned %d", result); 290 } 291 292 if (result == TPM_SUCCESS) { 293 LogDebug("released auth for TCS %x TPM %x", 294 auth_mgr.auth_mapper[i].tcs_ctx, tpm_auth_handle); 295 } 296 /* 297 * Mark it as released, the "cont" flag indicates 298 * that it is no longer needed. 299 */ 300 auth_mgr.open_auth_sessions--; 301 auth_mgr.auth_mapper[i].full = FALSE; 302 auth_mgr.auth_mapper[i].tpm_handle = 0; 303 auth_mgr.auth_mapper[i].tcs_ctx = 0; 304 auth_mgr_swap_in(); 305 } 306 /* If the cont flag is TRUE, we have to keep the handle */ 307 } 308 } 309 310 return result; 311} 312 313TSS_RESULT 314auth_mgr_check(TCS_CONTEXT_HANDLE tcsContext, TPM_AUTHHANDLE *tpm_auth_handle) 315{ 316 UINT32 i; 317 TSS_RESULT result = TSS_SUCCESS; 318 319 for (i = 0; i < auth_mgr.auth_mapper_size; i++) { 320 if (auth_mgr.auth_mapper[i].full == TRUE && 321 auth_mgr.auth_mapper[i].tpm_handle == *tpm_auth_handle && 322 auth_mgr.auth_mapper[i].tcs_ctx == tcsContext) { 323 /* We have a record of this session, now swap it into the TPM if need be. */ 324 if (auth_mgr.auth_mapper[i].swap) { 325 LogDebugFn("TPM_LoadAuthContext for TCS %x TPM %x", tcsContext, 326 auth_mgr.auth_mapper[i].tpm_handle); 327 328 result = TPM_LoadAuthContext(auth_mgr.auth_mapper[i].swap_size, 329 auth_mgr.auth_mapper[i].swap, 330 tpm_auth_handle); 331 if (result == TSS_SUCCESS) { 332 free(auth_mgr.auth_mapper[i].swap); 333 auth_mgr.auth_mapper[i].swap = NULL; 334 auth_mgr.auth_mapper[i].swap_size = 0; 335 336 LogDebugFn("TPM_LoadAuthContext succeeded. Old TPM: %x, New" 337 " TPM: %x", auth_mgr.auth_mapper[i].tpm_handle, 338 *tpm_auth_handle); 339 340 auth_mgr.auth_mapper[i].tpm_handle = *tpm_auth_handle; 341 } else if (result == TPM_E_RESOURCES) { 342 if ((result = auth_mgr_swap_out(tcsContext))) { 343 LogDebugFn("TPM_LoadAuthContext failed with TPM_E_R" 344 "ESOURCES and swapping out failed, retur" 345 "ning error"); 346 return result; 347 } 348 349 LogDebugFn("Retrying TPM_LoadAuthContext after swap" 350 " out..."); 351 result = 352 TPM_LoadAuthContext(auth_mgr.auth_mapper[i].swap_size, 353 auth_mgr.auth_mapper[i].swap, 354 tpm_auth_handle); 355 free(auth_mgr.auth_mapper[i].swap); 356 auth_mgr.auth_mapper[i].swap = NULL; 357 auth_mgr.auth_mapper[i].swap_size = 0; 358 } else { 359 LogDebug("TPM_LoadAuthContext failed: 0x%x.", result); 360 } 361 } 362 363 return result; 364 } 365 } 366 367 LogDebugFn("Can't find auth for TCS handle %x, should be %x", tcsContext, *tpm_auth_handle); 368 return TCSERR(TSS_E_INTERNAL_ERROR); 369} 370 371TSS_RESULT 372auth_mgr_add(TCS_CONTEXT_HANDLE tcsContext, TCS_AUTHHANDLE tpm_auth_handle) 373{ 374 struct auth_map *tmp = auth_mgr.auth_mapper; 375 UINT32 i; 376 377 for (i = 0; i < auth_mgr.auth_mapper_size; i++) { 378 if (auth_mgr.auth_mapper[i].full == FALSE) { 379 auth_mgr.auth_mapper[i].tpm_handle = tpm_auth_handle; 380 auth_mgr.auth_mapper[i].tcs_ctx = tcsContext; 381 auth_mgr.auth_mapper[i].full = TRUE; 382 auth_mgr.open_auth_sessions++; 383 LogDebug("added auth for TCS %x TPM %x", tcsContext, tpm_auth_handle); 384 385 return TSS_SUCCESS; 386 } 387 } 388 389 390 LogDebugFn("Thread %ld growing the auth table to %u entries", THREAD_ID, 391 auth_mgr.auth_mapper_size + TSS_DEFAULT_AUTH_TABLE_SIZE); 392 393 auth_mgr.auth_mapper = calloc(auth_mgr.auth_mapper_size + 394 TSS_DEFAULT_AUTH_TABLE_SIZE, sizeof(struct auth_map)); 395 if (auth_mgr.auth_mapper == NULL) { 396 auth_mgr.auth_mapper = tmp; 397 return TCSERR(TSS_E_OUTOFMEMORY); 398 } else { 399 memcpy(auth_mgr.auth_mapper, tmp, 400 auth_mgr.auth_mapper_size * sizeof(struct auth_map)); 401 auth_mgr.auth_mapper_size += TSS_DEFAULT_AUTH_TABLE_SIZE; 402 LogDebugFn("Success."); 403 return TSS_SUCCESS; 404 } 405} 406 407/* A thread wants a new OIAP or OSAP session with the TPM. Returning TRUE indicates that we should 408 * allow it to open the session, FALSE to indicate that the request should be queued or have 409 * another thread's session swapped out to make room for it. 410 */ 411TSS_BOOL 412auth_mgr_req_new(TCS_CONTEXT_HANDLE hContext) 413{ 414 UINT32 i, opened = 0; 415 416 for (i = 0; i < auth_mgr.auth_mapper_size; i++) { 417 if (auth_mgr.auth_mapper[i].full == TRUE && 418 auth_mgr.auth_mapper[i].tcs_ctx == hContext) { 419 opened++; 420 } 421 } 422 423 /* If this TSP has already opened its max open auth handles, deny another open */ 424 if (opened >= MAX(2, (UINT32)auth_mgr.max_auth_sessions/2)) { 425 LogDebug("Max opened auth handles already opened."); 426 return FALSE; 427 } 428 429 /* if we have one opened already and there's a slot available, ok */ 430 if (opened && ((auth_mgr.max_auth_sessions - auth_mgr.open_auth_sessions) >= 1)) 431 return TRUE; 432 433 /* we don't already have one open and there are at least 2 slots left */ 434 if ((auth_mgr.max_auth_sessions - auth_mgr.open_auth_sessions) >= 2) 435 return TRUE; 436 437 LogDebug("Request for new auth handle denied by TCS. (%d opened sessions)", opened); 438 439 return FALSE; 440} 441 442TSS_RESULT 443auth_mgr_oiap(TCS_CONTEXT_HANDLE hContext, /* in */ 444 TCS_AUTHHANDLE *authHandle, /* out */ 445 TCPA_NONCE *nonce0) /* out */ 446{ 447 TSS_RESULT result; 448 449 /* are the maximum number of auth sessions open? */ 450 if (auth_mgr_req_new(hContext) == FALSE) { 451 if ((result = auth_mgr_swap_out(hContext))) 452 goto done; 453 } 454 455 if ((result = TCSP_OIAP_Internal(hContext, authHandle, nonce0))) 456 goto done; 457 458 /* success, add an entry to the table */ 459 result = auth_mgr_add(hContext, *authHandle); 460done: 461 return result; 462} 463 464TSS_RESULT 465auth_mgr_osap(TCS_CONTEXT_HANDLE hContext, /* in */ 466 TCPA_ENTITY_TYPE entityType, /* in */ 467 UINT32 entityValue, /* in */ 468 TCPA_NONCE nonceOddOSAP, /* in */ 469 TCS_AUTHHANDLE *authHandle, /* out */ 470 TCPA_NONCE *nonceEven, /* out */ 471 TCPA_NONCE *nonceEvenOSAP) /* out */ 472{ 473 TSS_RESULT result; 474 UINT32 newEntValue = 0; 475 476 /* if ET is not KEYHANDLE or KEY, newEntValue is a don't care */ 477 if (entityType == TCPA_ET_KEYHANDLE || entityType == TCPA_ET_KEY) { 478 if (ensureKeyIsLoaded(hContext, entityValue, &newEntValue)) 479 return TCSERR(TSS_E_FAIL); 480 } else { 481 newEntValue = entityValue; 482 } 483 484 /* are the maximum number of auth sessions open? */ 485 if (auth_mgr_req_new(hContext) == FALSE) { 486 if ((result = auth_mgr_swap_out(hContext))) 487 goto done; 488 } 489 490 if ((result = TCSP_OSAP_Internal(hContext, entityType, newEntValue, nonceOddOSAP, 491 authHandle, nonceEven, nonceEvenOSAP))) 492 goto done; 493 494 /* success, add an entry to the table */ 495 result = auth_mgr_add(hContext, *authHandle); 496done: 497 return result; 498} 499 500/* This function is only called directly from the TSP, we use other internal routines to free 501 * our handles. */ 502TSS_RESULT 503TCSP_TerminateHandle_Internal(TCS_CONTEXT_HANDLE hContext, /* in */ 504 TCS_AUTHHANDLE handle) /* in */ 505{ 506 TSS_RESULT result; 507 508 LogDebug("Entering TCSI_TerminateHandle"); 509 if ((result = ctx_verify_context(hContext))) 510 return result; 511 512 if ((result = auth_mgr_check(hContext, &handle))) 513 return result; 514 515 result = auth_mgr_release_auth_handle(handle, hContext, TRUE); 516 517 LogResult("Terminate Handle", result); 518 return result; 519} 520 521TSS_RESULT 522TPM_SaveAuthContext(TPM_AUTHHANDLE handle, UINT32 *size, BYTE **blob) 523{ 524 UINT64 offset; 525 UINT32 trash, bsize; 526 TSS_RESULT result; 527 BYTE txBlob[TSS_TPM_TXBLOB_SIZE]; 528 529 offset = 10; 530 LoadBlob_UINT32(&offset, handle, txBlob); 531 LoadBlob_Header(TPM_TAG_RQU_COMMAND, offset, TPM_ORD_SaveAuthContext, txBlob); 532 533 if ((result = req_mgr_submit_req(txBlob))) 534 return result; 535 536 result = UnloadBlob_Header(txBlob, &trash); 537 538 LogDebug("total packet size received from TPM: %u", trash); 539 540 if (!result) { 541 offset = 10; 542 UnloadBlob_UINT32(&offset, &bsize, txBlob); 543 544 LogDebug("Reported blob size from TPM: %u", bsize); 545 546 *blob = malloc(bsize); 547 if (*blob == NULL) { 548 LogError("malloc of %u bytes failed.", bsize); 549 return TCSERR(TSS_E_OUTOFMEMORY); 550 } 551 UnloadBlob(&offset, bsize, txBlob, *blob); 552 *size = bsize; 553 } 554 555 return result; 556} 557 558TSS_RESULT 559TPM_LoadAuthContext(UINT32 size, BYTE *blob, TPM_AUTHHANDLE *handle) 560{ 561 UINT64 offset; 562 UINT32 trash; 563 TSS_RESULT result; 564 BYTE txBlob[TSS_TPM_TXBLOB_SIZE]; 565 566 LogDebugFn("Loading %u byte auth blob back into TPM", size); 567 568 offset = 10; 569 LoadBlob_UINT32(&offset, size, txBlob); 570 LoadBlob(&offset, size, txBlob, blob); 571 LoadBlob_Header(TPM_TAG_RQU_COMMAND, offset, TPM_ORD_LoadAuthContext, txBlob); 572 573 if ((result = req_mgr_submit_req(txBlob))) 574 return result; 575 576 result = UnloadBlob_Header(txBlob, &trash); 577 578 if (!result) { 579 offset = 10; 580 UnloadBlob_UINT32(&offset, handle, txBlob); 581 } 582 583 return result; 584} 585