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 to parts separeted by a @. The later part is the kerbero
39 * realm the principal belongs to and the former 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 enterprise
= (flags
& KRB5_PRINCIPAL_PARSE_ENTERPRISE
);
210 #define RFLAGS (KRB5_PRINCIPAL_PARSE_NO_REALM|KRB5_PRINCIPAL_PARSE_REQUIRE_REALM)
212 if ((flags
& RFLAGS
) == RFLAGS
) {
213 krb5_set_error_message(context
, KRB5_ERR_NO_SERVICE
,
214 N_("Can't require both realm and "
215 "no realm at the same time", ""));
216 return KRB5_ERR_NO_SERVICE
;
220 /* count number of component,
221 * enterprise names only have one component
225 for(p
= name
; *p
; p
++){
228 krb5_set_error_message(context
, KRB5_PARSE_MALFORMED
,
229 N_("trailing \\ in principal name", ""));
230 return KRB5_PARSE_MALFORMED
;
239 comp
= calloc(ncomp
, sizeof(*comp
));
241 return krb5_enomem(context
);
244 p
= start
= q
= s
= strdup(name
);
247 return krb5_enomem(context
);
262 ret
= KRB5_PARSE_MALFORMED
;
263 krb5_set_error_message(context
, ret
,
264 N_("trailing \\ in principal name", ""));
267 }else if(enterprise
&& first_at
) {
270 }else if((c
== '/' && !enterprise
) || c
== '@'){
272 ret
= KRB5_PARSE_MALFORMED
;
273 krb5_set_error_message(context
, ret
,
274 N_("part after realm in principal name", ""));
277 comp
[n
] = malloc(q
- start
+ 1);
278 if (comp
[n
] == NULL
) {
279 ret
= krb5_enomem(context
);
282 memcpy(comp
[n
], start
, q
- start
);
283 comp
[n
][q
- start
] = 0;
291 if(got_realm
&& (c
== '/' || c
== '\0')) {
292 ret
= KRB5_PARSE_MALFORMED
;
293 krb5_set_error_message(context
, ret
,
294 N_("part after realm in principal name", ""));
300 if (flags
& KRB5_PRINCIPAL_PARSE_NO_REALM
) {
301 ret
= KRB5_PARSE_MALFORMED
;
302 krb5_set_error_message(context
, ret
,
303 N_("realm found in 'short' principal "
304 "expected to be without one", ""));
307 realm
= malloc(q
- start
+ 1);
309 ret
= krb5_enomem(context
);
312 memcpy(realm
, start
, q
- start
);
313 realm
[q
- start
] = 0;
315 if (flags
& KRB5_PRINCIPAL_PARSE_REQUIRE_REALM
) {
316 ret
= KRB5_PARSE_MALFORMED
;
317 krb5_set_error_message(context
, ret
,
318 N_("realm NOT found in principal "
319 "expected to be with one", ""));
321 } else if (flags
& KRB5_PRINCIPAL_PARSE_NO_REALM
) {
324 ret
= krb5_get_default_realm (context
, &realm
);
329 comp
[n
] = malloc(q
- start
+ 1);
330 if (comp
[n
] == NULL
) {
331 ret
= krb5_enomem(context
);
334 memcpy(comp
[n
], start
, q
- start
);
335 comp
[n
][q
- start
] = 0;
338 *principal
= calloc(1, sizeof(**principal
));
339 if (*principal
== NULL
) {
340 ret
= krb5_enomem(context
);
344 (*principal
)->name
.name_type
= KRB5_NT_ENTERPRISE_PRINCIPAL
;
346 (*principal
)->name
.name_type
= KRB5_NT_PRINCIPAL
;
347 (*principal
)->name
.name_string
.val
= comp
;
348 princ_num_comp(*principal
) = n
;
349 (*principal
)->realm
= realm
;
363 * Parse a name into a krb5_principal structure
365 * @param context Kerberos 5 context
366 * @param name name to parse into a Kerberos principal
367 * @param principal returned principal, free with krb5_free_principal().
369 * @return An krb5 error code, see krb5_get_error_message().
371 * @ingroup krb5_principal
374 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
375 krb5_parse_name(krb5_context context
,
377 krb5_principal
*principal
)
379 return krb5_parse_name_flags(context
, name
, 0, principal
);
382 static const char quotable_chars
[] = " \n\t\b\\/@";
383 static const char replace_chars
[] = " ntb\\/@";
384 static const char nq_chars
[] = " \\/@";
386 #define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0);
389 quote_string(const char *s
, char *out
, size_t idx
, size_t len
, int display
)
392 for(p
= s
; *p
&& idx
< len
; p
++){
393 q
= strchr(quotable_chars
, *p
);
395 add_char(out
, idx
, len
, replace_chars
[q
- quotable_chars
]);
397 add_char(out
, idx
, len
, '\\');
398 add_char(out
, idx
, len
, replace_chars
[q
- quotable_chars
]);
400 add_char(out
, idx
, len
, *p
);
408 static krb5_error_code
409 unparse_name_fixed(krb5_context context
,
410 krb5_const_principal principal
,
417 int short_form
= (flags
& KRB5_PRINCIPAL_UNPARSE_SHORT
) != 0;
418 int no_realm
= (flags
& KRB5_PRINCIPAL_UNPARSE_NO_REALM
) != 0;
419 int display
= (flags
& KRB5_PRINCIPAL_UNPARSE_DISPLAY
) != 0;
421 if (!no_realm
&& princ_realm(principal
) == NULL
) {
422 krb5_set_error_message(context
, ERANGE
,
423 N_("Realm missing from principal, "
424 "can't unparse", ""));
428 for(i
= 0; i
< princ_num_comp(principal
); i
++){
430 add_char(name
, idx
, len
, '/');
431 idx
= quote_string(princ_ncomp(principal
, i
), name
, idx
, len
, display
);
433 krb5_set_error_message(context
, ERANGE
,
434 N_("Out of space printing principal", ""));
438 /* add realm if different from default realm */
439 if(short_form
&& !no_realm
) {
442 ret
= krb5_get_default_realm(context
, &r
);
445 if(strcmp(princ_realm(principal
), r
) != 0)
449 if(!short_form
&& !no_realm
) {
450 add_char(name
, idx
, len
, '@');
451 idx
= quote_string(princ_realm(principal
), name
, idx
, len
, display
);
453 krb5_set_error_message(context
, ERANGE
,
454 N_("Out of space printing "
455 "realm of principal", ""));
463 * Unparse the principal name to a fixed buffer
465 * @param context A Kerberos context.
466 * @param principal principal to unparse
467 * @param name buffer to write name to
468 * @param len length of buffer
470 * @return An krb5 error code, see krb5_get_error_message().
472 * @ingroup krb5_principal
475 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
476 krb5_unparse_name_fixed(krb5_context context
,
477 krb5_const_principal principal
,
481 return unparse_name_fixed(context
, principal
, name
, len
, 0);
485 * Unparse the principal name to a fixed buffer. The realm is skipped
486 * if its a default realm.
488 * @param context A Kerberos context.
489 * @param principal principal to unparse
490 * @param name buffer to write name to
491 * @param len length of buffer
493 * @return An krb5 error code, see krb5_get_error_message().
495 * @ingroup krb5_principal
498 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
499 krb5_unparse_name_fixed_short(krb5_context context
,
500 krb5_const_principal principal
,
504 return unparse_name_fixed(context
, principal
, name
, len
,
505 KRB5_PRINCIPAL_UNPARSE_SHORT
);
509 * Unparse the principal name with unparse flags to a fixed buffer.
511 * @param context A Kerberos context.
512 * @param principal principal to unparse
513 * @param flags unparse flags
514 * @param name buffer to write name to
515 * @param len length of buffer
517 * @return An krb5 error code, see krb5_get_error_message().
519 * @ingroup krb5_principal
522 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
523 krb5_unparse_name_fixed_flags(krb5_context context
,
524 krb5_const_principal principal
,
529 return unparse_name_fixed(context
, principal
, name
, len
, flags
);
532 static krb5_error_code
533 unparse_name(krb5_context context
,
534 krb5_const_principal principal
,
538 size_t len
= 0, plen
;
542 if (princ_realm(principal
)) {
543 plen
= strlen(princ_realm(principal
));
545 if(strcspn(princ_realm(principal
), quotable_chars
) == plen
)
551 for(i
= 0; i
< princ_num_comp(principal
); i
++){
552 plen
= strlen(princ_ncomp(principal
, i
));
553 if(strcspn(princ_ncomp(principal
, i
), quotable_chars
) == plen
)
562 return krb5_enomem(context
);
563 ret
= unparse_name_fixed(context
, principal
, *name
, len
, flags
);
572 * Unparse the Kerberos name into a string
574 * @param context Kerberos 5 context
575 * @param principal principal to query
576 * @param name resulting string, free with krb5_xfree()
578 * @return An krb5 error code, see krb5_get_error_message().
580 * @ingroup krb5_principal
583 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
584 krb5_unparse_name(krb5_context context
,
585 krb5_const_principal principal
,
588 return unparse_name(context
, principal
, name
, 0);
592 * Unparse the Kerberos name into a string
594 * @param context Kerberos 5 context
595 * @param principal principal to query
596 * @param flags flag to determine the behavior
597 * @param name resulting string, free with krb5_xfree()
599 * @return An krb5 error code, see krb5_get_error_message().
601 * @ingroup krb5_principal
604 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
605 krb5_unparse_name_flags(krb5_context context
,
606 krb5_const_principal principal
,
610 return unparse_name(context
, principal
, name
, flags
);
614 * Unparse the principal name to a allocated buffer. The realm is
615 * skipped if its a default realm.
617 * @param context A Kerberos context.
618 * @param principal principal to unparse
619 * @param name returned buffer, free with krb5_xfree()
621 * @return An krb5 error code, see krb5_get_error_message().
623 * @ingroup krb5_principal
626 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
627 krb5_unparse_name_short(krb5_context context
,
628 krb5_const_principal principal
,
631 return unparse_name(context
, principal
, name
, KRB5_PRINCIPAL_UNPARSE_SHORT
);
635 * Set a new realm for a principal, and as a side-effect free the
638 * @param context A Kerberos context.
639 * @param principal principal set the realm for
640 * @param realm the new realm to set
642 * @return An krb5 error code, see krb5_get_error_message().
644 * @ingroup krb5_principal
647 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
648 krb5_principal_set_realm(krb5_context context
,
649 krb5_principal principal
,
650 krb5_const_realm realm
)
652 if (princ_realm(principal
))
653 free(princ_realm(principal
));
655 princ_realm(principal
) = strdup(realm
);
656 if (princ_realm(principal
) == NULL
)
657 return krb5_enomem(context
);
661 #ifndef HEIMDAL_SMALLER
663 * Build a principal using vararg style building
665 * @param context A Kerberos context.
666 * @param principal returned principal
667 * @param rlen length of realm
668 * @param realm realm name
669 * @param ... a list of components ended with NULL.
671 * @return An krb5 error code, see krb5_get_error_message().
673 * @ingroup krb5_principal
676 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
677 krb5_build_principal(krb5_context context
,
678 krb5_principal
*principal
,
680 krb5_const_realm realm
,
686 ret
= krb5_build_principal_va(context
, principal
, rlen
, realm
, ap
);
693 * Build a principal using vararg style building
695 * @param context A Kerberos context.
696 * @param principal returned principal
697 * @param realm realm name
698 * @param ... a list of components ended with NULL.
700 * @return An krb5 error code, see krb5_get_error_message().
702 * @ingroup krb5_principal
705 /* coverity[+alloc : arg-*1] */
706 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
707 krb5_make_principal(krb5_context context
,
708 krb5_principal
*principal
,
709 krb5_const_realm realm
,
716 ret
= krb5_get_default_realm(context
, &r
);
722 ret
= krb5_build_principal_va(context
, principal
, strlen(realm
), realm
, ap
);
729 static krb5_error_code
730 append_component(krb5_context context
, krb5_principal p
,
734 heim_general_string
*tmp
;
735 size_t len
= princ_num_comp(p
);
737 tmp
= realloc(princ_comp(p
), (len
+ 1) * sizeof(*tmp
));
739 return krb5_enomem(context
);
741 princ_ncomp(p
, len
) = malloc(comp_len
+ 1);
742 if (princ_ncomp(p
, len
) == NULL
)
743 return krb5_enomem(context
);
744 memcpy (princ_ncomp(p
, len
), comp
, comp_len
);
745 princ_ncomp(p
, len
)[comp_len
] = '\0';
750 static krb5_error_code
751 va_ext_princ(krb5_context context
, krb5_principal p
, va_list ap
)
753 krb5_error_code ret
= 0;
759 if ((len
= va_arg(ap
, int)) == 0)
761 s
= va_arg(ap
, const char*);
762 if ((ret
= append_component(context
, p
, s
, len
)) != 0)
768 static krb5_error_code
769 va_princ(krb5_context context
, krb5_principal p
, va_list ap
)
771 krb5_error_code ret
= 0;
776 if ((s
= va_arg(ap
, const char*)) == NULL
)
778 if ((ret
= append_component(context
, p
, s
, strlen(s
))) != 0)
784 static krb5_error_code
785 build_principal(krb5_context context
,
786 krb5_principal
*principal
,
788 krb5_const_realm realm
,
789 krb5_error_code (*func
)(krb5_context
, krb5_principal
, va_list),
795 p
= calloc(1, sizeof(*p
));
797 return krb5_enomem(context
);
798 princ_type(p
) = KRB5_NT_PRINCIPAL
;
800 princ_realm(p
) = strdup(realm
);
801 if (p
->realm
== NULL
) {
803 return krb5_enomem(context
);
806 ret
= func(context
, p
, ap
);
810 krb5_free_principal(context
, p
);
814 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
815 krb5_build_principal_va(krb5_context context
,
816 krb5_principal
*principal
,
818 krb5_const_realm realm
,
821 return build_principal(context
, principal
, rlen
, realm
, va_princ
, ap
);
824 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
825 krb5_build_principal_va_ext(krb5_context context
,
826 krb5_principal
*principal
,
828 krb5_const_realm realm
,
831 return build_principal(context
, principal
, rlen
, realm
, va_ext_princ
, ap
);
835 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
836 krb5_build_principal_ext(krb5_context context
,
837 krb5_principal
*principal
,
839 krb5_const_realm realm
,
845 ret
= krb5_build_principal_va_ext(context
, principal
, rlen
, realm
, ap
);
853 * @param context A Kerberos context.
854 * @param inprinc principal to copy
855 * @param outprinc copied principal, free with krb5_free_principal()
857 * @return An krb5 error code, see krb5_get_error_message().
859 * @ingroup krb5_principal
863 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
864 krb5_copy_principal(krb5_context context
,
865 krb5_const_principal inprinc
,
866 krb5_principal
*outprinc
)
868 krb5_principal p
= malloc(sizeof(*p
));
870 return krb5_enomem(context
);
871 if(copy_Principal(inprinc
, p
)) {
873 return krb5_enomem(context
);
880 * Return TRUE iff princ1 == princ2 (without considering the realm)
882 * @param context Kerberos 5 context
883 * @param princ1 first principal to compare
884 * @param princ2 second principal to compare
886 * @return non zero if equal, 0 if not
888 * @ingroup krb5_principal
889 * @see krb5_principal_compare()
890 * @see krb5_realm_compare()
893 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
894 krb5_principal_compare_any_realm(krb5_context context
,
895 krb5_const_principal princ1
,
896 krb5_const_principal princ2
)
899 if(princ_num_comp(princ1
) != princ_num_comp(princ2
))
901 for(i
= 0; i
< princ_num_comp(princ1
); i
++){
902 if(strcmp(princ_ncomp(princ1
, i
), princ_ncomp(princ2
, i
)) != 0)
908 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
909 _krb5_principal_compare_PrincipalName(krb5_context context
,
910 krb5_const_principal princ1
,
911 PrincipalName
*princ2
)
914 if (princ_num_comp(princ1
) != princ2
->name_string
.len
)
916 for(i
= 0; i
< princ_num_comp(princ1
); i
++){
917 if(strcmp(princ_ncomp(princ1
, i
), princ2
->name_string
.val
[i
]) != 0)
925 * Compares the two principals, including realm of the principals and returns
926 * TRUE if they are the same and FALSE if not.
928 * @param context Kerberos 5 context
929 * @param princ1 first principal to compare
930 * @param princ2 second principal to compare
932 * @ingroup krb5_principal
933 * @see krb5_principal_compare_any_realm()
934 * @see krb5_realm_compare()
938 * return TRUE iff princ1 == princ2
941 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
942 krb5_principal_compare(krb5_context context
,
943 krb5_const_principal princ1
,
944 krb5_const_principal princ2
)
946 if ((princ_type(princ1
) == KRB5_NT_SRV_HST_NEEDS_CANON
||
947 princ_type(princ2
) == KRB5_NT_SRV_HST_NEEDS_CANON
) &&
948 princ_type(princ2
) != princ_type(princ1
)) {
950 krb5_boolean princs_eq
;
951 krb5_const_principal princ2canon
;
952 krb5_const_principal other_princ
;
953 krb5_principal try_princ
;
954 krb5_name_canon_iterator nci
;
956 if (princ_type(princ1
) == KRB5_NT_SRV_HST_NEEDS_CANON
) {
957 princ2canon
= princ1
;
958 other_princ
= princ2
;
960 princ2canon
= princ2
;
961 other_princ
= princ1
;
964 ret
= krb5_name_canon_iterator_start(context
, princ2canon
, NULL
, &nci
);
968 ret
= krb5_name_canon_iterate_princ(context
, &nci
, &try_princ
,
970 if (ret
|| try_princ
== NULL
)
972 princs_eq
= krb5_principal_compare(context
, try_princ
, other_princ
);
974 krb5_free_name_canon_iterator(context
, nci
);
977 } while (nci
!= NULL
);
978 krb5_free_name_canon_iterator(context
, nci
);
982 * Either neither princ requires canonicalization, both do, or
983 * no applicable name canonicalization rules were found and we fell
984 * through (chances are we'll fail here too in that last case).
985 * We're not going to do n^2 comparisons in the case of both princs
986 * requiring canonicalization.
988 if(!krb5_realm_compare(context
, princ1
, princ2
))
990 return krb5_principal_compare_any_realm(context
, princ1
, princ2
);
994 * return TRUE iff realm(princ1) == realm(princ2)
996 * @param context Kerberos 5 context
997 * @param princ1 first principal to compare
998 * @param princ2 second principal to compare
1000 * @ingroup krb5_principal
1001 * @see krb5_principal_compare_any_realm()
1002 * @see krb5_principal_compare()
1005 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1006 krb5_realm_compare(krb5_context context
,
1007 krb5_const_principal princ1
,
1008 krb5_const_principal princ2
)
1010 return strcmp(princ_realm(princ1
), princ_realm(princ2
)) == 0;
1014 * return TRUE iff princ matches pattern
1016 * @ingroup krb5_principal
1019 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1020 krb5_principal_match(krb5_context context
,
1021 krb5_const_principal princ
,
1022 krb5_const_principal pattern
)
1025 if(princ_num_comp(princ
) != princ_num_comp(pattern
))
1027 if(fnmatch(princ_realm(pattern
), princ_realm(princ
), 0) != 0)
1029 for(i
= 0; i
< princ_num_comp(princ
); i
++){
1030 if(fnmatch(princ_ncomp(pattern
, i
), princ_ncomp(princ
, i
), 0) != 0)
1037 * This is the original krb5_sname_to_principal(), renamed to be a
1038 * helper of the new one.
1040 static krb5_error_code
1041 krb5_sname_to_principal_old(krb5_context context
,
1043 const char *hostname
,
1046 krb5_principal
*ret_princ
)
1048 krb5_error_code ret
;
1049 char localhost
[MAXHOSTNAMELEN
];
1050 char **realms
= NULL
, *host
= NULL
;
1052 if(type
!= KRB5_NT_SRV_HST
&& type
!= KRB5_NT_UNKNOWN
) {
1053 krb5_set_error_message(context
, KRB5_SNAME_UNSUPP_NAMETYPE
,
1054 N_("unsupported name type %d", ""),
1056 return KRB5_SNAME_UNSUPP_NAMETYPE
;
1058 if(hostname
== NULL
) {
1059 ret
= gethostname(localhost
, sizeof(localhost
) - 1);
1062 krb5_set_error_message(context
, ret
,
1063 N_("Failed to get local hostname", ""));
1066 localhost
[sizeof(localhost
) - 1] = '\0';
1067 hostname
= localhost
;
1071 if(type
== KRB5_NT_SRV_HST
) {
1073 ret
= krb5_expand_hostname(context
, hostname
, &host
);
1075 ret
= krb5_expand_hostname_realms(context
, hostname
,
1083 } else if (!realm
) {
1084 ret
= krb5_get_host_realm(context
, hostname
, &realms
);
1090 ret
= krb5_make_principal(context
, ret_princ
, realm
, sname
,
1095 krb5_free_host_realm(context
, realms
);
1099 static const struct {
1103 { "UNKNOWN", KRB5_NT_UNKNOWN
},
1104 { "PRINCIPAL", KRB5_NT_PRINCIPAL
},
1105 { "SRV_INST", KRB5_NT_SRV_INST
},
1106 { "SRV_HST", KRB5_NT_SRV_HST
},
1107 { "SRV_XHST", KRB5_NT_SRV_XHST
},
1108 { "UID", KRB5_NT_UID
},
1109 { "X500_PRINCIPAL", KRB5_NT_X500_PRINCIPAL
},
1110 { "SMTP_NAME", KRB5_NT_SMTP_NAME
},
1111 { "ENTERPRISE_PRINCIPAL", KRB5_NT_ENTERPRISE_PRINCIPAL
},
1112 { "ENT_PRINCIPAL_AND_ID", KRB5_NT_ENT_PRINCIPAL_AND_ID
},
1113 { "MS_PRINCIPAL", KRB5_NT_MS_PRINCIPAL
},
1114 { "MS_PRINCIPAL_AND_ID", KRB5_NT_MS_PRINCIPAL_AND_ID
},
1115 { "SRV_HST_NEEDS_CANON", KRB5_NT_SRV_HST_NEEDS_CANON
},
1120 * Parse nametype string and return a nametype integer
1122 * @ingroup krb5_principal
1125 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1126 krb5_parse_nametype(krb5_context context
, const char *str
, int32_t *nametype
)
1130 for(i
= 0; nametypes
[i
].type
; i
++) {
1131 if (strcasecmp(nametypes
[i
].type
, str
) == 0) {
1132 *nametype
= nametypes
[i
].value
;
1136 krb5_set_error_message(context
, KRB5_PARSE_MALFORMED
,
1137 N_("Failed to find name type %s", ""), str
);
1138 return KRB5_PARSE_MALFORMED
;
1142 * Returns true if name is Kerberos NULL name
1144 * @ingroup krb5_principal
1147 krb5_boolean KRB5_LIB_FUNCTION
1148 krb5_principal_is_null(krb5_context context
, krb5_const_principal principal
)
1150 if (principal
->name
.name_type
== KRB5_NT_WELLKNOWN
&&
1151 principal
->name
.name_string
.len
== 2 &&
1152 strcmp(principal
->name
.name_string
.val
[0], "WELLKNOWN") == 0 &&
1153 strcmp(principal
->name
.name_string
.val
[1], "NULL") == 0)
1158 const char _krb5_wellknown_lkdc
[] = "WELLKNOWN:COM.APPLE.LKDC";
1159 static const char lkdc_prefix
[] = "LKDC:";
1162 * Returns true if name is Kerberos an LKDC realm
1164 * @ingroup krb5_principal
1167 krb5_boolean KRB5_LIB_FUNCTION
1168 krb5_realm_is_lkdc(const char *realm
)
1171 return strncmp(realm
, lkdc_prefix
, sizeof(lkdc_prefix
)-1) == 0 ||
1172 strncmp(realm
, _krb5_wellknown_lkdc
, sizeof(_krb5_wellknown_lkdc
) - 1) == 0;
1176 * Returns true if name is Kerberos an LKDC realm
1178 * @ingroup krb5_principal
1181 krb5_boolean KRB5_LIB_FUNCTION
1182 krb5_principal_is_lkdc(krb5_context context
, krb5_const_principal principal
)
1184 return krb5_realm_is_lkdc(principal
->realm
);
1188 * Returns true if name is Kerberos an LKDC realm
1190 * @ingroup krb5_principal
1193 krb5_boolean KRB5_LIB_FUNCTION
1194 krb5_principal_is_pku2u(krb5_context context
, krb5_const_principal principal
)
1196 return strcmp(principal
->realm
, KRB5_PKU2U_REALM_NAME
) == 0;
1200 * Check if the cname part of the principal is a krbtgt principal
1202 * @ingroup krb5_principal
1205 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1206 krb5_principal_is_krbtgt(krb5_context context
, krb5_const_principal p
)
1208 return p
->name
.name_string
.len
== 2 &&
1209 strcmp(p
->name
.name_string
.val
[0], KRB5_TGS_NAME
) == 0;
1213 typedef enum krb5_name_canon_rule_type
{
1214 KRB5_NCRT_BOGUS
= 0,
1217 KRB5_NCRT_RES_SEARCHLIST
,
1219 } krb5_name_canon_rule_type
;
1221 struct krb5_name_canon_rule
{
1222 krb5_name_canon_rule next
;
1223 krb5_name_canon_rule_type type
;
1224 krb5_name_canon_rule_options options
;
1227 unsigned int mindots
;
1231 * Create a principal for the given service running on the given
1232 * hostname. If KRB5_NT_SRV_HST is used, the hostname is canonicalized
1233 * according the configured name canonicalization rules, with
1234 * canonicalization delayed in some cases. One rule involves DNS, which
1235 * is insecure unless DNSSEC is used, but we don't use DNSSEC-capable
1236 * resolver APIs here, so that if DNSSEC is used we wouldn't know it.
1238 * Canonicalization is immediate (not delayed) only when there is only
1239 * one canonicalization rule and that rule indicates that we should do a
1240 * host lookup by name (i.e., DNS).
1242 * @param context A Kerberos context.
1243 * @param hostname hostname to use
1244 * @param sname Service name to use
1245 * @param type name type of pricipal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN.
1246 * @param ret_princ return principal, free with krb5_free_principal().
1248 * @return An krb5 error code, see krb5_get_error_message().
1250 * @ingroup krb5_principal
1253 /* coverity[+alloc : arg-*4] */
1254 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1255 krb5_sname_to_principal(krb5_context context
,
1256 const char *hostname
,
1259 krb5_principal
*ret_princ
)
1261 char *realm
, *remote_host
;
1262 krb5_error_code ret
;
1264 char localname
[MAXHOSTNAMELEN
];
1268 if ((type
!= KRB5_NT_UNKNOWN
) &&
1269 (type
!= KRB5_NT_SRV_HST
))
1270 return KRB5_SNAME_UNSUPP_NAMETYPE
;
1272 /* if hostname is NULL, use local hostname */
1274 if (gethostname(localname
, MAXHOSTNAMELEN
))
1276 hostname
= localname
;
1279 /* if sname is NULL, use "host" */
1283 remote_host
= strdup(hostname
);
1285 return krb5_enomem(context
);
1287 if (type
== KRB5_NT_SRV_HST
) {
1288 krb5_name_canon_rule rules
;
1290 /* Lower-case the hostname, because that's the convention */
1291 for (cp
= remote_host
; *cp
; cp
++)
1292 if (isupper((int) (*cp
)))
1293 *cp
= tolower((int) (*cp
));
1295 ret
= _krb5_get_name_canon_rules(context
, &rules
);
1297 _krb5_debug(context
, 5, "Failed to get name canon rules: ret = %d",
1301 if (rules
->type
== KRB5_NCRT_NSS
&& rules
->next
== NULL
) {
1302 _krb5_debug(context
, 5, "Using nss for name canon immediately "
1303 "(without reverse lookups)");
1304 /* For the default rule we'll just canonicalize here */
1305 ret
= krb5_sname_to_principal_old(context
, NULL
,
1310 _krb5_free_name_canon_rules(context
, rules
);
1313 _krb5_free_name_canon_rules(context
, rules
);
1316 /* Trailing dot(s) would be bad */
1317 if (remote_host
[0]) {
1318 cp
= remote_host
+ strlen(remote_host
)-1;
1323 realm
= ""; /* "Referral realm" -- borrowed from newer MIT */
1325 ret
= krb5_build_principal(context
, ret_princ
, strlen(realm
),
1326 realm
, sname
, remote_host
,
1329 if (type
== KRB5_NT_SRV_HST
) {
1331 * Hostname canonicalization is done elsewhere (in
1332 * krb5_get_credentials() and krb5_kt_get_entry()).
1334 * We use special magic to indicate to those functions that
1335 * this principal name requires canonicalization.
1337 (*ret_princ
)->name
.name_type
= KRB5_NT_SRV_HST_NEEDS_CANON
;
1339 _krb5_debug(context
, 5, "Building a delayed canon principal for %s/%s@",
1340 sname
, remote_host
);
1348 * Helper function to parse name canonicalization rule tokens.
1350 static krb5_error_code
1351 rule_parse_token(krb5_context context
, krb5_name_canon_rule rule
,
1357 * Rules consist of a sequence of tokens, some of which indicate
1358 * what type of rule the rule is, and some of which set rule options
1359 * or ancilliary data. First rule type token wins.
1361 /* Rule type tokens: */
1362 if (strcmp(tok
, "as-is") == 0) {
1363 if (rule
->type
== KRB5_NCRT_BOGUS
)
1364 rule
->type
= KRB5_NCRT_AS_IS
;
1365 } else if (strcmp(tok
, "qualify") == 0) {
1366 if (rule
->type
== KRB5_NCRT_BOGUS
)
1367 rule
->type
= KRB5_NCRT_QUALIFY
;
1368 } else if (strcmp(tok
, "use-resolver-searchlist") == 0) {
1369 if (rule
->type
== KRB5_NCRT_BOGUS
)
1370 rule
->type
= KRB5_NCRT_RES_SEARCHLIST
;
1371 } else if (strcmp(tok
, "nss") == 0) {
1372 if (rule
->type
== KRB5_NCRT_BOGUS
)
1373 rule
->type
= KRB5_NCRT_NSS
;
1375 } else if (strcmp(tok
, "secure") == 0) {
1376 rule
->options
|= KRB5_NCRO_SECURE
;
1377 } else if (strcmp(tok
, "ccache_only") == 0) {
1378 rule
->options
|= KRB5_NCRO_GC_ONLY
;
1379 } else if (strcmp(tok
, "no_referrals") == 0) {
1380 rule
->options
|= KRB5_NCRO_NO_REFERRALS
;
1381 rule
->options
&= ~KRB5_NCRO_USE_REFERRALS
;
1382 } else if (strcmp(tok
, "use_referrals") == 0) {
1383 rule
->options
|= KRB5_NCRO_USE_REFERRALS
;
1384 rule
->options
&= ~KRB5_NCRO_NO_REFERRALS
;
1385 /* Rule ancilliary data: */
1386 } else if (strncmp(tok
, "domain=", strlen("domain=")) == 0) {
1388 rule
->domain
= strdup(tok
+ strlen("domain="));
1390 return krb5_enomem(context
);
1391 } else if (strncmp(tok
, "realm=", strlen("realm=")) == 0) {
1393 rule
->realm
= strdup(tok
+ strlen("realm="));
1395 return krb5_enomem(context
);
1396 } else if (strncmp(tok
, "mindots=", strlen("mindots=")) == 0) {
1398 n
= strtol(tok
+ strlen("mindots="), NULL
, 10);
1399 if (errno
== 0 && n
> 0 && n
< 8)
1402 /* ignore bogus tokens; it's not like we can print to stderr */
1403 /* XXX Trace bogus tokens! */
1408 * This helper function expands the DNS search list rule into qualify
1409 * rules, one for each domain in the resolver search list.
1411 static krb5_error_code
1412 expand_search_list(krb5_context context
, krb5_name_canon_rule
*r
, size_t *n
,
1413 size_t insert_point
)
1415 #if defined(HAVE_RES_NINIT) || defined(HAVE_RES_SEARCH)
1416 #ifdef USE_RES_NINIT
1417 struct __res_state statbuf
;
1418 #endif /* USE_RES_NINIT */
1419 krb5_name_canon_rule_options opts
;
1420 krb5_name_canon_rule new_r
;
1422 char **domains
= NULL
;
1423 size_t search_list_len
;
1428 heim_assert((*n
) > insert_point
,
1429 "name canon search list rule expansion: internal error");
1430 free((*r
)[insert_point
].domain
);
1431 free((*r
)[insert_point
].realm
);
1432 (*r
)[insert_point
].domain
= NULL
;
1433 (*r
)[insert_point
].realm
= NULL
;
1434 opts
= (*r
)[insert_point
].options
;
1437 * Would it be worthwhile to move this into context->os_context and
1438 * krb5_os_init_context()?
1440 #ifdef USE_RES_NINIT
1441 ret
= res_ninit(&statbuf
);
1443 return ENOENT
; /* XXX Create a better error */
1444 dnsrch
= statbuf
.dnsrch
;
1445 search_list_len
= sizeof (statbuf
.dnsrch
) / sizeof (*statbuf
.dnsrch
);
1449 return ENOENT
; /* XXX Create a better error */
1450 dnsrch
= _res
.dnsrch
;
1451 search_list_len
= sizeof (_res
.dnsrch
) / sizeof (*_res
.dnsrch
);
1452 #endif /* USE_RES_NINIT */
1454 for (i
= 0; i
< search_list_len
; i
++) {
1455 if (!dnsrch
|| dnsrch
[i
] == NULL
) {
1456 search_list_len
= i
;
1461 if (search_list_len
== 0) {
1462 /* Invalidate this entry and return */
1463 (*r
)[insert_point
].type
= KRB5_NCRT_BOGUS
;
1468 * Pre-strdup() the search list so the realloc() below is the last
1469 * point at which we can fail with ENOMEM.
1471 domains
= calloc(search_list_len
, sizeof (*domains
));
1472 if (domains
== NULL
)
1473 return krb5_enomem(context
);
1474 for (i
= 0; i
< search_list_len
; i
++) {
1475 if ((domains
[i
] = strdup(dnsrch
[i
])) == NULL
) {
1478 return krb5_enomem(context
);
1482 if (search_list_len
> 1) {
1483 /* The -1 here is because we re-use this rule as one of the new rules */
1484 new_r
= realloc(*r
, sizeof (**r
) * ((*n
) + search_list_len
- 1));
1485 if (new_r
== NULL
) {
1486 for (i
= 0; i
< search_list_len
; i
++)
1489 return krb5_enomem(context
);
1492 new_r
= *r
; /* search_list_len == 1 */
1495 /* Make room for the new rules */
1496 if (insert_point
< (*n
) - 1) {
1497 _krb5_debug(context
, 5, "Inserting %ld qualify rules in place of a "
1498 "resolver searchlist rule", (unsigned long)search_list_len
);
1500 * Move the rules that follow the search list rule down by
1501 * search_list_len - 1 rules.
1503 memmove(&new_r
[insert_point
+ search_list_len
],
1504 &new_r
[insert_point
+ 1],
1505 sizeof (new_r
[0]) * ((*n
) - (insert_point
+ 1)));
1509 * Clear in case the search-list rule is at the end of the rules;
1510 * realloc() won't have done this for us.
1512 memset(&new_r
[insert_point
], 0, sizeof (new_r
[0]) * search_list_len
);
1514 /* Setup the new rules */
1515 for (i
= 0; i
< search_list_len
; i
++) {
1516 _krb5_debug(context
, 5, "Inserting qualify rule with domain=%s",
1518 new_r
[insert_point
+ i
].type
= KRB5_NCRT_QUALIFY
;
1519 new_r
[insert_point
+ i
].domain
= domains
[i
];
1520 new_r
[insert_point
+ i
].options
= opts
;
1525 *n
+= search_list_len
- 1; /* -1 because we're replacing one rule */
1527 #ifdef USE_RES_NINIT
1528 res_ndestroy(&statbuf
);
1529 #endif /* USE_RES_NINIT */
1532 /* No resolver API by which to get search list -> use name service */
1533 if ((*r
)[insert_point
].options
& KRB5_NCRO_SECURE
)
1535 (*r
)[insert_point
].type
= KRB5_NCRT_NSS
;
1536 #endif /* HAVE_RES_NINIT || HAVE_RES_SEARCH */
1542 * Helper function to parse name canonicalization rules.
1544 static krb5_error_code
1545 parse_name_canon_rules(krb5_context context
, char **rulestrs
,
1546 krb5_name_canon_rule
*rules
)
1548 krb5_error_code ret
;
1554 krb5_name_canon_rule r
;
1556 for (cpp
= rulestrs
; *cpp
; cpp
++)
1559 if ((r
= calloc(n
, sizeof (*r
))) == NULL
)
1560 return krb5_enomem(context
);
1562 /* This code is written without use of strtok_r() :( */
1563 for (i
= 0, k
= 0; i
< n
; i
++) {
1567 cp
= strpbrk(cp
, ":");
1569 *cp
++ = '\0'; /* delimit token */
1570 ret
= rule_parse_token(context
, &r
[k
], tok
);
1571 } while (cp
&& *cp
);
1572 /* Loosely validate parsed rule */
1573 if (r
[k
].type
== KRB5_NCRT_BOGUS
||
1574 (r
[k
].type
== KRB5_NCRT_QUALIFY
&& !r
[k
].domain
) ||
1575 (r
[k
].type
== KRB5_NCRT_NSS
&& (r
[k
].domain
|| r
[k
].realm
))) {
1576 /* Invalid rule; mark it so and clean up */
1577 r
[k
].type
= KRB5_NCRT_BOGUS
;
1582 /* XXX Trace this! */
1583 continue; /* bogus rule */
1585 k
++; /* good rule */
1588 /* Expand search list rules */
1589 for (i
= 0; i
< n
; i
++) {
1590 if (r
[i
].type
!= KRB5_NCRT_RES_SEARCHLIST
)
1592 ret
= expand_search_list(context
, &r
, &n
, i
);
1597 /* The first rule has to be valid */
1599 for (i
= 0; i
< n
; i
++) {
1600 if (r
[i
].type
!= KRB5_NCRT_BOGUS
) {
1605 if (k
> 0 && k
< n
) {
1607 memset(&r
[k
], 0, sizeof (r
[k
])); /* KRB5_NCRT_BOGUS is 0 */
1610 /* Setup next pointers */
1611 for (i
= 1, k
= 0; i
< n
; i
++) {
1612 if (r
[i
].type
== KRB5_NCRT_BOGUS
)
1619 return 0; /* We don't communicate bad rule errors here */
1623 * This function returns an array of host-based service name
1624 * canonicalization rules. The array of rules is organized as a list.
1625 * See the definition of krb5_name_canon_rule.
1627 * @param context A Kerberos context.
1628 * @param rules Output location for array of rules.
1630 KRB5_LIB_FUNCTION krb5_error_code
1631 _krb5_get_name_canon_rules(krb5_context context
, krb5_name_canon_rule
*rules
)
1633 krb5_error_code ret
;
1634 char **values
= NULL
;
1638 ret
= krb5_get_default_realm(context
, &realm
);
1639 if (ret
== KRB5_CONFIG_NODEFREALM
|| ret
== KRB5_CONFIG_CANTOPEN
)
1645 values
= krb5_config_get_strings(context
, NULL
,
1648 "name_canon_rules", NULL
);
1652 values
= krb5_config_get_strings(context
, NULL
,
1654 "name_canon_rules", NULL
);
1657 if (!values
|| !values
[0]) {
1658 /* Default rule: do the dreaded getaddrinfo()/getnameinfo() dance */
1659 if ((*rules
= calloc(1, sizeof (**rules
))) == NULL
)
1660 return krb5_enomem(context
);
1661 (*rules
)->type
= KRB5_NCRT_NSS
;
1665 ret
= parse_name_canon_rules(context
, values
, rules
);
1666 krb5_config_free_strings(values
);
1672 krb5_name_canon_rule r
;
1673 for (k
= 0, r
= *rules
; r
; r
= r
->next
, k
++) {
1674 _krb5_debug(context
, 5,
1675 "Name canon rule %ld type=%d, options=%x, mindots=%d, "
1676 "domain=%s, realm=%s",
1677 (unsigned long)k
, r
->type
, r
->options
, r
->mindots
,
1678 r
->domain
? r
->domain
: "<none>",
1679 r
->realm
? r
->realm
: "<none>"
1684 if ((*rules
)[0].type
!= KRB5_NCRT_BOGUS
)
1685 return 0; /* success! */
1688 /* fall through to return default rule */
1689 _krb5_debug(context
, 5, "All name canon rules are bogus!");
1694 static krb5_error_code
1695 get_host_realm(krb5_context context
, const char *hostname
, char **realm
)
1697 krb5_error_code ret
;
1698 char **hrealms
= NULL
;
1701 if ((ret
= krb5_get_host_realm(context
, hostname
, &hrealms
)))
1704 return KRB5_ERR_HOST_REALM_UNKNOWN
; /* krb5_set_error() already done */
1706 krb5_free_host_realm(context
, hrealms
);
1707 return KRB5_ERR_HOST_REALM_UNKNOWN
; /* krb5_set_error() already done */
1709 *realm
= strdup(hrealms
[0]);
1710 krb5_free_host_realm(context
, hrealms
);
1715 * Apply a name canonicalization rule to a principal.
1717 * @param context Kerberos context
1718 * @param rule name canon rule
1719 * @param in_princ principal name
1720 * @param out_print resulting principal name
1721 * @param rule_opts options for this rule
1723 KRB5_LIB_FUNCTION krb5_error_code
1724 _krb5_apply_name_canon_rule(krb5_context context
, krb5_name_canon_rule rule
,
1725 krb5_const_principal in_princ
, krb5_principal
*out_princ
,
1726 krb5_name_canon_rule_options
*rule_opts
)
1728 krb5_error_code ret
;
1729 unsigned int ndots
= 0;
1731 const char *sname
= NULL
;
1732 const char *hostname
= NULL
;
1733 char *new_hostname
= NULL
;
1736 heim_assert(in_princ
->name
.name_type
== KRB5_NT_SRV_HST_NEEDS_CANON
,
1737 "internal error: principal does not need canon");
1742 if (rule
->type
== KRB5_NCRT_BOGUS
)
1743 return 0; /* rule doesn't apply */
1745 sname
= krb5_principal_get_comp_string(context
, in_princ
, 0);
1746 hostname
= krb5_principal_get_comp_string(context
, in_princ
, 1);
1748 _krb5_debug(context
, 5, "Applying a name rule (type %d) to %s", rule
->type
,
1751 *rule_opts
= rule
->options
;
1754 switch (rule
->type
) {
1755 case KRB5_NCRT_AS_IS
:
1756 if (rule
->mindots
> 0) {
1757 for (cp
= strchr(hostname
, '.'); cp
&& *cp
; cp
= strchr(cp
, '.'))
1759 if (ndots
< rule
->mindots
)
1760 goto out
; /* *out_princ == NULL; rule doesn't apply */
1763 cp
= strstr(hostname
, rule
->domain
);
1765 goto out
; /* *out_princ == NULL; rule doesn't apply */
1766 if (cp
!= hostname
&& cp
[-1] != '.')
1769 /* Rule matches, copy princ with hostname as-is, with normal magic */
1770 realm
= rule
->realm
;
1772 ret
= get_host_realm(context
, hostname
, &realm
);
1776 _krb5_debug(context
, 5, "As-is rule building a princ with realm=%s, "
1777 "sname=%s, and hostname=%s", realm
, sname
, hostname
);
1778 ret
= krb5_build_principal(context
, out_princ
,
1780 realm
, sname
, hostname
,
1785 case KRB5_NCRT_QUALIFY
:
1787 * Note that we should never get these rules even if specified
1788 * in krb5.conf. See rule parser.
1790 heim_assert(rule
->domain
!= NULL
,
1791 "missing domain for qualify name canon rule");
1792 cp
= strchr(hostname
, '.');
1793 if (cp
&& (cp
= strstr(cp
, rule
->domain
))) {
1794 new_hostname
= strdup(hostname
);
1795 if (new_hostname
== NULL
) {
1796 ret
= krb5_enomem(context
);
1803 asprintf(&new_hostname
, "%s%s%s", hostname
,
1804 rule
->domain
[0] != '.' ? "." : "",
1806 if (new_hostname
== NULL
) {
1807 ret
= krb5_enomem(context
);
1811 realm
= rule
->realm
;
1813 ret
= get_host_realm(context
, new_hostname
, &realm
);
1817 _krb5_debug(context
, 5, "Building a princ with realm=%s, sname=%s, "
1818 "and hostname=%s", realm
, sname
, new_hostname
);
1819 ret
= krb5_build_principal(context
, out_princ
,
1820 strlen(realm
), realm
,
1821 sname
, new_hostname
, (char *)0);
1827 _krb5_debug(context
, 5, "Using name service lookups (without "
1828 "reverse lookups)");
1829 ret
= krb5_sname_to_principal_old(context
, rule
->realm
,
1833 if (rule
->next
!= NULL
&&
1834 (ret
== KRB5_ERR_BAD_HOSTNAME
||
1835 ret
== KRB5_ERR_HOST_REALM_UNKNOWN
))
1837 * Bad hostname / realm unknown -> rule inapplicable if
1838 * there's more rules. If it's the last rule then we want
1839 * to return all errors from krb5_sname_to_principal_old()
1847 /* Can't happen, but we need this to shut up gcc */
1852 if (!ret
&& *out_princ
) {
1853 krb5_error_code ret2
;
1856 ret2
= krb5_unparse_name(context
, *out_princ
, &unparsed
);
1858 _krb5_debug(context
, 5, "Couldn't unparse resulting princ! (%d)",
1861 _krb5_debug(context
, 5, "Name canon rule application yields this "
1862 "unparsed princ: %s", unparsed
);
1866 _krb5_debug(context
, 5, "Name canon rule did not apply");
1868 _krb5_debug(context
, 5, "Name canon rule application error: %d", ret
);
1872 if (realm
!= rule
->realm
)
1875 (*out_princ
)->name
.name_type
= KRB5_NT_SRV_HST
;
1877 krb5_set_error_message(context
, ret
,
1878 N_("Name canon rule application failed", ""));
1883 * Free name canonicalization rules
1885 KRB5_LIB_FUNCTION
void
1886 _krb5_free_name_canon_rules(krb5_context context
, krb5_name_canon_rule rules
)
1888 krb5_name_canon_rule r
;
1890 for (r
= rules
; r
; r
= r
->next
) {
1899 struct krb5_name_canon_iterator
{
1900 krb5_name_canon_rule rules
;
1901 krb5_name_canon_rule rule
;
1902 krb5_const_principal in_princ
;
1903 krb5_principal tmp_princ
;
1910 * Initialize name canonicalization iterator.
1912 * @param context Kerberos context
1913 * @param in_princ principal name to be canonicalized OR
1914 * @param in_creds credentials whose server is to be canonicalized
1915 * @param iter output iterator object
1917 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1918 krb5_name_canon_iterator_start(krb5_context context
,
1919 krb5_const_principal in_princ
,
1920 krb5_creds
*in_creds
,
1921 krb5_name_canon_iterator
*iter
)
1923 krb5_error_code ret
;
1924 krb5_name_canon_iterator state
;
1925 krb5_const_principal princ
;
1929 state
= calloc(1, sizeof (*state
));
1931 return krb5_enomem(context
);
1932 princ
= in_princ
? in_princ
: in_creds
->server
;
1934 if (princ_type(princ
) != KRB5_NT_SRV_HST_NEEDS_CANON
) {
1936 * Name needs no canon -> trivial iterator; we still want an
1937 * iterator just so as to keep callers simple.
1939 state
->is_trivial
= 1;
1940 state
->creds
= in_creds
;
1942 ret
= _krb5_get_name_canon_rules(context
, &state
->rules
);
1945 state
->rule
= state
->rules
;
1948 state
->in_princ
= princ
;
1950 if (!state
->is_trivial
) {
1951 ret
= krb5_copy_creds(context
, in_creds
, &state
->creds
);
1954 state
->tmp_princ
= state
->creds
->server
; /* so we don't leak */
1961 krb5_free_name_canon_iterator(context
, state
);
1962 return krb5_enomem(context
);
1966 * Helper for name canon iteration.
1968 static krb5_error_code
1969 krb5_name_canon_iterate(krb5_context context
,
1970 krb5_name_canon_iterator
*iter
,
1971 krb5_name_canon_rule_options
*rule_opts
)
1973 krb5_error_code ret
;
1974 krb5_name_canon_iterator state
= *iter
;
1982 krb5_free_name_canon_iterator(context
, state
);
1987 if (state
->is_trivial
&& !state
->done
) {
1993 krb5_free_principal(context
, state
->tmp_princ
);
1994 ret
= _krb5_apply_name_canon_rule(context
, state
->rule
,
1995 state
->in_princ
, &state
->tmp_princ
, rule_opts
);
1998 state
->rule
= state
->rule
->next
;
1999 } while (state
->rule
!= NULL
&& state
->tmp_princ
== NULL
);
2001 if (state
->tmp_princ
== NULL
) {
2002 krb5_free_name_canon_iterator(context
, state
);
2007 state
->creds
->server
= state
->tmp_princ
;
2008 if (state
->rule
== NULL
)
2014 * Iteratively apply name canon rules, outputing a principal and rule
2015 * options each time. Iteration completes when the @iter is NULL on
2016 * return or when an error is returned. Callers must free the iterator
2017 * if they abandon it mid-way.
2019 * @param context Kerberos context
2020 * @param iter name canon rule iterator (input/output)
2021 * @param try_princ output principal name
2022 * @param rule_opts output rule options
2024 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2025 krb5_name_canon_iterate_princ(krb5_context context
,
2026 krb5_name_canon_iterator
*iter
,
2027 krb5_principal
*try_princ
,
2028 krb5_name_canon_rule_options
*rule_opts
)
2030 krb5_error_code ret
;
2033 ret
= krb5_name_canon_iterate(context
, iter
, rule_opts
);
2035 *try_princ
= (*iter
)->tmp_princ
;
2040 * Iteratively apply name canon rules, outputing a krb5_creds and rule
2041 * options each time. Iteration completes when the @iter is NULL on
2042 * return or when an error is returned. Callers must free the iterator
2043 * if they abandon it mid-way.
2045 * @param context Kerberos context
2046 * @param iter name canon rule iterator
2047 * @param try_creds output krb5_creds
2048 * @param rule_opts output rule options
2050 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2051 krb5_name_canon_iterate_creds(krb5_context context
,
2052 krb5_name_canon_iterator
*iter
,
2053 krb5_creds
**try_creds
,
2054 krb5_name_canon_rule_options
*rule_opts
)
2056 krb5_error_code ret
;
2059 ret
= krb5_name_canon_iterate(context
, iter
, rule_opts
);
2061 *try_creds
= (*iter
)->creds
;
2066 * Free a name canonicalization rule iterator.
2068 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
2069 krb5_free_name_canon_iterator(krb5_context context
,
2070 krb5_name_canon_iterator iter
)
2074 if (!iter
->is_trivial
) {
2076 krb5_free_creds(context
, iter
->creds
);
2077 iter
->tmp_princ
= NULL
;
2079 if (iter
->tmp_princ
)
2080 krb5_free_principal(context
, iter
->tmp_princ
);
2081 _krb5_free_name_canon_rules(context
, iter
->rules
);