1/* 2 * Copyright (c) 2004 - 2009 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#include "hx_locl.h" 37#include <wind.h> 38#include "char_map.h" 39 40/** 41 * @page page_name PKIX/X.509 Names 42 * 43 * There are several names in PKIX/X.509, GeneralName and Name. 44 * 45 * A Name consists of an ordered list of Relative Distinguished Names 46 * (RDN). Each RDN consists of an unordered list of typed strings. The 47 * types are defined by OID and have long and short description. For 48 * example id-at-commonName (2.5.4.3) have the long name CommonName 49 * and short name CN. The string itself can be of several encoding, 50 * UTF8, UTF16, Teltex string, etc. The type limit what encoding 51 * should be used. 52 * 53 * GeneralName is a broader nametype that can contains al kind of 54 * stuff like Name, IP addresses, partial Name, etc. 55 * 56 * Name is mapped into a hx509_name object. 57 * 58 * Parse and string name into a hx509_name object with hx509_parse_name(), 59 * make it back into string representation with hx509_name_to_string(). 60 * 61 * Name string are defined rfc2253, rfc1779 and X.501. 62 * 63 * See the library functions here: @ref hx509_name 64 */ 65 66static const struct { 67 const char *n; 68 const heim_oid *o; 69 wind_profile_flags flags; 70} no[] = { 71 { "C", &asn1_oid_id_at_countryName }, 72 { "CN", &asn1_oid_id_at_commonName }, 73 { "DC", &asn1_oid_id_domainComponent }, 74 { "L", &asn1_oid_id_at_localityName }, 75 { "O", &asn1_oid_id_at_organizationName }, 76 { "OU", &asn1_oid_id_at_organizationalUnitName }, 77 { "S", &asn1_oid_id_at_stateOrProvinceName }, 78 { "STREET", &asn1_oid_id_at_streetAddress }, 79 { "UID", &asn1_oid_id_Userid }, 80 { "emailAddress", &asn1_oid_id_pkcs9_emailAddress }, 81 { "serialNumber", &asn1_oid_id_at_serialNumber }, 82 { "title", &asn1_oid_id_at_title }, 83 { "description", &asn1_oid_id_at_description } 84}; 85 86static char * 87quote_string(const char *f, size_t len, int flags, size_t *rlen) 88{ 89 size_t i, j, tolen; 90 const unsigned char *from = (const unsigned char *)f; 91 unsigned char *to; 92 93 tolen = len * 3 + 1; 94 to = malloc(tolen); 95 if (to == NULL) 96 return NULL; 97 98 for (i = 0, j = 0; i < len; i++) { 99 unsigned char map = char_map[from[i]] & flags; 100 if (i == 0 && (map & Q_RFC2253_QUOTE_FIRST)) { 101 to[j++] = '\\'; 102 to[j++] = from[i]; 103 } else if ((i + 1) == len && (map & Q_RFC2253_QUOTE_LAST)) { 104 105 to[j++] = '\\'; 106 to[j++] = from[i]; 107 } else if (map & Q_RFC2253_QUOTE) { 108 to[j++] = '\\'; 109 to[j++] = from[i]; 110 } else if (map & Q_RFC2253_HEX) { 111 int l = snprintf((char *)&to[j], tolen - j - 1, 112 "#%02x", (unsigned char)from[i]); 113 j += l; 114 } else { 115 to[j++] = from[i]; 116 } 117 } 118 to[j] = '\0'; 119 assert(j < tolen); 120 *rlen = j; 121 return (char *)to; 122} 123 124 125static int 126append_string(char **str, size_t *total_len, const char *ss, 127 size_t len, int quote) 128{ 129 char *s, *qs; 130 131 if (quote) 132 qs = quote_string(ss, len, Q_RFC2253, &len); 133 else 134 qs = rk_UNCONST(ss); 135 136 s = realloc(*str, len + *total_len + 1); 137 if (s == NULL) 138 _hx509_abort("allocation failure"); /* XXX */ 139 memcpy(s + *total_len, qs, len); 140 if (qs != ss) 141 free(qs); 142 s[*total_len + len] = '\0'; 143 *str = s; 144 *total_len += len; 145 return 0; 146} 147 148static char * 149oidtostring(const heim_oid *type) 150{ 151 char *s; 152 size_t i; 153 154 for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) { 155 if (der_heim_oid_cmp(no[i].o, type) == 0) 156 return strdup(no[i].n); 157 } 158 if (der_print_heim_oid(type, '.', &s) != 0) 159 return NULL; 160 return s; 161} 162 163static int 164stringtooid(const char *name, size_t len, heim_oid *oid) 165{ 166 int ret; 167 size_t i; 168 char *s; 169 170 memset(oid, 0, sizeof(*oid)); 171 172 for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) { 173 if (strncasecmp(no[i].n, name, len) == 0) 174 return der_copy_oid(no[i].o, oid); 175 } 176 s = malloc(len + 1); 177 if (s == NULL) 178 return ENOMEM; 179 memcpy(s, name, len); 180 s[len] = '\0'; 181 ret = der_parse_heim_oid(s, ".", oid); 182 free(s); 183 return ret; 184} 185 186/** 187 * Convert the hx509 name object into a printable string. 188 * The resulting string should be freed with free(). 189 * 190 * @param name name to print 191 * @param str the string to return 192 * 193 * @return An hx509 error code, see hx509_get_error_string(). 194 * 195 * @ingroup hx509_name 196 */ 197 198int 199hx509_name_to_string(const hx509_name name, char **str) 200{ 201 return _hx509_Name_to_string(&name->der_name, str); 202} 203 204int 205_hx509_Name_to_string(const Name *n, char **str) 206{ 207 size_t total_len = 0; 208 size_t i, j, m; 209 int ret; 210 211 *str = strdup(""); 212 if (*str == NULL) 213 return ENOMEM; 214 215 for (m = n->u.rdnSequence.len; m > 0; m--) { 216 size_t len; 217 i = m - 1; 218 219 for (j = 0; j < n->u.rdnSequence.val[i].len; j++) { 220 DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value; 221 char *oidname; 222 char *ss; 223 224 oidname = oidtostring(&n->u.rdnSequence.val[i].val[j].type); 225 226 switch(ds->element) { 227 case choice_DirectoryString_ia5String: 228 ss = ds->u.ia5String.data; 229 len = ds->u.ia5String.length; 230 break; 231 case choice_DirectoryString_printableString: 232 ss = ds->u.printableString.data; 233 len = ds->u.printableString.length; 234 break; 235 case choice_DirectoryString_utf8String: 236 ss = ds->u.utf8String; 237 len = strlen(ss); 238 break; 239 case choice_DirectoryString_bmpString: { 240 const uint16_t *bmp = ds->u.bmpString.data; 241 size_t bmplen = ds->u.bmpString.length; 242 size_t k; 243 244 ret = wind_ucs2utf8_length(bmp, bmplen, &k); 245 if (ret) 246 return ret; 247 248 ss = malloc(k + 1); 249 if (ss == NULL) 250 _hx509_abort("allocation failure"); /* XXX */ 251 ret = wind_ucs2utf8(bmp, bmplen, ss, &k); 252 if (ret) { 253 free(ss); 254 return ret; 255 } 256 ss[k] = '\0'; 257 len = k; 258 break; 259 } 260 case choice_DirectoryString_teletexString: 261 ss = ds->u.teletexString; 262 len = strlen(ss); 263 break; 264 case choice_DirectoryString_universalString: { 265 const uint32_t *uni = ds->u.universalString.data; 266 size_t unilen = ds->u.universalString.length; 267 size_t k; 268 269 ret = wind_ucs4utf8_length(uni, unilen, &k); 270 if (ret) 271 return ret; 272 273 k += 1; 274 ss = malloc(k); 275 if (ss == NULL) 276 _hx509_abort("allocation failure"); /* XXX */ 277 ret = wind_ucs4utf8(uni, unilen, ss, &k); 278 if (ret) { 279 free(ss); 280 return ret; 281 } 282 ss[k] = '\0'; 283 len = k; 284 break; 285 } 286 default: 287 _hx509_abort("unknown directory type: %d", ds->element); 288 exit(1); 289 } 290 append_string(str, &total_len, oidname, strlen(oidname), 0); 291 free(oidname); 292 append_string(str, &total_len, "=", 1, 0); 293 append_string(str, &total_len, ss, len, 1); 294 if (ds->element == choice_DirectoryString_bmpString || 295 ds->element == choice_DirectoryString_universalString) 296 { 297 free(ss); 298 } 299 if (j + 1 < n->u.rdnSequence.val[i].len) 300 append_string(str, &total_len, "+", 1, 0); 301 } 302 303 if (i > 0) 304 append_string(str, &total_len, ",", 1, 0); 305 } 306 return 0; 307} 308 309#define COPYCHARARRAY(_ds,_el,_l,_n) \ 310 (_l) = strlen(_ds->u._el); \ 311 (_n) = malloc((_l) * sizeof((_n)[0])); \ 312 if ((_n) == NULL) \ 313 return ENOMEM; \ 314 for (i = 0; i < (_l); i++) \ 315 (_n)[i] = _ds->u._el[i] 316 317 318#define COPYVALARRAY(_ds,_el,_l,_n) \ 319 (_l) = _ds->u._el.length; \ 320 (_n) = malloc((_l) * sizeof((_n)[0])); \ 321 if ((_n) == NULL) \ 322 return ENOMEM; \ 323 for (i = 0; i < (_l); i++) \ 324 (_n)[i] = _ds->u._el.data[i] 325 326#define COPYVOIDARRAY(_ds,_el,_l,_n) \ 327 (_l) = _ds->u._el.length; \ 328 (_n) = malloc((_l) * sizeof((_n)[0])); \ 329 if ((_n) == NULL) \ 330 return ENOMEM; \ 331 for (i = 0; i < (_l); i++) \ 332 (_n)[i] = ((unsigned char *)_ds->u._el.data)[i] 333 334 335 336static int 337dsstringprep(const DirectoryString *ds, uint32_t **rname, size_t *rlen) 338{ 339 wind_profile_flags flags __attribute__((__unused__)); 340 size_t i, len; 341 int ret; 342 uint32_t *name; 343 344 *rname = NULL; 345 *rlen = 0; 346 347 switch(ds->element) { 348 case choice_DirectoryString_ia5String: 349 flags = WIND_PROFILE_LDAP; 350 COPYVOIDARRAY(ds, ia5String, len, name); 351 break; 352 case choice_DirectoryString_printableString: 353 flags = WIND_PROFILE_LDAP; 354 flags |= WIND_PROFILE_LDAP_CASE_EXACT_ATTRIBUTE; 355 COPYVOIDARRAY(ds, printableString, len, name); 356 break; 357 case choice_DirectoryString_teletexString: 358 flags = WIND_PROFILE_LDAP_CASE; 359 COPYCHARARRAY(ds, teletexString, len, name); 360 break; 361 case choice_DirectoryString_bmpString: 362 flags = WIND_PROFILE_LDAP; 363 COPYVALARRAY(ds, bmpString, len, name); 364 break; 365 case choice_DirectoryString_universalString: 366 flags = WIND_PROFILE_LDAP; 367 COPYVALARRAY(ds, universalString, len, name); 368 break; 369 case choice_DirectoryString_utf8String: 370 flags = WIND_PROFILE_LDAP; 371 ret = wind_utf8ucs4_length(ds->u.utf8String, &len); 372 if (ret) 373 return ret; 374 name = malloc(len * sizeof(name[0])); 375 if (name == NULL) 376 return ENOMEM; 377 ret = wind_utf8ucs4(ds->u.utf8String, name, &len); 378 if (ret) { 379 free(name); 380 return ret; 381 } 382 break; 383 default: 384 _hx509_abort("unknown directory type: %d", ds->element); 385 } 386 387 *rlen = len; 388 /* try a couple of times to get the length right, XXX gross */ 389 for (i = 0; i < 4; i++) { 390 *rlen = *rlen * 2; 391 *rname = malloc(*rlen * sizeof((*rname)[0])); 392#ifdef HAVE_WIND 393 ret = wind_stringprep(name, len, *rname, rlen, 394 WIND_PROFILE_LDAP|flags); 395 if (ret == WIND_ERR_OVERRUN) { 396 free(*rname); 397 *rname = NULL; 398 continue; 399 } else 400 break; 401#else 402 ret = 0; 403 memcpy(*rname, name, len * sizeof(name[0])); 404 *rlen = len; 405 break; 406#endif 407 } 408 free(name); 409 if (ret) { 410 if (*rname) 411 free(*rname); 412 *rname = NULL; 413 *rlen = 0; 414 return ret; 415 } 416 417 return 0; 418} 419 420int 421_hx509_name_ds_cmp(const DirectoryString *ds1, 422 const DirectoryString *ds2, 423 int *diff) 424{ 425 uint32_t *ds1lp, *ds2lp; 426 size_t ds1len, ds2len, i; 427 int ret; 428 429 ret = dsstringprep(ds1, &ds1lp, &ds1len); 430 if (ret) 431 return ret; 432 ret = dsstringprep(ds2, &ds2lp, &ds2len); 433 if (ret) { 434 free(ds1lp); 435 return ret; 436 } 437 438 if (ds1len != ds2len) 439 *diff = (int)(ds1len - ds2len); 440 else { 441 for (i = 0; i < ds1len; i++) { 442 *diff = ds1lp[i] - ds2lp[i]; 443 if (*diff) 444 break; 445 } 446 } 447 free(ds1lp); 448 free(ds2lp); 449 450 return 0; 451} 452 453int 454_hx509_name_cmp(const Name *n1, const Name *n2, int *c) 455{ 456 int ret; 457 size_t i, j; 458 459 *c = n1->u.rdnSequence.len - n2->u.rdnSequence.len; 460 if (*c) 461 return 0; 462 463 for (i = 0 ; i < n1->u.rdnSequence.len; i++) { 464 *c = n1->u.rdnSequence.val[i].len - n2->u.rdnSequence.val[i].len; 465 if (*c) 466 return 0; 467 468 for (j = 0; j < n1->u.rdnSequence.val[i].len; j++) { 469 *c = der_heim_oid_cmp(&n1->u.rdnSequence.val[i].val[j].type, 470 &n1->u.rdnSequence.val[i].val[j].type); 471 if (*c) 472 return 0; 473 474 ret = _hx509_name_ds_cmp(&n1->u.rdnSequence.val[i].val[j].value, 475 &n2->u.rdnSequence.val[i].val[j].value, 476 c); 477 if (ret) 478 return ret; 479 if (*c) 480 return 0; 481 } 482 } 483 *c = 0; 484 return 0; 485} 486 487/** 488 * Compare to hx509 name object, useful for sorting. 489 * 490 * @param n1 a hx509 name object. 491 * @param n2 a hx509 name object. 492 * 493 * @return 0 the objects are the same, returns > 0 is n2 is "larger" 494 * then n2, < 0 if n1 is "smaller" then n2. 495 * 496 * @ingroup hx509_name 497 */ 498 499int 500hx509_name_cmp(hx509_name n1, hx509_name n2) 501{ 502 int ret, diff; 503 ret = _hx509_name_cmp(&n1->der_name, &n2->der_name, &diff); 504 if (ret) 505 return ret; 506 return diff; 507} 508 509int 510hx509_name_get_component(hx509_name name, int rdn, const heim_oid *type, unsigned *count, char **str) 511{ 512 Name *n = &name->der_name; 513 size_t len, ulen; 514 uint32_t *ds; 515 unsigned i; 516 int ret; 517 518 if (str) 519 *str = NULL; 520 521 if (rdn >= n->u.rdnSequence.len) 522 return ERANGE; 523 524 for (i = *count; i < n->u.rdnSequence.val[rdn].len; i++) { 525 if (der_heim_oid_cmp(&n->u.rdnSequence.val[rdn].val[i].type, type) == 0) { 526 527 *count = i + 1; 528 529 if (str == NULL) 530 return HX509_NAME_MALFORMED; 531 532 ret = dsstringprep(&n->u.rdnSequence.val[rdn].val[i].value, &ds, &len); 533 if (ret) 534 return ret; 535 536 ret = wind_ucs4utf8_length(ds, len, &ulen); 537 if (ret) { 538 free(ds); 539 return ret; 540 } 541 542 ulen += 1; 543 544 *str = malloc(ulen); 545 if (str == NULL) { 546 free(ds); 547 return ENOMEM; 548 } 549 ret = wind_ucs4utf8(ds, len, *str, &ulen); 550 free(ds); 551 if (ret) { 552 free(*str); 553 *str = NULL; 554 return ret; 555 } 556 return 0; 557 } 558 } 559 if (str == NULL) 560 return 0; 561 return HX509_NAME_MALFORMED; 562} 563 564int 565hx509_name_from_Name(const Name *n, hx509_name *name) 566{ 567 int ret; 568 *name = calloc(1, sizeof(**name)); 569 if (*name == NULL) 570 return ENOMEM; 571 ret = copy_Name(n, &(*name)->der_name); 572 if (ret) { 573 free(*name); 574 *name = NULL; 575 } 576 return ret; 577} 578 579int 580_hx509_name_modify(hx509_context context, 581 Name *name, 582 int append, 583 const heim_oid *oid, 584 const char *str) 585{ 586 RelativeDistinguishedName *rdn; 587 int ret; 588 void *ptr; 589 590 ptr = realloc(name->u.rdnSequence.val, 591 sizeof(name->u.rdnSequence.val[0]) * 592 (name->u.rdnSequence.len + 1)); 593 if (ptr == NULL) { 594 hx509_set_error_string(context, 0, ENOMEM, "Out of memory"); 595 return ENOMEM; 596 } 597 name->u.rdnSequence.val = ptr; 598 599 if (append) { 600 rdn = &name->u.rdnSequence.val[name->u.rdnSequence.len]; 601 } else { 602 memmove(&name->u.rdnSequence.val[1], 603 &name->u.rdnSequence.val[0], 604 name->u.rdnSequence.len * 605 sizeof(name->u.rdnSequence.val[0])); 606 607 rdn = &name->u.rdnSequence.val[0]; 608 } 609 rdn->val = malloc(sizeof(rdn->val[0])); 610 if (rdn->val == NULL) 611 return ENOMEM; 612 rdn->len = 1; 613 ret = der_copy_oid(oid, &rdn->val[0].type); 614 if (ret) 615 return ret; 616 rdn->val[0].value.element = choice_DirectoryString_utf8String; 617 rdn->val[0].value.u.utf8String = strdup(str); 618 if (rdn->val[0].value.u.utf8String == NULL) 619 return ENOMEM; 620 name->u.rdnSequence.len += 1; 621 622 return 0; 623} 624 625/** 626 * Parse a string into a hx509 name object. 627 * 628 * @param context A hx509 context. 629 * @param str a string to parse. 630 * @param name the resulting object, NULL in case of error. 631 * 632 * @return An hx509 error code, see hx509_get_error_string(). 633 * 634 * @ingroup hx509_name 635 */ 636 637int 638hx509_parse_name(hx509_context context, const char *str, hx509_name *name) 639{ 640 char *p, *p0, *q, *endp; 641 size_t len; 642 hx509_name n; 643 int ret; 644 645 *name = NULL; 646 647 n = calloc(1, sizeof(*n)); 648 if (n == NULL) { 649 hx509_set_error_string(context, 0, ENOMEM, "out of memory"); 650 return ENOMEM; 651 } 652 653 n->der_name.element = choice_Name_rdnSequence; 654 655 p0 = p = strdup(str); 656 if (p == NULL) { 657 free(n); 658 hx509_set_error_string(context, 0, ENOMEM, "out of memory"); 659 return ENOMEM; 660 } 661 endp = p + strlen(p); 662 663 while (p != NULL && *p != '\0') { 664 heim_oid oid; 665 int last; 666 667 /* unquote the string */ 668 for (q = p; *q != '\0'; q++) { 669 if (q[0] == '\\' && q[1] != '\0') { 670 memmove(q, q + 1, endp - q - 1); 671 endp--; 672 endp[0] = '\0'; 673 } else if (*q == ',') 674 break; 675 } 676 if (*q != '\0') { 677 len = (q - p); 678 last = 1; 679 } else { 680 len = strlen(p); 681 last = 0; 682 } 683 684 q = strchr(p, '='); 685 if (q == NULL) { 686 ret = HX509_PARSING_NAME_FAILED; 687 hx509_set_error_string(context, 0, ret, "missing = in %s", p); 688 goto out; 689 } 690 if (q == p) { 691 ret = HX509_PARSING_NAME_FAILED; 692 hx509_set_error_string(context, 0, ret, 693 "missing name before = in %s", p); 694 goto out; 695 } 696 697 if ((size_t)(q - p) > len) { 698 ret = HX509_PARSING_NAME_FAILED; 699 hx509_set_error_string(context, 0, ret, " = after , in %s", p); 700 goto out; 701 } 702 703 ret = stringtooid(p, q - p, &oid); 704 if (ret) { 705 ret = HX509_PARSING_NAME_FAILED; 706 hx509_set_error_string(context, 0, ret, 707 "unknown type: %.*s", (int)(q - p), p); 708 goto out; 709 } 710 711 { 712 size_t pstr_len = len - (q - p) - 1; 713 const char *pstr = p + (q - p) + 1; 714 char *r; 715 716 r = malloc(pstr_len + 1); 717 if (r == NULL) { 718 der_free_oid(&oid); 719 ret = ENOMEM; 720 hx509_set_error_string(context, 0, ret, "out of memory"); 721 goto out; 722 } 723 memcpy(r, pstr, pstr_len); 724 r[pstr_len] = '\0'; 725 726 ret = _hx509_name_modify(context, &n->der_name, 0, &oid, r); 727 free(r); 728 der_free_oid(&oid); 729 if(ret) 730 goto out; 731 } 732 p += len + last; 733 } 734 free(p0); 735 *name = n; 736 737 return 0; 738out: 739 free(p0); 740 hx509_name_free(&n); 741 return HX509_NAME_MALFORMED; 742} 743 744/** 745 * Copy a hx509 name object. 746 * 747 * @param context A hx509 cotext. 748 * @param from the name to copy from 749 * @param to the name to copy to 750 * 751 * @return An hx509 error code, see hx509_get_error_string(). 752 * 753 * @ingroup hx509_name 754 */ 755 756int 757hx509_name_copy(hx509_context context, const hx509_name from, hx509_name *to) 758{ 759 int ret; 760 761 *to = calloc(1, sizeof(**to)); 762 if (*to == NULL) 763 return ENOMEM; 764 ret = copy_Name(&from->der_name, &(*to)->der_name); 765 if (ret) { 766 free(*to); 767 *to = NULL; 768 return ENOMEM; 769 } 770 return 0; 771} 772 773/** 774 * Convert a hx509_name into a Name. 775 * 776 * @param from the name to copy from 777 * @param to the name to copy to 778 * 779 * @return An hx509 error code, see hx509_get_error_string(). 780 * 781 * @ingroup hx509_name 782 */ 783 784int 785hx509_name_to_Name(const hx509_name from, Name *to) 786{ 787 return copy_Name(&from->der_name, to); 788} 789 790int 791hx509_name_normalize(hx509_context context, hx509_name name) 792{ 793 return 0; 794} 795 796/** 797 * Expands variables in the name using env. Variables are on the form 798 * ${name}. Useful when dealing with certificate templates. 799 * 800 * @param context A hx509 cotext. 801 * @param name the name to expand. 802 * @param env environment variable to expand. 803 * 804 * @return An hx509 error code, see hx509_get_error_string(). 805 * 806 * @ingroup hx509_name 807 */ 808 809int 810hx509_name_expand(hx509_context context, 811 hx509_name name, 812 hx509_env env) 813{ 814 Name *n = &name->der_name; 815 size_t i, j; 816 817 if (env == NULL) 818 return 0; 819 820 if (n->element != choice_Name_rdnSequence) { 821 hx509_set_error_string(context, 0, EINVAL, "RDN not of supported type"); 822 return EINVAL; 823 } 824 825 for (i = 0 ; i < n->u.rdnSequence.len; i++) { 826 for (j = 0; j < n->u.rdnSequence.val[i].len; j++) { 827 /** Only UTF8String rdnSequence names are allowed */ 828 /* 829 THIS SHOULD REALLY BE: 830 COMP = n->u.rdnSequence.val[i].val[j]; 831 normalize COMP to utf8 832 check if there are variables 833 expand variables 834 convert back to orignal format, store in COMP 835 free normalized utf8 string 836 */ 837 DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value; 838 char *p, *p2; 839 struct rk_strpool *strpool = NULL; 840 841 if (ds->element != choice_DirectoryString_utf8String) { 842 hx509_set_error_string(context, 0, EINVAL, "unsupported type"); 843 return EINVAL; 844 } 845 p = strstr(ds->u.utf8String, "${"); 846 if (p) { 847 strpool = rk_strpoolprintf(strpool, "%.*s", 848 (int)(p - ds->u.utf8String), 849 ds->u.utf8String); 850 if (strpool == NULL) { 851 hx509_set_error_string(context, 0, ENOMEM, "out of memory"); 852 return ENOMEM; 853 } 854 } 855 while (p != NULL) { 856 /* expand variables */ 857 const char *value; 858 p2 = strchr(p, '}'); 859 if (p2 == NULL) { 860 hx509_set_error_string(context, 0, EINVAL, "missing }"); 861 rk_strpoolfree(strpool); 862 return EINVAL; 863 } 864 p += 2; 865 value = hx509_env_lfind(context, env, p, p2 - p); 866 if (value == NULL) { 867 hx509_set_error_string(context, 0, EINVAL, 868 "variable %.*s missing", 869 (int)(p2 - p), p); 870 rk_strpoolfree(strpool); 871 return EINVAL; 872 } 873 strpool = rk_strpoolprintf(strpool, "%s", value); 874 if (strpool == NULL) { 875 hx509_set_error_string(context, 0, ENOMEM, "out of memory"); 876 return ENOMEM; 877 } 878 p2++; 879 880 p = strstr(p2, "${"); 881 if (p) 882 strpool = rk_strpoolprintf(strpool, "%.*s", 883 (int)(p - p2), p2); 884 else 885 strpool = rk_strpoolprintf(strpool, "%s", p2); 886 if (strpool == NULL) { 887 hx509_set_error_string(context, 0, ENOMEM, "out of memory"); 888 return ENOMEM; 889 } 890 } 891 if (strpool) { 892 free(ds->u.utf8String); 893 ds->u.utf8String = rk_strpoolcollect(strpool); 894 if (ds->u.utf8String == NULL) { 895 hx509_set_error_string(context, 0, ENOMEM, "out of memory"); 896 return ENOMEM; 897 } 898 } 899 } 900 } 901 return 0; 902} 903 904/** 905 * Free a hx509 name object, upond return *name will be NULL. 906 * 907 * @param name a hx509 name object to be freed. 908 * 909 * @ingroup hx509_name 910 */ 911 912void 913hx509_name_free(hx509_name *name) 914{ 915 free_Name(&(*name)->der_name); 916 memset(*name, 0, sizeof(**name)); 917 free(*name); 918 *name = NULL; 919} 920 921/** 922 * Convert a DER encoded name info a string. 923 * 924 * @param data data to a DER/BER encoded name 925 * @param length length of data 926 * @param str the resulting string, is NULL on failure. 927 * 928 * @return An hx509 error code, see hx509_get_error_string(). 929 * 930 * @ingroup hx509_name 931 */ 932 933int 934hx509_unparse_der_name(const void *data, size_t length, char **str) 935{ 936 Name name; 937 int ret; 938 939 *str = NULL; 940 941 ret = decode_Name(data, length, &name, NULL); 942 if (ret) 943 return ret; 944 ret = _hx509_Name_to_string(&name, str); 945 free_Name(&name); 946 return ret; 947} 948 949/** 950 * Convert a hx509_name object to DER encoded name. 951 * 952 * @param name name to concert 953 * @param os data to a DER encoded name, free the resulting octet 954 * string with hx509_xfree(os->data). 955 * 956 * @return An hx509 error code, see hx509_get_error_string(). 957 * 958 * @ingroup hx509_name 959 */ 960 961int 962hx509_name_binary(const hx509_name name, heim_octet_string *os) 963{ 964 size_t size; 965 int ret; 966 967 ASN1_MALLOC_ENCODE(Name, os->data, os->length, &name->der_name, &size, ret); 968 if (ret) 969 return ret; 970 if (os->length != size) 971 _hx509_abort("internal ASN.1 encoder error"); 972 973 return 0; 974} 975 976int 977_hx509_unparse_Name(const Name *aname, char **str) 978{ 979 hx509_name name; 980 int ret; 981 982 ret = hx509_name_from_Name(aname, &name); 983 if (ret) 984 return ret; 985 986 ret = hx509_name_to_string(name, str); 987 hx509_name_free(&name); 988 return ret; 989} 990 991/** 992 * Unparse the hx509 name in name into a string. 993 * 994 * @param name the name to check if its empty/null. 995 * 996 * @return non zero if the name is empty/null. 997 * 998 * @ingroup hx509_name 999 */ 1000 1001int 1002hx509_name_is_null_p(const hx509_name name) 1003{ 1004 return name->der_name.u.rdnSequence.len == 0; 1005} 1006 1007/** 1008 * Unparse the hx509 name in name into a string. 1009 * 1010 * @param name the name to print 1011 * @param str an allocated string returns the name in string form 1012 * 1013 * @return An hx509 error code, see hx509_get_error_string(). 1014 * 1015 * @ingroup hx509_name 1016 */ 1017 1018int 1019hx509_general_name_unparse(GeneralName *name, char **str) 1020{ 1021 struct rk_strpool *strpool = NULL; 1022 1023 *str = NULL; 1024 1025 switch (name->element) { 1026 case choice_GeneralName_otherName: { 1027 char *oid; 1028 hx509_oid_sprint(&name->u.otherName.type_id, &oid); 1029 if (oid == NULL) 1030 return ENOMEM; 1031 strpool = rk_strpoolprintf(strpool, "otherName: %s", oid); 1032 free(oid); 1033 break; 1034 } 1035 case choice_GeneralName_rfc822Name: 1036 strpool = rk_strpoolprintf(strpool, "rfc822Name: %.*s\n", 1037 (int)name->u.rfc822Name.length, 1038 (char *)name->u.rfc822Name.data); 1039 break; 1040 case choice_GeneralName_dNSName: 1041 strpool = rk_strpoolprintf(strpool, "dNSName: %.*s\n", 1042 (int)name->u.dNSName.length, 1043 (char *)name->u.dNSName.data); 1044 break; 1045 case choice_GeneralName_directoryName: { 1046 Name dir; 1047 char *s; 1048 int ret; 1049 memset(&dir, 0, sizeof(dir)); 1050 dir.element = (enum Name_enum)name->u.directoryName.element; 1051 dir.u.rdnSequence = name->u.directoryName.u.rdnSequence; 1052 ret = _hx509_unparse_Name(&dir, &s); 1053 if (ret) 1054 return ret; 1055 strpool = rk_strpoolprintf(strpool, "directoryName: %s", s); 1056 free(s); 1057 break; 1058 } 1059 case choice_GeneralName_uniformResourceIdentifier: 1060 strpool = rk_strpoolprintf(strpool, "URI: %.*s", 1061 (int)name->u.uniformResourceIdentifier.length, 1062 (char *)name->u.uniformResourceIdentifier.data); 1063 break; 1064 case choice_GeneralName_iPAddress: { 1065 unsigned char *a = name->u.iPAddress.data; 1066 1067 strpool = rk_strpoolprintf(strpool, "IPAddress: "); 1068 if (strpool == NULL) 1069 break; 1070 if (name->u.iPAddress.length == 4) 1071 strpool = rk_strpoolprintf(strpool, "%d.%d.%d.%d", 1072 a[0], a[1], a[2], a[3]); 1073 else if (name->u.iPAddress.length == 16) 1074 strpool = rk_strpoolprintf(strpool, 1075 "%02X:%02X:%02X:%02X:" 1076 "%02X:%02X:%02X:%02X:" 1077 "%02X:%02X:%02X:%02X:" 1078 "%02X:%02X:%02X:%02X", 1079 a[0], a[1], a[2], a[3], 1080 a[4], a[5], a[6], a[7], 1081 a[8], a[9], a[10], a[11], 1082 a[12], a[13], a[14], a[15]); 1083 else 1084 strpool = rk_strpoolprintf(strpool, 1085 "unknown IP address of length %lu", 1086 (unsigned long)name->u.iPAddress.length); 1087 break; 1088 } 1089 case choice_GeneralName_registeredID: { 1090 char *oid; 1091 hx509_oid_sprint(&name->u.registeredID, &oid); 1092 if (oid == NULL) 1093 return ENOMEM; 1094 strpool = rk_strpoolprintf(strpool, "registeredID: %s", oid); 1095 free(oid); 1096 break; 1097 } 1098 default: 1099 return EINVAL; 1100 } 1101 if (strpool == NULL) 1102 return ENOMEM; 1103 1104 *str = rk_strpoolcollect(strpool); 1105 1106 return 0; 1107} 1108