1 /* 2 * The contents of this file are subject to the Mozilla Public 3 * License Version 1.1 (the "License"); you may not use this file 4 * except in compliance with the License. You may obtain a copy of 5 * the License at http://www.mozilla.org/MPL/ 6 * 7 * Software distributed under the License is distributed on an "AS 8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 9 * implied. See the License for the specific language governing 10 * rights and limitations under the License. 11 * 12 * The Original Code is the Netscape security libraries. 13 * 14 * The Initial Developer of the Original Code is Netscape 15 * Communications Corporation. Portions created by Netscape are 16 * Copyright (C) 1994-2000 Netscape Communications Corporation. All 17 * Rights Reserved. 18 * 19 * Contributor(s): 20 * 21 * Alternatively, the contents of this file may be used under the 22 * terms of the GNU General Public License Version 2 or later (the 23 * "GPL"), in which case the provisions of the GPL are applicable 24 * instead of those above. If you wish to allow use of your 25 * version of this file only under the terms of the GPL and not to 26 * allow others to use your version of this file under the MPL, 27 * indicate your decision by deleting the provisions above and 28 * replace them with the notice and other provisions required by 29 * the GPL. If you do not delete the provisions above, a recipient 30 * may use your version of this file under either the MPL or the 31 * GPL. 32 * 33 * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 34 * Use is subject to license terms. 35 * 36 * Copyright 2018, Joyent, Inc. 37 * 38 * File: rdn_parser.c 39 */ 40 41 #include <strings.h> 42 #include <stdlib.h> 43 #include <kmfapi.h> 44 #include <kmfapiP.h> 45 #include <ber_der.h> 46 #include <rdn_parser.h> 47 #include <stdio.h> 48 #include <values.h> 49 #include <libcustr.h> 50 51 /* 52 * The order here is important. The OIDs are arranged in order of 53 * significance. The CN is the most specific value, the C (country) 54 * is less specific, etc. Add to this list with care. 55 */ 56 static const struct NameToKind name2kinds[] = { 57 { "CN", OID_AVA_COMMON_NAME, (KMF_OID *)&KMFOID_CommonName}, 58 { "SN", OID_AVA_SURNAME, (KMF_OID *)&KMFOID_Surname}, 59 { "GN", OID_AVA_GIVEN_NAME, (KMF_OID *)&KMFOID_GivenName}, 60 { "emailAddress", OID_PKCS9_EMAIL_ADDRESS, (KMF_OID *)&KMFOID_EmailAddress}, 61 { "E", OID_PKCS9_EMAIL_ADDRESS, (KMF_OID *)&KMFOID_EmailAddress}, 62 { "MAIL", OID_RFC1274_MAIL, (KMF_OID *)&KMFOID_RFC822mailbox}, 63 { "STREET", OID_AVA_STREET_ADDRESS, (KMF_OID *)&KMFOID_StreetAddress}, 64 { "UID", OID_RFC1274_UID, (KMF_OID *)&KMFOID_userid}, 65 { "OU", OID_AVA_ORGANIZATIONAL_UNIT_NAME, 66 (KMF_OID *)&KMFOID_OrganizationalUnitName}, 67 { "O", OID_AVA_ORGANIZATION_NAME, (KMF_OID *)&KMFOID_OrganizationName}, 68 { "L", OID_AVA_LOCALITY, (KMF_OID *)&KMFOID_LocalityName}, 69 { "ST", OID_AVA_STATE_OR_PROVINCE, 70 (KMF_OID *)&KMFOID_StateProvinceName}, 71 { "C", OID_AVA_COUNTRY_NAME, (KMF_OID *)&KMFOID_CountryName}, 72 { "DC", OID_AVA_DC, (KMF_OID *)&KMFOID_domainComponent}, 73 { 0, OID_UNKNOWN, NULL} 74 }; 75 76 static KMF_BOOL 77 IsPrintable(unsigned char *data, unsigned len) 78 { 79 unsigned char ch, *end; 80 81 end = data + len; 82 while (data < end) { 83 ch = *data++; 84 if (!IS_PRINTABLE(ch)) { 85 return (B_FALSE); 86 } 87 } 88 return (B_TRUE); 89 } 90 91 static KMF_BOOL 92 Is7Bit(unsigned char *data, unsigned len) 93 { 94 unsigned char ch, *end; 95 96 end = data + len; 97 while (data < end) { 98 ch = *data++; 99 if ((ch & 0x80)) { 100 return (B_FALSE); 101 } 102 } 103 return (B_TRUE); 104 } 105 106 static void 107 skipSpace(char **pbp, char *endptr) 108 { 109 char *bp = *pbp; 110 while (bp < endptr && OPTIONAL_SPACE(*bp)) { 111 bp++; 112 } 113 *pbp = bp; 114 } 115 116 static KMF_RETURN 117 scanTag(char **pbp, char *endptr, char *tagBuf, int tagBufSize) 118 { 119 char *bp, *tagBufp; 120 int taglen; 121 122 if (tagBufSize <= 0) 123 return (KMF_ERR_INTERNAL); 124 125 /* skip optional leading space */ 126 skipSpace(pbp, endptr); 127 if (*pbp == endptr) { 128 /* nothing left */ 129 return (KMF_ERR_RDN_PARSER); 130 } 131 132 /* fill tagBuf */ 133 taglen = 0; 134 bp = *pbp; 135 tagBufp = tagBuf; 136 while (bp < endptr && !OPTIONAL_SPACE(*bp) && (*bp != C_EQUAL)) { 137 if (++taglen >= tagBufSize) { 138 *pbp = bp; 139 return (KMF_ERR_RDN_PARSER); 140 } 141 *tagBufp++ = *bp++; 142 } 143 /* null-terminate tagBuf -- guaranteed at least one space left */ 144 *tagBufp++ = 0; 145 *pbp = bp; 146 147 /* 148 * skip trailing spaces till we hit something - should be 149 * an equal sign 150 */ 151 skipSpace(pbp, endptr); 152 if (*pbp == endptr) { 153 /* nothing left */ 154 return (KMF_ERR_RDN_PARSER); 155 } 156 if (**pbp != C_EQUAL) { 157 /* should be an equal sign */ 158 return (KMF_ERR_RDN_PARSER); 159 } 160 /* skip over the equal sign */ 161 (*pbp)++; 162 163 return (KMF_OK); 164 } 165 166 static KMF_RETURN 167 scanVal(char **pbp, char *endptr, char *valBuf, int valBufSize) 168 { 169 char *bp, *valBufp; 170 int vallen; 171 boolean_t isQuoted; 172 173 if (valBufSize <= 0) 174 return (KMF_ERR_INTERNAL); 175 176 /* skip optional leading space */ 177 skipSpace(pbp, endptr); 178 if (*pbp == endptr) { 179 /* nothing left */ 180 return (KMF_ERR_RDN_PARSER); 181 } 182 183 bp = *pbp; 184 185 /* quoted? */ 186 if (*bp == C_DOUBLE_QUOTE) { 187 isQuoted = B_TRUE; 188 /* skip over it */ 189 bp++; 190 } else { 191 isQuoted = B_FALSE; 192 } 193 194 valBufp = valBuf; 195 vallen = 0; 196 while (bp < endptr) { 197 char c = *bp; 198 if (c == C_BACKSLASH) { 199 /* escape character */ 200 bp++; 201 if (bp >= endptr) { 202 /* 203 * escape charater must appear with paired char 204 */ 205 *pbp = bp; 206 return (KMF_ERR_RDN_PARSER); 207 } 208 } else if (!isQuoted && SPECIAL_CHAR(c)) { 209 /* unescaped special and not within quoted value */ 210 break; 211 } else if (c == C_DOUBLE_QUOTE) { 212 /* reached unescaped double quote */ 213 break; 214 } 215 /* append character */ 216 vallen++; 217 if (vallen >= valBufSize) { 218 *pbp = bp; 219 return (KMF_ERR_RDN_PARSER); 220 } 221 *valBufp++ = *bp++; 222 } 223 224 /* stip trailing spaces from unquoted values */ 225 if (!isQuoted) { 226 if (valBufp > valBuf) { 227 valBufp--; 228 while ((valBufp > valBuf) && OPTIONAL_SPACE(*valBufp)) { 229 valBufp--; 230 } 231 valBufp++; 232 } 233 } 234 235 if (isQuoted) { 236 /* insist that we stopped on a double quote */ 237 if (*bp != C_DOUBLE_QUOTE) { 238 *pbp = bp; 239 return (KMF_ERR_RDN_PARSER); 240 } 241 /* skip over the quote and skip optional space */ 242 bp++; 243 skipSpace(&bp, endptr); 244 } 245 246 *pbp = bp; 247 248 if (valBufp == valBuf) { 249 /* empty value -- not allowed */ 250 return (KMF_ERR_RDN_PARSER); 251 } 252 253 /* null-terminate valBuf -- guaranteed at least one space left */ 254 *valBufp++ = 0; 255 256 return (KMF_OK); 257 } 258 259 static KMF_RETURN 260 CreateRDN(KMF_X509_TYPE_VALUE_PAIR *ava, KMF_X509_RDN *newrdn) 261 { 262 /* Each RDN has 1 AttrTypeAndValue */ 263 (void) memset(newrdn, 0, sizeof (KMF_X509_RDN)); 264 newrdn->numberOfPairs = 1; 265 newrdn->AttributeTypeAndValue = ava; 266 267 return (KMF_OK); 268 } 269 270 static KMF_RETURN 271 copy_oid(KMF_OID *dst, KMF_OID *src) 272 { 273 KMF_RETURN ret = KMF_OK; 274 275 if (dst == NULL || src == NULL) 276 return (KMF_ERR_BAD_PARAMETER); 277 278 dst->Data = malloc(src->Length); 279 if (dst->Data == NULL) 280 return (KMF_ERR_MEMORY); 281 282 dst->Length = src->Length; 283 (void) memcpy(dst->Data, src->Data, src->Length); 284 285 return (ret); 286 } 287 288 static KMF_RETURN 289 CreateAVA(KMF_OID *oid, int valueType, char *value, 290 KMF_X509_TYPE_VALUE_PAIR **newava) 291 { 292 int rv = KMF_OK; 293 KMF_X509_TYPE_VALUE_PAIR *ava = NULL; 294 295 *newava = NULL; 296 ava = (KMF_X509_TYPE_VALUE_PAIR*) malloc( 297 sizeof (KMF_X509_TYPE_VALUE_PAIR)); 298 if (ava == NULL) { 299 return (KMF_ERR_MEMORY); 300 } else { 301 (void) memset(ava, 0, sizeof (KMF_X509_TYPE_VALUE_PAIR)); 302 ava->valueType = valueType; 303 ava->value.Data = malloc(strlen(value)); 304 if (ava->value.Data == NULL) { 305 free(ava); 306 return (KMF_ERR_MEMORY); 307 } 308 (void) memcpy(ava->value.Data, value, strlen(value)); 309 ava->value.Length = strlen(value); 310 311 rv = copy_oid(&ava->type, oid); 312 if (rv != KMF_OK) { 313 /* Illegal AVA type */ 314 free(ava->value.Data); 315 free(ava); 316 return (rv); 317 } 318 } 319 *newava = ava; 320 321 return (rv); 322 } 323 324 static KMF_RETURN 325 ParseRdnAttribute(char **pbp, char *endptr, boolean_t singleAVA, 326 KMF_X509_TYPE_VALUE_PAIR **a) 327 { 328 KMF_RETURN rv; 329 const struct NameToKind *n2k; 330 int vt; 331 int valLen; 332 char *bp; 333 334 char tagBuf[32]; 335 char valBuf[384]; 336 337 rv = scanTag(pbp, endptr, tagBuf, sizeof (tagBuf)); 338 if (rv != KMF_OK) 339 return (rv); 340 rv = scanVal(pbp, endptr, valBuf, sizeof (valBuf)); 341 if (rv != KMF_OK) 342 return (rv); 343 344 /* insist that if we haven't finished we've stopped on a separator */ 345 bp = *pbp; 346 if (bp < endptr) { 347 if (singleAVA || (*bp != ',' && *bp != ';')) { 348 *pbp = bp; 349 return (KMF_ERR_RDN_ATTR); 350 } 351 /* ok, skip over separator */ 352 bp++; 353 } 354 *pbp = bp; 355 356 for (n2k = name2kinds; n2k->name; n2k++) { 357 if (strcasecmp(n2k->name, tagBuf) == 0) { 358 valLen = strlen(valBuf); 359 if (n2k->kind == OID_AVA_COUNTRY_NAME) { 360 vt = BER_PRINTABLE_STRING; 361 if (valLen != 2) { 362 return (KMF_ERR_RDN_ATTR); 363 } 364 if (!IsPrintable((unsigned char *) valBuf, 2)) { 365 return (KMF_ERR_RDN_ATTR); 366 } 367 } else if ((n2k->kind == OID_PKCS9_EMAIL_ADDRESS) || 368 (n2k->kind == OID_RFC1274_MAIL)) { 369 vt = BER_IA5STRING; 370 } else { 371 /* 372 * Hack -- for rationale see X.520 373 * DirectoryString defn 374 */ 375 if (IsPrintable((unsigned char *)valBuf, 376 valLen)) { 377 vt = BER_PRINTABLE_STRING; 378 } else if (Is7Bit((unsigned char *)valBuf, 379 valLen)) { 380 vt = BER_T61STRING; 381 } 382 } 383 rv = CreateAVA(n2k->OID, vt, (char *)valBuf, a); 384 return (rv); 385 } 386 } 387 /* matched no kind -- invalid tag */ 388 return (KMF_ERR_RDN_ATTR); 389 } 390 391 static int 392 rdnavcompare(const void *a, const void *b) 393 { 394 KMF_X509_RDN *r1, *r2; 395 KMF_X509_TYPE_VALUE_PAIR *av1, *av2; 396 int i, p1, p2; 397 const struct NameToKind *n2k; 398 KMF_OID *oidrec; 399 400 r1 = (KMF_X509_RDN *)a; 401 r2 = (KMF_X509_RDN *)b; 402 403 av1 = r1->AttributeTypeAndValue; 404 av2 = r2->AttributeTypeAndValue; 405 406 p1 = p2 = MAXINT; 407 /* 408 * The "Name2Kinds" list is ordered by significance. 409 * Compare the "ranking" of each of the OIDs to determine 410 * the result. 411 */ 412 for (n2k = name2kinds, i = 0; 413 n2k->name && (p1 == MAXINT || p2 == MAXINT); 414 n2k++, i++) { 415 oidrec = n2k->OID; 416 if (oidrec != NULL) { 417 if (IsEqualOid(&av1->type, oidrec)) 418 p1 = i; 419 if (IsEqualOid(&av2->type, oidrec)) 420 p2 = i; 421 } 422 } 423 424 if (p1 > p2) 425 return (-1); 426 else if (p1 < p2) 427 return (1); 428 else /* If equal, treat as if it is less than */ 429 return (1); 430 } 431 432 static KMF_RETURN 433 ParseDistinguishedName(char *buf, int len, KMF_X509_NAME *name) 434 { 435 KMF_RETURN rv = KMF_OK; 436 char *bp, *e; 437 KMF_X509_TYPE_VALUE_PAIR *ava = NULL; 438 KMF_X509_RDN rdn; 439 440 (void) memset(name, 0, sizeof (KMF_X509_NAME)); 441 e = buf + len; 442 bp = buf; 443 while (bp < e) { 444 rv = ParseRdnAttribute(&bp, e, B_FALSE, &ava); 445 if (rv != KMF_OK) goto loser; 446 rv = CreateRDN(ava, &rdn); 447 if (rv != KMF_OK) goto loser; 448 if (AddRDN(name, &rdn) != KMF_OK) goto loser; 449 skipSpace(&bp, e); 450 } 451 452 /* 453 * Canonicalize the DN by sorting the elements 454 * in little-endian order, as per RFC 1485: 455 * "The name is presented/input in a little-endian 456 * order (most significant component last)." 457 */ 458 qsort((void *)name->RelativeDistinguishedName, 459 name->numberOfRDNs, sizeof (KMF_X509_RDN), rdnavcompare); 460 461 /* return result */ 462 return (rv); 463 464 loser: 465 kmf_free_dn(name); 466 return (rv); 467 } 468 469 static KMF_BOOL 470 IsEqualData(KMF_DATA *d1, KMF_DATA *d2) 471 { 472 return ((d1->Length == d2->Length) && 473 !memcmp(d1->Data, d2->Data, d1->Length)); 474 } 475 476 /* 477 * Generic routine to compare 2 RDN structures. 478 * 479 * Because the ordering of the AV pairs may not be 480 * the same, we must compare each AV pair individually 481 * 482 * Return 0 if equal, 1 if not. 483 */ 484 int 485 kmf_compare_rdns(KMF_X509_NAME *name1, KMF_X509_NAME *name2) 486 { 487 int i, j; 488 boolean_t avfound; 489 KMF_X509_RDN *r1, *r2; 490 KMF_X509_TYPE_VALUE_PAIR *av1, *av2; 491 492 if (name1 == NULL || name2 == NULL) 493 return (1); 494 495 if (name1->numberOfRDNs != name2->numberOfRDNs) 496 return (1); 497 498 for (i = 0; i < name1->numberOfRDNs; i++) { 499 r1 = (KMF_X509_RDN *)&name1->RelativeDistinguishedName[i]; 500 av1 = (KMF_X509_TYPE_VALUE_PAIR *)r1->AttributeTypeAndValue; 501 502 avfound = FALSE; 503 for (j = 0; j < name2->numberOfRDNs && !avfound; j++) { 504 r2 = (KMF_X509_RDN *) 505 &name2->RelativeDistinguishedName[j]; 506 av2 = (KMF_X509_TYPE_VALUE_PAIR *) 507 r2->AttributeTypeAndValue; 508 509 avfound = (IsEqualOid(&av1->type, &av2->type) && 510 IsEqualData(&av1->value, &av2->value)); 511 } 512 /* 513 * If the current AV from name1 was not found in name2, 514 * we are done. 515 */ 516 if (!avfound) 517 return (1); 518 } 519 520 /* If we got this far, it must be a match */ 521 return (0); 522 } 523 524 /* 525 * kmf_dn_parser 526 * 527 * Public interface for parsing a Distinguished name in 528 * human-readable format into a binary KMF_X509_NAME. 529 */ 530 KMF_RETURN 531 kmf_dn_parser(char *string, KMF_X509_NAME *name) 532 { 533 KMF_RETURN err; 534 535 if (string == NULL || name == NULL) 536 return (KMF_ERR_BAD_PARAMETER); 537 538 err = ParseDistinguishedName(string, (int)strlen(string), name); 539 return (err); 540 } 541 542 static const char hexdigits[] = "0123456789abcdef"; 543 544 static KMF_RETURN 545 binvalue_to_string(KMF_DATA *data, custr_t *str) 546 { 547 size_t i; 548 uchar_t c; 549 550 if (custr_appendc(str, '#') != 0) 551 return (KMF_ERR_MEMORY); 552 553 for (i = 0; i < data->Length; i++) { 554 c = data->Data[i]; 555 if (custr_appendc(str, hexdigits[(c >> 4) & 0xf]) != 0 || 556 custr_appendc(str, hexdigits[(c & 0xf)]) != 0) { 557 return (KMF_ERR_MEMORY); 558 } 559 } 560 561 return (KMF_OK); 562 } 563 564 /* 565 * Convert an RDN value into a printable name with appropriate escaping. 566 * The rules are taken from RFC4514. While it is dealing with LDAP 567 * distinguished names, both LDAP and x509 certificates are based on the 568 * same underlying ITU standards, and as far as I can determine, the same 569 * rules apply (or at least the rules for LDAP DNs apply the same to x509 570 * DNs). 571 */ 572 static KMF_RETURN 573 value_to_string(KMF_DATA *data, custr_t *str) 574 { 575 size_t i; 576 uchar_t c; 577 578 for (i = 0; i < data->Length; i++) { 579 c = data->Data[i]; 580 581 /* 582 * While technically not required, it is suggested that 583 * printable non-ascii characters (e.g. multi-byte UTF-8 584 * characters) are converted as escaped hex (as well as 585 * unprintable characters). AFAIK there is no one canonical 586 * string representation (e.g. attribute names are case 587 * insensitive, so 'CN=foo' and 'cn=foo' convert to the same 588 * binary representation, but there is nothing to say if 589 * either string form is canonical), so this shouldn't 590 * pose a problem. 591 */ 592 if (c < ' ' || c >= 0x7f) { 593 /* 594 * RFC4514 specifies the hex form in a DN string as 595 * \{hex}{hex}. OpenSSL uses capitals for A-F so we 596 * do the same. 597 */ 598 if (custr_append_printf(str, "\\%02hhX", c) != 0) 599 return (KMF_ERR_MEMORY); 600 continue; 601 } 602 603 switch (c) { 604 case '#': 605 /* Escape # if at the start of a value */ 606 if (i != 0) 607 break; 608 /* FALLTHROUGH */ 609 case ' ': 610 /* Escape ' ' if at the start or end of a value */ 611 if (i != 0 && i + 1 != data->Length) 612 break; 613 /* FALLTHROUGH */ 614 case '"': 615 case '+': 616 case ',': 617 case ';': 618 case '<': 619 case '>': 620 case '\\': 621 /* Escape these */ 622 if (custr_appendc(str, '\\') != 0) 623 return (KMF_ERR_MEMORY); 624 } 625 626 if (custr_appendc(str, c) != 0) 627 return (KMF_ERR_MEMORY); 628 } 629 630 return (KMF_OK); 631 } 632 633 /* 634 * Translate an attribute/value pair into a string. If the attribute OID 635 * is a well known OID (in name2kinds) we use the name instead of the OID. 636 */ 637 static KMF_RETURN 638 ava_to_string(KMF_X509_TYPE_VALUE_PAIR *tvp, custr_t *str) 639 { 640 KMF_OID *kind_oid; 641 KMF_OID *rdn_oid = &tvp->type; 642 const char *attr = NULL; 643 size_t i; 644 KMF_RETURN ret = KMF_OK; 645 boolean_t found = B_FALSE; 646 647 for (i = 0; name2kinds[i].name != NULL; i++) { 648 kind_oid = name2kinds[i].OID; 649 650 if (!IsEqualOid(kind_oid, rdn_oid)) 651 continue; 652 653 attr = name2kinds[i].name; 654 found = B_TRUE; 655 break; 656 } 657 658 if (!found && (attr = kmf_oid_to_string(rdn_oid)) == NULL) { 659 ret = KMF_ERR_MEMORY; 660 goto done; 661 } 662 if (custr_append(str, attr) != 0) { 663 ret = KMF_ERR_MEMORY; 664 goto done; 665 } 666 if (custr_appendc(str, '=') != 0) { 667 ret = KMF_ERR_MEMORY; 668 goto done; 669 } 670 671 /* 672 * RFC4514 indicates that an oid=value pair should have the value 673 * printed as #xxxxxx. In addition, we also print as a binary 674 * value if the BER tag does not indicate the value is some sort 675 * of printable string. 676 */ 677 switch (tvp->valueType) { 678 case BER_UTF8_STRING: 679 case BER_PRINTABLE_STRING: 680 case BER_T61STRING: 681 case BER_IA5STRING: 682 if (found) { 683 ret = value_to_string(&tvp->value, str); 684 break; 685 } 686 /*FALLTHROUGH*/ 687 default: 688 ret = binvalue_to_string(&tvp->value, str); 689 break; 690 } 691 692 done: 693 if (!found) 694 free((void *)attr); 695 696 return (ret); 697 } 698 699 static KMF_RETURN 700 rdn_to_string(KMF_X509_RDN *rdn, custr_t *str) 701 { 702 KMF_RETURN ret; 703 size_t i; 704 705 for (i = 0; i < rdn->numberOfPairs; i++) { 706 if (i > 0 && custr_appendc(str, '+') != 0) 707 return (KMF_ERR_MEMORY); 708 709 ret = ava_to_string(&rdn->AttributeTypeAndValue[i], str); 710 if (ret != KMF_OK) 711 return (ret); 712 } 713 714 return (KMF_OK); 715 } 716 717 /* 718 * kmf_dn_to_string 719 * 720 * Take a binary KMF_X509_NAME and convert it into a human readable string. 721 */ 722 KMF_RETURN 723 kmf_dn_to_string(KMF_X509_NAME *name, char **string) 724 { 725 custr_t *str = NULL; 726 KMF_RETURN err = KMF_OK; 727 size_t i; 728 729 if (name == NULL || string == NULL) 730 return (KMF_ERR_BAD_PARAMETER); 731 732 *string = NULL; 733 734 if (custr_alloc(&str) != 0) 735 return (KMF_ERR_MEMORY); 736 737 for (i = 0; i < name->numberOfRDNs; i++) { 738 KMF_X509_RDN *rdn = &name->RelativeDistinguishedName[i]; 739 740 if (i > 0 && custr_append(str, ", ") != 0) { 741 err = KMF_ERR_MEMORY; 742 goto done; 743 } 744 745 if ((err = rdn_to_string(rdn, str)) != KMF_OK) 746 goto done; 747 } 748 749 if ((*string = strdup(custr_cstr(str))) == NULL) 750 err = KMF_ERR_MEMORY; 751 752 done: 753 custr_free(str); 754 return (err); 755 } 756