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