1 /* 2 * Copyright (c) 2017, Linaro Limited 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 #include <bitstring.h> 8 #include <crypto/crypto.h> 9 #include <kernel/msg_param.h> 10 #include <kernel/mutex.h> 11 #include <kernel/refcount.h> 12 #include <kernel/thread.h> 13 #include <optee_msg_supplicant.h> 14 #include <string.h> 15 #include <tee_api_defines_extensions.h> 16 #include <tee/tadb.h> 17 #include <tee/tee_fs.h> 18 #include <tee/tee_fs_rpc.h> 19 #include <tee/tee_pobj.h> 20 #include <tee/tee_svc_storage.h> 21 #include <utee_defines.h> 22 23 #define TADB_MAX_BUFFER_SIZE (64U * 1024) 24 25 #define TADB_AUTH_ENC_ALG TEE_ALG_AES_GCM 26 #define TADB_IV_SIZE TEE_AES_BLOCK_SIZE 27 #define TADB_TAG_SIZE TEE_AES_BLOCK_SIZE 28 #define TADB_KEY_SIZE TEE_AES_MAX_KEY_SIZE 29 30 struct tee_tadb_dir { 31 const struct tee_file_operations *ops; 32 struct tee_file_handle *fh; 33 int nbits; 34 bitstr_t *files; 35 }; 36 37 struct tadb_entry { 38 struct tee_tadb_property prop; 39 uint32_t file_number; 40 uint8_t iv[TADB_IV_SIZE]; 41 uint8_t tag[TADB_TAG_SIZE]; 42 uint8_t key[TADB_KEY_SIZE]; 43 }; 44 45 struct tadb_header { 46 uint32_t opaque_len; 47 uint8_t opaque[]; 48 }; 49 50 struct tee_tadb_ta_write { 51 struct tee_tadb_dir *db; 52 int fd; 53 struct tadb_entry entry; 54 size_t pos; 55 void *ctx; 56 }; 57 58 struct tee_tadb_ta_read { 59 struct tee_tadb_dir *db; 60 int fd; 61 struct tadb_entry entry; 62 size_t pos; 63 void *ctx; 64 uint64_t ta_cookie; 65 struct mobj *ta_mobj; 66 uint8_t *ta_buf; 67 }; 68 69 static const char tadb_obj_id[] = "ta.db"; 70 static struct tee_tadb_dir *tadb_db; 71 static struct refcount tadb_db_refc; 72 static struct mutex tadb_mutex = MUTEX_INITIALIZER; 73 74 static void file_num_to_str(char *buf, size_t blen, uint32_t file_number) 75 { 76 snprintf(buf, blen, "%" PRIu32 ".ta", file_number); 77 } 78 79 static bool is_null_uuid(const TEE_UUID *uuid) 80 { 81 const TEE_UUID null_uuid = { 0 }; 82 83 return !memcmp(uuid, &null_uuid, sizeof(*uuid)); 84 } 85 86 static TEE_Result ta_operation_open(unsigned int cmd, uint32_t file_number, 87 int *fd) 88 { 89 struct mobj *mobj; 90 TEE_Result res; 91 void *va; 92 uint64_t cookie; 93 struct optee_msg_param params[] = { 94 [0] = { .attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT, 95 .u.value.a = cmd }, 96 [2] = { .attr = OPTEE_MSG_ATTR_TYPE_VALUE_OUTPUT } 97 }; 98 99 va = tee_fs_rpc_cache_alloc(TEE_FS_NAME_MAX, &mobj, &cookie); 100 if (!va) 101 return TEE_ERROR_OUT_OF_MEMORY; 102 103 if (!msg_param_init_memparam(params + 1, mobj, 0, TEE_FS_NAME_MAX, 104 cookie, MSG_PARAM_MEM_DIR_IN)) 105 return TEE_ERROR_BAD_STATE; 106 107 file_num_to_str(va, TEE_FS_NAME_MAX, file_number); 108 109 res = thread_rpc_cmd(OPTEE_MSG_RPC_CMD_FS, ARRAY_SIZE(params), params); 110 if (!res) 111 *fd = params[2].u.value.a; 112 113 return res; 114 } 115 116 static TEE_Result ta_operation_remove(uint32_t file_number) 117 { 118 struct mobj *mobj; 119 void *va; 120 uint64_t cookie; 121 struct optee_msg_param params[2] = { 122 [0] = { .attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT, 123 .u.value.a = OPTEE_MRF_REMOVE }, 124 }; 125 126 va = tee_fs_rpc_cache_alloc(TEE_FS_NAME_MAX, &mobj, &cookie); 127 if (!va) 128 return TEE_ERROR_OUT_OF_MEMORY; 129 130 if (!msg_param_init_memparam(params + 1, mobj, 0, TEE_FS_NAME_MAX, 131 cookie, MSG_PARAM_MEM_DIR_IN)) 132 return TEE_ERROR_BAD_STATE; 133 134 file_num_to_str(va, TEE_FS_NAME_MAX, file_number); 135 136 return thread_rpc_cmd(OPTEE_MSG_RPC_CMD_FS, ARRAY_SIZE(params), params); 137 } 138 139 static TEE_Result maybe_grow_files(struct tee_tadb_dir *db, int idx) 140 { 141 void *p; 142 143 if (idx < db->nbits) 144 return TEE_SUCCESS; 145 146 p = realloc(db->files, bitstr_size(idx + 1)); 147 if (!p) 148 return TEE_ERROR_OUT_OF_MEMORY; 149 db->files = p; 150 151 bit_nclear(db->files, db->nbits, idx); 152 db->nbits = idx + 1; 153 154 return TEE_SUCCESS; 155 } 156 157 static TEE_Result set_file(struct tee_tadb_dir *db, int idx) 158 { 159 TEE_Result res = maybe_grow_files(db, idx); 160 161 if (!res) 162 bit_set(db->files, idx); 163 164 return res; 165 } 166 167 static void clear_file(struct tee_tadb_dir *db, int idx) 168 { 169 if (idx < db->nbits) 170 bit_clear(db->files, idx); 171 } 172 173 static bool test_file(struct tee_tadb_dir *db, int idx) 174 { 175 if (idx < db->nbits) 176 return bit_test(db->files, idx); 177 178 return false; 179 } 180 181 static TEE_Result read_ent(struct tee_tadb_dir *db, size_t idx, 182 struct tadb_entry *entry) 183 { 184 size_t l = sizeof(*entry); 185 TEE_Result res = db->ops->read(db->fh, idx * l, entry, &l); 186 187 if (!res && l != sizeof(*entry)) 188 return TEE_ERROR_ITEM_NOT_FOUND; 189 190 return res; 191 } 192 193 static TEE_Result write_ent(struct tee_tadb_dir *db, size_t idx, 194 const struct tadb_entry *entry) 195 { 196 const size_t l = sizeof(*entry); 197 198 return db->ops->write(db->fh, idx * l, entry, l); 199 } 200 201 static TEE_Result tadb_open(struct tee_tadb_dir **db_ret) 202 { 203 TEE_Result res; 204 struct tee_tadb_dir *db = calloc(1, sizeof(*db)); 205 struct tee_pobj po = { 206 .obj_id = (void *)tadb_obj_id, 207 .obj_id_len = sizeof(tadb_obj_id) 208 }; 209 210 if (!db) 211 return TEE_ERROR_OUT_OF_MEMORY; 212 213 db->ops = tee_svc_storage_file_ops(TEE_STORAGE_PRIVATE); 214 215 res = db->ops->open(&po, NULL, &db->fh); 216 if (res == TEE_ERROR_ITEM_NOT_FOUND) 217 res = db->ops->create(&po, false, NULL, 0, NULL, 0, NULL, 0, 218 &db->fh); 219 220 if (res) 221 free(db); 222 else 223 *db_ret = db; 224 225 return res; 226 } 227 228 static TEE_Result tee_tadb_open(struct tee_tadb_dir **db) 229 { 230 if (!refcount_inc(&tadb_db_refc)) { 231 TEE_Result res; 232 233 mutex_lock(&tadb_mutex); 234 res = tadb_open(&tadb_db); 235 if (!res) 236 refcount_set(&tadb_db_refc, 1); 237 mutex_unlock(&tadb_mutex); 238 if (res) 239 return res; 240 } 241 242 *db = tadb_db; 243 return TEE_SUCCESS; 244 } 245 246 static void tadb_put(struct tee_tadb_dir *db) 247 { 248 if (refcount_dec(&tadb_db_refc)) { 249 mutex_lock(&tadb_mutex); 250 if (!refcount_val(&tadb_db_refc) && tadb_db) { 251 db->ops->close(&db->fh); 252 free(db->files); 253 free(db); 254 tadb_db = NULL; 255 } 256 mutex_unlock(&tadb_mutex); 257 } 258 } 259 260 static void tee_tadb_close(struct tee_tadb_dir *db) 261 { 262 tadb_put(db); 263 } 264 265 static TEE_Result tadb_authenc_init(TEE_OperationMode mode, 266 const struct tadb_entry *entry, 267 void **ctx_ret) 268 { 269 TEE_Result res; 270 void *ctx; 271 size_t sz; 272 const size_t enc_size = entry->prop.custom_size + entry->prop.bin_size; 273 274 res = crypto_authenc_get_ctx_size(TADB_AUTH_ENC_ALG, &sz); 275 if (res) 276 return res; 277 278 ctx = malloc(sz); 279 if (!ctx) 280 return TEE_ERROR_OUT_OF_MEMORY; 281 282 res = crypto_authenc_init(ctx, TADB_AUTH_ENC_ALG, mode, 283 entry->key, sizeof(entry->key), 284 entry->iv, sizeof(entry->iv), 285 sizeof(entry->tag), 0, enc_size); 286 if (res) 287 free(ctx); 288 else 289 *ctx_ret = ctx; 290 291 return res; 292 } 293 294 static TEE_Result tadb_update_payload(void *ctx, TEE_OperationMode mode, 295 const void *src, size_t len, void *dst) 296 { 297 TEE_Result res; 298 size_t sz = len; 299 300 res = crypto_authenc_update_payload(ctx, TADB_AUTH_ENC_ALG, mode, 301 (const uint8_t *)src, len, dst, 302 &sz); 303 assert(res || sz == len); 304 return res; 305 } 306 307 static TEE_Result populate_files(struct tee_tadb_dir *db) 308 { 309 TEE_Result res; 310 size_t idx; 311 312 /* 313 * If db->files isn't NULL the bitfield is already populated and 314 * there's nothing left to do here for now. 315 */ 316 if (db->files) 317 return TEE_SUCCESS; 318 319 /* 320 * Iterate over the TA database and set the bits in the bit field 321 * for used file numbers. Note that set_file() will allocate and 322 * grow the bitfield as needed. 323 * 324 * At the same time clean out duplicate file numbers, the first 325 * entry with the file number has precedence. Duplicate entries is 326 * not supposed to be able to happen, but if it still does better 327 * to clean it out here instead of letting the error spread with 328 * unexpected side effects. 329 */ 330 for (idx = 0;; idx++) { 331 struct tadb_entry entry; 332 333 res = read_ent(db, idx, &entry); 334 if (res) { 335 if (res == TEE_ERROR_ITEM_NOT_FOUND) 336 return TEE_SUCCESS; 337 goto err; 338 } 339 340 if (is_null_uuid(&entry.prop.uuid)) 341 continue; 342 343 if (test_file(db, entry.file_number)) { 344 IMSG("Clearing duplicate file number %" PRIu32, 345 entry.file_number); 346 memset(&entry, 0, sizeof(entry)); 347 res = write_ent(db, idx, &entry); 348 if (res) 349 goto err; 350 continue; 351 } 352 353 res = set_file(db, entry.file_number); 354 if (res) 355 goto err; 356 } 357 358 err: 359 free(db->files); 360 db->files = NULL; 361 db->nbits = 0; 362 363 return res; 364 } 365 366 TEE_Result tee_tadb_ta_create(const struct tee_tadb_property *property, 367 struct tee_tadb_ta_write **ta_ret) 368 { 369 TEE_Result res; 370 struct tee_tadb_ta_write *ta; 371 int i = 0; 372 373 if (is_null_uuid(&property->uuid)) 374 return TEE_ERROR_GENERIC; 375 376 ta = calloc(1, sizeof(*ta)); 377 if (!ta) 378 return TEE_ERROR_OUT_OF_MEMORY; 379 380 res = tee_tadb_open(&ta->db); 381 if (res) 382 goto err; 383 384 mutex_lock(&tadb_mutex); 385 386 /* 387 * Since we're going to search for next free file number below we 388 * need to populate the bitfield holding used file numbers. 389 */ 390 res = populate_files(ta->db); 391 if (res) 392 goto err_mutex; 393 394 if (ta->db->files) { 395 bit_ffc(ta->db->files, ta->db->nbits, &i); 396 if (i == -1) 397 i = ta->db->nbits; 398 } 399 400 res = set_file(ta->db, i); 401 if (res) 402 goto err_mutex; 403 404 mutex_unlock(&tadb_mutex); 405 406 ta->entry.file_number = i; 407 ta->entry.prop = *property; 408 409 res = crypto_rng_read(ta->entry.iv, sizeof(ta->entry.iv)); 410 if (res) 411 goto err; 412 413 res = crypto_rng_read(ta->entry.key, sizeof(ta->entry.key)); 414 if (res) 415 goto err; 416 417 res = ta_operation_open(OPTEE_MRF_CREATE, ta->entry.file_number, 418 &ta->fd); 419 if (res) 420 goto err; 421 422 res = tadb_authenc_init(TEE_MODE_ENCRYPT, &ta->entry, &ta->ctx); 423 if (res) 424 goto err; 425 426 *ta_ret = ta; 427 428 return TEE_SUCCESS; 429 430 err_mutex: 431 mutex_unlock(&tadb_mutex); 432 err: 433 tadb_put(ta->db); 434 free(ta); 435 436 return res; 437 } 438 439 TEE_Result tee_tadb_ta_write(struct tee_tadb_ta_write *ta, const void *buf, 440 size_t len) 441 { 442 TEE_Result res; 443 const uint8_t *rb = buf; 444 size_t rl = len; 445 struct tee_fs_rpc_operation op; 446 447 while (rl) { 448 size_t wl = MIN(rl, TADB_MAX_BUFFER_SIZE); 449 void *wb; 450 451 res = tee_fs_rpc_write_init(&op, OPTEE_MSG_RPC_CMD_FS, ta->fd, 452 ta->pos, wl, &wb); 453 if (res) 454 return res; 455 456 res = tadb_update_payload(ta->ctx, TEE_MODE_ENCRYPT, 457 rb, wl, wb); 458 if (res) 459 return res; 460 461 res = tee_fs_rpc_write_final(&op); 462 if (res) 463 return res; 464 465 rl -= wl; 466 rb += wl; 467 ta->pos += wl; 468 } 469 470 return TEE_SUCCESS; 471 } 472 473 void tee_tadb_ta_close_and_delete(struct tee_tadb_ta_write *ta) 474 { 475 crypto_authenc_final(ta->ctx, TADB_AUTH_ENC_ALG); 476 free(ta->ctx); 477 tee_fs_rpc_close(OPTEE_MSG_RPC_CMD_FS, ta->fd); 478 ta_operation_remove(ta->entry.file_number); 479 480 mutex_lock(&tadb_mutex); 481 clear_file(ta->db, ta->entry.file_number); 482 mutex_unlock(&tadb_mutex); 483 484 tadb_put(ta->db); 485 free(ta); 486 } 487 488 static TEE_Result find_ent(struct tee_tadb_dir *db, const TEE_UUID *uuid, 489 size_t *idx_ret, struct tadb_entry *entry_ret) 490 { 491 TEE_Result res; 492 size_t idx; 493 494 /* 495 * Search for the provided uuid, if it's found return the index it 496 * has together with TEE_SUCCESS. 497 * 498 * If the uuid can't be found return the number indexes together 499 * with TEE_ERROR_ITEM_NOT_FOUND. 500 */ 501 for (idx = 0;; idx++) { 502 struct tadb_entry entry; 503 504 res = read_ent(db, idx, &entry); 505 if (res) { 506 if (res == TEE_ERROR_ITEM_NOT_FOUND) 507 break; 508 return res; 509 } 510 511 if (!memcmp(&entry.prop.uuid, uuid, sizeof(*uuid))) { 512 if (entry_ret) 513 *entry_ret = entry; 514 break; 515 } 516 } 517 518 *idx_ret = idx; 519 return res; 520 } 521 522 static TEE_Result find_free_ent_idx(struct tee_tadb_dir *db, size_t *idx) 523 { 524 const TEE_UUID null_uuid = { 0 }; 525 TEE_Result res = find_ent(db, &null_uuid, idx, NULL); 526 527 /* 528 * Note that *idx is set to the number of entries on 529 * TEE_ERROR_ITEM_NOT_FOUND. 530 */ 531 if (res == TEE_ERROR_ITEM_NOT_FOUND) 532 return TEE_SUCCESS; 533 return res; 534 } 535 536 TEE_Result tee_tadb_ta_close_and_commit(struct tee_tadb_ta_write *ta) 537 { 538 TEE_Result res; 539 size_t dsz = 0; 540 size_t sz = sizeof(ta->entry.tag); 541 size_t idx; 542 struct tadb_entry old_ent; 543 bool have_old_ent = false; 544 545 res = crypto_authenc_enc_final(ta->ctx, TADB_AUTH_ENC_ALG, 546 NULL, 0, NULL, &dsz, 547 ta->entry.tag, &sz); 548 if (res) 549 goto err; 550 551 tee_fs_rpc_close(OPTEE_MSG_RPC_CMD_FS, ta->fd); 552 553 mutex_lock(&tadb_mutex); 554 /* 555 * First try to find an existing TA to replace. If there's one 556 * we'll use the entry, but we should also remove the old encrypted 557 * file. 558 * 559 * If there isn't an existing TA to replace, grab a new entry. 560 */ 561 res = find_ent(ta->db, &ta->entry.prop.uuid, &idx, &old_ent); 562 if (!res) { 563 have_old_ent = true; 564 } else { 565 res = find_free_ent_idx(ta->db, &idx); 566 if (res) 567 goto err_mutex; 568 } 569 res = write_ent(ta->db, idx, &ta->entry); 570 if (res) 571 goto err_mutex; 572 if (have_old_ent) 573 clear_file(ta->db, old_ent.file_number); 574 mutex_unlock(&tadb_mutex); 575 576 crypto_authenc_final(ta->ctx, TADB_AUTH_ENC_ALG); 577 free(ta->ctx); 578 tadb_put(ta->db); 579 free(ta); 580 if (have_old_ent) 581 ta_operation_remove(old_ent.file_number); 582 return TEE_SUCCESS; 583 584 err_mutex: 585 mutex_unlock(&tadb_mutex); 586 err: 587 tee_tadb_ta_close_and_delete(ta); 588 return res; 589 } 590 591 TEE_Result tee_tadb_ta_delete(const TEE_UUID *uuid) 592 { 593 const struct tadb_entry null_entry = { { { 0 } } }; 594 struct tee_tadb_dir *db; 595 struct tadb_entry entry; 596 size_t idx; 597 TEE_Result res; 598 599 if (is_null_uuid(uuid)) 600 return TEE_ERROR_GENERIC; 601 602 res = tee_tadb_open(&db); 603 if (res) 604 return res; 605 606 mutex_lock(&tadb_mutex); 607 res = find_ent(db, uuid, &idx, &entry); 608 if (res) { 609 mutex_unlock(&tadb_mutex); 610 tee_tadb_close(db); 611 return res; 612 } 613 614 clear_file(db, entry.file_number); 615 res = write_ent(db, idx, &null_entry); 616 mutex_unlock(&tadb_mutex); 617 618 tee_tadb_close(db); 619 if (res) 620 return res; 621 622 ta_operation_remove(entry.file_number); 623 return TEE_SUCCESS; 624 } 625 626 TEE_Result tee_tadb_ta_open(const TEE_UUID *uuid, 627 struct tee_tadb_ta_read **ta_ret) 628 { 629 TEE_Result res; 630 size_t idx; 631 struct tee_tadb_ta_read *ta; 632 static struct tadb_entry last_entry; 633 634 if (is_null_uuid(uuid)) 635 return TEE_ERROR_GENERIC; 636 637 ta = calloc(1, sizeof(*ta)); 638 if (!ta) 639 return TEE_ERROR_OUT_OF_MEMORY; 640 641 if (!memcmp(uuid, &last_entry.prop.uuid, sizeof(*uuid))) { 642 ta->entry = last_entry; 643 } else { 644 res = tee_tadb_open(&ta->db); 645 if (res) 646 goto err_free; /* Mustn't all tadb_put() */ 647 648 mutex_read_lock(&tadb_mutex); 649 res = find_ent(ta->db, uuid, &idx, &ta->entry); 650 mutex_read_unlock(&tadb_mutex); 651 if (res) 652 goto err; 653 } 654 655 res = ta_operation_open(OPTEE_MRF_OPEN, ta->entry.file_number, &ta->fd); 656 if (res) 657 goto err; 658 659 res = tadb_authenc_init(TEE_MODE_DECRYPT, &ta->entry, &ta->ctx); 660 if (res) 661 goto err; 662 663 *ta_ret = ta; 664 665 return TEE_SUCCESS; 666 err: 667 tadb_put(ta->db); 668 err_free: 669 free(ta); 670 return res; 671 } 672 673 const struct tee_tadb_property * 674 tee_tadb_ta_get_property(struct tee_tadb_ta_read *ta) 675 { 676 return &ta->entry.prop; 677 } 678 679 static TEE_Result ta_load(struct tee_tadb_ta_read *ta) 680 { 681 TEE_Result res; 682 const size_t sz = ta->entry.prop.custom_size + ta->entry.prop.bin_size; 683 struct optee_msg_param params[2] = { 684 [0] = { .attr = OPTEE_MSG_ATTR_TYPE_VALUE_INPUT, 685 .u.value.a = OPTEE_MRF_READ, 686 .u.value.b = ta->fd, 687 .u.value.c = 0 }, 688 }; 689 690 if (ta->ta_mobj) 691 return TEE_SUCCESS; 692 693 ta->ta_mobj = thread_rpc_alloc_payload(sz, &ta->ta_cookie); 694 if (!ta->ta_mobj) 695 return TEE_ERROR_OUT_OF_MEMORY; 696 697 ta->ta_buf = mobj_get_va(ta->ta_mobj, 0); 698 assert(ta->ta_buf); 699 700 if (!msg_param_init_memparam(params + 1, ta->ta_mobj, 0, sz, 701 ta->ta_cookie, MSG_PARAM_MEM_DIR_OUT)) 702 return TEE_ERROR_BAD_STATE; 703 704 res = thread_rpc_cmd(OPTEE_MSG_RPC_CMD_FS, ARRAY_SIZE(params), params); 705 if (res) { 706 thread_rpc_free_payload(ta->ta_cookie, ta->ta_mobj); 707 ta->ta_mobj = NULL; 708 } 709 return res; 710 } 711 712 TEE_Result tee_tadb_ta_read(struct tee_tadb_ta_read *ta, void *buf, size_t *len) 713 { 714 TEE_Result res; 715 const size_t sz = ta->entry.prop.custom_size + ta->entry.prop.bin_size; 716 size_t l = MIN(*len, sz - ta->pos); 717 718 res = ta_load(ta); 719 if (res) 720 return res; 721 722 if (buf) { 723 res = tadb_update_payload(ta->ctx, TEE_MODE_DECRYPT, 724 ta->ta_buf + ta->pos, l, buf); 725 if (res) 726 return res; 727 } else { 728 size_t num_bytes = 0; 729 size_t b_size = MIN(SIZE_4K, l); 730 uint8_t *b = malloc(b_size); 731 732 if (!b) 733 return TEE_ERROR_OUT_OF_MEMORY; 734 735 while (num_bytes < l) { 736 size_t n = MIN(b_size, l - num_bytes); 737 738 res = tadb_update_payload(ta->ctx, TEE_MODE_DECRYPT, 739 ta->ta_buf + ta->pos, n, b); 740 if (res) 741 break; 742 num_bytes += n; 743 } 744 745 free(b); 746 if (res) 747 return res; 748 } 749 750 ta->pos += l; 751 *len = l; 752 return TEE_SUCCESS; 753 } 754 755 void tee_tadb_ta_close(struct tee_tadb_ta_read *ta) 756 { 757 crypto_authenc_final(ta->ctx, TADB_AUTH_ENC_ALG); 758 free(ta->ctx); 759 if (ta->ta_mobj) 760 thread_rpc_free_payload(ta->ta_cookie, ta->ta_mobj); 761 tee_fs_rpc_close(OPTEE_MSG_RPC_CMD_FS, ta->fd); 762 tadb_put(ta->db); 763 free(ta); 764 } 765