1 /* $NetBSD: principal.c,v 1.2 2017/01/28 21:31:49 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan 5 * (Royal Institute of Technology, Stockholm, Sweden). 6 * 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 /** 37 * @page krb5_principal_intro The principal handing functions. 38 * 39 * A Kerberos principal is a email address looking string that 40 * contains two parts separated by @. The second part is the kerberos 41 * realm the principal belongs to and the first is a list of 0 or 42 * more components. For example 43 * @verbatim 44 lha@SU.SE 45 host/hummel.it.su.se@SU.SE 46 host/admin@H5L.ORG 47 @endverbatim 48 * 49 * See the library functions here: @ref krb5_principal 50 */ 51 52 #include "krb5_locl.h" 53 #ifdef HAVE_RES_SEARCH 54 #define USE_RESOLVER 55 #endif 56 #ifdef HAVE_ARPA_NAMESER_H 57 #include <arpa/nameser.h> 58 #endif 59 #include <fnmatch.h> 60 #include <krb5/resolve.h> 61 62 #define princ_num_comp(P) ((P)->name.name_string.len) 63 #define princ_type(P) ((P)->name.name_type) 64 #define princ_comp(P) ((P)->name.name_string.val) 65 #define princ_ncomp(P, N) ((P)->name.name_string.val[(N)]) 66 #define princ_realm(P) ((P)->realm) 67 68 static krb5_error_code 69 set_default_princ_type(krb5_principal p, NAME_TYPE defnt) 70 { 71 if (princ_num_comp(p) > 1 && strcmp(princ_ncomp(p, 0), KRB5_TGS_NAME) == 0) 72 princ_type(p) = KRB5_NT_SRV_INST; 73 else if (princ_num_comp(p) > 1 && strcmp(princ_ncomp(p, 0), "host") == 0) 74 princ_type(p) = KRB5_NT_SRV_HST; 75 else if (princ_num_comp(p) > 1 && strcmp(princ_ncomp(p, 0), "kca_service") == 0) 76 princ_type(p) = KRB5_NT_SRV_HST; 77 else if (princ_num_comp(p) == 2 && 78 strcmp(princ_ncomp(p, 0), KRB5_WELLKNOWN_NAME) == 0) 79 princ_type(p) = KRB5_NT_WELLKNOWN; 80 else if (princ_num_comp(p) == 1 && strchr(princ_ncomp(p, 0), '@') != NULL) 81 princ_type(p) = KRB5_NT_SMTP_NAME; 82 else 83 princ_type(p) = defnt; 84 return 0; 85 } 86 87 static krb5_error_code append_component(krb5_context, krb5_principal, 88 const char *, size_t); 89 90 /** 91 * Frees a Kerberos principal allocated by the library with 92 * krb5_parse_name(), krb5_make_principal() or any other related 93 * principal functions. 94 * 95 * @param context A Kerberos context. 96 * @param p a principal to free. 97 * 98 * @return An krb5 error code, see krb5_get_error_message(). 99 * 100 * @ingroup krb5_principal 101 */ 102 103 KRB5_LIB_FUNCTION void KRB5_LIB_CALL 104 krb5_free_principal(krb5_context context, 105 krb5_principal p) 106 { 107 if(p){ 108 free_Principal(p); 109 free(p); 110 } 111 } 112 113 /** 114 * Set the type of the principal 115 * 116 * @param context A Kerberos context. 117 * @param principal principal to set the type for 118 * @param type the new type 119 * 120 * @return An krb5 error code, see krb5_get_error_message(). 121 * 122 * @ingroup krb5_principal 123 */ 124 125 KRB5_LIB_FUNCTION void KRB5_LIB_CALL 126 krb5_principal_set_type(krb5_context context, 127 krb5_principal principal, 128 int type) 129 { 130 princ_type(principal) = type; 131 } 132 133 /** 134 * Get the type of the principal 135 * 136 * @param context A Kerberos context. 137 * @param principal principal to get the type for 138 * 139 * @return the type of principal 140 * 141 * @ingroup krb5_principal 142 */ 143 144 KRB5_LIB_FUNCTION int KRB5_LIB_CALL 145 krb5_principal_get_type(krb5_context context, 146 krb5_const_principal principal) 147 { 148 return princ_type(principal); 149 } 150 151 /** 152 * Get the realm of the principal 153 * 154 * @param context A Kerberos context. 155 * @param principal principal to get the realm for 156 * 157 * @return realm of the principal, don't free or use after krb5_principal is freed 158 * 159 * @ingroup krb5_principal 160 */ 161 162 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL 163 krb5_principal_get_realm(krb5_context context, 164 krb5_const_principal principal) 165 { 166 return princ_realm(principal); 167 } 168 169 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL 170 krb5_principal_get_comp_string(krb5_context context, 171 krb5_const_principal principal, 172 unsigned int component) 173 { 174 if(component >= princ_num_comp(principal)) 175 return NULL; 176 return princ_ncomp(principal, component); 177 } 178 179 /** 180 * Get number of component is principal. 181 * 182 * @param context Kerberos 5 context 183 * @param principal principal to query 184 * 185 * @return number of components in string 186 * 187 * @ingroup krb5_principal 188 */ 189 190 KRB5_LIB_FUNCTION unsigned int KRB5_LIB_CALL 191 krb5_principal_get_num_comp(krb5_context context, 192 krb5_const_principal principal) 193 { 194 return princ_num_comp(principal); 195 } 196 197 /** 198 * Parse a name into a krb5_principal structure, flags controls the behavior. 199 * 200 * @param context Kerberos 5 context 201 * @param name name to parse into a Kerberos principal 202 * @param flags flags to control the behavior 203 * @param principal returned principal, free with krb5_free_principal(). 204 * 205 * @return An krb5 error code, see krb5_get_error_message(). 206 * 207 * @ingroup krb5_principal 208 */ 209 210 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 211 krb5_parse_name_flags(krb5_context context, 212 const char *name, 213 int flags, 214 krb5_principal *principal) 215 { 216 krb5_error_code ret; 217 heim_general_string *comp; 218 heim_general_string realm = NULL; 219 int ncomp; 220 221 const char *p; 222 char *q; 223 char *s; 224 char *start; 225 226 int n; 227 char c; 228 int got_realm = 0; 229 int first_at = 1; 230 int no_realm = flags & KRB5_PRINCIPAL_PARSE_NO_REALM; 231 int require_realm = flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM; 232 int enterprise = flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE; 233 int ignore_realm = flags & KRB5_PRINCIPAL_PARSE_IGNORE_REALM; 234 int no_def_realm = flags & KRB5_PRINCIPAL_PARSE_NO_DEF_REALM; 235 236 *principal = NULL; 237 238 if (no_realm && require_realm) { 239 krb5_set_error_message(context, KRB5_ERR_NO_SERVICE, 240 N_("Can't require both realm and " 241 "no realm at the same time", "")); 242 return KRB5_ERR_NO_SERVICE; 243 } 244 245 /* count number of component, 246 * enterprise names only have one component 247 */ 248 ncomp = 1; 249 if (!enterprise) { 250 for (p = name; *p; p++) { 251 if (*p=='\\') { 252 if (!p[1]) { 253 krb5_set_error_message(context, KRB5_PARSE_MALFORMED, 254 N_("trailing \\ in principal name", "")); 255 return KRB5_PARSE_MALFORMED; 256 } 257 p++; 258 } else if (*p == '/') 259 ncomp++; 260 else if (*p == '@') 261 break; 262 } 263 } 264 comp = calloc(ncomp, sizeof(*comp)); 265 if (comp == NULL) 266 return krb5_enomem(context); 267 268 n = 0; 269 p = start = q = s = strdup(name); 270 if (start == NULL) { 271 free(comp); 272 return krb5_enomem(context); 273 } 274 while (*p) { 275 c = *p++; 276 if (c == '\\') { 277 c = *p++; 278 if (c == 'n') 279 c = '\n'; 280 else if (c == 't') 281 c = '\t'; 282 else if (c == 'b') 283 c = '\b'; 284 else if (c == '0') 285 c = '\0'; 286 else if (c == '\0') { 287 ret = KRB5_PARSE_MALFORMED; 288 krb5_set_error_message(context, ret, 289 N_("trailing \\ in principal name", "")); 290 goto exit; 291 } 292 } else if (enterprise && first_at) { 293 if (c == '@') 294 first_at = 0; 295 } else if ((c == '/' && !enterprise) || c == '@') { 296 if (got_realm) { 297 ret = KRB5_PARSE_MALFORMED; 298 krb5_set_error_message(context, ret, 299 N_("part after realm in principal name", "")); 300 goto exit; 301 } else { 302 comp[n] = malloc(q - start + 1); 303 if (comp[n] == NULL) { 304 ret = krb5_enomem(context); 305 goto exit; 306 } 307 memcpy(comp[n], start, q - start); 308 comp[n][q - start] = 0; 309 n++; 310 } 311 if (c == '@') 312 got_realm = 1; 313 start = q; 314 continue; 315 } 316 if (got_realm && (c == '/' || c == '\0')) { 317 ret = KRB5_PARSE_MALFORMED; 318 krb5_set_error_message(context, ret, 319 N_("part after realm in principal name", "")); 320 goto exit; 321 } 322 *q++ = c; 323 } 324 if (got_realm) { 325 if (no_realm) { 326 ret = KRB5_PARSE_MALFORMED; 327 krb5_set_error_message(context, ret, 328 N_("realm found in 'short' principal " 329 "expected to be without one", "")); 330 goto exit; 331 } 332 if (!ignore_realm) { 333 realm = malloc(q - start + 1); 334 if (realm == NULL) { 335 ret = krb5_enomem(context); 336 goto exit; 337 } 338 memcpy(realm, start, q - start); 339 realm[q - start] = 0; 340 } 341 } else { 342 if (require_realm) { 343 ret = KRB5_PARSE_MALFORMED; 344 krb5_set_error_message(context, ret, 345 N_("realm NOT found in principal " 346 "expected to be with one", "")); 347 goto exit; 348 } else if (no_realm || no_def_realm) { 349 realm = NULL; 350 } else { 351 ret = krb5_get_default_realm(context, &realm); 352 if (ret) 353 goto exit; 354 } 355 356 comp[n] = malloc(q - start + 1); 357 if (comp[n] == NULL) { 358 ret = krb5_enomem(context); 359 goto exit; 360 } 361 memcpy(comp[n], start, q - start); 362 comp[n][q - start] = 0; 363 n++; 364 } 365 *principal = calloc(1, sizeof(**principal)); 366 if (*principal == NULL) { 367 ret = krb5_enomem(context); 368 goto exit; 369 } 370 (*principal)->name.name_string.val = comp; 371 princ_num_comp(*principal) = n; 372 (*principal)->realm = realm; 373 if (enterprise) 374 princ_type(*principal) = KRB5_NT_ENTERPRISE_PRINCIPAL; 375 else 376 set_default_princ_type(*principal, KRB5_NT_PRINCIPAL); 377 free(s); 378 return 0; 379 exit: 380 while (n>0) { 381 free(comp[--n]); 382 } 383 free(comp); 384 krb5_free_default_realm(context, realm); 385 free(s); 386 return ret; 387 } 388 389 /** 390 * Parse a name into a krb5_principal structure 391 * 392 * @param context Kerberos 5 context 393 * @param name name to parse into a Kerberos principal 394 * @param principal returned principal, free with krb5_free_principal(). 395 * 396 * @return An krb5 error code, see krb5_get_error_message(). 397 * 398 * @ingroup krb5_principal 399 */ 400 401 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 402 krb5_parse_name(krb5_context context, 403 const char *name, 404 krb5_principal *principal) 405 { 406 return krb5_parse_name_flags(context, name, 0, principal); 407 } 408 409 static const char quotable_chars[] = " \n\t\b\\/@"; 410 static const char replace_chars[] = " ntb\\/@"; 411 412 #define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0); 413 414 static size_t 415 quote_string(const char *s, char *out, size_t idx, size_t len, int display) 416 { 417 const char *p, *q; 418 for(p = s; *p && idx < len; p++){ 419 q = strchr(quotable_chars, *p); 420 if (q && display) { 421 add_char(out, idx, len, replace_chars[q - quotable_chars]); 422 } else if (q) { 423 add_char(out, idx, len, '\\'); 424 add_char(out, idx, len, replace_chars[q - quotable_chars]); 425 }else 426 add_char(out, idx, len, *p); 427 } 428 if(idx < len) 429 out[idx] = '\0'; 430 return idx; 431 } 432 433 434 static krb5_error_code 435 unparse_name_fixed(krb5_context context, 436 krb5_const_principal principal, 437 char *name, 438 size_t len, 439 int flags) 440 { 441 size_t idx = 0; 442 size_t i; 443 int short_form = (flags & KRB5_PRINCIPAL_UNPARSE_SHORT) != 0; 444 int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) != 0; 445 int display = (flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) != 0; 446 447 if (!no_realm && princ_realm(principal) == NULL) { 448 krb5_set_error_message(context, ERANGE, 449 N_("Realm missing from principal, " 450 "can't unparse", "")); 451 return ERANGE; 452 } 453 454 for(i = 0; i < princ_num_comp(principal); i++){ 455 if(i) 456 add_char(name, idx, len, '/'); 457 idx = quote_string(princ_ncomp(principal, i), name, idx, len, display); 458 if(idx == len) { 459 krb5_set_error_message(context, ERANGE, 460 N_("Out of space printing principal", "")); 461 return ERANGE; 462 } 463 } 464 /* add realm if different from default realm */ 465 if(short_form && !no_realm) { 466 krb5_realm r; 467 krb5_error_code ret; 468 ret = krb5_get_default_realm(context, &r); 469 if(ret) 470 return ret; 471 if(strcmp(princ_realm(principal), r) != 0) 472 short_form = 0; 473 krb5_free_default_realm(context, r); 474 } 475 if(!short_form && !no_realm) { 476 add_char(name, idx, len, '@'); 477 idx = quote_string(princ_realm(principal), name, idx, len, display); 478 if(idx == len) { 479 krb5_set_error_message(context, ERANGE, 480 N_("Out of space printing " 481 "realm of principal", "")); 482 return ERANGE; 483 } 484 } 485 return 0; 486 } 487 488 /** 489 * Unparse the principal name to a fixed buffer 490 * 491 * @param context A Kerberos context. 492 * @param principal principal to unparse 493 * @param name buffer to write name to 494 * @param len length of buffer 495 * 496 * @return An krb5 error code, see krb5_get_error_message(). 497 * 498 * @ingroup krb5_principal 499 */ 500 501 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 502 krb5_unparse_name_fixed(krb5_context context, 503 krb5_const_principal principal, 504 char *name, 505 size_t len) 506 { 507 return unparse_name_fixed(context, principal, name, len, 0); 508 } 509 510 /** 511 * Unparse the principal name to a fixed buffer. The realm is skipped 512 * if its a default realm. 513 * 514 * @param context A Kerberos context. 515 * @param principal principal to unparse 516 * @param name buffer to write name to 517 * @param len length of buffer 518 * 519 * @return An krb5 error code, see krb5_get_error_message(). 520 * 521 * @ingroup krb5_principal 522 */ 523 524 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 525 krb5_unparse_name_fixed_short(krb5_context context, 526 krb5_const_principal principal, 527 char *name, 528 size_t len) 529 { 530 return unparse_name_fixed(context, principal, name, len, 531 KRB5_PRINCIPAL_UNPARSE_SHORT); 532 } 533 534 /** 535 * Unparse the principal name with unparse flags to a fixed buffer. 536 * 537 * @param context A Kerberos context. 538 * @param principal principal to unparse 539 * @param flags unparse flags 540 * @param name buffer to write name to 541 * @param len length of buffer 542 * 543 * @return An krb5 error code, see krb5_get_error_message(). 544 * 545 * @ingroup krb5_principal 546 */ 547 548 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 549 krb5_unparse_name_fixed_flags(krb5_context context, 550 krb5_const_principal principal, 551 int flags, 552 char *name, 553 size_t len) 554 { 555 return unparse_name_fixed(context, principal, name, len, flags); 556 } 557 558 static krb5_error_code 559 unparse_name(krb5_context context, 560 krb5_const_principal principal, 561 char **name, 562 int flags) 563 { 564 size_t len = 0, plen; 565 size_t i; 566 krb5_error_code ret; 567 /* count length */ 568 if (princ_realm(principal)) { 569 plen = strlen(princ_realm(principal)); 570 571 if(strcspn(princ_realm(principal), quotable_chars) == plen) 572 len += plen; 573 else 574 len += 2*plen; 575 len++; /* '@' */ 576 } 577 for(i = 0; i < princ_num_comp(principal); i++){ 578 plen = strlen(princ_ncomp(principal, i)); 579 if(strcspn(princ_ncomp(principal, i), quotable_chars) == plen) 580 len += plen; 581 else 582 len += 2*plen; 583 len++; 584 } 585 len++; /* '\0' */ 586 *name = malloc(len); 587 if(*name == NULL) 588 return krb5_enomem(context); 589 ret = unparse_name_fixed(context, principal, *name, len, flags); 590 if(ret) { 591 free(*name); 592 *name = NULL; 593 } 594 return ret; 595 } 596 597 /** 598 * Unparse the Kerberos name into a string 599 * 600 * @param context Kerberos 5 context 601 * @param principal principal to query 602 * @param name resulting string, free with krb5_xfree() 603 * 604 * @return An krb5 error code, see krb5_get_error_message(). 605 * 606 * @ingroup krb5_principal 607 */ 608 609 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 610 krb5_unparse_name(krb5_context context, 611 krb5_const_principal principal, 612 char **name) 613 { 614 return unparse_name(context, principal, name, 0); 615 } 616 617 /** 618 * Unparse the Kerberos name into a string 619 * 620 * @param context Kerberos 5 context 621 * @param principal principal to query 622 * @param flags flag to determine the behavior 623 * @param name resulting string, free with krb5_xfree() 624 * 625 * @return An krb5 error code, see krb5_get_error_message(). 626 * 627 * @ingroup krb5_principal 628 */ 629 630 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 631 krb5_unparse_name_flags(krb5_context context, 632 krb5_const_principal principal, 633 int flags, 634 char **name) 635 { 636 return unparse_name(context, principal, name, flags); 637 } 638 639 /** 640 * Unparse the principal name to a allocated buffer. The realm is 641 * skipped if its a default realm. 642 * 643 * @param context A Kerberos context. 644 * @param principal principal to unparse 645 * @param name returned buffer, free with krb5_xfree() 646 * 647 * @return An krb5 error code, see krb5_get_error_message(). 648 * 649 * @ingroup krb5_principal 650 */ 651 652 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 653 krb5_unparse_name_short(krb5_context context, 654 krb5_const_principal principal, 655 char **name) 656 { 657 return unparse_name(context, principal, name, KRB5_PRINCIPAL_UNPARSE_SHORT); 658 } 659 660 /** 661 * Set a new realm for a principal, and as a side-effect free the 662 * previous realm. 663 * 664 * @param context A Kerberos context. 665 * @param principal principal set the realm for 666 * @param realm the new realm to set 667 * 668 * @return An krb5 error code, see krb5_get_error_message(). 669 * 670 * @ingroup krb5_principal 671 */ 672 673 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 674 krb5_principal_set_realm(krb5_context context, 675 krb5_principal principal, 676 krb5_const_realm realm) 677 { 678 if (princ_realm(principal)) 679 free(princ_realm(principal)); 680 681 if (realm == NULL) 682 princ_realm(principal) = NULL; 683 else if ((princ_realm(principal) = strdup(realm)) == NULL) 684 return krb5_enomem(context); 685 return 0; 686 } 687 688 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 689 krb5_principal_set_comp_string(krb5_context context, 690 krb5_principal principal, 691 unsigned int k, 692 const char *component) 693 { 694 char *s; 695 size_t i; 696 697 for (i = princ_num_comp(principal); i <= k; i++) 698 append_component(context, principal, "", 0); 699 s = strdup(component); 700 if (s == NULL) 701 return krb5_enomem(context); 702 free(princ_ncomp(principal, k)); 703 princ_ncomp(principal, k) = s; 704 return 0; 705 } 706 707 #ifndef HEIMDAL_SMALLER 708 /** 709 * Build a principal using vararg style building 710 * 711 * @param context A Kerberos context. 712 * @param principal returned principal 713 * @param rlen length of realm 714 * @param realm realm name 715 * @param ... a list of components ended with NULL. 716 * 717 * @return An krb5 error code, see krb5_get_error_message(). 718 * 719 * @ingroup krb5_principal 720 */ 721 722 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 723 krb5_build_principal(krb5_context context, 724 krb5_principal *principal, 725 int rlen, 726 krb5_const_realm realm, 727 ...) 728 { 729 krb5_error_code ret; 730 va_list ap; 731 va_start(ap, realm); 732 ret = krb5_build_principal_va(context, principal, rlen, realm, ap); 733 va_end(ap); 734 return ret; 735 } 736 #endif 737 738 /** 739 * Build a principal using vararg style building 740 * 741 * @param context A Kerberos context. 742 * @param principal returned principal 743 * @param realm realm name 744 * @param ... a list of components ended with NULL. 745 * 746 * @return An krb5 error code, see krb5_get_error_message(). 747 * 748 * @ingroup krb5_principal 749 */ 750 751 /* coverity[+alloc : arg-*1] */ 752 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 753 krb5_make_principal(krb5_context context, 754 krb5_principal *principal, 755 krb5_const_realm realm, 756 ...) 757 { 758 krb5_error_code ret; 759 krb5_realm r = NULL; 760 va_list ap; 761 if(realm == NULL) { 762 ret = krb5_get_default_realm(context, &r); 763 if(ret) 764 return ret; 765 realm = r; 766 } 767 va_start(ap, realm); 768 ret = krb5_build_principal_va(context, principal, strlen(realm), realm, ap); 769 va_end(ap); 770 if(r) 771 krb5_free_default_realm(context, r); 772 return ret; 773 } 774 775 static krb5_error_code 776 append_component(krb5_context context, krb5_principal p, 777 const char *comp, 778 size_t comp_len) 779 { 780 heim_general_string *tmp; 781 size_t len = princ_num_comp(p); 782 783 tmp = realloc(princ_comp(p), (len + 1) * sizeof(*tmp)); 784 if(tmp == NULL) 785 return krb5_enomem(context); 786 princ_comp(p) = tmp; 787 princ_ncomp(p, len) = malloc(comp_len + 1); 788 if (princ_ncomp(p, len) == NULL) 789 return krb5_enomem(context); 790 memcpy (princ_ncomp(p, len), comp, comp_len); 791 princ_ncomp(p, len)[comp_len] = '\0'; 792 princ_num_comp(p)++; 793 return 0; 794 } 795 796 static krb5_error_code 797 va_ext_princ(krb5_context context, krb5_principal p, va_list ap) 798 { 799 krb5_error_code ret = 0; 800 801 while (1){ 802 const char *s; 803 int len; 804 805 if ((len = va_arg(ap, int)) == 0) 806 break; 807 s = va_arg(ap, const char*); 808 if ((ret = append_component(context, p, s, len)) != 0) 809 break; 810 } 811 return ret; 812 } 813 814 static krb5_error_code 815 va_princ(krb5_context context, krb5_principal p, va_list ap) 816 { 817 krb5_error_code ret = 0; 818 819 while (1){ 820 const char *s; 821 822 if ((s = va_arg(ap, const char*)) == NULL) 823 break; 824 if ((ret = append_component(context, p, s, strlen(s))) != 0) 825 break; 826 } 827 return ret; 828 } 829 830 static krb5_error_code 831 build_principal(krb5_context context, 832 krb5_principal *principal, 833 int rlen, 834 krb5_const_realm realm, 835 krb5_error_code (*func)(krb5_context, krb5_principal, va_list), 836 va_list ap) 837 { 838 krb5_error_code ret; 839 krb5_principal p; 840 841 *principal = NULL; 842 p = calloc(1, sizeof(*p)); 843 if (p == NULL) 844 return krb5_enomem(context); 845 846 princ_realm(p) = strdup(realm); 847 if (p->realm == NULL) { 848 free(p); 849 return krb5_enomem(context); 850 } 851 852 ret = func(context, p, ap); 853 if (ret == 0) { 854 *principal = p; 855 set_default_princ_type(p, KRB5_NT_PRINCIPAL); 856 } else 857 krb5_free_principal(context, p); 858 return ret; 859 } 860 861 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 862 krb5_build_principal_va(krb5_context context, 863 krb5_principal *principal, 864 int rlen, 865 krb5_const_realm realm, 866 va_list ap) 867 { 868 return build_principal(context, principal, rlen, realm, va_princ, ap); 869 } 870 871 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 872 krb5_build_principal_va_ext(krb5_context context, 873 krb5_principal *principal, 874 int rlen, 875 krb5_const_realm realm, 876 va_list ap) 877 { 878 return build_principal(context, principal, rlen, realm, va_ext_princ, ap); 879 } 880 881 882 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 883 krb5_build_principal_ext(krb5_context context, 884 krb5_principal *principal, 885 int rlen, 886 krb5_const_realm realm, 887 ...) 888 { 889 krb5_error_code ret; 890 va_list ap; 891 va_start(ap, realm); 892 ret = krb5_build_principal_va_ext(context, principal, rlen, realm, ap); 893 va_end(ap); 894 return ret; 895 } 896 897 /** 898 * Copy a principal 899 * 900 * @param context A Kerberos context. 901 * @param inprinc principal to copy 902 * @param outprinc copied principal, free with krb5_free_principal() 903 * 904 * @return An krb5 error code, see krb5_get_error_message(). 905 * 906 * @ingroup krb5_principal 907 */ 908 909 910 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 911 krb5_copy_principal(krb5_context context, 912 krb5_const_principal inprinc, 913 krb5_principal *outprinc) 914 { 915 krb5_principal p = malloc(sizeof(*p)); 916 if (p == NULL) 917 return krb5_enomem(context); 918 if(copy_Principal(inprinc, p)) { 919 free(p); 920 return krb5_enomem(context); 921 } 922 *outprinc = p; 923 return 0; 924 } 925 926 /** 927 * Return TRUE iff princ1 == princ2 (without considering the realm) 928 * 929 * @param context Kerberos 5 context 930 * @param princ1 first principal to compare 931 * @param princ2 second principal to compare 932 * 933 * @return non zero if equal, 0 if not 934 * 935 * @ingroup krb5_principal 936 * @see krb5_principal_compare() 937 * @see krb5_realm_compare() 938 */ 939 940 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 941 krb5_principal_compare_any_realm(krb5_context context, 942 krb5_const_principal princ1, 943 krb5_const_principal princ2) 944 { 945 size_t i; 946 if(princ_num_comp(princ1) != princ_num_comp(princ2)) 947 return FALSE; 948 for(i = 0; i < princ_num_comp(princ1); i++){ 949 if(strcmp(princ_ncomp(princ1, i), princ_ncomp(princ2, i)) != 0) 950 return FALSE; 951 } 952 return TRUE; 953 } 954 955 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 956 _krb5_principal_compare_PrincipalName(krb5_context context, 957 krb5_const_principal princ1, 958 PrincipalName *princ2) 959 { 960 size_t i; 961 if (princ_num_comp(princ1) != princ2->name_string.len) 962 return FALSE; 963 for(i = 0; i < princ_num_comp(princ1); i++){ 964 if(strcmp(princ_ncomp(princ1, i), princ2->name_string.val[i]) != 0) 965 return FALSE; 966 } 967 return TRUE; 968 } 969 970 971 /** 972 * Compares the two principals, including realm of the principals and returns 973 * TRUE if they are the same and FALSE if not. 974 * 975 * @param context Kerberos 5 context 976 * @param princ1 first principal to compare 977 * @param princ2 second principal to compare 978 * 979 * @ingroup krb5_principal 980 * @see krb5_principal_compare_any_realm() 981 * @see krb5_realm_compare() 982 */ 983 984 /* 985 * return TRUE iff princ1 == princ2 986 */ 987 988 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 989 krb5_principal_compare(krb5_context context, 990 krb5_const_principal princ1, 991 krb5_const_principal princ2) 992 { 993 if (!krb5_realm_compare(context, princ1, princ2)) 994 return FALSE; 995 return krb5_principal_compare_any_realm(context, princ1, princ2); 996 } 997 998 /** 999 * return TRUE iff realm(princ1) == realm(princ2) 1000 * 1001 * @param context Kerberos 5 context 1002 * @param princ1 first principal to compare 1003 * @param princ2 second principal to compare 1004 * 1005 * @ingroup krb5_principal 1006 * @see krb5_principal_compare_any_realm() 1007 * @see krb5_principal_compare() 1008 */ 1009 1010 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 1011 krb5_realm_compare(krb5_context context, 1012 krb5_const_principal princ1, 1013 krb5_const_principal princ2) 1014 { 1015 return strcmp(princ_realm(princ1), princ_realm(princ2)) == 0; 1016 } 1017 1018 /** 1019 * return TRUE iff princ matches pattern 1020 * 1021 * @ingroup krb5_principal 1022 */ 1023 1024 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 1025 krb5_principal_match(krb5_context context, 1026 krb5_const_principal princ, 1027 krb5_const_principal pattern) 1028 { 1029 size_t i; 1030 if(princ_num_comp(princ) != princ_num_comp(pattern)) 1031 return FALSE; 1032 if(fnmatch(princ_realm(pattern), princ_realm(princ), 0) != 0) 1033 return FALSE; 1034 for(i = 0; i < princ_num_comp(princ); i++){ 1035 if(fnmatch(princ_ncomp(pattern, i), princ_ncomp(princ, i), 0) != 0) 1036 return FALSE; 1037 } 1038 return TRUE; 1039 } 1040 1041 /* 1042 * This is the original krb5_sname_to_principal(), renamed to be a 1043 * helper of the new one. 1044 */ 1045 static krb5_error_code 1046 krb5_sname_to_principal_old(krb5_context context, 1047 const char *realm, 1048 const char *hostname, 1049 const char *sname, 1050 int32_t type, 1051 krb5_principal *ret_princ) 1052 { 1053 krb5_error_code ret; 1054 char localhost[MAXHOSTNAMELEN]; 1055 char **realms = NULL, *host = NULL; 1056 1057 if(type != KRB5_NT_SRV_HST && type != KRB5_NT_UNKNOWN) { 1058 krb5_set_error_message(context, KRB5_SNAME_UNSUPP_NAMETYPE, 1059 N_("unsupported name type %d", ""), 1060 (int)type); 1061 return KRB5_SNAME_UNSUPP_NAMETYPE; 1062 } 1063 if(hostname == NULL) { 1064 ret = gethostname(localhost, sizeof(localhost) - 1); 1065 if (ret != 0) { 1066 ret = errno; 1067 krb5_set_error_message(context, ret, 1068 N_("Failed to get local hostname", "")); 1069 return ret; 1070 } 1071 localhost[sizeof(localhost) - 1] = '\0'; 1072 hostname = localhost; 1073 } 1074 if(sname == NULL) 1075 sname = "host"; 1076 if(type == KRB5_NT_SRV_HST) { 1077 if (realm) 1078 ret = krb5_expand_hostname(context, hostname, &host); 1079 else 1080 ret = krb5_expand_hostname_realms(context, hostname, 1081 &host, &realms); 1082 if (ret) 1083 return ret; 1084 strlwr(host); 1085 hostname = host; 1086 if (!realm) 1087 realm = realms[0]; 1088 } else if (!realm) { 1089 ret = krb5_get_host_realm(context, hostname, &realms); 1090 if(ret) 1091 return ret; 1092 realm = realms[0]; 1093 } 1094 1095 ret = krb5_make_principal(context, ret_princ, realm, sname, 1096 hostname, NULL); 1097 if(host) 1098 free(host); 1099 if (realms) 1100 krb5_free_host_realm(context, realms); 1101 return ret; 1102 } 1103 1104 static const struct { 1105 const char *type; 1106 int32_t value; 1107 } nametypes[] = { 1108 { "UNKNOWN", KRB5_NT_UNKNOWN }, 1109 { "PRINCIPAL", KRB5_NT_PRINCIPAL }, 1110 { "SRV_INST", KRB5_NT_SRV_INST }, 1111 { "SRV_HST", KRB5_NT_SRV_HST }, 1112 { "SRV_XHST", KRB5_NT_SRV_XHST }, 1113 { "UID", KRB5_NT_UID }, 1114 { "X500_PRINCIPAL", KRB5_NT_X500_PRINCIPAL }, 1115 { "SMTP_NAME", KRB5_NT_SMTP_NAME }, 1116 { "ENTERPRISE_PRINCIPAL", KRB5_NT_ENTERPRISE_PRINCIPAL }, 1117 { "WELLKNOWN", KRB5_NT_WELLKNOWN }, 1118 { "SRV_HST_DOMAIN", KRB5_NT_SRV_HST_DOMAIN }, 1119 { "ENT_PRINCIPAL_AND_ID", KRB5_NT_ENT_PRINCIPAL_AND_ID }, 1120 { "MS_PRINCIPAL", KRB5_NT_MS_PRINCIPAL }, 1121 { "MS_PRINCIPAL_AND_ID", KRB5_NT_MS_PRINCIPAL_AND_ID }, 1122 { "SRV_HST_NEEDS_CANON", KRB5_NT_SRV_HST_NEEDS_CANON }, 1123 { NULL, 0 } 1124 }; 1125 1126 /** 1127 * Parse nametype string and return a nametype integer 1128 * 1129 * @ingroup krb5_principal 1130 */ 1131 1132 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1133 krb5_parse_nametype(krb5_context context, const char *str, int32_t *nametype) 1134 { 1135 size_t i; 1136 1137 for(i = 0; nametypes[i].type; i++) { 1138 if (strcasecmp(nametypes[i].type, str) == 0) { 1139 *nametype = nametypes[i].value; 1140 return 0; 1141 } 1142 } 1143 krb5_set_error_message(context, KRB5_PARSE_MALFORMED, 1144 N_("Failed to find name type %s", ""), str); 1145 return KRB5_PARSE_MALFORMED; 1146 } 1147 1148 /** 1149 * Returns true if name is Kerberos NULL name 1150 * 1151 * @ingroup krb5_principal 1152 */ 1153 1154 krb5_boolean KRB5_LIB_FUNCTION 1155 krb5_principal_is_null(krb5_context context, krb5_const_principal principal) 1156 { 1157 if (principal->name.name_type == KRB5_NT_WELLKNOWN && 1158 principal->name.name_string.len == 2 && 1159 strcmp(principal->name.name_string.val[0], "WELLKNOWN") == 0 && 1160 strcmp(principal->name.name_string.val[1], "NULL") == 0) 1161 return TRUE; 1162 return FALSE; 1163 } 1164 1165 const char _krb5_wellknown_lkdc[] = "WELLKNOWN:COM.APPLE.LKDC"; 1166 static const char lkdc_prefix[] = "LKDC:"; 1167 1168 /** 1169 * Returns true if name is Kerberos an LKDC realm 1170 * 1171 * @ingroup krb5_principal 1172 */ 1173 1174 krb5_boolean KRB5_LIB_FUNCTION 1175 krb5_realm_is_lkdc(const char *realm) 1176 { 1177 1178 return strncmp(realm, lkdc_prefix, sizeof(lkdc_prefix)-1) == 0 || 1179 strncmp(realm, _krb5_wellknown_lkdc, sizeof(_krb5_wellknown_lkdc) - 1) == 0; 1180 } 1181 1182 /** 1183 * Returns true if name is Kerberos an LKDC realm 1184 * 1185 * @ingroup krb5_principal 1186 */ 1187 1188 krb5_boolean KRB5_LIB_FUNCTION 1189 krb5_principal_is_lkdc(krb5_context context, krb5_const_principal principal) 1190 { 1191 return krb5_realm_is_lkdc(principal->realm); 1192 } 1193 1194 /** 1195 * Returns true if name is Kerberos an LKDC realm 1196 * 1197 * @ingroup krb5_principal 1198 */ 1199 1200 krb5_boolean KRB5_LIB_FUNCTION 1201 krb5_principal_is_pku2u(krb5_context context, krb5_const_principal principal) 1202 { 1203 return strcmp(principal->realm, KRB5_PKU2U_REALM_NAME) == 0; 1204 } 1205 1206 /** 1207 * Check if the cname part of the principal is a krbtgt principal 1208 * 1209 * @ingroup krb5_principal 1210 */ 1211 1212 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 1213 krb5_principal_is_krbtgt(krb5_context context, krb5_const_principal p) 1214 { 1215 return p->name.name_string.len == 2 && 1216 strcmp(p->name.name_string.val[0], KRB5_TGS_NAME) == 0; 1217 } 1218 1219 /** 1220 * Returns true iff name is an WELLKNOWN:ORG.H5L.HOSTBASED-SERVICE 1221 * 1222 * @ingroup krb5_principal 1223 */ 1224 1225 krb5_boolean KRB5_LIB_FUNCTION 1226 krb5_principal_is_gss_hostbased_service(krb5_context context, 1227 krb5_const_principal principal) 1228 { 1229 if (principal == NULL) 1230 return FALSE; 1231 if (principal->name.name_string.len != 2) 1232 return FALSE; 1233 if (strcmp(principal->name.name_string.val[1], KRB5_GSS_HOSTBASED_SERVICE_NAME) != 0) 1234 return FALSE; 1235 return TRUE; 1236 } 1237 1238 /** 1239 * Check if the cname part of the principal is a initial or renewed krbtgt principal 1240 * 1241 * @ingroup krb5_principal 1242 */ 1243 1244 krb5_boolean KRB5_LIB_FUNCTION 1245 krb5_principal_is_root_krbtgt(krb5_context context, krb5_const_principal p) 1246 { 1247 return p->name.name_string.len == 2 && 1248 strcmp(p->name.name_string.val[0], KRB5_TGS_NAME) == 0 && 1249 strcmp(p->name.name_string.val[1], p->realm) == 0; 1250 } 1251 1252 static int 1253 tolower_ascii(int c) 1254 { 1255 if (c >= 'A' || c <= 'Z') 1256 return 'a' + (c - 'A'); 1257 return c; 1258 } 1259 1260 typedef enum krb5_name_canon_rule_type { 1261 KRB5_NCRT_BOGUS = 0, 1262 KRB5_NCRT_AS_IS, 1263 KRB5_NCRT_QUALIFY, 1264 KRB5_NCRT_NSS 1265 } krb5_name_canon_rule_type; 1266 1267 #ifdef UINT8_MAX 1268 #define MAXDOTS UINT8_MAX 1269 #else 1270 #define MAXDOTS (255U) 1271 #endif 1272 #ifdef UINT16_MAX 1273 #define MAXORDER UINT16_MAX 1274 #else 1275 #define MAXORDER (65535U) 1276 #endif 1277 1278 struct krb5_name_canon_rule_data { 1279 krb5_name_canon_rule_type type; 1280 krb5_name_canon_rule_options options; 1281 uint8_t mindots; /* match this many dots or more */ 1282 uint8_t maxdots; /* match no more than this many dots */ 1283 uint16_t explicit_order; /* given order */ 1284 uint16_t order; /* actual order */ 1285 char *match_domain; /* match this stem */ 1286 char *match_realm; /* match this realm */ 1287 char *domain; /* qualify with this domain */ 1288 char *realm; /* qualify with this realm */ 1289 }; 1290 1291 /** 1292 * Create a principal for the given service running on the given 1293 * hostname. If KRB5_NT_SRV_HST is used, the hostname is canonicalized 1294 * according the configured name canonicalization rules, with 1295 * canonicalization delayed in some cases. One rule involves DNS, which 1296 * is insecure unless DNSSEC is used, but we don't use DNSSEC-capable 1297 * resolver APIs here, so that if DNSSEC is used we wouldn't know it. 1298 * 1299 * Canonicalization is immediate (not delayed) only when there is only 1300 * one canonicalization rule and that rule indicates that we should do a 1301 * host lookup by name (i.e., DNS). 1302 * 1303 * @param context A Kerberos context. 1304 * @param hostname hostname to use 1305 * @param sname Service name to use 1306 * @param type name type of principal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN. 1307 * @param ret_princ return principal, free with krb5_free_principal(). 1308 * 1309 * @return An krb5 error code, see krb5_get_error_message(). 1310 * 1311 * @ingroup krb5_principal 1312 */ 1313 1314 /* coverity[+alloc : arg-*4] */ 1315 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1316 krb5_sname_to_principal(krb5_context context, 1317 const char *hostname, 1318 const char *sname, 1319 int32_t type, 1320 krb5_principal *ret_princ) 1321 { 1322 char *realm, *remote_host; 1323 krb5_error_code ret; 1324 register char *cp; 1325 char localname[MAXHOSTNAMELEN]; 1326 1327 *ret_princ = NULL; 1328 1329 if ((type != KRB5_NT_UNKNOWN) && 1330 (type != KRB5_NT_SRV_HST)) 1331 return KRB5_SNAME_UNSUPP_NAMETYPE; 1332 1333 /* if hostname is NULL, use local hostname */ 1334 if (hostname == NULL) { 1335 if (gethostname(localname, MAXHOSTNAMELEN)) 1336 return errno; 1337 hostname = localname; 1338 } 1339 1340 /* if sname is NULL, use "host" */ 1341 if (sname == NULL) 1342 sname = "host"; 1343 1344 remote_host = strdup(hostname); 1345 if (remote_host == NULL) 1346 return krb5_enomem(context); 1347 1348 if (type == KRB5_NT_SRV_HST) { 1349 krb5_name_canon_rule rules; 1350 1351 /* Lower-case the hostname, because that's the convention */ 1352 for (cp = remote_host; *cp; cp++) 1353 if (isupper((int) (*cp))) 1354 *cp = tolower((int) (*cp)); 1355 1356 /* 1357 * If there is only one name canon rule and it says to 1358 * canonicalize the old way, do that now, as we used to. 1359 */ 1360 ret = _krb5_get_name_canon_rules(context, &rules); 1361 if (ret) { 1362 _krb5_debug(context, 5, "Failed to get name canon rules: ret = %d", 1363 ret); 1364 free(remote_host); 1365 return ret; 1366 } 1367 if (rules[0].type == KRB5_NCRT_NSS && 1368 rules[1].type == KRB5_NCRT_BOGUS) { 1369 _krb5_debug(context, 5, "Using nss for name canon immediately"); 1370 ret = krb5_sname_to_principal_old(context, rules[0].realm, 1371 remote_host, sname, 1372 KRB5_NT_SRV_HST, ret_princ); 1373 free(remote_host); 1374 return ret; 1375 } 1376 } 1377 1378 /* Remove trailing dots */ 1379 if (remote_host[0]) { 1380 for (cp = remote_host + strlen(remote_host)-1; 1381 *cp == '.' && cp > remote_host; 1382 cp--) { 1383 *cp = '\0'; 1384 } 1385 } 1386 1387 realm = ""; /* "Referral realm" */ 1388 1389 ret = krb5_build_principal(context, ret_princ, strlen(realm), 1390 realm, sname, remote_host, 1391 (char *)0); 1392 1393 if (ret == 0 && type == KRB5_NT_SRV_HST) { 1394 /* 1395 * Hostname canonicalization is done elsewhere (in 1396 * krb5_get_credentials() and krb5_kt_get_entry()). 1397 * 1398 * We overload the name type to indicate to those functions that 1399 * this principal name requires canonicalization. 1400 * 1401 * We can't use the empty realm to denote the need to 1402 * canonicalize the hostname too: it would mean that users who 1403 * want to assert knowledge of a service's realm must also know 1404 * the canonical hostname, but in practice they don't. 1405 */ 1406 (*ret_princ)->name.name_type = KRB5_NT_SRV_HST_NEEDS_CANON; 1407 1408 _krb5_debug(context, 5, "Building a delayed canon principal for %s/%s@", 1409 sname, remote_host); 1410 } 1411 1412 free(remote_host); 1413 return ret; 1414 } 1415 1416 static void 1417 tolower_str(char *s) 1418 { 1419 for (; *s != '\0'; s++) { 1420 if (isupper(*s)) 1421 *s = tolower_ascii(*s); 1422 } 1423 } 1424 1425 static krb5_error_code 1426 rule_parse_token(krb5_context context, krb5_name_canon_rule rule, 1427 const char *tok) 1428 { 1429 long int n; 1430 int needs_type = rule->type == KRB5_NCRT_BOGUS; 1431 1432 /* 1433 * Rules consist of a sequence of tokens, some of which indicate 1434 * what type of rule the rule is, and some of which set rule options 1435 * or ancilliary data. Last rule type token wins. 1436 */ 1437 1438 /* Rule type tokens: */ 1439 if (needs_type && strcmp(tok, "as-is") == 0) { 1440 rule->type = KRB5_NCRT_AS_IS; 1441 } else if (needs_type && strcmp(tok, "qualify") == 0) { 1442 rule->type = KRB5_NCRT_QUALIFY; 1443 } else if (needs_type && strcmp(tok, "nss") == 0) { 1444 rule->type = KRB5_NCRT_NSS; 1445 /* Rule options: */ 1446 } else if (strcmp(tok, "use_fast") == 0) { 1447 rule->options |= KRB5_NCRO_USE_FAST; 1448 } else if (strcmp(tok, "use_dnssec") == 0) { 1449 rule->options |= KRB5_NCRO_USE_DNSSEC; 1450 } else if (strcmp(tok, "ccache_only") == 0) { 1451 rule->options |= KRB5_NCRO_GC_ONLY; 1452 } else if (strcmp(tok, "no_referrals") == 0) { 1453 rule->options |= KRB5_NCRO_NO_REFERRALS; 1454 } else if (strcmp(tok, "use_referrals") == 0) { 1455 rule->options &= ~KRB5_NCRO_NO_REFERRALS; 1456 if (rule->realm == NULL) { 1457 rule->realm = strdup(""); 1458 if (rule->realm == NULL) 1459 return krb5_enomem(context); 1460 } 1461 } else if (strcmp(tok, "lookup_realm") == 0) { 1462 rule->options |= KRB5_NCRO_LOOKUP_REALM; 1463 free(rule->realm); 1464 rule->realm = NULL; 1465 /* Rule ancilliary data: */ 1466 } else if (strncmp(tok, "domain=", strlen("domain=")) == 0) { 1467 free(rule->domain); 1468 rule->domain = strdup(tok + strlen("domain=")); 1469 if (rule->domain == NULL) 1470 return krb5_enomem(context); 1471 tolower_str(rule->domain); 1472 } else if (strncmp(tok, "realm=", strlen("realm=")) == 0) { 1473 free(rule->realm); 1474 rule->realm = strdup(tok + strlen("realm=")); 1475 if (rule->realm == NULL) 1476 return krb5_enomem(context); 1477 } else if (strncmp(tok, "match_domain=", strlen("match_domain=")) == 0) { 1478 free(rule->match_domain); 1479 rule->match_domain = strdup(tok + strlen("match_domain=")); 1480 if (rule->match_domain == NULL) 1481 return krb5_enomem(context); 1482 tolower_str(rule->match_domain); 1483 } else if (strncmp(tok, "match_realm=", strlen("match_realm=")) == 0) { 1484 free(rule->match_realm); 1485 rule->match_realm = strdup(tok + strlen("match_realm=")); 1486 if (rule->match_realm == NULL) 1487 return krb5_enomem(context); 1488 } else if (strncmp(tok, "mindots=", strlen("mindots=")) == 0) { 1489 errno = 0; 1490 n = strtol(tok + strlen("mindots="), NULL, 10); 1491 if (errno == 0 && n > 0 && n <= MAXDOTS) 1492 rule->mindots = n; 1493 } else if (strncmp(tok, "maxdots=", strlen("maxdots=")) == 0) { 1494 errno = 0; 1495 n = strtol(tok + strlen("maxdots="), NULL, 10); 1496 if (errno == 0 && n > 0 && n <= MAXDOTS) 1497 rule->maxdots = n; 1498 } else if (strncmp(tok, "order=", strlen("order=")) == 0) { 1499 errno = 0; 1500 n = strtol(tok + strlen("order="), NULL, 10); 1501 if (errno == 0 && n > 0 && n <= MAXORDER) 1502 rule->explicit_order = n; 1503 } else { 1504 _krb5_debug(context, 5, 1505 "Unrecognized name canonicalization rule token %s", tok); 1506 return EINVAL; 1507 } 1508 return 0; 1509 } 1510 1511 static int 1512 rule_cmp(const void *a, const void *b) 1513 { 1514 krb5_const_name_canon_rule left = a; 1515 krb5_const_name_canon_rule right = b; 1516 1517 if (left->type == KRB5_NCRT_BOGUS && 1518 right->type == KRB5_NCRT_BOGUS) 1519 return 0; 1520 if (left->type == KRB5_NCRT_BOGUS) 1521 return 1; 1522 if (right->type == KRB5_NCRT_BOGUS) 1523 return -1; 1524 if (left->explicit_order < right->explicit_order) 1525 return -1; 1526 if (left->explicit_order > right->explicit_order) 1527 return 1; 1528 return left->order - right->order; 1529 } 1530 1531 static krb5_error_code 1532 parse_name_canon_rules(krb5_context context, char **rulestrs, 1533 krb5_name_canon_rule *rules) 1534 { 1535 krb5_error_code ret; 1536 char *tok; 1537 char *cp; 1538 char **cpp; 1539 size_t n; 1540 size_t i, k; 1541 int do_sort = 0; 1542 krb5_name_canon_rule r; 1543 1544 *rules = NULL; 1545 1546 for (n =0, cpp = rulestrs; cpp != NULL && *cpp != NULL; cpp++) 1547 n++; 1548 1549 n += 2; /* Always at least one rule; two for the default case */ 1550 1551 if ((r = calloc(n, sizeof (*r))) == NULL) 1552 return krb5_enomem(context); 1553 1554 for (k = 0; k < n; k++) { 1555 r[k].type = KRB5_NCRT_BOGUS; 1556 r[k].match_domain = NULL; 1557 r[k].match_realm = NULL; 1558 r[k].domain = NULL; 1559 r[k].realm = NULL; 1560 } 1561 1562 for (i = 0, k = 0; i < n && rulestrs != NULL && rulestrs[i] != NULL; i++) { 1563 cp = rulestrs[i]; 1564 r[k].explicit_order = MAXORDER; /* mark order, see below */ 1565 r[k].maxdots = MAXDOTS; 1566 r[k].order = k; /* default order */ 1567 1568 /* Tokenize and parse value */ 1569 do { 1570 tok = cp; 1571 cp = strchr(cp, ':'); /* XXX use strtok_r() */ 1572 if (cp) 1573 *cp++ = '\0'; /* delimit token */ 1574 ret = rule_parse_token(context, &r[k], tok); 1575 if (ret == EINVAL) { 1576 r[k].type = KRB5_NCRT_BOGUS; 1577 break; 1578 } 1579 if (ret) { 1580 _krb5_free_name_canon_rules(context, r); 1581 return ret; 1582 } 1583 } while (cp && *cp); 1584 if (r[k].explicit_order != MAXORDER) 1585 do_sort = 1; 1586 1587 /* Validate parsed rule */ 1588 if (r[k].type == KRB5_NCRT_BOGUS || 1589 (r[k].type == KRB5_NCRT_QUALIFY && !r[k].domain) || 1590 (r[k].type == KRB5_NCRT_NSS && r[k].domain)) { 1591 /* Invalid rule; mark it so and clean up */ 1592 r[k].type = KRB5_NCRT_BOGUS; 1593 free(r[k].match_domain); 1594 free(r[k].match_realm); 1595 free(r[k].domain); 1596 free(r[k].realm); 1597 r[k].realm = NULL; 1598 r[k].domain = NULL; 1599 r[k].match_domain = NULL; 1600 r[k].match_realm = NULL; 1601 _krb5_debug(context, 5, 1602 "Ignoring invalid name canonicalization rule %lu", 1603 (unsigned long)i); 1604 continue; 1605 } 1606 k++; /* good rule */ 1607 } 1608 1609 if (do_sort) { 1610 /* 1611 * Note that we make make this a stable sort by using appareance 1612 * and explicit order. 1613 */ 1614 qsort(r, n, sizeof(r[0]), rule_cmp); 1615 } 1616 1617 if (r[0].type == KRB5_NCRT_BOGUS) { 1618 /* No rules, or no valid rules */ 1619 r[0].type = KRB5_NCRT_NSS; 1620 } 1621 1622 *rules = r; 1623 return 0; /* We don't communicate bad rule errors here */ 1624 } 1625 1626 /* 1627 * This exists only because the hostname canonicalization behavior in Heimdal 1628 * (and other implementations of Kerberos) has been to use getaddrinfo(), 1629 * unsafe though it is, for ages. We can't fix it in one day. 1630 */ 1631 static void 1632 make_rules_safe(krb5_context context, krb5_name_canon_rule rules) 1633 { 1634 /* 1635 * If the only rule were to use the name service (getaddrinfo()) then we're 1636 * bound to fail. We could try to convert that rule to an as-is rule, but 1637 * when we do get a validating resolver we'd be unhappy that we did such a 1638 * conversion. Better let the user get failures and make them think about 1639 * their naming rules. 1640 */ 1641 if (rules == NULL) 1642 return; 1643 for (; rules[0].type != KRB5_NCRT_BOGUS; rules++) { 1644 if (rules->type == KRB5_NCRT_NSS) 1645 rules->options |= KRB5_NCRO_USE_DNSSEC; 1646 else 1647 rules->options |= KRB5_NCRO_USE_FAST; 1648 } 1649 } 1650 1651 /** 1652 * This function returns an array of host-based service name 1653 * canonicalization rules. The array of rules is organized as a list. 1654 * See the definition of krb5_name_canon_rule. 1655 * 1656 * @param context A Kerberos context. 1657 * @param rules Output location for array of rules. 1658 */ 1659 KRB5_LIB_FUNCTION krb5_error_code 1660 _krb5_get_name_canon_rules(krb5_context context, krb5_name_canon_rule *rules) 1661 { 1662 krb5_error_code ret; 1663 char **values = NULL; 1664 1665 *rules = context->name_canon_rules; 1666 if (*rules != NULL) 1667 return 0; 1668 1669 values = krb5_config_get_strings(context, NULL, 1670 "libdefaults", "name_canon_rules", NULL); 1671 ret = parse_name_canon_rules(context, values, rules); 1672 krb5_config_free_strings(values); 1673 if (ret) 1674 return ret; 1675 1676 if (krb5_config_get_bool_default(context, NULL, FALSE, 1677 "libdefaults", "safe_name_canon", NULL)) 1678 make_rules_safe(context, *rules); 1679 1680 heim_assert(rules != NULL && (*rules)[0].type != KRB5_NCRT_BOGUS, 1681 "internal error in parsing principal name " 1682 "canonicalization rules"); 1683 1684 /* Memoize */ 1685 context->name_canon_rules = *rules; 1686 1687 return 0; 1688 } 1689 1690 static krb5_error_code 1691 get_host_realm(krb5_context context, const char *hostname, char **realm) 1692 { 1693 krb5_error_code ret; 1694 char **hrealms = NULL; 1695 1696 *realm = NULL; 1697 ret = krb5_get_host_realm(context, hostname, &hrealms); 1698 if (ret) 1699 return ret; 1700 if (hrealms == NULL) 1701 return KRB5_ERR_HOST_REALM_UNKNOWN; /* krb5_set_error() already done */ 1702 if (hrealms[0] == NULL) { 1703 krb5_free_host_realm(context, hrealms); 1704 return KRB5_ERR_HOST_REALM_UNKNOWN; /* krb5_set_error() already done */ 1705 } 1706 *realm = strdup(hrealms[0]); 1707 krb5_free_host_realm(context, hrealms); 1708 if (*realm == NULL) 1709 return krb5_enomem(context); 1710 return 0; 1711 } 1712 1713 static int 1714 is_domain_suffix(const char *domain, const char *suffix) 1715 { 1716 size_t dlen = strlen(domain); 1717 size_t slen = strlen(suffix); 1718 1719 if (dlen < slen + 2) 1720 return 0; 1721 1722 if (strcasecmp(domain + (dlen - slen), suffix) != 0) 1723 return 0; 1724 1725 if (domain[(dlen - slen) - 1] != '.') 1726 return 0; 1727 return 1; 1728 } 1729 1730 /* 1731 * Applies a name canonicalization rule to a principal. 1732 * 1733 * Returns zero and no out_princ if the rule does not match. 1734 * Returns zero and an out_princ if the rule does match. 1735 */ 1736 static krb5_error_code 1737 apply_name_canon_rule(krb5_context context, krb5_name_canon_rule rules, 1738 size_t rule_idx, krb5_const_principal in_princ, 1739 krb5_principal *out_princ, 1740 krb5_name_canon_rule_options *rule_opts) 1741 { 1742 krb5_name_canon_rule rule = &rules[rule_idx]; 1743 krb5_error_code ret; 1744 unsigned int ndots = 0; 1745 krb5_principal nss = NULL; 1746 const char *sname = NULL; 1747 const char *orig_hostname = NULL; 1748 const char *new_hostname = NULL; 1749 const char *new_realm = NULL; 1750 const char *port = ""; 1751 const char *cp; 1752 char *hostname_sans_port = NULL; 1753 char *hostname_with_port = NULL; 1754 char *tmp_hostname = NULL; 1755 char *tmp_realm = NULL; 1756 1757 *out_princ = NULL; /* Signal no match */ 1758 1759 if (rule_opts != NULL) 1760 *rule_opts = rule->options; 1761 1762 if (rule->type == KRB5_NCRT_BOGUS) 1763 return 0; /* rule doesn't apply */ 1764 1765 sname = krb5_principal_get_comp_string(context, in_princ, 0); 1766 orig_hostname = krb5_principal_get_comp_string(context, in_princ, 1); 1767 1768 /* 1769 * Some apps want to use the very non-standard svc/hostname:port@REALM 1770 * form. We do our best to support that here :( 1771 */ 1772 port = strchr(orig_hostname, ':'); 1773 if (port != NULL) { 1774 hostname_sans_port = strndup(orig_hostname, port - orig_hostname); 1775 if (hostname_sans_port == NULL) 1776 return krb5_enomem(context); 1777 orig_hostname = hostname_sans_port; 1778 } 1779 1780 _krb5_debug(context, 5, N_("Applying a name rule (type %d) to %s", ""), 1781 rule->type, orig_hostname); 1782 1783 if (rule->mindots > 0 || rule->maxdots > 0) { 1784 for (cp = strchr(orig_hostname, '.'); cp && *cp; cp = strchr(cp + 1, '.')) 1785 ndots++; 1786 } 1787 if (rule->mindots > 0 && ndots < rule->mindots) 1788 return 0; 1789 if (ndots > rule->maxdots) 1790 return 0; 1791 1792 if (rule->match_domain != NULL && 1793 !is_domain_suffix(orig_hostname, rule->match_domain)) 1794 return 0; 1795 1796 if (rule->match_realm != NULL && 1797 strcmp(rule->match_realm, in_princ->realm) != 0) 1798 return 0; 1799 1800 new_realm = rule->realm; 1801 switch (rule->type) { 1802 case KRB5_NCRT_AS_IS: 1803 break; 1804 1805 case KRB5_NCRT_QUALIFY: 1806 heim_assert(rule->domain != NULL, 1807 "missing domain for qualify name canon rule"); 1808 if (asprintf(&tmp_hostname, "%s.%s", orig_hostname, 1809 rule->domain) == -1 || tmp_hostname == NULL) { 1810 ret = krb5_enomem(context); 1811 goto out; 1812 } 1813 new_hostname = tmp_hostname; 1814 break; 1815 1816 case KRB5_NCRT_NSS: 1817 if ((rule->options & KRB5_NCRO_USE_DNSSEC)) { 1818 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 1819 krb5_set_error_message(context, ret, 1820 "Secure hostname resolution not supported"); 1821 goto out; 1822 } 1823 _krb5_debug(context, 5, "Using name service lookups"); 1824 ret = krb5_sname_to_principal_old(context, rule->realm, 1825 orig_hostname, sname, 1826 KRB5_NT_SRV_HST, 1827 &nss); 1828 if (rules[rule_idx + 1].type != KRB5_NCRT_BOGUS && 1829 (ret == KRB5_ERR_BAD_HOSTNAME || 1830 ret == KRB5_ERR_HOST_REALM_UNKNOWN)) { 1831 /* 1832 * Bad hostname / realm unknown -> rule inapplicable if 1833 * there's more rules. If it's the last rule then we want 1834 * to return all errors from krb5_sname_to_principal_old() 1835 * here. 1836 */ 1837 ret = 0; 1838 goto out; 1839 } 1840 if (ret) 1841 goto out; 1842 1843 new_hostname = krb5_principal_get_comp_string(context, nss, 1); 1844 new_realm = krb5_principal_get_realm(context, nss); 1845 break; 1846 1847 default: 1848 /* Can't happen */ 1849 ret = 0; 1850 goto out; 1851 } 1852 1853 /* 1854 * This rule applies. 1855 * 1856 * Copy in_princ and mutate the copy per the matched rule. 1857 * 1858 * This way we apply to principals with two or more components, such as 1859 * domain-based names. 1860 */ 1861 ret = krb5_copy_principal(context, in_princ, out_princ); 1862 if (ret) 1863 goto out; 1864 1865 if (new_realm == NULL && (rule->options & KRB5_NCRO_LOOKUP_REALM) != 0) { 1866 ret = get_host_realm(context, new_hostname, &tmp_realm); 1867 if (ret) 1868 goto out; 1869 new_realm = tmp_realm; 1870 } 1871 1872 /* If we stripped off a :port, add it back in */ 1873 if (port != NULL && new_hostname != NULL) { 1874 if (asprintf(&hostname_with_port, "%s%s", new_hostname, port) == -1 || 1875 hostname_with_port == NULL) { 1876 ret = krb5_enomem(context); 1877 goto out; 1878 } 1879 new_hostname = hostname_with_port; 1880 } 1881 1882 if (new_realm != NULL) 1883 krb5_principal_set_realm(context, *out_princ, new_realm); 1884 if (new_hostname != NULL) 1885 krb5_principal_set_comp_string(context, *out_princ, 1, new_hostname); 1886 if (princ_type(*out_princ) == KRB5_NT_SRV_HST_NEEDS_CANON) 1887 princ_type(*out_princ) = KRB5_NT_SRV_HST; 1888 1889 /* Trace rule application */ 1890 { 1891 krb5_error_code ret2; 1892 char *unparsed; 1893 1894 ret2 = krb5_unparse_name(context, *out_princ, &unparsed); 1895 if (ret2) { 1896 _krb5_debug(context, 5, 1897 N_("Couldn't unparse canonicalized princicpal (%d)", 1898 ""), 1899 ret); 1900 } else { 1901 _krb5_debug(context, 5, 1902 N_("Name canon rule application yields %s", ""), 1903 unparsed); 1904 free(unparsed); 1905 } 1906 } 1907 1908 out: 1909 free(hostname_sans_port); 1910 free(hostname_with_port); 1911 free(tmp_hostname); 1912 free(tmp_realm); 1913 krb5_free_principal(context, nss); 1914 if (ret) 1915 krb5_set_error_message(context, ret, 1916 N_("Name canon rule application failed", "")); 1917 return ret; 1918 } 1919 1920 /** 1921 * Free name canonicalization rules 1922 */ 1923 KRB5_LIB_FUNCTION void 1924 _krb5_free_name_canon_rules(krb5_context context, krb5_name_canon_rule rules) 1925 { 1926 size_t k; 1927 1928 if (rules == NULL) 1929 return; 1930 1931 for (k = 0; rules[k].type != KRB5_NCRT_BOGUS; k++) { 1932 free(rules[k].match_domain); 1933 free(rules[k].match_realm); 1934 free(rules[k].domain); 1935 free(rules[k].realm); 1936 } 1937 free(rules); 1938 } 1939 1940 struct krb5_name_canon_iterator_data { 1941 krb5_name_canon_rule rules; 1942 krb5_const_principal in_princ; /* given princ */ 1943 krb5_const_principal out_princ; /* princ to be output */ 1944 krb5_principal tmp_princ; /* to be freed */ 1945 int is_trivial; /* no canon to be done */ 1946 int done; /* no more rules to be applied */ 1947 size_t cursor; /* current/next rule */ 1948 }; 1949 1950 /** 1951 * Initialize name canonicalization iterator. 1952 * 1953 * @param context Kerberos context 1954 * @param in_princ principal name to be canonicalized OR 1955 * @param iter output iterator object 1956 */ 1957 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1958 krb5_name_canon_iterator_start(krb5_context context, 1959 krb5_const_principal in_princ, 1960 krb5_name_canon_iterator *iter) 1961 { 1962 krb5_error_code ret; 1963 krb5_name_canon_iterator state; 1964 1965 *iter = NULL; 1966 1967 state = calloc(1, sizeof (*state)); 1968 if (state == NULL) 1969 return krb5_enomem(context); 1970 state->in_princ = in_princ; 1971 1972 if (princ_type(state->in_princ) == KRB5_NT_SRV_HST_NEEDS_CANON) { 1973 ret = _krb5_get_name_canon_rules(context, &state->rules); 1974 if (ret) 1975 goto out; 1976 } else { 1977 /* Name needs no canon -> trivial iterator: in_princ is canonical */ 1978 state->is_trivial = 1; 1979 } 1980 1981 *iter = state; 1982 return 0; 1983 1984 out: 1985 krb5_free_name_canon_iterator(context, state); 1986 return krb5_enomem(context); 1987 } 1988 1989 /* 1990 * Helper for name canon iteration. 1991 */ 1992 static krb5_error_code 1993 name_canon_iterate(krb5_context context, 1994 krb5_name_canon_iterator *iter, 1995 krb5_name_canon_rule_options *rule_opts) 1996 { 1997 krb5_error_code ret; 1998 krb5_name_canon_iterator state = *iter; 1999 2000 if (rule_opts) 2001 *rule_opts = 0; 2002 2003 if (state == NULL) 2004 return 0; 2005 2006 if (state->done) { 2007 krb5_free_name_canon_iterator(context, state); 2008 *iter = NULL; 2009 return 0; 2010 } 2011 2012 if (state->is_trivial && !state->done) { 2013 state->out_princ = state->in_princ; 2014 state->done = 1; 2015 return 0; 2016 } 2017 2018 heim_assert(state->rules != NULL && 2019 state->rules[state->cursor].type != KRB5_NCRT_BOGUS, 2020 "Internal error during name canonicalization"); 2021 2022 do { 2023 krb5_free_principal(context, state->tmp_princ); 2024 ret = apply_name_canon_rule(context, state->rules, state->cursor, 2025 state->in_princ, &state->tmp_princ, rule_opts); 2026 if (ret) { 2027 krb5_free_name_canon_iterator(context, state); 2028 *iter = NULL; 2029 return ret; 2030 } 2031 state->cursor++; 2032 } while (state->tmp_princ == NULL && 2033 state->rules[state->cursor].type != KRB5_NCRT_BOGUS); 2034 2035 if (state->rules[state->cursor].type == KRB5_NCRT_BOGUS) 2036 state->done = 1; 2037 2038 state->out_princ = state->tmp_princ; 2039 if (state->tmp_princ == NULL) { 2040 krb5_free_name_canon_iterator(context, state); 2041 *iter = NULL; 2042 return 0; 2043 } 2044 return 0; 2045 } 2046 2047 /** 2048 * Iteratively apply name canon rules, outputing a principal and rule 2049 * options each time. Iteration completes when the @iter is NULL on 2050 * return or when an error is returned. Callers must free the iterator 2051 * if they abandon it mid-way. 2052 * 2053 * @param context Kerberos context 2054 * @param iter name canon rule iterator (input/output) 2055 * @param try_princ output principal name 2056 * @param rule_opts output rule options 2057 */ 2058 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2059 krb5_name_canon_iterate(krb5_context context, 2060 krb5_name_canon_iterator *iter, 2061 krb5_const_principal *try_princ, 2062 krb5_name_canon_rule_options *rule_opts) 2063 { 2064 krb5_error_code ret; 2065 2066 *try_princ = NULL; 2067 2068 ret = name_canon_iterate(context, iter, rule_opts); 2069 if (*iter) 2070 *try_princ = (*iter)->out_princ; 2071 return ret; 2072 } 2073 2074 /** 2075 * Free a name canonicalization rule iterator. 2076 */ 2077 KRB5_LIB_FUNCTION void KRB5_LIB_CALL 2078 krb5_free_name_canon_iterator(krb5_context context, 2079 krb5_name_canon_iterator iter) 2080 { 2081 if (iter == NULL) 2082 return; 2083 if (iter->tmp_princ) 2084 krb5_free_principal(context, iter->tmp_princ); 2085 free(iter); 2086 } 2087