1/* $NetBSD: principal.c,v 1.4 2023/06/19 21:41:44 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 44lha@SU.SE 45host/hummel.it.su.se@SU.SE 46host/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 68static krb5_error_code 69set_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 87static 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 103KRB5_LIB_FUNCTION void KRB5_LIB_CALL 104krb5_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 125KRB5_LIB_FUNCTION void KRB5_LIB_CALL 126krb5_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 144KRB5_LIB_FUNCTION int KRB5_LIB_CALL 145krb5_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 162KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL 163krb5_principal_get_realm(krb5_context context, 164 krb5_const_principal principal) 165{ 166 return princ_realm(principal); 167} 168 169KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL 170krb5_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 190KRB5_LIB_FUNCTION unsigned int KRB5_LIB_CALL 191krb5_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 210KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 211krb5_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; 379exit: 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 401KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 402krb5_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 409static const char quotable_chars[] = " \n\t\b\\/@"; 410static const char replace_chars[] = " ntb\\/@"; 411 412#define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0); 413 414static size_t 415quote_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 434static krb5_error_code 435unparse_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 501KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 502krb5_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 524KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 525krb5_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 548KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 549krb5_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 558static krb5_error_code 559unparse_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 609KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 610krb5_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 630KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 631krb5_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 652KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 653krb5_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 673KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 674krb5_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 688KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 689krb5_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 722KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 723krb5_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] */ 752KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 753krb5_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 775static krb5_error_code 776append_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 796static krb5_error_code 797va_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 814static krb5_error_code 815va_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 830static krb5_error_code 831build_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 861KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 862krb5_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 871KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 872krb5_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 882KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 883krb5_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 910KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 911krb5_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 940KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 941krb5_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 955KRB5_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 988KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 989krb5_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 1010KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 1011krb5_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 1024KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 1025krb5_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 */ 1045static krb5_error_code 1046krb5_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 1104static 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 1132KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1133krb5_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 1154krb5_boolean KRB5_LIB_FUNCTION 1155krb5_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 1165const char _krb5_wellknown_lkdc[] = "WELLKNOWN:COM.APPLE.LKDC"; 1166static const char lkdc_prefix[] = "LKDC:"; 1167 1168/** 1169 * Returns true if name is Kerberos an LKDC realm 1170 * 1171 * @ingroup krb5_principal 1172 */ 1173 1174krb5_boolean KRB5_LIB_FUNCTION 1175krb5_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 1188krb5_boolean KRB5_LIB_FUNCTION 1189krb5_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 1200krb5_boolean KRB5_LIB_FUNCTION 1201krb5_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 1212KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 1213krb5_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 1225krb5_boolean KRB5_LIB_FUNCTION 1226krb5_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 1244krb5_boolean KRB5_LIB_FUNCTION 1245krb5_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/** 1253 * Returns true iff name is WELLKNOWN/ANONYMOUS 1254 * 1255 * @ingroup krb5_principal 1256 */ 1257 1258KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL 1259krb5_principal_is_anonymous(krb5_context context, 1260 krb5_const_principal p, 1261 unsigned int flags) 1262{ 1263 /* 1264 * Heimdal versions 7.5 and below left the name-type at KRB5_NT_PRINCIPAL 1265 * even with anonymous pkinit responses. To retain interoperability with 1266 * legacy KDCs, the name-type is not checked by the client after requesting 1267 * a fully anonymous ticket. 1268 */ 1269 if (!(flags & KRB5_ANON_IGNORE_NAME_TYPE) && 1270 p->name.name_type != KRB5_NT_WELLKNOWN && 1271 p->name.name_type != KRB5_NT_UNKNOWN) 1272 return FALSE; 1273 1274 if (p->name.name_string.len != 2 || 1275 strcmp(p->name.name_string.val[0], KRB5_WELLKNOWN_NAME) != 0 || 1276 strcmp(p->name.name_string.val[1], KRB5_ANON_NAME) != 0) 1277 return FALSE; 1278 1279 /* 1280 * While unauthenticated clients SHOULD get "WELLKNOWN:ANONYMOUS" as their 1281 * realm, Heimdal KDCs prior to 7.0 returned the requested realm. While 1282 * such tickets might lead *servers* to unwittingly grant access to fully 1283 * anonymous clients, trusting that the client was authenticated to the 1284 * realm in question, doing it right is the KDC's job, the client should 1285 * not refuse such a ticket. 1286 * 1287 * If we ever do decide to enforce WELLKNOWN:ANONYMOUS for unauthenticated 1288 * clients, it is essential that calls that pass KRB5_ANON_MATCH_ANY still 1289 * ignore the realm, as in that case either case matches one of the two 1290 * possible conditions. 1291 */ 1292 if (flags & KRB5_ANON_MATCH_UNAUTHENTICATED) 1293 return TRUE; 1294 1295 /* 1296 * Finally, authenticated clients that asked to be only anonymized do 1297 * legitimately expect a non-anon realm. 1298 */ 1299 return strcmp(p->realm, KRB5_ANON_REALM) != 0; 1300} 1301 1302static int 1303tolower_ascii(int c) 1304{ 1305 if (c >= 'A' || c <= 'Z') 1306 return 'a' + (c - 'A'); 1307 return c; 1308} 1309 1310typedef enum krb5_name_canon_rule_type { 1311 KRB5_NCRT_BOGUS = 0, 1312 KRB5_NCRT_AS_IS, 1313 KRB5_NCRT_QUALIFY, 1314 KRB5_NCRT_NSS 1315} krb5_name_canon_rule_type; 1316 1317#ifdef UINT8_MAX 1318#define MAXDOTS UINT8_MAX 1319#else 1320#define MAXDOTS (255U) 1321#endif 1322#ifdef UINT16_MAX 1323#define MAXORDER UINT16_MAX 1324#else 1325#define MAXORDER (65535U) 1326#endif 1327 1328struct krb5_name_canon_rule_data { 1329 krb5_name_canon_rule_type type; 1330 krb5_name_canon_rule_options options; 1331 uint8_t mindots; /* match this many dots or more */ 1332 uint8_t maxdots; /* match no more than this many dots */ 1333 uint16_t explicit_order; /* given order */ 1334 uint16_t order; /* actual order */ 1335 char *match_domain; /* match this stem */ 1336 char *match_realm; /* match this realm */ 1337 char *domain; /* qualify with this domain */ 1338 char *realm; /* qualify with this realm */ 1339}; 1340 1341/** 1342 * Create a principal for the given service running on the given 1343 * hostname. If KRB5_NT_SRV_HST is used, the hostname is canonicalized 1344 * according the configured name canonicalization rules, with 1345 * canonicalization delayed in some cases. One rule involves DNS, which 1346 * is insecure unless DNSSEC is used, but we don't use DNSSEC-capable 1347 * resolver APIs here, so that if DNSSEC is used we wouldn't know it. 1348 * 1349 * Canonicalization is immediate (not delayed) only when there is only 1350 * one canonicalization rule and that rule indicates that we should do a 1351 * host lookup by name (i.e., DNS). 1352 * 1353 * @param context A Kerberos context. 1354 * @param hostname hostname to use 1355 * @param sname Service name to use 1356 * @param type name type of principal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN. 1357 * @param ret_princ return principal, free with krb5_free_principal(). 1358 * 1359 * @return An krb5 error code, see krb5_get_error_message(). 1360 * 1361 * @ingroup krb5_principal 1362 */ 1363 1364/* coverity[+alloc : arg-*4] */ 1365KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 1366krb5_sname_to_principal(krb5_context context, 1367 const char *hostname, 1368 const char *sname, 1369 int32_t type, 1370 krb5_principal *ret_princ) 1371{ 1372 char *realm, *remote_host; 1373 krb5_error_code ret; 1374 register char *cp; 1375 char localname[MAXHOSTNAMELEN]; 1376 1377 *ret_princ = NULL; 1378 1379 if ((type != KRB5_NT_UNKNOWN) && 1380 (type != KRB5_NT_SRV_HST)) 1381 return KRB5_SNAME_UNSUPP_NAMETYPE; 1382 1383 /* if hostname is NULL, use local hostname */ 1384 if (hostname == NULL) { 1385 if (gethostname(localname, MAXHOSTNAMELEN)) 1386 return errno; 1387 hostname = localname; 1388 } 1389 1390 /* if sname is NULL, use "host" */ 1391 if (sname == NULL) 1392 sname = "host"; 1393 1394 remote_host = strdup(hostname); 1395 if (remote_host == NULL) 1396 return krb5_enomem(context); 1397 1398 if (type == KRB5_NT_SRV_HST) { 1399 krb5_name_canon_rule rules; 1400 1401 /* Lower-case the hostname, because that's the convention */ 1402 for (cp = remote_host; *cp; cp++) 1403 if (isupper((int) (*cp))) 1404 *cp = tolower((int) (*cp)); 1405 1406 /* 1407 * If there is only one name canon rule and it says to 1408 * canonicalize the old way, do that now, as we used to. 1409 */ 1410 ret = _krb5_get_name_canon_rules(context, &rules); 1411 if (ret) { 1412 _krb5_debug(context, 5, "Failed to get name canon rules: ret = %d", 1413 ret); 1414 free(remote_host); 1415 return ret; 1416 } 1417 if (rules[0].type == KRB5_NCRT_NSS && 1418 rules[1].type == KRB5_NCRT_BOGUS) { 1419 _krb5_debug(context, 5, "Using nss for name canon immediately"); 1420 ret = krb5_sname_to_principal_old(context, rules[0].realm, 1421 remote_host, sname, 1422 KRB5_NT_SRV_HST, ret_princ); 1423 free(remote_host); 1424 return ret; 1425 } 1426 } 1427 1428 /* Remove trailing dots */ 1429 if (remote_host[0]) { 1430 for (cp = remote_host + strlen(remote_host)-1; 1431 *cp == '.' && cp > remote_host; 1432 cp--) { 1433 *cp = '\0'; 1434 } 1435 } 1436 1437 realm = ""; /* "Referral realm" */ 1438 1439 ret = krb5_build_principal(context, ret_princ, strlen(realm), 1440 realm, sname, remote_host, 1441 (char *)0); 1442 1443 if (ret == 0 && type == KRB5_NT_SRV_HST) { 1444 /* 1445 * Hostname canonicalization is done elsewhere (in 1446 * krb5_get_credentials() and krb5_kt_get_entry()). 1447 * 1448 * We overload the name type to indicate to those functions that 1449 * this principal name requires canonicalization. 1450 * 1451 * We can't use the empty realm to denote the need to 1452 * canonicalize the hostname too: it would mean that users who 1453 * want to assert knowledge of a service's realm must also know 1454 * the canonical hostname, but in practice they don't. 1455 */ 1456 (*ret_princ)->name.name_type = KRB5_NT_SRV_HST_NEEDS_CANON; 1457 1458 _krb5_debug(context, 5, "Building a delayed canon principal for %s/%s@", 1459 sname, remote_host); 1460 } 1461 1462 free(remote_host); 1463 return ret; 1464} 1465 1466static void 1467tolower_str(char *s) 1468{ 1469 for (; *s != '\0'; s++) { 1470 if (isupper(*s)) 1471 *s = tolower_ascii(*s); 1472 } 1473} 1474 1475static krb5_error_code 1476rule_parse_token(krb5_context context, krb5_name_canon_rule rule, 1477 const char *tok) 1478{ 1479 long int n; 1480 int needs_type = rule->type == KRB5_NCRT_BOGUS; 1481 1482 /* 1483 * Rules consist of a sequence of tokens, some of which indicate 1484 * what type of rule the rule is, and some of which set rule options 1485 * or ancilliary data. Last rule type token wins. 1486 */ 1487 1488 /* Rule type tokens: */ 1489 if (needs_type && strcmp(tok, "as-is") == 0) { 1490 rule->type = KRB5_NCRT_AS_IS; 1491 } else if (needs_type && strcmp(tok, "qualify") == 0) { 1492 rule->type = KRB5_NCRT_QUALIFY; 1493 } else if (needs_type && strcmp(tok, "nss") == 0) { 1494 rule->type = KRB5_NCRT_NSS; 1495 /* Rule options: */ 1496 } else if (strcmp(tok, "use_fast") == 0) { 1497 rule->options |= KRB5_NCRO_USE_FAST; 1498 } else if (strcmp(tok, "use_dnssec") == 0) { 1499 rule->options |= KRB5_NCRO_USE_DNSSEC; 1500 } else if (strcmp(tok, "ccache_only") == 0) { 1501 rule->options |= KRB5_NCRO_GC_ONLY; 1502 } else if (strcmp(tok, "no_referrals") == 0) { 1503 rule->options |= KRB5_NCRO_NO_REFERRALS; 1504 } else if (strcmp(tok, "use_referrals") == 0) { 1505 rule->options &= ~KRB5_NCRO_NO_REFERRALS; 1506 if (rule->realm == NULL) { 1507 rule->realm = strdup(""); 1508 if (rule->realm == NULL) 1509 return krb5_enomem(context); 1510 } 1511 } else if (strcmp(tok, "lookup_realm") == 0) { 1512 rule->options |= KRB5_NCRO_LOOKUP_REALM; 1513 free(rule->realm); 1514 rule->realm = NULL; 1515 /* Rule ancilliary data: */ 1516 } else if (strncmp(tok, "domain=", strlen("domain=")) == 0) { 1517 free(rule->domain); 1518 rule->domain = strdup(tok + strlen("domain=")); 1519 if (rule->domain == NULL) 1520 return krb5_enomem(context); 1521 tolower_str(rule->domain); 1522 } else if (strncmp(tok, "realm=", strlen("realm=")) == 0) { 1523 free(rule->realm); 1524 rule->realm = strdup(tok + strlen("realm=")); 1525 if (rule->realm == NULL) 1526 return krb5_enomem(context); 1527 } else if (strncmp(tok, "match_domain=", strlen("match_domain=")) == 0) { 1528 free(rule->match_domain); 1529 rule->match_domain = strdup(tok + strlen("match_domain=")); 1530 if (rule->match_domain == NULL) 1531 return krb5_enomem(context); 1532 tolower_str(rule->match_domain); 1533 } else if (strncmp(tok, "match_realm=", strlen("match_realm=")) == 0) { 1534 free(rule->match_realm); 1535 rule->match_realm = strdup(tok + strlen("match_realm=")); 1536 if (rule->match_realm == NULL) 1537 return krb5_enomem(context); 1538 } else if (strncmp(tok, "mindots=", strlen("mindots=")) == 0) { 1539 errno = 0; 1540 n = strtol(tok + strlen("mindots="), NULL, 10); 1541 if (errno == 0 && n > 0 && n <= MAXDOTS) 1542 rule->mindots = n; 1543 } else if (strncmp(tok, "maxdots=", strlen("maxdots=")) == 0) { 1544 errno = 0; 1545 n = strtol(tok + strlen("maxdots="), NULL, 10); 1546 if (errno == 0 && n > 0 && n <= MAXDOTS) 1547 rule->maxdots = n; 1548 } else if (strncmp(tok, "order=", strlen("order=")) == 0) { 1549 errno = 0; 1550 n = strtol(tok + strlen("order="), NULL, 10); 1551 if (errno == 0 && n > 0 && n <= MAXORDER) 1552 rule->explicit_order = n; 1553 } else { 1554 _krb5_debug(context, 5, 1555 "Unrecognized name canonicalization rule token %s", tok); 1556 return EINVAL; 1557 } 1558 return 0; 1559} 1560 1561static int 1562rule_cmp(const void *a, const void *b) 1563{ 1564 krb5_const_name_canon_rule left = a; 1565 krb5_const_name_canon_rule right = b; 1566 1567 if (left->type == KRB5_NCRT_BOGUS && 1568 right->type == KRB5_NCRT_BOGUS) 1569 return 0; 1570 if (left->type == KRB5_NCRT_BOGUS) 1571 return 1; 1572 if (right->type == KRB5_NCRT_BOGUS) 1573 return -1; 1574 if (left->explicit_order < right->explicit_order) 1575 return -1; 1576 if (left->explicit_order > right->explicit_order) 1577 return 1; 1578 return left->order - right->order; 1579} 1580 1581static krb5_error_code 1582parse_name_canon_rules(krb5_context context, char **rulestrs, 1583 krb5_name_canon_rule *rules) 1584{ 1585 krb5_error_code ret; 1586 char *tok; 1587 char *cp; 1588 char **cpp; 1589 size_t n; 1590 size_t i, k; 1591 int do_sort = 0; 1592 krb5_name_canon_rule r; 1593 1594 *rules = NULL; 1595 1596 for (n =0, cpp = rulestrs; cpp != NULL && *cpp != NULL; cpp++) 1597 n++; 1598 1599 n += 2; /* Always at least one rule; two for the default case */ 1600 1601 if ((r = calloc(n, sizeof (*r))) == NULL) 1602 return krb5_enomem(context); 1603 1604 for (k = 0; k < n; k++) { 1605 r[k].type = KRB5_NCRT_BOGUS; 1606 r[k].match_domain = NULL; 1607 r[k].match_realm = NULL; 1608 r[k].domain = NULL; 1609 r[k].realm = NULL; 1610 } 1611 1612 for (i = 0, k = 0; i < n && rulestrs != NULL && rulestrs[i] != NULL; i++) { 1613 cp = rulestrs[i]; 1614 r[k].explicit_order = MAXORDER; /* mark order, see below */ 1615 r[k].maxdots = MAXDOTS; 1616 r[k].order = k; /* default order */ 1617 1618 /* Tokenize and parse value */ 1619 do { 1620 tok = cp; 1621 cp = strchr(cp, ':'); /* XXX use strtok_r() */ 1622 if (cp) 1623 *cp++ = '\0'; /* delimit token */ 1624 ret = rule_parse_token(context, &r[k], tok); 1625 if (ret == EINVAL) { 1626 r[k].type = KRB5_NCRT_BOGUS; 1627 break; 1628 } 1629 if (ret) { 1630 _krb5_free_name_canon_rules(context, r); 1631 return ret; 1632 } 1633 } while (cp && *cp); 1634 if (r[k].explicit_order != MAXORDER) 1635 do_sort = 1; 1636 1637 /* Validate parsed rule */ 1638 if (r[k].type == KRB5_NCRT_BOGUS || 1639 (r[k].type == KRB5_NCRT_QUALIFY && !r[k].domain) || 1640 (r[k].type == KRB5_NCRT_NSS && r[k].domain)) { 1641 /* Invalid rule; mark it so and clean up */ 1642 r[k].type = KRB5_NCRT_BOGUS; 1643 free(r[k].match_domain); 1644 free(r[k].match_realm); 1645 free(r[k].domain); 1646 free(r[k].realm); 1647 r[k].realm = NULL; 1648 r[k].domain = NULL; 1649 r[k].match_domain = NULL; 1650 r[k].match_realm = NULL; 1651 _krb5_debug(context, 5, 1652 "Ignoring invalid name canonicalization rule %lu", 1653 (unsigned long)i); 1654 continue; 1655 } 1656 k++; /* good rule */ 1657 } 1658 1659 if (do_sort) { 1660 /* 1661 * Note that we make make this a stable sort by using appareance 1662 * and explicit order. 1663 */ 1664 qsort(r, n, sizeof(r[0]), rule_cmp); 1665 } 1666 1667 if (r[0].type == KRB5_NCRT_BOGUS) { 1668 /* No rules, or no valid rules */ 1669 r[0].type = KRB5_NCRT_NSS; 1670 } 1671 1672 *rules = r; 1673 return 0; /* We don't communicate bad rule errors here */ 1674} 1675 1676/* 1677 * This exists only because the hostname canonicalization behavior in Heimdal 1678 * (and other implementations of Kerberos) has been to use getaddrinfo(), 1679 * unsafe though it is, for ages. We can't fix it in one day. 1680 */ 1681static void 1682make_rules_safe(krb5_context context, krb5_name_canon_rule rules) 1683{ 1684 /* 1685 * If the only rule were to use the name service (getaddrinfo()) then we're 1686 * bound to fail. We could try to convert that rule to an as-is rule, but 1687 * when we do get a validating resolver we'd be unhappy that we did such a 1688 * conversion. Better let the user get failures and make them think about 1689 * their naming rules. 1690 */ 1691 if (rules == NULL) 1692 return; 1693 for (; rules[0].type != KRB5_NCRT_BOGUS; rules++) { 1694 if (rules->type == KRB5_NCRT_NSS) 1695 rules->options |= KRB5_NCRO_USE_DNSSEC; 1696 else 1697 rules->options |= KRB5_NCRO_USE_FAST; 1698 } 1699} 1700 1701/** 1702 * This function returns an array of host-based service name 1703 * canonicalization rules. The array of rules is organized as a list. 1704 * See the definition of krb5_name_canon_rule. 1705 * 1706 * @param context A Kerberos context. 1707 * @param rules Output location for array of rules. 1708 */ 1709KRB5_LIB_FUNCTION krb5_error_code 1710_krb5_get_name_canon_rules(krb5_context context, krb5_name_canon_rule *rules) 1711{ 1712 krb5_error_code ret; 1713 char **values = NULL; 1714 1715 *rules = context->name_canon_rules; 1716 if (*rules != NULL) 1717 return 0; 1718 1719 values = krb5_config_get_strings(context, NULL, 1720 "libdefaults", "name_canon_rules", NULL); 1721 ret = parse_name_canon_rules(context, values, rules); 1722 krb5_config_free_strings(values); 1723 if (ret) 1724 return ret; 1725 1726 if (krb5_config_get_bool_default(context, NULL, FALSE, 1727 "libdefaults", "safe_name_canon", NULL)) 1728 make_rules_safe(context, *rules); 1729 1730 heim_assert(rules != NULL && (*rules)[0].type != KRB5_NCRT_BOGUS, 1731 "internal error in parsing principal name " 1732 "canonicalization rules"); 1733 1734 /* Memoize */ 1735 context->name_canon_rules = *rules; 1736 1737 return 0; 1738} 1739 1740static krb5_error_code 1741get_host_realm(krb5_context context, const char *hostname, char **realm) 1742{ 1743 krb5_error_code ret; 1744 char **hrealms = NULL; 1745 1746 *realm = NULL; 1747 ret = krb5_get_host_realm(context, hostname, &hrealms); 1748 if (ret) 1749 return ret; 1750 if (hrealms == NULL) 1751 return KRB5_ERR_HOST_REALM_UNKNOWN; /* krb5_set_error() already done */ 1752 if (hrealms[0] == NULL) { 1753 krb5_free_host_realm(context, hrealms); 1754 return KRB5_ERR_HOST_REALM_UNKNOWN; /* krb5_set_error() already done */ 1755 } 1756 *realm = strdup(hrealms[0]); 1757 krb5_free_host_realm(context, hrealms); 1758 if (*realm == NULL) 1759 return krb5_enomem(context); 1760 return 0; 1761} 1762 1763static int 1764is_domain_suffix(const char *domain, const char *suffix) 1765{ 1766 size_t dlen = strlen(domain); 1767 size_t slen = strlen(suffix); 1768 1769 if (dlen < slen + 2) 1770 return 0; 1771 1772 if (strcasecmp(domain + (dlen - slen), suffix) != 0) 1773 return 0; 1774 1775 if (domain[(dlen - slen) - 1] != '.') 1776 return 0; 1777 return 1; 1778} 1779 1780/* 1781 * Applies a name canonicalization rule to a principal. 1782 * 1783 * Returns zero and no out_princ if the rule does not match. 1784 * Returns zero and an out_princ if the rule does match. 1785 */ 1786static krb5_error_code 1787apply_name_canon_rule(krb5_context context, krb5_name_canon_rule rules, 1788 size_t rule_idx, krb5_const_principal in_princ, 1789 krb5_principal *out_princ, 1790 krb5_name_canon_rule_options *rule_opts) 1791{ 1792 krb5_name_canon_rule rule = &rules[rule_idx]; 1793 krb5_error_code ret; 1794 unsigned int ndots = 0; 1795 krb5_principal nss = NULL; 1796 const char *sname = NULL; 1797 const char *orig_hostname = NULL; 1798 const char *new_hostname = NULL; 1799 const char *new_realm = NULL; 1800 const char *port = ""; 1801 const char *cp; 1802 char *hostname_sans_port = NULL; 1803 char *hostname_with_port = NULL; 1804 char *tmp_hostname = NULL; 1805 char *tmp_realm = NULL; 1806 1807 *out_princ = NULL; /* Signal no match */ 1808 1809 if (rule_opts != NULL) 1810 *rule_opts = rule->options; 1811 1812 if (rule->type == KRB5_NCRT_BOGUS) 1813 return 0; /* rule doesn't apply */ 1814 1815 sname = krb5_principal_get_comp_string(context, in_princ, 0); 1816 orig_hostname = krb5_principal_get_comp_string(context, in_princ, 1); 1817 1818 /* 1819 * Some apps want to use the very non-standard svc/hostname:port@REALM 1820 * form. We do our best to support that here :( 1821 */ 1822 port = strchr(orig_hostname, ':'); 1823 if (port != NULL) { 1824 hostname_sans_port = strndup(orig_hostname, port - orig_hostname); 1825 if (hostname_sans_port == NULL) 1826 return krb5_enomem(context); 1827 orig_hostname = hostname_sans_port; 1828 } 1829 1830 _krb5_debug(context, 5, N_("Applying a name rule (type %d) to %s", ""), 1831 rule->type, orig_hostname); 1832 1833 if (rule->mindots > 0 || rule->maxdots > 0) { 1834 for (cp = strchr(orig_hostname, '.'); cp && *cp; cp = strchr(cp + 1, '.')) 1835 ndots++; 1836 } 1837 if (rule->mindots > 0 && ndots < rule->mindots) 1838 return 0; 1839 if (ndots > rule->maxdots) 1840 return 0; 1841 1842 if (rule->match_domain != NULL && 1843 !is_domain_suffix(orig_hostname, rule->match_domain)) 1844 return 0; 1845 1846 if (rule->match_realm != NULL && 1847 strcmp(rule->match_realm, in_princ->realm) != 0) 1848 return 0; 1849 1850 new_realm = rule->realm; 1851 switch (rule->type) { 1852 case KRB5_NCRT_AS_IS: 1853 break; 1854 1855 case KRB5_NCRT_QUALIFY: 1856 heim_assert(rule->domain != NULL, 1857 "missing domain for qualify name canon rule"); 1858 if (asprintf(&tmp_hostname, "%s.%s", orig_hostname, 1859 rule->domain) == -1 || tmp_hostname == NULL) { 1860 ret = krb5_enomem(context); 1861 goto out; 1862 } 1863 new_hostname = tmp_hostname; 1864 break; 1865 1866 case KRB5_NCRT_NSS: 1867 if ((rule->options & KRB5_NCRO_USE_DNSSEC)) { 1868 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; 1869 krb5_set_error_message(context, ret, 1870 "Secure hostname resolution not supported"); 1871 goto out; 1872 } 1873 _krb5_debug(context, 5, "Using name service lookups"); 1874 ret = krb5_sname_to_principal_old(context, rule->realm, 1875 orig_hostname, sname, 1876 KRB5_NT_SRV_HST, 1877 &nss); 1878 if (rules[rule_idx + 1].type != KRB5_NCRT_BOGUS && 1879 (ret == KRB5_ERR_BAD_HOSTNAME || 1880 ret == KRB5_ERR_HOST_REALM_UNKNOWN)) { 1881 /* 1882 * Bad hostname / realm unknown -> rule inapplicable if 1883 * there's more rules. If it's the last rule then we want 1884 * to return all errors from krb5_sname_to_principal_old() 1885 * here. 1886 */ 1887 ret = 0; 1888 goto out; 1889 } 1890 if (ret) 1891 goto out; 1892 1893 new_hostname = krb5_principal_get_comp_string(context, nss, 1); 1894 new_realm = krb5_principal_get_realm(context, nss); 1895 break; 1896 1897 default: 1898 /* Can't happen */ 1899 ret = 0; 1900 goto out; 1901 } 1902 1903 /* 1904 * This rule applies. 1905 * 1906 * Copy in_princ and mutate the copy per the matched rule. 1907 * 1908 * This way we apply to principals with two or more components, such as 1909 * domain-based names. 1910 */ 1911 ret = krb5_copy_principal(context, in_princ, out_princ); 1912 if (ret) 1913 goto out; 1914 1915 if (new_realm == NULL && (rule->options & KRB5_NCRO_LOOKUP_REALM) != 0) { 1916 ret = get_host_realm(context, new_hostname, &tmp_realm); 1917 if (ret) 1918 goto out; 1919 new_realm = tmp_realm; 1920 } 1921 1922 /* If we stripped off a :port, add it back in */ 1923 if (port != NULL && new_hostname != NULL) { 1924 if (asprintf(&hostname_with_port, "%s%s", new_hostname, port) == -1 || 1925 hostname_with_port == NULL) { 1926 ret = krb5_enomem(context); 1927 goto out; 1928 } 1929 new_hostname = hostname_with_port; 1930 } 1931 1932 if (new_realm != NULL) 1933 krb5_principal_set_realm(context, *out_princ, new_realm); 1934 if (new_hostname != NULL) 1935 krb5_principal_set_comp_string(context, *out_princ, 1, new_hostname); 1936 if (princ_type(*out_princ) == KRB5_NT_SRV_HST_NEEDS_CANON) 1937 princ_type(*out_princ) = KRB5_NT_SRV_HST; 1938 1939 /* Trace rule application */ 1940 { 1941 krb5_error_code ret2; 1942 char *unparsed; 1943 1944 ret2 = krb5_unparse_name(context, *out_princ, &unparsed); 1945 if (ret2) { 1946 _krb5_debug(context, 5, 1947 N_("Couldn't unparse canonicalized princicpal (%d)", 1948 ""), 1949 ret); 1950 } else { 1951 _krb5_debug(context, 5, 1952 N_("Name canon rule application yields %s", ""), 1953 unparsed); 1954 free(unparsed); 1955 } 1956 } 1957 1958out: 1959 free(hostname_sans_port); 1960 free(hostname_with_port); 1961 free(tmp_hostname); 1962 free(tmp_realm); 1963 krb5_free_principal(context, nss); 1964 if (ret) 1965 krb5_set_error_message(context, ret, 1966 N_("Name canon rule application failed", "")); 1967 return ret; 1968} 1969 1970/** 1971 * Free name canonicalization rules 1972 */ 1973KRB5_LIB_FUNCTION void 1974_krb5_free_name_canon_rules(krb5_context context, krb5_name_canon_rule rules) 1975{ 1976 size_t k; 1977 1978 if (rules == NULL) 1979 return; 1980 1981 for (k = 0; rules[k].type != KRB5_NCRT_BOGUS; k++) { 1982 free(rules[k].match_domain); 1983 free(rules[k].match_realm); 1984 free(rules[k].domain); 1985 free(rules[k].realm); 1986 } 1987 free(rules); 1988} 1989 1990struct krb5_name_canon_iterator_data { 1991 krb5_name_canon_rule rules; 1992 krb5_const_principal in_princ; /* given princ */ 1993 krb5_const_principal out_princ; /* princ to be output */ 1994 krb5_principal tmp_princ; /* to be freed */ 1995 int is_trivial; /* no canon to be done */ 1996 int done; /* no more rules to be applied */ 1997 size_t cursor; /* current/next rule */ 1998}; 1999 2000/** 2001 * Initialize name canonicalization iterator. 2002 * 2003 * @param context Kerberos context 2004 * @param in_princ principal name to be canonicalized OR 2005 * @param iter output iterator object 2006 */ 2007KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2008krb5_name_canon_iterator_start(krb5_context context, 2009 krb5_const_principal in_princ, 2010 krb5_name_canon_iterator *iter) 2011{ 2012 krb5_error_code ret; 2013 krb5_name_canon_iterator state; 2014 2015 *iter = NULL; 2016 2017 state = calloc(1, sizeof (*state)); 2018 if (state == NULL) 2019 return krb5_enomem(context); 2020 state->in_princ = in_princ; 2021 2022 if (princ_type(state->in_princ) == KRB5_NT_SRV_HST_NEEDS_CANON) { 2023 ret = _krb5_get_name_canon_rules(context, &state->rules); 2024 if (ret) 2025 goto out; 2026 } else { 2027 /* Name needs no canon -> trivial iterator: in_princ is canonical */ 2028 state->is_trivial = 1; 2029 } 2030 2031 *iter = state; 2032 return 0; 2033 2034out: 2035 krb5_free_name_canon_iterator(context, state); 2036 return krb5_enomem(context); 2037} 2038 2039/* 2040 * Helper for name canon iteration. 2041 */ 2042static krb5_error_code 2043name_canon_iterate(krb5_context context, 2044 krb5_name_canon_iterator *iter, 2045 krb5_name_canon_rule_options *rule_opts) 2046{ 2047 krb5_error_code ret; 2048 krb5_name_canon_iterator state = *iter; 2049 2050 if (rule_opts) 2051 *rule_opts = 0; 2052 2053 if (state == NULL) 2054 return 0; 2055 2056 if (state->done) { 2057 krb5_free_name_canon_iterator(context, state); 2058 *iter = NULL; 2059 return 0; 2060 } 2061 2062 if (state->is_trivial && !state->done) { 2063 state->out_princ = state->in_princ; 2064 state->done = 1; 2065 return 0; 2066 } 2067 2068 heim_assert(state->rules != NULL && 2069 state->rules[state->cursor].type != KRB5_NCRT_BOGUS, 2070 "Internal error during name canonicalization"); 2071 2072 do { 2073 krb5_free_principal(context, state->tmp_princ); 2074 ret = apply_name_canon_rule(context, state->rules, state->cursor, 2075 state->in_princ, &state->tmp_princ, rule_opts); 2076 if (ret) { 2077 krb5_free_name_canon_iterator(context, state); 2078 *iter = NULL; 2079 return ret; 2080 } 2081 state->cursor++; 2082 } while (state->tmp_princ == NULL && 2083 state->rules[state->cursor].type != KRB5_NCRT_BOGUS); 2084 2085 if (state->rules[state->cursor].type == KRB5_NCRT_BOGUS) 2086 state->done = 1; 2087 2088 state->out_princ = state->tmp_princ; 2089 if (state->tmp_princ == NULL) { 2090 krb5_free_name_canon_iterator(context, state); 2091 *iter = NULL; 2092 return 0; 2093 } 2094 return 0; 2095} 2096 2097/** 2098 * Iteratively apply name canon rules, outputing a principal and rule 2099 * options each time. Iteration completes when the @iter is NULL on 2100 * return or when an error is returned. Callers must free the iterator 2101 * if they abandon it mid-way. 2102 * 2103 * @param context Kerberos context 2104 * @param iter name canon rule iterator (input/output) 2105 * @param try_princ output principal name 2106 * @param rule_opts output rule options 2107 */ 2108KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL 2109krb5_name_canon_iterate(krb5_context context, 2110 krb5_name_canon_iterator *iter, 2111 krb5_const_principal *try_princ, 2112 krb5_name_canon_rule_options *rule_opts) 2113{ 2114 krb5_error_code ret; 2115 2116 *try_princ = NULL; 2117 2118 ret = name_canon_iterate(context, iter, rule_opts); 2119 if (*iter) 2120 *try_princ = (*iter)->out_princ; 2121 return ret; 2122} 2123 2124/** 2125 * Free a name canonicalization rule iterator. 2126 */ 2127KRB5_LIB_FUNCTION void KRB5_LIB_CALL 2128krb5_free_name_canon_iterator(krb5_context context, 2129 krb5_name_canon_iterator iter) 2130{ 2131 if (iter == NULL) 2132 return; 2133 if (iter->tmp_princ) 2134 krb5_free_principal(context, iter->tmp_princ); 2135 free(iter); 2136} 2137