1 /* 2 * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the OpenSSL license (the "License"). You may not use 5 * this file except in compliance with the License. You can obtain a copy 6 * in the file LICENSE in the source distribution or at 7 * https://www.openssl.org/source/license.html 8 */ 9 10 #include <stdio.h> 11 #include "crypto/ctype.h" 12 #include <limits.h> 13 #include "internal/cryptlib.h" 14 #include <openssl/lhash.h> 15 #include <openssl/asn1.h> 16 #include "crypto/objects.h" 17 #include <openssl/bn.h> 18 #include "crypto/asn1.h" 19 #include "obj_local.h" 20 21 /* obj_dat.h is generated from objects.h by obj_dat.pl */ 22 #include "obj_dat.h" 23 24 DECLARE_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, sn); 25 DECLARE_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, ln); 26 DECLARE_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, obj); 27 28 #define ADDED_DATA 0 29 #define ADDED_SNAME 1 30 #define ADDED_LNAME 2 31 #define ADDED_NID 3 32 33 struct added_obj_st { 34 int type; 35 ASN1_OBJECT *obj; 36 }; 37 38 static int new_nid = NUM_NID; 39 static LHASH_OF(ADDED_OBJ) *added = NULL; 40 41 static int sn_cmp(const ASN1_OBJECT *const *a, const unsigned int *b) 42 { 43 return strcmp((*a)->sn, nid_objs[*b].sn); 44 } 45 46 IMPLEMENT_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, sn); 47 48 static int ln_cmp(const ASN1_OBJECT *const *a, const unsigned int *b) 49 { 50 return strcmp((*a)->ln, nid_objs[*b].ln); 51 } 52 53 IMPLEMENT_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, ln); 54 55 static unsigned long added_obj_hash(const ADDED_OBJ *ca) 56 { 57 const ASN1_OBJECT *a; 58 int i; 59 unsigned long ret = 0; 60 unsigned char *p; 61 62 a = ca->obj; 63 switch (ca->type) { 64 case ADDED_DATA: 65 ret = a->length << 20L; 66 p = (unsigned char *)a->data; 67 for (i = 0; i < a->length; i++) 68 ret ^= p[i] << ((i * 3) % 24); 69 break; 70 case ADDED_SNAME: 71 ret = OPENSSL_LH_strhash(a->sn); 72 break; 73 case ADDED_LNAME: 74 ret = OPENSSL_LH_strhash(a->ln); 75 break; 76 case ADDED_NID: 77 ret = a->nid; 78 break; 79 default: 80 /* abort(); */ 81 return 0; 82 } 83 ret &= 0x3fffffffL; 84 ret |= ((unsigned long)ca->type) << 30L; 85 return ret; 86 } 87 88 static int added_obj_cmp(const ADDED_OBJ *ca, const ADDED_OBJ *cb) 89 { 90 ASN1_OBJECT *a, *b; 91 int i; 92 93 i = ca->type - cb->type; 94 if (i) 95 return i; 96 a = ca->obj; 97 b = cb->obj; 98 switch (ca->type) { 99 case ADDED_DATA: 100 i = (a->length - b->length); 101 if (i) 102 return i; 103 return memcmp(a->data, b->data, (size_t)a->length); 104 case ADDED_SNAME: 105 if (a->sn == NULL) 106 return -1; 107 else if (b->sn == NULL) 108 return 1; 109 else 110 return strcmp(a->sn, b->sn); 111 case ADDED_LNAME: 112 if (a->ln == NULL) 113 return -1; 114 else if (b->ln == NULL) 115 return 1; 116 else 117 return strcmp(a->ln, b->ln); 118 case ADDED_NID: 119 return a->nid - b->nid; 120 default: 121 /* abort(); */ 122 return 0; 123 } 124 } 125 126 static int init_added(void) 127 { 128 if (added != NULL) 129 return 1; 130 added = lh_ADDED_OBJ_new(added_obj_hash, added_obj_cmp); 131 return added != NULL; 132 } 133 134 static void cleanup1_doall(ADDED_OBJ *a) 135 { 136 a->obj->nid = 0; 137 a->obj->flags |= ASN1_OBJECT_FLAG_DYNAMIC | 138 ASN1_OBJECT_FLAG_DYNAMIC_STRINGS | ASN1_OBJECT_FLAG_DYNAMIC_DATA; 139 } 140 141 static void cleanup2_doall(ADDED_OBJ *a) 142 { 143 a->obj->nid++; 144 } 145 146 static void cleanup3_doall(ADDED_OBJ *a) 147 { 148 if (--a->obj->nid == 0) 149 ASN1_OBJECT_free(a->obj); 150 OPENSSL_free(a); 151 } 152 153 void obj_cleanup_int(void) 154 { 155 if (added == NULL) 156 return; 157 lh_ADDED_OBJ_set_down_load(added, 0); 158 lh_ADDED_OBJ_doall(added, cleanup1_doall); /* zero counters */ 159 lh_ADDED_OBJ_doall(added, cleanup2_doall); /* set counters */ 160 lh_ADDED_OBJ_doall(added, cleanup3_doall); /* free objects */ 161 lh_ADDED_OBJ_free(added); 162 added = NULL; 163 } 164 165 int OBJ_new_nid(int num) 166 { 167 int i; 168 169 i = new_nid; 170 new_nid += num; 171 return i; 172 } 173 174 int OBJ_add_object(const ASN1_OBJECT *obj) 175 { 176 ASN1_OBJECT *o; 177 ADDED_OBJ *ao[4] = { NULL, NULL, NULL, NULL }, *aop; 178 int i; 179 180 if (added == NULL) 181 if (!init_added()) 182 return 0; 183 if ((o = OBJ_dup(obj)) == NULL) 184 goto err; 185 if ((ao[ADDED_NID] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL) 186 goto err2; 187 if ((o->length != 0) && (obj->data != NULL)) 188 if ((ao[ADDED_DATA] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL) 189 goto err2; 190 if (o->sn != NULL) 191 if ((ao[ADDED_SNAME] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL) 192 goto err2; 193 if (o->ln != NULL) 194 if ((ao[ADDED_LNAME] = OPENSSL_malloc(sizeof(*ao[0]))) == NULL) 195 goto err2; 196 197 for (i = ADDED_DATA; i <= ADDED_NID; i++) { 198 if (ao[i] != NULL) { 199 ao[i]->type = i; 200 ao[i]->obj = o; 201 aop = lh_ADDED_OBJ_insert(added, ao[i]); 202 /* memory leak, but should not normally matter */ 203 OPENSSL_free(aop); 204 } 205 } 206 o->flags &= 207 ~(ASN1_OBJECT_FLAG_DYNAMIC | ASN1_OBJECT_FLAG_DYNAMIC_STRINGS | 208 ASN1_OBJECT_FLAG_DYNAMIC_DATA); 209 210 return o->nid; 211 err2: 212 OBJerr(OBJ_F_OBJ_ADD_OBJECT, ERR_R_MALLOC_FAILURE); 213 err: 214 for (i = ADDED_DATA; i <= ADDED_NID; i++) 215 OPENSSL_free(ao[i]); 216 ASN1_OBJECT_free(o); 217 return NID_undef; 218 } 219 220 ASN1_OBJECT *OBJ_nid2obj(int n) 221 { 222 ADDED_OBJ ad, *adp; 223 ASN1_OBJECT ob; 224 225 if ((n >= 0) && (n < NUM_NID)) { 226 if ((n != NID_undef) && (nid_objs[n].nid == NID_undef)) { 227 OBJerr(OBJ_F_OBJ_NID2OBJ, OBJ_R_UNKNOWN_NID); 228 return NULL; 229 } 230 return (ASN1_OBJECT *)&(nid_objs[n]); 231 } else if (added == NULL) { 232 OBJerr(OBJ_F_OBJ_NID2OBJ, OBJ_R_UNKNOWN_NID); 233 return NULL; 234 } else { 235 ad.type = ADDED_NID; 236 ad.obj = &ob; 237 ob.nid = n; 238 adp = lh_ADDED_OBJ_retrieve(added, &ad); 239 if (adp != NULL) 240 return adp->obj; 241 else { 242 OBJerr(OBJ_F_OBJ_NID2OBJ, OBJ_R_UNKNOWN_NID); 243 return NULL; 244 } 245 } 246 } 247 248 const char *OBJ_nid2sn(int n) 249 { 250 ADDED_OBJ ad, *adp; 251 ASN1_OBJECT ob; 252 253 if ((n >= 0) && (n < NUM_NID)) { 254 if ((n != NID_undef) && (nid_objs[n].nid == NID_undef)) { 255 OBJerr(OBJ_F_OBJ_NID2SN, OBJ_R_UNKNOWN_NID); 256 return NULL; 257 } 258 return nid_objs[n].sn; 259 } else if (added == NULL) 260 return NULL; 261 else { 262 ad.type = ADDED_NID; 263 ad.obj = &ob; 264 ob.nid = n; 265 adp = lh_ADDED_OBJ_retrieve(added, &ad); 266 if (adp != NULL) 267 return adp->obj->sn; 268 else { 269 OBJerr(OBJ_F_OBJ_NID2SN, OBJ_R_UNKNOWN_NID); 270 return NULL; 271 } 272 } 273 } 274 275 const char *OBJ_nid2ln(int n) 276 { 277 ADDED_OBJ ad, *adp; 278 ASN1_OBJECT ob; 279 280 if ((n >= 0) && (n < NUM_NID)) { 281 if ((n != NID_undef) && (nid_objs[n].nid == NID_undef)) { 282 OBJerr(OBJ_F_OBJ_NID2LN, OBJ_R_UNKNOWN_NID); 283 return NULL; 284 } 285 return nid_objs[n].ln; 286 } else if (added == NULL) 287 return NULL; 288 else { 289 ad.type = ADDED_NID; 290 ad.obj = &ob; 291 ob.nid = n; 292 adp = lh_ADDED_OBJ_retrieve(added, &ad); 293 if (adp != NULL) 294 return adp->obj->ln; 295 else { 296 OBJerr(OBJ_F_OBJ_NID2LN, OBJ_R_UNKNOWN_NID); 297 return NULL; 298 } 299 } 300 } 301 302 static int obj_cmp(const ASN1_OBJECT *const *ap, const unsigned int *bp) 303 { 304 int j; 305 const ASN1_OBJECT *a = *ap; 306 const ASN1_OBJECT *b = &nid_objs[*bp]; 307 308 j = (a->length - b->length); 309 if (j) 310 return j; 311 if (a->length == 0) 312 return 0; 313 return memcmp(a->data, b->data, a->length); 314 } 315 316 IMPLEMENT_OBJ_BSEARCH_CMP_FN(const ASN1_OBJECT *, unsigned int, obj); 317 318 int OBJ_obj2nid(const ASN1_OBJECT *a) 319 { 320 const unsigned int *op; 321 ADDED_OBJ ad, *adp; 322 323 if (a == NULL) 324 return NID_undef; 325 if (a->nid != 0) 326 return a->nid; 327 328 if (a->length == 0) 329 return NID_undef; 330 331 if (added != NULL) { 332 ad.type = ADDED_DATA; 333 ad.obj = (ASN1_OBJECT *)a; /* XXX: ugly but harmless */ 334 adp = lh_ADDED_OBJ_retrieve(added, &ad); 335 if (adp != NULL) 336 return adp->obj->nid; 337 } 338 op = OBJ_bsearch_obj(&a, obj_objs, NUM_OBJ); 339 if (op == NULL) 340 return NID_undef; 341 return nid_objs[*op].nid; 342 } 343 344 /* 345 * Convert an object name into an ASN1_OBJECT if "noname" is not set then 346 * search for short and long names first. This will convert the "dotted" form 347 * into an object: unlike OBJ_txt2nid it can be used with any objects, not 348 * just registered ones. 349 */ 350 351 ASN1_OBJECT *OBJ_txt2obj(const char *s, int no_name) 352 { 353 int nid = NID_undef; 354 ASN1_OBJECT *op; 355 unsigned char *buf; 356 unsigned char *p; 357 const unsigned char *cp; 358 int i, j; 359 360 if (!no_name) { 361 if (((nid = OBJ_sn2nid(s)) != NID_undef) || 362 ((nid = OBJ_ln2nid(s)) != NID_undef)) 363 return OBJ_nid2obj(nid); 364 } 365 366 /* Work out size of content octets */ 367 i = a2d_ASN1_OBJECT(NULL, 0, s, -1); 368 if (i <= 0) { 369 /* Don't clear the error */ 370 /* 371 * ERR_clear_error(); 372 */ 373 return NULL; 374 } 375 /* Work out total size */ 376 j = ASN1_object_size(0, i, V_ASN1_OBJECT); 377 if (j < 0) 378 return NULL; 379 380 if ((buf = OPENSSL_malloc(j)) == NULL) { 381 OBJerr(OBJ_F_OBJ_TXT2OBJ, ERR_R_MALLOC_FAILURE); 382 return NULL; 383 } 384 385 p = buf; 386 /* Write out tag+length */ 387 ASN1_put_object(&p, 0, i, V_ASN1_OBJECT, V_ASN1_UNIVERSAL); 388 /* Write out contents */ 389 a2d_ASN1_OBJECT(p, i, s, -1); 390 391 cp = buf; 392 op = d2i_ASN1_OBJECT(NULL, &cp, j); 393 OPENSSL_free(buf); 394 return op; 395 } 396 397 int OBJ_obj2txt(char *buf, int buf_len, const ASN1_OBJECT *a, int no_name) 398 { 399 int i, n = 0, len, nid, first, use_bn; 400 BIGNUM *bl; 401 unsigned long l; 402 const unsigned char *p; 403 char tbuf[DECIMAL_SIZE(i) + DECIMAL_SIZE(l) + 2]; 404 405 /* Ensure that, at every state, |buf| is NUL-terminated. */ 406 if (buf && buf_len > 0) 407 buf[0] = '\0'; 408 409 if ((a == NULL) || (a->data == NULL)) 410 return 0; 411 412 if (!no_name && (nid = OBJ_obj2nid(a)) != NID_undef) { 413 const char *s; 414 s = OBJ_nid2ln(nid); 415 if (s == NULL) 416 s = OBJ_nid2sn(nid); 417 if (s) { 418 if (buf) 419 OPENSSL_strlcpy(buf, s, buf_len); 420 n = strlen(s); 421 return n; 422 } 423 } 424 425 len = a->length; 426 p = a->data; 427 428 first = 1; 429 bl = NULL; 430 431 /* 432 * RFC 2578 (STD 58) says this about OBJECT IDENTIFIERs: 433 * 434 * > 3.5. OBJECT IDENTIFIER values 435 * > 436 * > An OBJECT IDENTIFIER value is an ordered list of non-negative 437 * > numbers. For the SMIv2, each number in the list is referred to as a 438 * > sub-identifier, there are at most 128 sub-identifiers in a value, 439 * > and each sub-identifier has a maximum value of 2^32-1 (4294967295 440 * > decimal). 441 * 442 * So a legitimate OID according to this RFC is at most (32 * 128 / 7), 443 * i.e. 586 bytes long. 444 * 445 * Ref: https://datatracker.ietf.org/doc/html/rfc2578#section-3.5 446 */ 447 if (len > 586) 448 goto err; 449 450 while (len > 0) { 451 l = 0; 452 use_bn = 0; 453 for (;;) { 454 unsigned char c = *p++; 455 len--; 456 if ((len == 0) && (c & 0x80)) 457 goto err; 458 if (use_bn) { 459 if (!BN_add_word(bl, c & 0x7f)) 460 goto err; 461 } else 462 l |= c & 0x7f; 463 if (!(c & 0x80)) 464 break; 465 if (!use_bn && (l > (ULONG_MAX >> 7L))) { 466 if (bl == NULL && (bl = BN_new()) == NULL) 467 goto err; 468 if (!BN_set_word(bl, l)) 469 goto err; 470 use_bn = 1; 471 } 472 if (use_bn) { 473 if (!BN_lshift(bl, bl, 7)) 474 goto err; 475 } else 476 l <<= 7L; 477 } 478 479 if (first) { 480 first = 0; 481 if (l >= 80) { 482 i = 2; 483 if (use_bn) { 484 if (!BN_sub_word(bl, 80)) 485 goto err; 486 } else 487 l -= 80; 488 } else { 489 i = (int)(l / 40); 490 l -= (long)(i * 40); 491 } 492 if (buf && (buf_len > 1)) { 493 *buf++ = i + '0'; 494 *buf = '\0'; 495 buf_len--; 496 } 497 n++; 498 } 499 500 if (use_bn) { 501 char *bndec; 502 bndec = BN_bn2dec(bl); 503 if (!bndec) 504 goto err; 505 i = strlen(bndec); 506 if (buf) { 507 if (buf_len > 1) { 508 *buf++ = '.'; 509 *buf = '\0'; 510 buf_len--; 511 } 512 OPENSSL_strlcpy(buf, bndec, buf_len); 513 if (i > buf_len) { 514 buf += buf_len; 515 buf_len = 0; 516 } else { 517 buf += i; 518 buf_len -= i; 519 } 520 } 521 n++; 522 n += i; 523 OPENSSL_free(bndec); 524 } else { 525 BIO_snprintf(tbuf, sizeof(tbuf), ".%lu", l); 526 i = strlen(tbuf); 527 if (buf && (buf_len > 0)) { 528 OPENSSL_strlcpy(buf, tbuf, buf_len); 529 if (i > buf_len) { 530 buf += buf_len; 531 buf_len = 0; 532 } else { 533 buf += i; 534 buf_len -= i; 535 } 536 } 537 n += i; 538 l = 0; 539 } 540 } 541 542 BN_free(bl); 543 return n; 544 545 err: 546 BN_free(bl); 547 return -1; 548 } 549 550 int OBJ_txt2nid(const char *s) 551 { 552 ASN1_OBJECT *obj; 553 int nid; 554 obj = OBJ_txt2obj(s, 0); 555 nid = OBJ_obj2nid(obj); 556 ASN1_OBJECT_free(obj); 557 return nid; 558 } 559 560 int OBJ_ln2nid(const char *s) 561 { 562 ASN1_OBJECT o; 563 const ASN1_OBJECT *oo = &o; 564 ADDED_OBJ ad, *adp; 565 const unsigned int *op; 566 567 o.ln = s; 568 if (added != NULL) { 569 ad.type = ADDED_LNAME; 570 ad.obj = &o; 571 adp = lh_ADDED_OBJ_retrieve(added, &ad); 572 if (adp != NULL) 573 return adp->obj->nid; 574 } 575 op = OBJ_bsearch_ln(&oo, ln_objs, NUM_LN); 576 if (op == NULL) 577 return NID_undef; 578 return nid_objs[*op].nid; 579 } 580 581 int OBJ_sn2nid(const char *s) 582 { 583 ASN1_OBJECT o; 584 const ASN1_OBJECT *oo = &o; 585 ADDED_OBJ ad, *adp; 586 const unsigned int *op; 587 588 o.sn = s; 589 if (added != NULL) { 590 ad.type = ADDED_SNAME; 591 ad.obj = &o; 592 adp = lh_ADDED_OBJ_retrieve(added, &ad); 593 if (adp != NULL) 594 return adp->obj->nid; 595 } 596 op = OBJ_bsearch_sn(&oo, sn_objs, NUM_SN); 597 if (op == NULL) 598 return NID_undef; 599 return nid_objs[*op].nid; 600 } 601 602 const void *OBJ_bsearch_(const void *key, const void *base, int num, int size, 603 int (*cmp) (const void *, const void *)) 604 { 605 return OBJ_bsearch_ex_(key, base, num, size, cmp, 0); 606 } 607 608 const void *OBJ_bsearch_ex_(const void *key, const void *base_, int num, 609 int size, 610 int (*cmp) (const void *, const void *), 611 int flags) 612 { 613 const char *base = base_; 614 int l, h, i = 0, c = 0; 615 const char *p = NULL; 616 617 if (num == 0) 618 return NULL; 619 l = 0; 620 h = num; 621 while (l < h) { 622 i = (l + h) / 2; 623 p = &(base[i * size]); 624 c = (*cmp) (key, p); 625 if (c < 0) 626 h = i; 627 else if (c > 0) 628 l = i + 1; 629 else 630 break; 631 } 632 #ifdef CHARSET_EBCDIC 633 /* 634 * THIS IS A KLUDGE - Because the *_obj is sorted in ASCII order, and I 635 * don't have perl (yet), we revert to a *LINEAR* search when the object 636 * wasn't found in the binary search. 637 */ 638 if (c != 0) { 639 for (i = 0; i < num; ++i) { 640 p = &(base[i * size]); 641 c = (*cmp) (key, p); 642 if (c == 0 || (c < 0 && (flags & OBJ_BSEARCH_VALUE_ON_NOMATCH))) 643 return p; 644 } 645 } 646 #endif 647 if (c != 0 && !(flags & OBJ_BSEARCH_VALUE_ON_NOMATCH)) 648 p = NULL; 649 else if (c == 0 && (flags & OBJ_BSEARCH_FIRST_VALUE_ON_MATCH)) { 650 while (i > 0 && (*cmp) (key, &(base[(i - 1) * size])) == 0) 651 i--; 652 p = &(base[i * size]); 653 } 654 return p; 655 } 656 657 /* 658 * Parse a BIO sink to create some extra oid's objects. 659 * Line format:<OID:isdigit or '.']><isspace><SN><isspace><LN> 660 */ 661 int OBJ_create_objects(BIO *in) 662 { 663 char buf[512]; 664 int i, num = 0; 665 char *o, *s, *l = NULL; 666 667 for (;;) { 668 s = o = NULL; 669 i = BIO_gets(in, buf, 512); 670 if (i <= 0) 671 return num; 672 buf[i - 1] = '\0'; 673 if (!ossl_isalnum(buf[0])) 674 return num; 675 o = s = buf; 676 while (ossl_isdigit(*s) || *s == '.') 677 s++; 678 if (*s != '\0') { 679 *(s++) = '\0'; 680 while (ossl_isspace(*s)) 681 s++; 682 if (*s == '\0') { 683 s = NULL; 684 } else { 685 l = s; 686 while (*l != '\0' && !ossl_isspace(*l)) 687 l++; 688 if (*l != '\0') { 689 *(l++) = '\0'; 690 while (ossl_isspace(*l)) 691 l++; 692 if (*l == '\0') { 693 l = NULL; 694 } 695 } else { 696 l = NULL; 697 } 698 } 699 } else { 700 s = NULL; 701 } 702 if (*o == '\0') 703 return num; 704 if (!OBJ_create(o, s, l)) 705 return num; 706 num++; 707 } 708 } 709 710 int OBJ_create(const char *oid, const char *sn, const char *ln) 711 { 712 ASN1_OBJECT *tmpoid = NULL; 713 int ok = 0; 714 715 /* Check to see if short or long name already present */ 716 if ((sn != NULL && OBJ_sn2nid(sn) != NID_undef) 717 || (ln != NULL && OBJ_ln2nid(ln) != NID_undef)) { 718 OBJerr(OBJ_F_OBJ_CREATE, OBJ_R_OID_EXISTS); 719 return 0; 720 } 721 722 /* Convert numerical OID string to an ASN1_OBJECT structure */ 723 tmpoid = OBJ_txt2obj(oid, 1); 724 if (tmpoid == NULL) 725 return 0; 726 727 /* If NID is not NID_undef then object already exists */ 728 if (OBJ_obj2nid(tmpoid) != NID_undef) { 729 OBJerr(OBJ_F_OBJ_CREATE, OBJ_R_OID_EXISTS); 730 goto err; 731 } 732 733 tmpoid->nid = OBJ_new_nid(1); 734 tmpoid->sn = (char *)sn; 735 tmpoid->ln = (char *)ln; 736 737 ok = OBJ_add_object(tmpoid); 738 739 tmpoid->sn = NULL; 740 tmpoid->ln = NULL; 741 742 err: 743 ASN1_OBJECT_free(tmpoid); 744 return ok; 745 } 746 747 size_t OBJ_length(const ASN1_OBJECT *obj) 748 { 749 if (obj == NULL) 750 return 0; 751 return obj->length; 752 } 753 754 const unsigned char *OBJ_get0_data(const ASN1_OBJECT *obj) 755 { 756 if (obj == NULL) 757 return NULL; 758 return obj->data; 759 } 760