2 * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * @page krb5_principal_intro The principal handing functions.
37 * A Kerberos principal is a email address looking string that
38 * contains two parts separated by @. The second part is the kerberos
39 * realm the principal belongs to and the first is a list of 0 or
40 * more components. For example
43 host/hummel.it.su.se@SU.SE
47 * See the library functions here: @ref krb5_principal
50 #include "krb5_locl.h"
51 #ifdef HAVE_RES_SEARCH
54 #ifdef HAVE_ARPA_NAMESER_H
55 #include <arpa/nameser.h>
60 #define princ_num_comp(P) ((P)->name.name_string.len)
61 #define princ_type(P) ((P)->name.name_type)
62 #define princ_comp(P) ((P)->name.name_string.val)
63 #define princ_ncomp(P, N) ((P)->name.name_string.val[(N)])
64 #define princ_realm(P) ((P)->realm)
67 * Frees a Kerberos principal allocated by the library with
68 * krb5_parse_name(), krb5_make_principal() or any other related
69 * principal functions.
71 * @param context A Kerberos context.
72 * @param p a principal to free.
74 * @return An krb5 error code, see krb5_get_error_message().
76 * @ingroup krb5_principal
79 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
80 krb5_free_principal(krb5_context context
,
90 * Set the type of the principal
92 * @param context A Kerberos context.
93 * @param principal principal to set the type for
94 * @param type the new type
96 * @return An krb5 error code, see krb5_get_error_message().
98 * @ingroup krb5_principal
101 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
102 krb5_principal_set_type(krb5_context context
,
103 krb5_principal principal
,
106 princ_type(principal
) = type
;
110 * Get the type of the principal
112 * @param context A Kerberos context.
113 * @param principal principal to get the type for
115 * @return the type of principal
117 * @ingroup krb5_principal
120 KRB5_LIB_FUNCTION
int KRB5_LIB_CALL
121 krb5_principal_get_type(krb5_context context
,
122 krb5_const_principal principal
)
124 return princ_type(principal
);
128 * Get the realm of the principal
130 * @param context A Kerberos context.
131 * @param principal principal to get the realm for
133 * @return realm of the principal, don't free or use after krb5_principal is freed
135 * @ingroup krb5_principal
138 KRB5_LIB_FUNCTION
const char* KRB5_LIB_CALL
139 krb5_principal_get_realm(krb5_context context
,
140 krb5_const_principal principal
)
142 return princ_realm(principal
);
145 KRB5_LIB_FUNCTION
const char* KRB5_LIB_CALL
146 krb5_principal_get_comp_string(krb5_context context
,
147 krb5_const_principal principal
,
148 unsigned int component
)
150 if(component
>= princ_num_comp(principal
))
152 return princ_ncomp(principal
, component
);
156 * Get number of component is principal.
158 * @param context Kerberos 5 context
159 * @param principal principal to query
161 * @return number of components in string
163 * @ingroup krb5_principal
166 KRB5_LIB_FUNCTION
unsigned int KRB5_LIB_CALL
167 krb5_principal_get_num_comp(krb5_context context
,
168 krb5_const_principal principal
)
170 return princ_num_comp(principal
);
174 * Parse a name into a krb5_principal structure, flags controls the behavior.
176 * @param context Kerberos 5 context
177 * @param name name to parse into a Kerberos principal
178 * @param flags flags to control the behavior
179 * @param principal returned principal, free with krb5_free_principal().
181 * @return An krb5 error code, see krb5_get_error_message().
183 * @ingroup krb5_principal
186 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
187 krb5_parse_name_flags(krb5_context context
,
190 krb5_principal
*principal
)
193 heim_general_string
*comp
;
194 heim_general_string realm
= NULL
;
206 int no_realm
= flags
& KRB5_PRINCIPAL_PARSE_NO_REALM
;
207 int require_realm
= flags
& KRB5_PRINCIPAL_PARSE_REQUIRE_REALM
;
208 int enterprise
= flags
& KRB5_PRINCIPAL_PARSE_ENTERPRISE
;
209 int ignore_realm
= flags
& KRB5_PRINCIPAL_PARSE_IGNORE_REALM
;
210 int no_def_realm
= flags
& KRB5_PRINCIPAL_PARSE_NO_DEF_REALM
;
214 if (no_realm
&& require_realm
) {
215 krb5_set_error_message(context
, KRB5_ERR_NO_SERVICE
,
216 N_("Can't require both realm and "
217 "no realm at the same time", ""));
218 return KRB5_ERR_NO_SERVICE
;
221 /* count number of component,
222 * enterprise names only have one component
226 for (p
= name
; *p
; p
++) {
229 krb5_set_error_message(context
, KRB5_PARSE_MALFORMED
,
230 N_("trailing \\ in principal name", ""));
231 return KRB5_PARSE_MALFORMED
;
234 } else if (*p
== '/')
240 comp
= calloc(ncomp
, sizeof(*comp
));
242 return krb5_enomem(context
);
245 p
= start
= q
= s
= strdup(name
);
248 return krb5_enomem(context
);
262 else if (c
== '\0') {
263 ret
= KRB5_PARSE_MALFORMED
;
264 krb5_set_error_message(context
, ret
,
265 N_("trailing \\ in principal name", ""));
268 } else if (enterprise
&& first_at
) {
271 } else if ((c
== '/' && !enterprise
) || c
== '@') {
273 ret
= KRB5_PARSE_MALFORMED
;
274 krb5_set_error_message(context
, ret
,
275 N_("part after realm in principal name", ""));
278 comp
[n
] = malloc(q
- start
+ 1);
279 if (comp
[n
] == NULL
) {
280 ret
= krb5_enomem(context
);
283 memcpy(comp
[n
], start
, q
- start
);
284 comp
[n
][q
- start
] = 0;
292 if (got_realm
&& (c
== '/' || c
== '\0')) {
293 ret
= KRB5_PARSE_MALFORMED
;
294 krb5_set_error_message(context
, ret
,
295 N_("part after realm in principal name", ""));
302 ret
= KRB5_PARSE_MALFORMED
;
303 krb5_set_error_message(context
, ret
,
304 N_("realm found in 'short' principal "
305 "expected to be without one", ""));
309 realm
= malloc(q
- start
+ 1);
311 ret
= krb5_enomem(context
);
314 memcpy(realm
, start
, q
- start
);
315 realm
[q
- start
] = 0;
319 ret
= KRB5_PARSE_MALFORMED
;
320 krb5_set_error_message(context
, ret
,
321 N_("realm NOT found in principal "
322 "expected to be with one", ""));
324 } else if (no_realm
|| no_def_realm
) {
327 ret
= krb5_get_default_realm(context
, &realm
);
332 comp
[n
] = malloc(q
- start
+ 1);
333 if (comp
[n
] == NULL
) {
334 ret
= krb5_enomem(context
);
337 memcpy(comp
[n
], start
, q
- start
);
338 comp
[n
][q
- start
] = 0;
341 *principal
= calloc(1, sizeof(**principal
));
342 if (*principal
== NULL
) {
343 ret
= krb5_enomem(context
);
347 (*principal
)->name
.name_type
= KRB5_NT_ENTERPRISE_PRINCIPAL
;
349 (*principal
)->name
.name_type
= KRB5_NT_PRINCIPAL
;
350 (*principal
)->name
.name_string
.val
= comp
;
351 princ_num_comp(*principal
) = n
;
352 (*principal
)->realm
= realm
;
366 * Parse a name into a krb5_principal structure
368 * @param context Kerberos 5 context
369 * @param name name to parse into a Kerberos principal
370 * @param principal returned principal, free with krb5_free_principal().
372 * @return An krb5 error code, see krb5_get_error_message().
374 * @ingroup krb5_principal
377 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
378 krb5_parse_name(krb5_context context
,
380 krb5_principal
*principal
)
382 return krb5_parse_name_flags(context
, name
, 0, principal
);
385 static const char quotable_chars
[] = " \n\t\b\\/@";
386 static const char replace_chars
[] = " ntb\\/@";
387 static const char nq_chars
[] = " \\/@";
389 #define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0);
392 quote_string(const char *s
, char *out
, size_t idx
, size_t len
, int display
)
395 for(p
= s
; *p
&& idx
< len
; p
++){
396 q
= strchr(quotable_chars
, *p
);
398 add_char(out
, idx
, len
, replace_chars
[q
- quotable_chars
]);
400 add_char(out
, idx
, len
, '\\');
401 add_char(out
, idx
, len
, replace_chars
[q
- quotable_chars
]);
403 add_char(out
, idx
, len
, *p
);
411 static krb5_error_code
412 unparse_name_fixed(krb5_context context
,
413 krb5_const_principal principal
,
420 int short_form
= (flags
& KRB5_PRINCIPAL_UNPARSE_SHORT
) != 0;
421 int no_realm
= (flags
& KRB5_PRINCIPAL_UNPARSE_NO_REALM
) != 0;
422 int display
= (flags
& KRB5_PRINCIPAL_UNPARSE_DISPLAY
) != 0;
424 if (!no_realm
&& princ_realm(principal
) == NULL
) {
425 krb5_set_error_message(context
, ERANGE
,
426 N_("Realm missing from principal, "
427 "can't unparse", ""));
431 for(i
= 0; i
< princ_num_comp(principal
); i
++){
433 add_char(name
, idx
, len
, '/');
434 idx
= quote_string(princ_ncomp(principal
, i
), name
, idx
, len
, display
);
436 krb5_set_error_message(context
, ERANGE
,
437 N_("Out of space printing principal", ""));
441 /* add realm if different from default realm */
442 if(short_form
&& !no_realm
) {
445 ret
= krb5_get_default_realm(context
, &r
);
448 if(strcmp(princ_realm(principal
), r
) != 0)
452 if(!short_form
&& !no_realm
) {
453 add_char(name
, idx
, len
, '@');
454 idx
= quote_string(princ_realm(principal
), name
, idx
, len
, display
);
456 krb5_set_error_message(context
, ERANGE
,
457 N_("Out of space printing "
458 "realm of principal", ""));
466 * Unparse the principal name to a fixed buffer
468 * @param context A Kerberos context.
469 * @param principal principal to unparse
470 * @param name buffer to write name to
471 * @param len length of buffer
473 * @return An krb5 error code, see krb5_get_error_message().
475 * @ingroup krb5_principal
478 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
479 krb5_unparse_name_fixed(krb5_context context
,
480 krb5_const_principal principal
,
484 return unparse_name_fixed(context
, principal
, name
, len
, 0);
488 * Unparse the principal name to a fixed buffer. The realm is skipped
489 * if its a default realm.
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
496 * @return An krb5 error code, see krb5_get_error_message().
498 * @ingroup krb5_principal
501 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
502 krb5_unparse_name_fixed_short(krb5_context context
,
503 krb5_const_principal principal
,
507 return unparse_name_fixed(context
, principal
, name
, len
,
508 KRB5_PRINCIPAL_UNPARSE_SHORT
);
512 * Unparse the principal name with unparse flags to a fixed buffer.
514 * @param context A Kerberos context.
515 * @param principal principal to unparse
516 * @param flags unparse flags
517 * @param name buffer to write name to
518 * @param len length of buffer
520 * @return An krb5 error code, see krb5_get_error_message().
522 * @ingroup krb5_principal
525 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
526 krb5_unparse_name_fixed_flags(krb5_context context
,
527 krb5_const_principal principal
,
532 return unparse_name_fixed(context
, principal
, name
, len
, flags
);
535 static krb5_error_code
536 unparse_name(krb5_context context
,
537 krb5_const_principal principal
,
541 size_t len
= 0, plen
;
545 if (princ_realm(principal
)) {
546 plen
= strlen(princ_realm(principal
));
548 if(strcspn(princ_realm(principal
), quotable_chars
) == plen
)
554 for(i
= 0; i
< princ_num_comp(principal
); i
++){
555 plen
= strlen(princ_ncomp(principal
, i
));
556 if(strcspn(princ_ncomp(principal
, i
), quotable_chars
) == plen
)
565 return krb5_enomem(context
);
566 ret
= unparse_name_fixed(context
, principal
, *name
, len
, flags
);
575 * Unparse the Kerberos name into a string
577 * @param context Kerberos 5 context
578 * @param principal principal to query
579 * @param name resulting string, free with krb5_xfree()
581 * @return An krb5 error code, see krb5_get_error_message().
583 * @ingroup krb5_principal
586 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
587 krb5_unparse_name(krb5_context context
,
588 krb5_const_principal principal
,
591 return unparse_name(context
, principal
, name
, 0);
595 * Unparse the Kerberos name into a string
597 * @param context Kerberos 5 context
598 * @param principal principal to query
599 * @param flags flag to determine the behavior
600 * @param name resulting string, free with krb5_xfree()
602 * @return An krb5 error code, see krb5_get_error_message().
604 * @ingroup krb5_principal
607 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
608 krb5_unparse_name_flags(krb5_context context
,
609 krb5_const_principal principal
,
613 return unparse_name(context
, principal
, name
, flags
);
617 * Unparse the principal name to a allocated buffer. The realm is
618 * skipped if its a default realm.
620 * @param context A Kerberos context.
621 * @param principal principal to unparse
622 * @param name returned buffer, free with krb5_xfree()
624 * @return An krb5 error code, see krb5_get_error_message().
626 * @ingroup krb5_principal
629 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
630 krb5_unparse_name_short(krb5_context context
,
631 krb5_const_principal principal
,
634 return unparse_name(context
, principal
, name
, KRB5_PRINCIPAL_UNPARSE_SHORT
);
638 * Set a new realm for a principal, and as a side-effect free the
641 * @param context A Kerberos context.
642 * @param principal principal set the realm for
643 * @param realm the new realm to set
645 * @return An krb5 error code, see krb5_get_error_message().
647 * @ingroup krb5_principal
650 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
651 krb5_principal_set_realm(krb5_context context
,
652 krb5_principal principal
,
653 krb5_const_realm realm
)
655 if (princ_realm(principal
))
656 free(princ_realm(principal
));
659 princ_realm(principal
) = NULL
;
660 else if ((princ_realm(principal
) = strdup(realm
)) == NULL
)
661 return krb5_enomem(context
);
665 #ifndef HEIMDAL_SMALLER
667 * Build a principal using vararg style building
669 * @param context A Kerberos context.
670 * @param principal returned principal
671 * @param rlen length of realm
672 * @param realm realm name
673 * @param ... a list of components ended with NULL.
675 * @return An krb5 error code, see krb5_get_error_message().
677 * @ingroup krb5_principal
680 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
681 krb5_build_principal(krb5_context context
,
682 krb5_principal
*principal
,
684 krb5_const_realm realm
,
690 ret
= krb5_build_principal_va(context
, principal
, rlen
, realm
, ap
);
697 * Build a principal using vararg style building
699 * @param context A Kerberos context.
700 * @param principal returned principal
701 * @param realm realm name
702 * @param ... a list of components ended with NULL.
704 * @return An krb5 error code, see krb5_get_error_message().
706 * @ingroup krb5_principal
709 /* coverity[+alloc : arg-*1] */
710 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
711 krb5_make_principal(krb5_context context
,
712 krb5_principal
*principal
,
713 krb5_const_realm realm
,
720 ret
= krb5_get_default_realm(context
, &r
);
726 ret
= krb5_build_principal_va(context
, principal
, strlen(realm
), realm
, ap
);
733 static krb5_error_code
734 append_component(krb5_context context
, krb5_principal p
,
738 heim_general_string
*tmp
;
739 size_t len
= princ_num_comp(p
);
741 tmp
= realloc(princ_comp(p
), (len
+ 1) * sizeof(*tmp
));
743 return krb5_enomem(context
);
745 princ_ncomp(p
, len
) = malloc(comp_len
+ 1);
746 if (princ_ncomp(p
, len
) == NULL
)
747 return krb5_enomem(context
);
748 memcpy (princ_ncomp(p
, len
), comp
, comp_len
);
749 princ_ncomp(p
, len
)[comp_len
] = '\0';
754 static krb5_error_code
755 va_ext_princ(krb5_context context
, krb5_principal p
, va_list ap
)
757 krb5_error_code ret
= 0;
763 if ((len
= va_arg(ap
, int)) == 0)
765 s
= va_arg(ap
, const char*);
766 if ((ret
= append_component(context
, p
, s
, len
)) != 0)
772 static krb5_error_code
773 va_princ(krb5_context context
, krb5_principal p
, va_list ap
)
775 krb5_error_code ret
= 0;
780 if ((s
= va_arg(ap
, const char*)) == NULL
)
782 if ((ret
= append_component(context
, p
, s
, strlen(s
))) != 0)
788 static krb5_error_code
789 build_principal(krb5_context context
,
790 krb5_principal
*principal
,
792 krb5_const_realm realm
,
793 krb5_error_code (*func
)(krb5_context
, krb5_principal
, va_list),
799 p
= calloc(1, sizeof(*p
));
801 return krb5_enomem(context
);
802 princ_type(p
) = KRB5_NT_PRINCIPAL
;
804 princ_realm(p
) = strdup(realm
);
805 if (p
->realm
== NULL
) {
807 return krb5_enomem(context
);
810 ret
= func(context
, p
, ap
);
814 krb5_free_principal(context
, p
);
818 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
819 krb5_build_principal_va(krb5_context context
,
820 krb5_principal
*principal
,
822 krb5_const_realm realm
,
825 return build_principal(context
, principal
, rlen
, realm
, va_princ
, ap
);
828 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
829 krb5_build_principal_va_ext(krb5_context context
,
830 krb5_principal
*principal
,
832 krb5_const_realm realm
,
835 return build_principal(context
, principal
, rlen
, realm
, va_ext_princ
, ap
);
839 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
840 krb5_build_principal_ext(krb5_context context
,
841 krb5_principal
*principal
,
843 krb5_const_realm realm
,
849 ret
= krb5_build_principal_va_ext(context
, principal
, rlen
, realm
, ap
);
857 * @param context A Kerberos context.
858 * @param inprinc principal to copy
859 * @param outprinc copied principal, free with krb5_free_principal()
861 * @return An krb5 error code, see krb5_get_error_message().
863 * @ingroup krb5_principal
867 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
868 krb5_copy_principal(krb5_context context
,
869 krb5_const_principal inprinc
,
870 krb5_principal
*outprinc
)
872 krb5_principal p
= malloc(sizeof(*p
));
874 return krb5_enomem(context
);
875 if(copy_Principal(inprinc
, p
)) {
877 return krb5_enomem(context
);
884 * Return TRUE iff princ1 == princ2 (without considering the realm)
886 * @param context Kerberos 5 context
887 * @param princ1 first principal to compare
888 * @param princ2 second principal to compare
890 * @return non zero if equal, 0 if not
892 * @ingroup krb5_principal
893 * @see krb5_principal_compare()
894 * @see krb5_realm_compare()
897 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
898 krb5_principal_compare_any_realm(krb5_context context
,
899 krb5_const_principal princ1
,
900 krb5_const_principal princ2
)
903 if(princ_num_comp(princ1
) != princ_num_comp(princ2
))
905 for(i
= 0; i
< princ_num_comp(princ1
); i
++){
906 if(strcmp(princ_ncomp(princ1
, i
), princ_ncomp(princ2
, i
)) != 0)
912 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
913 _krb5_principal_compare_PrincipalName(krb5_context context
,
914 krb5_const_principal princ1
,
915 PrincipalName
*princ2
)
918 if (princ_num_comp(princ1
) != princ2
->name_string
.len
)
920 for(i
= 0; i
< princ_num_comp(princ1
); i
++){
921 if(strcmp(princ_ncomp(princ1
, i
), princ2
->name_string
.val
[i
]) != 0)
929 * Compares the two principals, including realm of the principals and returns
930 * TRUE if they are the same and FALSE if not.
932 * @param context Kerberos 5 context
933 * @param princ1 first principal to compare
934 * @param princ2 second principal to compare
936 * @ingroup krb5_principal
937 * @see krb5_principal_compare_any_realm()
938 * @see krb5_realm_compare()
942 * return TRUE iff princ1 == princ2
945 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
946 krb5_principal_compare(krb5_context context
,
947 krb5_const_principal princ1
,
948 krb5_const_principal princ2
)
950 if ((princ_type(princ1
) == KRB5_NT_SRV_HST_NEEDS_CANON
||
951 princ_type(princ2
) == KRB5_NT_SRV_HST_NEEDS_CANON
) &&
952 princ_type(princ2
) != princ_type(princ1
)) {
954 krb5_boolean princs_eq
;
955 krb5_const_principal princ2canon
;
956 krb5_const_principal other_princ
;
957 krb5_principal try_princ
;
958 krb5_name_canon_iterator nci
;
960 if (princ_type(princ1
) == KRB5_NT_SRV_HST_NEEDS_CANON
) {
961 princ2canon
= princ1
;
962 other_princ
= princ2
;
964 princ2canon
= princ2
;
965 other_princ
= princ1
;
968 ret
= krb5_name_canon_iterator_start(context
, princ2canon
, NULL
, &nci
);
972 ret
= krb5_name_canon_iterate_princ(context
, &nci
, &try_princ
,
974 if (ret
|| try_princ
== NULL
)
976 princs_eq
= krb5_principal_compare(context
, try_princ
, other_princ
);
978 krb5_free_name_canon_iterator(context
, nci
);
981 } while (nci
!= NULL
);
982 krb5_free_name_canon_iterator(context
, nci
);
986 * Either neither princ requires canonicalization, both do, or
987 * no applicable name canonicalization rules were found and we fell
988 * through (chances are we'll fail here too in that last case).
989 * We're not going to do n^2 comparisons in the case of both princs
990 * requiring canonicalization.
992 if(!krb5_realm_compare(context
, princ1
, princ2
))
994 return krb5_principal_compare_any_realm(context
, princ1
, princ2
);
998 * return TRUE iff realm(princ1) == realm(princ2)
1000 * @param context Kerberos 5 context
1001 * @param princ1 first principal to compare
1002 * @param princ2 second principal to compare
1004 * @ingroup krb5_principal
1005 * @see krb5_principal_compare_any_realm()
1006 * @see krb5_principal_compare()
1009 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1010 krb5_realm_compare(krb5_context context
,
1011 krb5_const_principal princ1
,
1012 krb5_const_principal princ2
)
1014 return strcmp(princ_realm(princ1
), princ_realm(princ2
)) == 0;
1018 * return TRUE iff princ matches pattern
1020 * @ingroup krb5_principal
1023 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1024 krb5_principal_match(krb5_context context
,
1025 krb5_const_principal princ
,
1026 krb5_const_principal pattern
)
1029 if(princ_num_comp(princ
) != princ_num_comp(pattern
))
1031 if(fnmatch(princ_realm(pattern
), princ_realm(princ
), 0) != 0)
1033 for(i
= 0; i
< princ_num_comp(princ
); i
++){
1034 if(fnmatch(princ_ncomp(pattern
, i
), princ_ncomp(princ
, i
), 0) != 0)
1041 * This is the original krb5_sname_to_principal(), renamed to be a
1042 * helper of the new one.
1044 static krb5_error_code
1045 krb5_sname_to_principal_old(krb5_context context
,
1047 const char *hostname
,
1050 krb5_principal
*ret_princ
)
1052 krb5_error_code ret
;
1053 char localhost
[MAXHOSTNAMELEN
];
1054 char **realms
= NULL
, *host
= NULL
;
1056 if(type
!= KRB5_NT_SRV_HST
&& type
!= KRB5_NT_UNKNOWN
) {
1057 krb5_set_error_message(context
, KRB5_SNAME_UNSUPP_NAMETYPE
,
1058 N_("unsupported name type %d", ""),
1060 return KRB5_SNAME_UNSUPP_NAMETYPE
;
1062 if(hostname
== NULL
) {
1063 ret
= gethostname(localhost
, sizeof(localhost
) - 1);
1066 krb5_set_error_message(context
, ret
,
1067 N_("Failed to get local hostname", ""));
1070 localhost
[sizeof(localhost
) - 1] = '\0';
1071 hostname
= localhost
;
1075 if(type
== KRB5_NT_SRV_HST
) {
1077 ret
= krb5_expand_hostname(context
, hostname
, &host
);
1079 ret
= krb5_expand_hostname_realms(context
, hostname
,
1087 } else if (!realm
) {
1088 ret
= krb5_get_host_realm(context
, hostname
, &realms
);
1094 ret
= krb5_make_principal(context
, ret_princ
, realm
, sname
,
1099 krb5_free_host_realm(context
, realms
);
1103 static const struct {
1107 { "UNKNOWN", KRB5_NT_UNKNOWN
},
1108 { "PRINCIPAL", KRB5_NT_PRINCIPAL
},
1109 { "SRV_INST", KRB5_NT_SRV_INST
},
1110 { "SRV_HST", KRB5_NT_SRV_HST
},
1111 { "SRV_XHST", KRB5_NT_SRV_XHST
},
1112 { "UID", KRB5_NT_UID
},
1113 { "X500_PRINCIPAL", KRB5_NT_X500_PRINCIPAL
},
1114 { "SMTP_NAME", KRB5_NT_SMTP_NAME
},
1115 { "ENTERPRISE_PRINCIPAL", KRB5_NT_ENTERPRISE_PRINCIPAL
},
1116 { "ENT_PRINCIPAL_AND_ID", KRB5_NT_ENT_PRINCIPAL_AND_ID
},
1117 { "MS_PRINCIPAL", KRB5_NT_MS_PRINCIPAL
},
1118 { "MS_PRINCIPAL_AND_ID", KRB5_NT_MS_PRINCIPAL_AND_ID
},
1119 { "SRV_HST_NEEDS_CANON", KRB5_NT_SRV_HST_NEEDS_CANON
},
1124 * Parse nametype string and return a nametype integer
1126 * @ingroup krb5_principal
1129 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1130 krb5_parse_nametype(krb5_context context
, const char *str
, int32_t *nametype
)
1134 for(i
= 0; nametypes
[i
].type
; i
++) {
1135 if (strcasecmp(nametypes
[i
].type
, str
) == 0) {
1136 *nametype
= nametypes
[i
].value
;
1140 krb5_set_error_message(context
, KRB5_PARSE_MALFORMED
,
1141 N_("Failed to find name type %s", ""), str
);
1142 return KRB5_PARSE_MALFORMED
;
1146 * Returns true if name is Kerberos NULL name
1148 * @ingroup krb5_principal
1151 krb5_boolean KRB5_LIB_FUNCTION
1152 krb5_principal_is_null(krb5_context context
, krb5_const_principal principal
)
1154 if (principal
->name
.name_type
== KRB5_NT_WELLKNOWN
&&
1155 principal
->name
.name_string
.len
== 2 &&
1156 strcmp(principal
->name
.name_string
.val
[0], "WELLKNOWN") == 0 &&
1157 strcmp(principal
->name
.name_string
.val
[1], "NULL") == 0)
1162 const char _krb5_wellknown_lkdc
[] = "WELLKNOWN:COM.APPLE.LKDC";
1163 static const char lkdc_prefix
[] = "LKDC:";
1166 * Returns true if name is Kerberos an LKDC realm
1168 * @ingroup krb5_principal
1171 krb5_boolean KRB5_LIB_FUNCTION
1172 krb5_realm_is_lkdc(const char *realm
)
1175 return strncmp(realm
, lkdc_prefix
, sizeof(lkdc_prefix
)-1) == 0 ||
1176 strncmp(realm
, _krb5_wellknown_lkdc
, sizeof(_krb5_wellknown_lkdc
) - 1) == 0;
1180 * Returns true if name is Kerberos an LKDC realm
1182 * @ingroup krb5_principal
1185 krb5_boolean KRB5_LIB_FUNCTION
1186 krb5_principal_is_lkdc(krb5_context context
, krb5_const_principal principal
)
1188 return krb5_realm_is_lkdc(principal
->realm
);
1192 * Returns true if name is Kerberos an LKDC realm
1194 * @ingroup krb5_principal
1197 krb5_boolean KRB5_LIB_FUNCTION
1198 krb5_principal_is_pku2u(krb5_context context
, krb5_const_principal principal
)
1200 return strcmp(principal
->realm
, KRB5_PKU2U_REALM_NAME
) == 0;
1204 * Check if the cname part of the principal is a krbtgt principal
1206 * @ingroup krb5_principal
1209 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1210 krb5_principal_is_krbtgt(krb5_context context
, krb5_const_principal p
)
1212 return p
->name
.name_string
.len
== 2 &&
1213 strcmp(p
->name
.name_string
.val
[0], KRB5_TGS_NAME
) == 0;
1218 * Returns true iff name is an WELLKNOWN:ORG.H5L.HOSTBASED-SERVICE
1220 * @ingroup krb5_principal
1223 krb5_boolean KRB5_LIB_FUNCTION
1224 krb5_principal_is_gss_hostbased_service(krb5_context context
,
1225 krb5_const_principal principal
)
1227 if (principal
== NULL
)
1229 if (principal
->name
.name_string
.len
!= 2)
1231 if (strcmp(principal
->name
.name_string
.val
[1], KRB5_GSS_HOSTBASED_SERVICE_NAME
) != 0)
1237 * Check if the cname part of the principal is a initial or renewed krbtgt principal
1239 * @ingroup krb5_principal
1242 krb5_boolean KRB5_LIB_FUNCTION
1243 krb5_principal_is_root_krbtgt(krb5_context context
, krb5_const_principal p
)
1245 return p
->name
.name_string
.len
== 2 &&
1246 strcmp(p
->name
.name_string
.val
[0], KRB5_TGS_NAME
) == 0 &&
1247 strcmp(p
->name
.name_string
.val
[1], p
->realm
) == 0;
1251 typedef enum krb5_name_canon_rule_type
{
1252 KRB5_NCRT_BOGUS
= 0,
1255 KRB5_NCRT_RES_SEARCHLIST
,
1257 } krb5_name_canon_rule_type
;
1259 struct krb5_name_canon_rule_data
{
1260 krb5_name_canon_rule next
;
1261 krb5_name_canon_rule_type type
;
1262 krb5_name_canon_rule_options options
;
1265 unsigned int mindots
;
1269 * Create a principal for the given service running on the given
1270 * hostname. If KRB5_NT_SRV_HST is used, the hostname is canonicalized
1271 * according the configured name canonicalization rules, with
1272 * canonicalization delayed in some cases. One rule involves DNS, which
1273 * is insecure unless DNSSEC is used, but we don't use DNSSEC-capable
1274 * resolver APIs here, so that if DNSSEC is used we wouldn't know it.
1276 * Canonicalization is immediate (not delayed) only when there is only
1277 * one canonicalization rule and that rule indicates that we should do a
1278 * host lookup by name (i.e., DNS).
1280 * @param context A Kerberos context.
1281 * @param hostname hostname to use
1282 * @param sname Service name to use
1283 * @param type name type of pricipal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN.
1284 * @param ret_princ return principal, free with krb5_free_principal().
1286 * @return An krb5 error code, see krb5_get_error_message().
1288 * @ingroup krb5_principal
1291 /* coverity[+alloc : arg-*4] */
1292 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1293 krb5_sname_to_principal(krb5_context context
,
1294 const char *hostname
,
1297 krb5_principal
*ret_princ
)
1299 char *realm
, *remote_host
;
1300 krb5_error_code ret
;
1302 char localname
[MAXHOSTNAMELEN
];
1306 if ((type
!= KRB5_NT_UNKNOWN
) &&
1307 (type
!= KRB5_NT_SRV_HST
))
1308 return KRB5_SNAME_UNSUPP_NAMETYPE
;
1310 /* if hostname is NULL, use local hostname */
1312 if (gethostname(localname
, MAXHOSTNAMELEN
))
1314 hostname
= localname
;
1317 /* if sname is NULL, use "host" */
1321 remote_host
= strdup(hostname
);
1323 return krb5_enomem(context
);
1325 if (type
== KRB5_NT_SRV_HST
) {
1326 krb5_name_canon_rule rules
;
1328 /* Lower-case the hostname, because that's the convention */
1329 for (cp
= remote_host
; *cp
; cp
++)
1330 if (isupper((int) (*cp
)))
1331 *cp
= tolower((int) (*cp
));
1333 ret
= _krb5_get_name_canon_rules(context
, &rules
);
1335 _krb5_debug(context
, 5, "Failed to get name canon rules: ret = %d",
1339 if (rules
->type
== KRB5_NCRT_NSS
&& rules
->next
== NULL
) {
1340 _krb5_debug(context
, 5, "Using nss for name canon immediately "
1341 "(without reverse lookups)");
1342 /* For the default rule we'll just canonicalize here */
1343 ret
= krb5_sname_to_principal_old(context
, NULL
,
1348 _krb5_free_name_canon_rules(context
, rules
);
1351 _krb5_free_name_canon_rules(context
, rules
);
1354 /* Trailing dot(s) would be bad */
1355 if (remote_host
[0]) {
1356 cp
= remote_host
+ strlen(remote_host
)-1;
1361 realm
= ""; /* "Referral realm" -- borrowed from newer MIT */
1363 ret
= krb5_build_principal(context
, ret_princ
, strlen(realm
),
1364 realm
, sname
, remote_host
,
1367 if (type
== KRB5_NT_SRV_HST
) {
1369 * Hostname canonicalization is done elsewhere (in
1370 * krb5_get_credentials() and krb5_kt_get_entry()).
1372 * We use special magic to indicate to those functions that
1373 * this principal name requires canonicalization.
1375 (*ret_princ
)->name
.name_type
= KRB5_NT_SRV_HST_NEEDS_CANON
;
1377 _krb5_debug(context
, 5, "Building a delayed canon principal for %s/%s@",
1378 sname
, remote_host
);
1386 * Helper function to parse name canonicalization rule tokens.
1388 static krb5_error_code
1389 rule_parse_token(krb5_context context
, krb5_name_canon_rule rule
,
1395 * Rules consist of a sequence of tokens, some of which indicate
1396 * what type of rule the rule is, and some of which set rule options
1397 * or ancilliary data. First rule type token wins.
1399 /* Rule type tokens: */
1400 if (strcmp(tok
, "as-is") == 0) {
1401 if (rule
->type
== KRB5_NCRT_BOGUS
)
1402 rule
->type
= KRB5_NCRT_AS_IS
;
1403 } else if (strcmp(tok
, "qualify") == 0) {
1404 if (rule
->type
== KRB5_NCRT_BOGUS
)
1405 rule
->type
= KRB5_NCRT_QUALIFY
;
1406 } else if (strcmp(tok
, "use-resolver-searchlist") == 0) {
1407 if (rule
->type
== KRB5_NCRT_BOGUS
)
1408 rule
->type
= KRB5_NCRT_RES_SEARCHLIST
;
1409 } else if (strcmp(tok
, "nss") == 0) {
1410 if (rule
->type
== KRB5_NCRT_BOGUS
)
1411 rule
->type
= KRB5_NCRT_NSS
;
1413 } else if (strcmp(tok
, "secure") == 0) {
1414 rule
->options
|= KRB5_NCRO_SECURE
;
1415 } else if (strcmp(tok
, "ccache_only") == 0) {
1416 rule
->options
|= KRB5_NCRO_GC_ONLY
;
1417 } else if (strcmp(tok
, "no_referrals") == 0) {
1418 rule
->options
|= KRB5_NCRO_NO_REFERRALS
;
1419 rule
->options
&= ~KRB5_NCRO_USE_REFERRALS
;
1420 } else if (strcmp(tok
, "use_referrals") == 0) {
1421 rule
->options
|= KRB5_NCRO_USE_REFERRALS
;
1422 rule
->options
&= ~KRB5_NCRO_NO_REFERRALS
;
1423 /* Rule ancilliary data: */
1424 } else if (strncmp(tok
, "domain=", strlen("domain=")) == 0) {
1426 rule
->domain
= strdup(tok
+ strlen("domain="));
1428 return krb5_enomem(context
);
1429 } else if (strncmp(tok
, "realm=", strlen("realm=")) == 0) {
1431 rule
->realm
= strdup(tok
+ strlen("realm="));
1433 return krb5_enomem(context
);
1434 } else if (strncmp(tok
, "mindots=", strlen("mindots=")) == 0) {
1436 n
= strtol(tok
+ strlen("mindots="), NULL
, 10);
1437 if (errno
== 0 && n
> 0 && n
< 8)
1440 /* ignore bogus tokens; it's not like we can print to stderr */
1441 /* XXX Trace bogus tokens! */
1446 * This helper function expands the DNS search list rule into qualify
1447 * rules, one for each domain in the resolver search list.
1449 static krb5_error_code
1450 expand_search_list(krb5_context context
, krb5_name_canon_rule
*r
, size_t *n
,
1451 size_t insert_point
)
1453 #if defined(HAVE_RES_NINIT) || defined(HAVE_RES_SEARCH)
1454 #ifdef USE_RES_NINIT
1455 struct __res_state statbuf
;
1456 #endif /* USE_RES_NINIT */
1457 krb5_name_canon_rule_options opts
;
1458 krb5_name_canon_rule new_r
;
1460 char **domains
= NULL
;
1461 size_t search_list_len
;
1466 heim_assert((*n
) > insert_point
,
1467 "name canon search list rule expansion: internal error");
1468 free((*r
)[insert_point
].domain
);
1469 free((*r
)[insert_point
].realm
);
1470 (*r
)[insert_point
].domain
= NULL
;
1471 (*r
)[insert_point
].realm
= NULL
;
1472 opts
= (*r
)[insert_point
].options
;
1475 * Would it be worthwhile to move this into context->os_context and
1476 * krb5_os_init_context()?
1478 #ifdef USE_RES_NINIT
1479 ret
= res_ninit(&statbuf
);
1481 return ENOENT
; /* XXX Create a better error */
1482 dnsrch
= statbuf
.dnsrch
;
1483 search_list_len
= sizeof (statbuf
.dnsrch
) / sizeof (*statbuf
.dnsrch
);
1487 return ENOENT
; /* XXX Create a better error */
1488 dnsrch
= _res
.dnsrch
;
1489 search_list_len
= sizeof (_res
.dnsrch
) / sizeof (*_res
.dnsrch
);
1490 #endif /* USE_RES_NINIT */
1492 for (i
= 0; i
< search_list_len
; i
++) {
1493 if (!dnsrch
|| dnsrch
[i
] == NULL
) {
1494 search_list_len
= i
;
1499 if (search_list_len
== 0) {
1500 /* Invalidate this entry and return */
1501 (*r
)[insert_point
].type
= KRB5_NCRT_BOGUS
;
1506 * Pre-strdup() the search list so the realloc() below is the last
1507 * point at which we can fail with ENOMEM.
1509 domains
= calloc(search_list_len
, sizeof (*domains
));
1510 if (domains
== NULL
)
1511 return krb5_enomem(context
);
1512 for (i
= 0; i
< search_list_len
; i
++) {
1513 if ((domains
[i
] = strdup(dnsrch
[i
])) == NULL
) {
1516 return krb5_enomem(context
);
1520 if (search_list_len
> 1) {
1521 /* The -1 here is because we re-use this rule as one of the new rules */
1522 new_r
= realloc(*r
, sizeof (**r
) * ((*n
) + search_list_len
- 1));
1523 if (new_r
== NULL
) {
1524 for (i
= 0; i
< search_list_len
; i
++)
1527 return krb5_enomem(context
);
1530 new_r
= *r
; /* search_list_len == 1 */
1533 /* Make room for the new rules */
1534 if (insert_point
< (*n
) - 1) {
1535 _krb5_debug(context
, 5, "Inserting %ld qualify rules in place of a "
1536 "resolver searchlist rule", (unsigned long)search_list_len
);
1538 * Move the rules that follow the search list rule down by
1539 * search_list_len - 1 rules.
1541 memmove(&new_r
[insert_point
+ search_list_len
],
1542 &new_r
[insert_point
+ 1],
1543 sizeof (new_r
[0]) * ((*n
) - (insert_point
+ 1)));
1547 * Clear in case the search-list rule is at the end of the rules;
1548 * realloc() won't have done this for us.
1550 memset(&new_r
[insert_point
], 0, sizeof (new_r
[0]) * search_list_len
);
1552 /* Setup the new rules */
1553 for (i
= 0; i
< search_list_len
; i
++) {
1554 _krb5_debug(context
, 5, "Inserting qualify rule with domain=%s",
1556 new_r
[insert_point
+ i
].type
= KRB5_NCRT_QUALIFY
;
1557 new_r
[insert_point
+ i
].domain
= domains
[i
];
1558 new_r
[insert_point
+ i
].options
= opts
;
1563 *n
+= search_list_len
- 1; /* -1 because we're replacing one rule */
1565 #ifdef USE_RES_NINIT
1566 res_ndestroy(&statbuf
);
1567 #endif /* USE_RES_NINIT */
1570 /* No resolver API by which to get search list -> use name service */
1571 if ((*r
)[insert_point
].options
& KRB5_NCRO_SECURE
)
1573 (*r
)[insert_point
].type
= KRB5_NCRT_NSS
;
1574 #endif /* HAVE_RES_NINIT || HAVE_RES_SEARCH */
1580 * Helper function to parse name canonicalization rules.
1582 static krb5_error_code
1583 parse_name_canon_rules(krb5_context context
, char **rulestrs
,
1584 krb5_name_canon_rule
*rules
)
1586 krb5_error_code ret
;
1592 krb5_name_canon_rule r
;
1594 for (cpp
= rulestrs
; *cpp
; cpp
++)
1597 if ((r
= calloc(n
, sizeof (*r
))) == NULL
)
1598 return krb5_enomem(context
);
1600 /* This code is written without use of strtok_r() :( */
1601 for (i
= 0, k
= 0; i
< n
; i
++) {
1605 cp
= strpbrk(cp
, ":");
1607 *cp
++ = '\0'; /* delimit token */
1608 ret
= rule_parse_token(context
, &r
[k
], tok
);
1609 } while (cp
&& *cp
);
1610 /* Loosely validate parsed rule */
1611 if (r
[k
].type
== KRB5_NCRT_BOGUS
||
1612 (r
[k
].type
== KRB5_NCRT_QUALIFY
&& !r
[k
].domain
) ||
1613 (r
[k
].type
== KRB5_NCRT_NSS
&& (r
[k
].domain
|| r
[k
].realm
))) {
1614 /* Invalid rule; mark it so and clean up */
1615 r
[k
].type
= KRB5_NCRT_BOGUS
;
1620 /* XXX Trace this! */
1621 continue; /* bogus rule */
1623 k
++; /* good rule */
1626 /* Expand search list rules */
1627 for (i
= 0; i
< n
; i
++) {
1628 if (r
[i
].type
!= KRB5_NCRT_RES_SEARCHLIST
)
1630 ret
= expand_search_list(context
, &r
, &n
, i
);
1635 /* The first rule has to be valid */
1637 for (i
= 0; i
< n
; i
++) {
1638 if (r
[i
].type
!= KRB5_NCRT_BOGUS
) {
1643 if (k
> 0 && k
< n
) {
1645 memset(&r
[k
], 0, sizeof (r
[k
])); /* KRB5_NCRT_BOGUS is 0 */
1648 /* Setup next pointers */
1649 for (i
= 1, k
= 0; i
< n
; i
++) {
1650 if (r
[i
].type
== KRB5_NCRT_BOGUS
)
1657 return 0; /* We don't communicate bad rule errors here */
1661 * This function returns an array of host-based service name
1662 * canonicalization rules. The array of rules is organized as a list.
1663 * See the definition of krb5_name_canon_rule.
1665 * @param context A Kerberos context.
1666 * @param rules Output location for array of rules.
1668 KRB5_LIB_FUNCTION krb5_error_code
1669 _krb5_get_name_canon_rules(krb5_context context
, krb5_name_canon_rule
*rules
)
1671 krb5_error_code ret
;
1672 char **values
= NULL
;
1676 ret
= krb5_get_default_realm(context
, &realm
);
1677 if (ret
== KRB5_CONFIG_NODEFREALM
|| ret
== KRB5_CONFIG_CANTOPEN
)
1683 values
= krb5_config_get_strings(context
, NULL
,
1686 "name_canon_rules", NULL
);
1690 values
= krb5_config_get_strings(context
, NULL
,
1692 "name_canon_rules", NULL
);
1695 if (!values
|| !values
[0]) {
1696 /* Default rule: do the dreaded getaddrinfo()/getnameinfo() dance */
1697 if ((*rules
= calloc(1, sizeof (**rules
))) == NULL
)
1698 return krb5_enomem(context
);
1699 (*rules
)->type
= KRB5_NCRT_NSS
;
1703 ret
= parse_name_canon_rules(context
, values
, rules
);
1704 krb5_config_free_strings(values
);
1710 krb5_name_canon_rule r
;
1711 for (k
= 0, r
= *rules
; r
; r
= r
->next
, k
++) {
1712 _krb5_debug(context
, 5,
1713 "Name canon rule %ld type=%d, options=%x, mindots=%d, "
1714 "domain=%s, realm=%s",
1715 (unsigned long)k
, r
->type
, r
->options
, r
->mindots
,
1716 r
->domain
? r
->domain
: "<none>",
1717 r
->realm
? r
->realm
: "<none>"
1722 if ((*rules
)[0].type
!= KRB5_NCRT_BOGUS
)
1723 return 0; /* success! */
1726 /* fall through to return default rule */
1727 _krb5_debug(context
, 5, "All name canon rules are bogus!");
1732 static krb5_error_code
1733 get_host_realm(krb5_context context
, const char *hostname
, char **realm
)
1735 krb5_error_code ret
;
1736 char **hrealms
= NULL
;
1739 if ((ret
= krb5_get_host_realm(context
, hostname
, &hrealms
)))
1742 return KRB5_ERR_HOST_REALM_UNKNOWN
; /* krb5_set_error() already done */
1744 krb5_free_host_realm(context
, hrealms
);
1745 return KRB5_ERR_HOST_REALM_UNKNOWN
; /* krb5_set_error() already done */
1747 *realm
= strdup(hrealms
[0]);
1748 krb5_free_host_realm(context
, hrealms
);
1753 * Apply a name canonicalization rule to a principal.
1755 * @param context Kerberos context
1756 * @param rule name canon rule
1757 * @param in_princ principal name
1758 * @param out_print resulting principal name
1759 * @param rule_opts options for this rule
1761 KRB5_LIB_FUNCTION krb5_error_code
1762 _krb5_apply_name_canon_rule(krb5_context context
, krb5_name_canon_rule rule
,
1763 krb5_const_principal in_princ
, krb5_principal
*out_princ
,
1764 krb5_name_canon_rule_options
*rule_opts
)
1766 krb5_error_code ret
;
1767 unsigned int ndots
= 0;
1769 const char *sname
= NULL
;
1770 const char *hostname
= NULL
;
1771 char *new_hostname
= NULL
;
1774 heim_assert(in_princ
->name
.name_type
== KRB5_NT_SRV_HST_NEEDS_CANON
,
1775 "internal error: principal does not need canon");
1780 if (rule
->type
== KRB5_NCRT_BOGUS
)
1781 return 0; /* rule doesn't apply */
1783 sname
= krb5_principal_get_comp_string(context
, in_princ
, 0);
1784 hostname
= krb5_principal_get_comp_string(context
, in_princ
, 1);
1786 _krb5_debug(context
, 5, "Applying a name rule (type %d) to %s", rule
->type
,
1789 *rule_opts
= rule
->options
;
1792 switch (rule
->type
) {
1793 case KRB5_NCRT_AS_IS
:
1794 if (rule
->mindots
> 0) {
1795 for (cp
= strchr(hostname
, '.'); cp
&& *cp
; cp
= strchr(cp
, '.'))
1797 if (ndots
< rule
->mindots
)
1798 goto out
; /* *out_princ == NULL; rule doesn't apply */
1801 cp
= strstr(hostname
, rule
->domain
);
1803 goto out
; /* *out_princ == NULL; rule doesn't apply */
1804 if (cp
!= hostname
&& cp
[-1] != '.')
1807 /* Rule matches, copy princ with hostname as-is, with normal magic */
1808 realm
= rule
->realm
;
1810 ret
= get_host_realm(context
, hostname
, &realm
);
1814 _krb5_debug(context
, 5, "As-is rule building a princ with realm=%s, "
1815 "sname=%s, and hostname=%s", realm
, sname
, hostname
);
1816 ret
= krb5_build_principal(context
, out_princ
,
1818 realm
, sname
, hostname
,
1823 case KRB5_NCRT_QUALIFY
:
1825 * Note that we should never get these rules even if specified
1826 * in krb5.conf. See rule parser.
1828 heim_assert(rule
->domain
!= NULL
,
1829 "missing domain for qualify name canon rule");
1830 cp
= strchr(hostname
, '.');
1831 if (cp
&& (cp
= strstr(cp
, rule
->domain
))) {
1832 new_hostname
= strdup(hostname
);
1833 if (new_hostname
== NULL
) {
1834 ret
= krb5_enomem(context
);
1839 asprintf(&new_hostname
, "%s%s%s", hostname
,
1840 rule
->domain
[0] != '.' ? "." : "",
1842 if (new_hostname
== NULL
) {
1843 ret
= krb5_enomem(context
);
1847 realm
= rule
->realm
;
1849 ret
= get_host_realm(context
, new_hostname
, &realm
);
1853 _krb5_debug(context
, 5, "Building a princ with realm=%s, sname=%s, "
1854 "and hostname=%s", realm
, sname
, new_hostname
);
1855 ret
= krb5_build_principal(context
, out_princ
,
1856 strlen(realm
), realm
,
1857 sname
, new_hostname
, (char *)0);
1862 _krb5_debug(context
, 5, "Using name service lookups (without "
1863 "reverse lookups)");
1864 ret
= krb5_sname_to_principal_old(context
, rule
->realm
,
1868 if (rule
->next
!= NULL
&&
1869 (ret
== KRB5_ERR_BAD_HOSTNAME
||
1870 ret
== KRB5_ERR_HOST_REALM_UNKNOWN
))
1872 * Bad hostname / realm unknown -> rule inapplicable if
1873 * there's more rules. If it's the last rule then we want
1874 * to return all errors from krb5_sname_to_principal_old()
1882 /* Can't happen, but we need this to shut up gcc */
1887 if (!ret
&& *out_princ
) {
1888 krb5_error_code ret2
;
1891 ret2
= krb5_unparse_name(context
, *out_princ
, &unparsed
);
1893 _krb5_debug(context
, 5, "Couldn't unparse resulting princ! (%d)",
1896 _krb5_debug(context
, 5, "Name canon rule application yields this "
1897 "unparsed princ: %s", unparsed
);
1901 _krb5_debug(context
, 5, "Name canon rule did not apply");
1903 _krb5_debug(context
, 5, "Name canon rule application error: %d", ret
);
1907 if (realm
!= rule
->realm
)
1910 (*out_princ
)->name
.name_type
= KRB5_NT_SRV_HST
;
1912 krb5_set_error_message(context
, ret
,
1913 N_("Name canon rule application failed", ""));
1918 * Free name canonicalization rules
1920 KRB5_LIB_FUNCTION
void
1921 _krb5_free_name_canon_rules(krb5_context context
, krb5_name_canon_rule rules
)
1923 krb5_name_canon_rule r
;
1925 for (r
= rules
; r
; r
= r
->next
) {
1934 struct krb5_name_canon_iterator_data
{
1935 krb5_name_canon_rule rules
;
1936 krb5_name_canon_rule rule
;
1937 krb5_const_principal in_princ
;
1938 krb5_principal tmp_princ
;
1945 * Initialize name canonicalization iterator.
1947 * @param context Kerberos context
1948 * @param in_princ principal name to be canonicalized OR
1949 * @param in_creds credentials whose server is to be canonicalized
1950 * @param iter output iterator object
1952 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1953 krb5_name_canon_iterator_start(krb5_context context
,
1954 krb5_const_principal in_princ
,
1955 krb5_creds
*in_creds
,
1956 krb5_name_canon_iterator
*iter
)
1958 krb5_error_code ret
;
1959 krb5_name_canon_iterator state
;
1960 krb5_const_principal princ
;
1964 state
= calloc(1, sizeof (*state
));
1966 return krb5_enomem(context
);
1967 princ
= in_princ
? in_princ
: in_creds
->server
;
1969 if (princ_type(princ
) != KRB5_NT_SRV_HST_NEEDS_CANON
) {
1971 * Name needs no canon -> trivial iterator; we still want an
1972 * iterator just so as to keep callers simple.
1974 state
->is_trivial
= 1;
1975 state
->creds
= in_creds
;
1977 ret
= _krb5_get_name_canon_rules(context
, &state
->rules
);
1980 state
->rule
= state
->rules
;
1983 state
->in_princ
= princ
;
1985 if (!state
->is_trivial
) {
1986 ret
= krb5_copy_creds(context
, in_creds
, &state
->creds
);
1989 state
->tmp_princ
= state
->creds
->server
; /* so we don't leak */
1996 krb5_free_name_canon_iterator(context
, state
);
1997 return krb5_enomem(context
);
2001 * Helper for name canon iteration.
2003 static krb5_error_code
2004 krb5_name_canon_iterate(krb5_context context
,
2005 krb5_name_canon_iterator
*iter
,
2006 krb5_name_canon_rule_options
*rule_opts
)
2008 krb5_error_code ret
;
2009 krb5_name_canon_iterator state
= *iter
;
2017 krb5_free_name_canon_iterator(context
, state
);
2022 if (state
->is_trivial
&& !state
->done
) {
2028 krb5_free_principal(context
, state
->tmp_princ
);
2029 ret
= _krb5_apply_name_canon_rule(context
, state
->rule
,
2030 state
->in_princ
, &state
->tmp_princ
, rule_opts
);
2033 state
->rule
= state
->rule
->next
;
2034 } while (state
->rule
!= NULL
&& state
->tmp_princ
== NULL
);
2036 if (state
->tmp_princ
== NULL
) {
2037 krb5_free_name_canon_iterator(context
, state
);
2042 state
->creds
->server
= state
->tmp_princ
;
2043 if (state
->rule
== NULL
)
2049 * Iteratively apply name canon rules, outputing a principal and rule
2050 * options each time. Iteration completes when the @iter is NULL on
2051 * return or when an error is returned. Callers must free the iterator
2052 * if they abandon it mid-way.
2054 * @param context Kerberos context
2055 * @param iter name canon rule iterator (input/output)
2056 * @param try_princ output principal name
2057 * @param rule_opts output rule options
2059 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2060 krb5_name_canon_iterate_princ(krb5_context context
,
2061 krb5_name_canon_iterator
*iter
,
2062 krb5_principal
*try_princ
,
2063 krb5_name_canon_rule_options
*rule_opts
)
2065 krb5_error_code ret
;
2068 ret
= krb5_name_canon_iterate(context
, iter
, rule_opts
);
2070 *try_princ
= (*iter
)->tmp_princ
;
2075 * Iteratively apply name canon rules, outputing a krb5_creds and rule
2076 * options each time. Iteration completes when the @iter is NULL on
2077 * return or when an error is returned. Callers must free the iterator
2078 * if they abandon it mid-way.
2080 * @param context Kerberos context
2081 * @param iter name canon rule iterator
2082 * @param try_creds output krb5_creds
2083 * @param rule_opts output rule options
2085 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2086 krb5_name_canon_iterate_creds(krb5_context context
,
2087 krb5_name_canon_iterator
*iter
,
2088 krb5_creds
**try_creds
,
2089 krb5_name_canon_rule_options
*rule_opts
)
2091 krb5_error_code ret
;
2094 ret
= krb5_name_canon_iterate(context
, iter
, rule_opts
);
2096 *try_creds
= (*iter
)->creds
;
2101 * Free a name canonicalization rule iterator.
2103 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
2104 krb5_free_name_canon_iterator(krb5_context context
,
2105 krb5_name_canon_iterator iter
)
2109 if (!iter
->is_trivial
) {
2111 krb5_free_creds(context
, iter
->creds
);
2112 iter
->tmp_princ
= NULL
;
2114 if (iter
->tmp_princ
)
2115 krb5_free_principal(context
, iter
->tmp_princ
);
2116 _krb5_free_name_canon_rules(context
, iter
->rules
);