1 /* 2 * Copyright (c) 2019 Yubico AB. All rights reserved. 3 * Use of this source code is governed by a BSD-style 4 * license that can be found in the LICENSE file. 5 */ 6 7 #include <openssl/sha.h> 8 9 #include <string.h> 10 11 #include "fido.h" 12 #include "fido/credman.h" 13 #include "fido/es256.h" 14 15 #define CMD_CRED_METADATA 0x01 16 #define CMD_RP_BEGIN 0x02 17 #define CMD_RP_NEXT 0x03 18 #define CMD_RK_BEGIN 0x04 19 #define CMD_RK_NEXT 0x05 20 #define CMD_DELETE_CRED 0x06 21 22 static int 23 credman_grow_array(void **ptr, size_t *n_alloc, size_t *n_rx, size_t n, 24 size_t size) 25 { 26 void *new_ptr; 27 28 #ifdef FIDO_FUZZ 29 if (n > UINT8_MAX) { 30 fido_log_debug("%s: n > UINT8_MAX", __func__); 31 return (-1); 32 } 33 #endif 34 35 if (n < *n_alloc) 36 return (0); 37 38 /* sanity check */ 39 if (*n_rx > 0 || *n_rx > *n_alloc || n < *n_alloc) { 40 fido_log_debug("%s: n=%zu, n_rx=%zu, n_alloc=%zu", __func__, n, 41 *n_rx, *n_alloc); 42 return (-1); 43 } 44 45 if ((new_ptr = recallocarray(*ptr, *n_alloc, n, size)) == NULL) 46 return (-1); 47 48 *ptr = new_ptr; 49 *n_alloc = n; 50 51 return (0); 52 } 53 54 static int 55 credman_prepare_hmac(uint8_t cmd, const fido_blob_t *body, cbor_item_t **param, 56 fido_blob_t *hmac_data) 57 { 58 cbor_item_t *param_cbor[2]; 59 size_t n; 60 int ok = -1; 61 62 memset(¶m_cbor, 0, sizeof(param_cbor)); 63 64 if (body == NULL) 65 return (fido_blob_set(hmac_data, &cmd, sizeof(cmd))); 66 67 switch (cmd) { 68 case CMD_RK_BEGIN: 69 n = 1; 70 param_cbor[n - 1] = fido_blob_encode(body); 71 break; 72 case CMD_DELETE_CRED: 73 n = 2; 74 param_cbor[n - 1] = cbor_encode_pubkey(body); 75 break; 76 default: 77 fido_log_debug("%s: unknown cmd=0x%02x", __func__, cmd); 78 return (-1); 79 } 80 81 if (param_cbor[n - 1] == NULL) { 82 fido_log_debug("%s: cbor encode", __func__); 83 return (-1); 84 } 85 if ((*param = cbor_flatten_vector(param_cbor, n)) == NULL) { 86 fido_log_debug("%s: cbor_flatten_vector", __func__); 87 goto fail; 88 } 89 if (cbor_build_frame(cmd, param_cbor, n, hmac_data) < 0) { 90 fido_log_debug("%s: cbor_build_frame", __func__); 91 goto fail; 92 } 93 94 ok = 0; 95 fail: 96 cbor_vector_free(param_cbor, nitems(param_cbor)); 97 98 return (ok); 99 } 100 101 static int 102 credman_tx(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *param, 103 const char *pin) 104 { 105 fido_blob_t f; 106 fido_blob_t *ecdh = NULL; 107 fido_blob_t hmac; 108 es256_pk_t *pk = NULL; 109 cbor_item_t *argv[4]; 110 int r = FIDO_ERR_INTERNAL; 111 112 memset(&f, 0, sizeof(f)); 113 memset(&hmac, 0, sizeof(hmac)); 114 memset(&argv, 0, sizeof(argv)); 115 116 /* subCommand */ 117 if ((argv[0] = cbor_build_uint8(cmd)) == NULL) { 118 fido_log_debug("%s: cbor encode", __func__); 119 goto fail; 120 } 121 122 /* pinProtocol, pinAuth */ 123 if (pin != NULL) { 124 if (credman_prepare_hmac(cmd, param, &argv[1], &hmac) < 0) { 125 fido_log_debug("%s: credman_prepare_hmac", __func__); 126 goto fail; 127 } 128 if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) { 129 fido_log_debug("%s: fido_do_ecdh", __func__); 130 goto fail; 131 } 132 if ((r = cbor_add_pin_params(dev, &hmac, pk, ecdh, pin, 133 &argv[3], &argv[2])) != FIDO_OK) { 134 fido_log_debug("%s: cbor_add_pin_params", __func__); 135 goto fail; 136 } 137 } 138 139 /* framing and transmission */ 140 if (cbor_build_frame(CTAP_CBOR_CRED_MGMT_PRE, argv, nitems(argv), 141 &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) { 142 fido_log_debug("%s: fido_tx", __func__); 143 r = FIDO_ERR_TX; 144 goto fail; 145 } 146 147 r = FIDO_OK; 148 fail: 149 es256_pk_free(&pk); 150 fido_blob_free(&ecdh); 151 cbor_vector_free(argv, nitems(argv)); 152 free(f.ptr); 153 free(hmac.ptr); 154 155 return (r); 156 } 157 158 static int 159 credman_parse_metadata(const cbor_item_t *key, const cbor_item_t *val, 160 void *arg) 161 { 162 fido_credman_metadata_t *metadata = arg; 163 164 if (cbor_isa_uint(key) == false || 165 cbor_int_get_width(key) != CBOR_INT_8) { 166 fido_log_debug("%s: cbor type", __func__); 167 return (0); /* ignore */ 168 } 169 170 switch (cbor_get_uint8(key)) { 171 case 1: 172 return (cbor_decode_uint64(val, &metadata->rk_existing)); 173 case 2: 174 return (cbor_decode_uint64(val, &metadata->rk_remaining)); 175 default: 176 fido_log_debug("%s: cbor type", __func__); 177 return (0); /* ignore */ 178 } 179 } 180 181 static int 182 credman_rx_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, int ms) 183 { 184 unsigned char reply[FIDO_MAXMSG]; 185 int reply_len; 186 int r; 187 188 memset(metadata, 0, sizeof(*metadata)); 189 190 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 191 ms)) < 0) { 192 fido_log_debug("%s: fido_rx", __func__); 193 return (FIDO_ERR_RX); 194 } 195 196 if ((r = cbor_parse_reply(reply, (size_t)reply_len, metadata, 197 credman_parse_metadata)) != FIDO_OK) { 198 fido_log_debug("%s: credman_parse_metadata", __func__); 199 return (r); 200 } 201 202 return (FIDO_OK); 203 } 204 205 static int 206 credman_get_metadata_wait(fido_dev_t *dev, fido_credman_metadata_t *metadata, 207 const char *pin, int ms) 208 { 209 int r; 210 211 if ((r = credman_tx(dev, CMD_CRED_METADATA, NULL, pin)) != FIDO_OK || 212 (r = credman_rx_metadata(dev, metadata, ms)) != FIDO_OK) 213 return (r); 214 215 return (FIDO_OK); 216 } 217 218 int 219 fido_credman_get_dev_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, 220 const char *pin) 221 { 222 if (fido_dev_is_fido2(dev) == false) 223 return (FIDO_ERR_INVALID_COMMAND); 224 if (pin == NULL) 225 return (FIDO_ERR_INVALID_ARGUMENT); 226 227 return (credman_get_metadata_wait(dev, metadata, pin, -1)); 228 } 229 230 static int 231 credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg) 232 { 233 fido_cred_t *cred = arg; 234 235 if (cbor_isa_uint(key) == false || 236 cbor_int_get_width(key) != CBOR_INT_8) { 237 fido_log_debug("%s: cbor type", __func__); 238 return (0); /* ignore */ 239 } 240 241 switch (cbor_get_uint8(key)) { 242 case 6: /* user entity */ 243 return (cbor_decode_user(val, &cred->user)); 244 case 7: 245 return (cbor_decode_cred_id(val, &cred->attcred.id)); 246 case 8: 247 if (cbor_decode_pubkey(val, &cred->attcred.type, 248 &cred->attcred.pubkey) < 0) 249 return (-1); 250 cred->type = cred->attcred.type; /* XXX */ 251 return (0); 252 default: 253 fido_log_debug("%s: cbor type", __func__); 254 return (0); /* ignore */ 255 } 256 } 257 258 static void 259 credman_reset_rk(fido_credman_rk_t *rk) 260 { 261 for (size_t i = 0; i < rk->n_alloc; i++) { 262 fido_cred_reset_tx(&rk->ptr[i]); 263 fido_cred_reset_rx(&rk->ptr[i]); 264 } 265 266 free(rk->ptr); 267 rk->ptr = NULL; 268 memset(rk, 0, sizeof(*rk)); 269 } 270 271 static int 272 credman_parse_rk_count(const cbor_item_t *key, const cbor_item_t *val, 273 void *arg) 274 { 275 fido_credman_rk_t *rk = arg; 276 uint64_t n; 277 278 /* totalCredentials */ 279 if (cbor_isa_uint(key) == false || 280 cbor_int_get_width(key) != CBOR_INT_8 || 281 cbor_get_uint8(key) != 9) { 282 fido_log_debug("%s: cbor_type", __func__); 283 return (0); /* ignore */ 284 } 285 286 if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) { 287 fido_log_debug("%s: cbor_decode_uint64", __func__); 288 return (-1); 289 } 290 291 if (credman_grow_array((void **)&rk->ptr, &rk->n_alloc, &rk->n_rx, 292 (size_t)n, sizeof(*rk->ptr)) < 0) { 293 fido_log_debug("%s: credman_grow_array", __func__); 294 return (-1); 295 } 296 297 return (0); 298 } 299 300 static int 301 credman_rx_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms) 302 { 303 unsigned char reply[FIDO_MAXMSG]; 304 int reply_len; 305 int r; 306 307 credman_reset_rk(rk); 308 309 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 310 ms)) < 0) { 311 fido_log_debug("%s: fido_rx", __func__); 312 return (FIDO_ERR_RX); 313 } 314 315 /* adjust as needed */ 316 if ((r = cbor_parse_reply(reply, (size_t)reply_len, rk, 317 credman_parse_rk_count)) != FIDO_OK) { 318 fido_log_debug("%s: credman_parse_rk_count", __func__); 319 return (r); 320 } 321 322 if (rk->n_alloc == 0) { 323 fido_log_debug("%s: n_alloc=0", __func__); 324 return (FIDO_OK); 325 } 326 327 /* parse the first rk */ 328 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rk->ptr[0], 329 credman_parse_rk)) != FIDO_OK) { 330 fido_log_debug("%s: credman_parse_rk", __func__); 331 return (r); 332 } 333 334 rk->n_rx++; 335 336 return (FIDO_OK); 337 } 338 339 static int 340 credman_rx_next_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms) 341 { 342 unsigned char reply[FIDO_MAXMSG]; 343 int reply_len; 344 int r; 345 346 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 347 ms)) < 0) { 348 fido_log_debug("%s: fido_rx", __func__); 349 return (FIDO_ERR_RX); 350 } 351 352 /* sanity check */ 353 if (rk->n_rx >= rk->n_alloc) { 354 fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rk->n_rx, 355 rk->n_alloc); 356 return (FIDO_ERR_INTERNAL); 357 } 358 359 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rk->ptr[rk->n_rx], 360 credman_parse_rk)) != FIDO_OK) { 361 fido_log_debug("%s: credman_parse_rk", __func__); 362 return (r); 363 } 364 365 return (FIDO_OK); 366 } 367 368 static int 369 credman_get_rk_wait(fido_dev_t *dev, const char *rp_id, fido_credman_rk_t *rk, 370 const char *pin, int ms) 371 { 372 fido_blob_t rp_dgst; 373 uint8_t dgst[SHA256_DIGEST_LENGTH]; 374 int r; 375 376 if (SHA256((const unsigned char *)rp_id, strlen(rp_id), dgst) != dgst) { 377 fido_log_debug("%s: sha256", __func__); 378 return (FIDO_ERR_INTERNAL); 379 } 380 381 rp_dgst.ptr = dgst; 382 rp_dgst.len = sizeof(dgst); 383 384 if ((r = credman_tx(dev, CMD_RK_BEGIN, &rp_dgst, pin)) != FIDO_OK || 385 (r = credman_rx_rk(dev, rk, ms)) != FIDO_OK) 386 return (r); 387 388 while (rk->n_rx < rk->n_alloc) { 389 if ((r = credman_tx(dev, CMD_RK_NEXT, NULL, NULL)) != FIDO_OK || 390 (r = credman_rx_next_rk(dev, rk, ms)) != FIDO_OK) 391 return (r); 392 rk->n_rx++; 393 } 394 395 return (FIDO_OK); 396 } 397 398 int 399 fido_credman_get_dev_rk(fido_dev_t *dev, const char *rp_id, 400 fido_credman_rk_t *rk, const char *pin) 401 { 402 if (fido_dev_is_fido2(dev) == false) 403 return (FIDO_ERR_INVALID_COMMAND); 404 if (pin == NULL) 405 return (FIDO_ERR_INVALID_ARGUMENT); 406 407 return (credman_get_rk_wait(dev, rp_id, rk, pin, -1)); 408 } 409 410 static int 411 credman_del_rk_wait(fido_dev_t *dev, const unsigned char *cred_id, 412 size_t cred_id_len, const char *pin, int ms) 413 { 414 fido_blob_t cred; 415 int r; 416 417 memset(&cred, 0, sizeof(cred)); 418 419 if (fido_blob_set(&cred, cred_id, cred_id_len) < 0) 420 return (FIDO_ERR_INVALID_ARGUMENT); 421 422 if ((r = credman_tx(dev, CMD_DELETE_CRED, &cred, pin)) != FIDO_OK || 423 (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) 424 goto fail; 425 426 r = FIDO_OK; 427 fail: 428 free(cred.ptr); 429 430 return (r); 431 } 432 433 int 434 fido_credman_del_dev_rk(fido_dev_t *dev, const unsigned char *cred_id, 435 size_t cred_id_len, const char *pin) 436 { 437 if (fido_dev_is_fido2(dev) == false) 438 return (FIDO_ERR_INVALID_COMMAND); 439 if (pin == NULL) 440 return (FIDO_ERR_INVALID_ARGUMENT); 441 442 return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, -1)); 443 } 444 445 static int 446 credman_parse_rp(const cbor_item_t *key, const cbor_item_t *val, void *arg) 447 { 448 struct fido_credman_single_rp *rp = arg; 449 450 if (cbor_isa_uint(key) == false || 451 cbor_int_get_width(key) != CBOR_INT_8) { 452 fido_log_debug("%s: cbor type", __func__); 453 return (0); /* ignore */ 454 } 455 456 switch (cbor_get_uint8(key)) { 457 case 3: 458 return (cbor_decode_rp_entity(val, &rp->rp_entity)); 459 case 4: 460 return (fido_blob_decode(val, &rp->rp_id_hash)); 461 default: 462 fido_log_debug("%s: cbor type", __func__); 463 return (0); /* ignore */ 464 } 465 } 466 467 static void 468 credman_reset_rp(fido_credman_rp_t *rp) 469 { 470 for (size_t i = 0; i < rp->n_alloc; i++) { 471 free(rp->ptr[i].rp_entity.id); 472 free(rp->ptr[i].rp_entity.name); 473 rp->ptr[i].rp_entity.id = NULL; 474 rp->ptr[i].rp_entity.name = NULL; 475 free(rp->ptr[i].rp_id_hash.ptr); 476 memset(&rp->ptr[i].rp_id_hash, 0, 477 sizeof(rp->ptr[i].rp_id_hash)); 478 } 479 480 free(rp->ptr); 481 rp->ptr = NULL; 482 memset(rp, 0, sizeof(*rp)); 483 } 484 485 static int 486 credman_parse_rp_count(const cbor_item_t *key, const cbor_item_t *val, 487 void *arg) 488 { 489 fido_credman_rp_t *rp = arg; 490 uint64_t n; 491 492 /* totalRPs */ 493 if (cbor_isa_uint(key) == false || 494 cbor_int_get_width(key) != CBOR_INT_8 || 495 cbor_get_uint8(key) != 5) { 496 fido_log_debug("%s: cbor_type", __func__); 497 return (0); /* ignore */ 498 } 499 500 if (cbor_decode_uint64(val, &n) < 0 || n > SIZE_MAX) { 501 fido_log_debug("%s: cbor_decode_uint64", __func__); 502 return (-1); 503 } 504 505 if (credman_grow_array((void **)&rp->ptr, &rp->n_alloc, &rp->n_rx, 506 (size_t)n, sizeof(*rp->ptr)) < 0) { 507 fido_log_debug("%s: credman_grow_array", __func__); 508 return (-1); 509 } 510 511 return (0); 512 } 513 514 static int 515 credman_rx_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms) 516 { 517 unsigned char reply[FIDO_MAXMSG]; 518 int reply_len; 519 int r; 520 521 credman_reset_rp(rp); 522 523 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 524 ms)) < 0) { 525 fido_log_debug("%s: fido_rx", __func__); 526 return (FIDO_ERR_RX); 527 } 528 529 /* adjust as needed */ 530 if ((r = cbor_parse_reply(reply, (size_t)reply_len, rp, 531 credman_parse_rp_count)) != FIDO_OK) { 532 fido_log_debug("%s: credman_parse_rp_count", __func__); 533 return (r); 534 } 535 536 if (rp->n_alloc == 0) { 537 fido_log_debug("%s: n_alloc=0", __func__); 538 return (FIDO_OK); 539 } 540 541 /* parse the first rp */ 542 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rp->ptr[0], 543 credman_parse_rp)) != FIDO_OK) { 544 fido_log_debug("%s: credman_parse_rp", __func__); 545 return (r); 546 } 547 548 rp->n_rx++; 549 550 return (FIDO_OK); 551 } 552 553 static int 554 credman_rx_next_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms) 555 { 556 unsigned char reply[FIDO_MAXMSG]; 557 int reply_len; 558 int r; 559 560 if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply), 561 ms)) < 0) { 562 fido_log_debug("%s: fido_rx", __func__); 563 return (FIDO_ERR_RX); 564 } 565 566 /* sanity check */ 567 if (rp->n_rx >= rp->n_alloc) { 568 fido_log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rp->n_rx, 569 rp->n_alloc); 570 return (FIDO_ERR_INTERNAL); 571 } 572 573 if ((r = cbor_parse_reply(reply, (size_t)reply_len, &rp->ptr[rp->n_rx], 574 credman_parse_rp)) != FIDO_OK) { 575 fido_log_debug("%s: credman_parse_rp", __func__); 576 return (r); 577 } 578 579 return (FIDO_OK); 580 } 581 582 static int 583 credman_get_rp_wait(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin, 584 int ms) 585 { 586 int r; 587 588 if ((r = credman_tx(dev, CMD_RP_BEGIN, NULL, pin)) != FIDO_OK || 589 (r = credman_rx_rp(dev, rp, ms)) != FIDO_OK) 590 return (r); 591 592 while (rp->n_rx < rp->n_alloc) { 593 if ((r = credman_tx(dev, CMD_RP_NEXT, NULL, NULL)) != FIDO_OK || 594 (r = credman_rx_next_rp(dev, rp, ms)) != FIDO_OK) 595 return (r); 596 rp->n_rx++; 597 } 598 599 return (FIDO_OK); 600 } 601 602 int 603 fido_credman_get_dev_rp(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin) 604 { 605 if (fido_dev_is_fido2(dev) == false) 606 return (FIDO_ERR_INVALID_COMMAND); 607 if (pin == NULL) 608 return (FIDO_ERR_INVALID_ARGUMENT); 609 610 return (credman_get_rp_wait(dev, rp, pin, -1)); 611 } 612 613 fido_credman_rk_t * 614 fido_credman_rk_new(void) 615 { 616 return (calloc(1, sizeof(fido_credman_rk_t))); 617 } 618 619 void 620 fido_credman_rk_free(fido_credman_rk_t **rk_p) 621 { 622 fido_credman_rk_t *rk; 623 624 if (rk_p == NULL || (rk = *rk_p) == NULL) 625 return; 626 627 credman_reset_rk(rk); 628 free(rk); 629 *rk_p = NULL; 630 } 631 632 size_t 633 fido_credman_rk_count(const fido_credman_rk_t *rk) 634 { 635 return (rk->n_rx); 636 } 637 638 const fido_cred_t * 639 fido_credman_rk(const fido_credman_rk_t *rk, size_t idx) 640 { 641 if (idx >= rk->n_alloc) 642 return (NULL); 643 644 return (&rk->ptr[idx]); 645 } 646 647 fido_credman_metadata_t * 648 fido_credman_metadata_new(void) 649 { 650 return (calloc(1, sizeof(fido_credman_metadata_t))); 651 } 652 653 void 654 fido_credman_metadata_free(fido_credman_metadata_t **metadata_p) 655 { 656 fido_credman_metadata_t *metadata; 657 658 if (metadata_p == NULL || (metadata = *metadata_p) == NULL) 659 return; 660 661 free(metadata); 662 *metadata_p = NULL; 663 } 664 665 uint64_t 666 fido_credman_rk_existing(const fido_credman_metadata_t *metadata) 667 { 668 return (metadata->rk_existing); 669 } 670 671 uint64_t 672 fido_credman_rk_remaining(const fido_credman_metadata_t *metadata) 673 { 674 return (metadata->rk_remaining); 675 } 676 677 fido_credman_rp_t * 678 fido_credman_rp_new(void) 679 { 680 return (calloc(1, sizeof(fido_credman_rp_t))); 681 } 682 683 void 684 fido_credman_rp_free(fido_credman_rp_t **rp_p) 685 { 686 fido_credman_rp_t *rp; 687 688 if (rp_p == NULL || (rp = *rp_p) == NULL) 689 return; 690 691 credman_reset_rp(rp); 692 free(rp); 693 *rp_p = NULL; 694 } 695 696 size_t 697 fido_credman_rp_count(const fido_credman_rp_t *rp) 698 { 699 return (rp->n_rx); 700 } 701 702 const char * 703 fido_credman_rp_id(const fido_credman_rp_t *rp, size_t idx) 704 { 705 if (idx >= rp->n_alloc) 706 return (NULL); 707 708 return (rp->ptr[idx].rp_entity.id); 709 } 710 711 const char * 712 fido_credman_rp_name(const fido_credman_rp_t *rp, size_t idx) 713 { 714 if (idx >= rp->n_alloc) 715 return (NULL); 716 717 return (rp->ptr[idx].rp_entity.name); 718 } 719 720 size_t 721 fido_credman_rp_id_hash_len(const fido_credman_rp_t *rp, size_t idx) 722 { 723 if (idx >= rp->n_alloc) 724 return (0); 725 726 return (rp->ptr[idx].rp_id_hash.len); 727 } 728 729 const unsigned char * 730 fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *rp, size_t idx) 731 { 732 if (idx >= rp->n_alloc) 733 return (NULL); 734 735 return (rp->ptr[idx].rp_id_hash.ptr); 736 } 737