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)
66 static krb5_error_code
67 set_default_princ_type(krb5_principal p
, NAME_TYPE defnt
)
69 if (princ_num_comp(p
) > 1 && strcmp(princ_ncomp(p
, 0), KRB5_TGS_NAME
) == 0)
70 princ_type(p
) = KRB5_NT_SRV_INST
;
71 else if (princ_num_comp(p
) > 1 && strcmp(princ_ncomp(p
, 0), "host") == 0)
72 princ_type(p
) = KRB5_NT_SRV_HST
;
73 else if (princ_num_comp(p
) == 2 &&
74 strcmp(princ_ncomp(p
, 0), KRB5_WELLKNOWN_NAME
) == 0)
75 princ_type(p
) = KRB5_NT_WELLKNOWN
;
76 else if (princ_num_comp(p
) == 1 && strchr(princ_ncomp(p
, 0), '@') != NULL
)
77 princ_type(p
) = KRB5_NT_SMTP_NAME
;
79 princ_type(p
) = defnt
;
83 static krb5_error_code
append_component(krb5_context
, krb5_principal
,
84 const char *, size_t);
87 * Frees a Kerberos principal allocated by the library with
88 * krb5_parse_name(), krb5_make_principal() or any other related
89 * principal functions.
91 * @param context A Kerberos context.
92 * @param p a principal to free.
94 * @return An krb5 error code, see krb5_get_error_message().
96 * @ingroup krb5_principal
99 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
100 krb5_free_principal(krb5_context context
,
110 * Set the type of the principal
112 * @param context A Kerberos context.
113 * @param principal principal to set the type for
114 * @param type the new type
116 * @return An krb5 error code, see krb5_get_error_message().
118 * @ingroup krb5_principal
121 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
122 krb5_principal_set_type(krb5_context context
,
123 krb5_principal principal
,
126 princ_type(principal
) = type
;
130 * Get the type of the principal
132 * @param context A Kerberos context.
133 * @param principal principal to get the type for
135 * @return the type of principal
137 * @ingroup krb5_principal
140 KRB5_LIB_FUNCTION
int KRB5_LIB_CALL
141 krb5_principal_get_type(krb5_context context
,
142 krb5_const_principal principal
)
144 return princ_type(principal
);
148 * Get the realm of the principal
150 * @param context A Kerberos context.
151 * @param principal principal to get the realm for
153 * @return realm of the principal, don't free or use after krb5_principal is freed
155 * @ingroup krb5_principal
158 KRB5_LIB_FUNCTION
const char* KRB5_LIB_CALL
159 krb5_principal_get_realm(krb5_context context
,
160 krb5_const_principal principal
)
162 return princ_realm(principal
);
165 KRB5_LIB_FUNCTION
const char* KRB5_LIB_CALL
166 krb5_principal_get_comp_string(krb5_context context
,
167 krb5_const_principal principal
,
168 unsigned int component
)
170 if(component
>= princ_num_comp(principal
))
172 return princ_ncomp(principal
, component
);
176 * Get number of component is principal.
178 * @param context Kerberos 5 context
179 * @param principal principal to query
181 * @return number of components in string
183 * @ingroup krb5_principal
186 KRB5_LIB_FUNCTION
unsigned int KRB5_LIB_CALL
187 krb5_principal_get_num_comp(krb5_context context
,
188 krb5_const_principal principal
)
190 return princ_num_comp(principal
);
194 * Parse a name into a krb5_principal structure, flags controls the behavior.
196 * @param context Kerberos 5 context
197 * @param name name to parse into a Kerberos principal
198 * @param flags flags to control the behavior
199 * @param principal returned principal, free with krb5_free_principal().
201 * @return An krb5 error code, see krb5_get_error_message().
203 * @ingroup krb5_principal
206 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
207 krb5_parse_name_flags(krb5_context context
,
210 krb5_principal
*principal
)
213 heim_general_string
*comp
;
214 heim_general_string realm
= NULL
;
226 int no_realm
= flags
& KRB5_PRINCIPAL_PARSE_NO_REALM
;
227 int require_realm
= flags
& KRB5_PRINCIPAL_PARSE_REQUIRE_REALM
;
228 int enterprise
= flags
& KRB5_PRINCIPAL_PARSE_ENTERPRISE
;
229 int ignore_realm
= flags
& KRB5_PRINCIPAL_PARSE_IGNORE_REALM
;
230 int no_def_realm
= flags
& KRB5_PRINCIPAL_PARSE_NO_DEF_REALM
;
234 if (no_realm
&& require_realm
) {
235 krb5_set_error_message(context
, KRB5_ERR_NO_SERVICE
,
236 N_("Can't require both realm and "
237 "no realm at the same time", ""));
238 return KRB5_ERR_NO_SERVICE
;
241 /* count number of component,
242 * enterprise names only have one component
246 for (p
= name
; *p
; p
++) {
249 krb5_set_error_message(context
, KRB5_PARSE_MALFORMED
,
250 N_("trailing \\ in principal name", ""));
251 return KRB5_PARSE_MALFORMED
;
254 } else if (*p
== '/')
260 comp
= calloc(ncomp
, sizeof(*comp
));
262 return krb5_enomem(context
);
265 p
= start
= q
= s
= strdup(name
);
268 return krb5_enomem(context
);
282 else if (c
== '\0') {
283 ret
= KRB5_PARSE_MALFORMED
;
284 krb5_set_error_message(context
, ret
,
285 N_("trailing \\ in principal name", ""));
288 } else if (enterprise
&& first_at
) {
291 } else if ((c
== '/' && !enterprise
) || c
== '@') {
293 ret
= KRB5_PARSE_MALFORMED
;
294 krb5_set_error_message(context
, ret
,
295 N_("part after realm in principal name", ""));
298 comp
[n
] = malloc(q
- start
+ 1);
299 if (comp
[n
] == NULL
) {
300 ret
= krb5_enomem(context
);
303 memcpy(comp
[n
], start
, q
- start
);
304 comp
[n
][q
- start
] = 0;
312 if (got_realm
&& (c
== '/' || c
== '\0')) {
313 ret
= KRB5_PARSE_MALFORMED
;
314 krb5_set_error_message(context
, ret
,
315 N_("part after realm in principal name", ""));
322 ret
= KRB5_PARSE_MALFORMED
;
323 krb5_set_error_message(context
, ret
,
324 N_("realm found in 'short' principal "
325 "expected to be without one", ""));
329 realm
= malloc(q
- start
+ 1);
331 ret
= krb5_enomem(context
);
334 memcpy(realm
, start
, q
- start
);
335 realm
[q
- start
] = 0;
339 ret
= KRB5_PARSE_MALFORMED
;
340 krb5_set_error_message(context
, ret
,
341 N_("realm NOT found in principal "
342 "expected to be with one", ""));
344 } else if (no_realm
|| no_def_realm
) {
347 ret
= krb5_get_default_realm(context
, &realm
);
352 comp
[n
] = malloc(q
- start
+ 1);
353 if (comp
[n
] == NULL
) {
354 ret
= krb5_enomem(context
);
357 memcpy(comp
[n
], start
, q
- start
);
358 comp
[n
][q
- start
] = 0;
361 *principal
= calloc(1, sizeof(**principal
));
362 if (*principal
== NULL
) {
363 ret
= krb5_enomem(context
);
366 (*principal
)->name
.name_string
.val
= comp
;
367 princ_num_comp(*principal
) = n
;
368 (*principal
)->realm
= realm
;
370 princ_type(*principal
) = KRB5_NT_ENTERPRISE_PRINCIPAL
;
372 set_default_princ_type(*principal
, KRB5_NT_PRINCIPAL
);
380 krb5_free_default_realm(context
, realm
);
386 * Parse a name into a krb5_principal structure
388 * @param context Kerberos 5 context
389 * @param name name to parse into a Kerberos principal
390 * @param principal returned principal, free with krb5_free_principal().
392 * @return An krb5 error code, see krb5_get_error_message().
394 * @ingroup krb5_principal
397 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
398 krb5_parse_name(krb5_context context
,
400 krb5_principal
*principal
)
402 return krb5_parse_name_flags(context
, name
, 0, principal
);
405 static const char quotable_chars
[] = " \n\t\b\\/@";
406 static const char replace_chars
[] = " ntb\\/@";
408 #define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0);
411 quote_string(const char *s
, char *out
, size_t idx
, size_t len
, int display
)
414 for(p
= s
; *p
&& idx
< len
; p
++){
415 q
= strchr(quotable_chars
, *p
);
417 add_char(out
, idx
, len
, replace_chars
[q
- quotable_chars
]);
419 add_char(out
, idx
, len
, '\\');
420 add_char(out
, idx
, len
, replace_chars
[q
- quotable_chars
]);
422 add_char(out
, idx
, len
, *p
);
430 static krb5_error_code
431 unparse_name_fixed(krb5_context context
,
432 krb5_const_principal principal
,
439 int short_form
= (flags
& KRB5_PRINCIPAL_UNPARSE_SHORT
) != 0;
440 int no_realm
= (flags
& KRB5_PRINCIPAL_UNPARSE_NO_REALM
) != 0;
441 int display
= (flags
& KRB5_PRINCIPAL_UNPARSE_DISPLAY
) != 0;
443 if (!no_realm
&& princ_realm(principal
) == NULL
) {
444 krb5_set_error_message(context
, ERANGE
,
445 N_("Realm missing from principal, "
446 "can't unparse", ""));
450 for(i
= 0; i
< princ_num_comp(principal
); i
++){
452 add_char(name
, idx
, len
, '/');
453 idx
= quote_string(princ_ncomp(principal
, i
), name
, idx
, len
, display
);
455 krb5_set_error_message(context
, ERANGE
,
456 N_("Out of space printing principal", ""));
460 /* add realm if different from default realm */
461 if(short_form
&& !no_realm
) {
464 ret
= krb5_get_default_realm(context
, &r
);
467 if(strcmp(princ_realm(principal
), r
) != 0)
469 krb5_free_default_realm(context
, r
);
471 if(!short_form
&& !no_realm
) {
472 add_char(name
, idx
, len
, '@');
473 idx
= quote_string(princ_realm(principal
), name
, idx
, len
, display
);
475 krb5_set_error_message(context
, ERANGE
,
476 N_("Out of space printing "
477 "realm of principal", ""));
485 * Unparse the principal name to a fixed buffer
487 * @param context A Kerberos context.
488 * @param principal principal to unparse
489 * @param name buffer to write name to
490 * @param len length of buffer
492 * @return An krb5 error code, see krb5_get_error_message().
494 * @ingroup krb5_principal
497 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
498 krb5_unparse_name_fixed(krb5_context context
,
499 krb5_const_principal principal
,
503 return unparse_name_fixed(context
, principal
, name
, len
, 0);
507 * Unparse the principal name to a fixed buffer. The realm is skipped
508 * if its a default realm.
510 * @param context A Kerberos context.
511 * @param principal principal to unparse
512 * @param name buffer to write name to
513 * @param len length of buffer
515 * @return An krb5 error code, see krb5_get_error_message().
517 * @ingroup krb5_principal
520 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
521 krb5_unparse_name_fixed_short(krb5_context context
,
522 krb5_const_principal principal
,
526 return unparse_name_fixed(context
, principal
, name
, len
,
527 KRB5_PRINCIPAL_UNPARSE_SHORT
);
531 * Unparse the principal name with unparse flags to a fixed buffer.
533 * @param context A Kerberos context.
534 * @param principal principal to unparse
535 * @param flags unparse flags
536 * @param name buffer to write name to
537 * @param len length of buffer
539 * @return An krb5 error code, see krb5_get_error_message().
541 * @ingroup krb5_principal
544 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
545 krb5_unparse_name_fixed_flags(krb5_context context
,
546 krb5_const_principal principal
,
551 return unparse_name_fixed(context
, principal
, name
, len
, flags
);
554 static krb5_error_code
555 unparse_name(krb5_context context
,
556 krb5_const_principal principal
,
560 size_t len
= 0, plen
;
564 if (princ_realm(principal
)) {
565 plen
= strlen(princ_realm(principal
));
567 if(strcspn(princ_realm(principal
), quotable_chars
) == plen
)
573 for(i
= 0; i
< princ_num_comp(principal
); i
++){
574 plen
= strlen(princ_ncomp(principal
, i
));
575 if(strcspn(princ_ncomp(principal
, i
), quotable_chars
) == plen
)
584 return krb5_enomem(context
);
585 ret
= unparse_name_fixed(context
, principal
, *name
, len
, flags
);
594 * Unparse the Kerberos name into a string
596 * @param context Kerberos 5 context
597 * @param principal principal to query
598 * @param name resulting string, free with krb5_xfree()
600 * @return An krb5 error code, see krb5_get_error_message().
602 * @ingroup krb5_principal
605 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
606 krb5_unparse_name(krb5_context context
,
607 krb5_const_principal principal
,
610 return unparse_name(context
, principal
, name
, 0);
614 * Unparse the Kerberos name into a string
616 * @param context Kerberos 5 context
617 * @param principal principal to query
618 * @param flags flag to determine the behavior
619 * @param name resulting string, 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_flags(krb5_context context
,
628 krb5_const_principal principal
,
632 return unparse_name(context
, principal
, name
, flags
);
636 * Unparse the principal name to a allocated buffer. The realm is
637 * skipped if its a default realm.
639 * @param context A Kerberos context.
640 * @param principal principal to unparse
641 * @param name returned buffer, free with krb5_xfree()
643 * @return An krb5 error code, see krb5_get_error_message().
645 * @ingroup krb5_principal
648 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
649 krb5_unparse_name_short(krb5_context context
,
650 krb5_const_principal principal
,
653 return unparse_name(context
, principal
, name
, KRB5_PRINCIPAL_UNPARSE_SHORT
);
657 * Set a new realm for a principal, and as a side-effect free the
660 * @param context A Kerberos context.
661 * @param principal principal set the realm for
662 * @param realm the new realm to set
664 * @return An krb5 error code, see krb5_get_error_message().
666 * @ingroup krb5_principal
669 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
670 krb5_principal_set_realm(krb5_context context
,
671 krb5_principal principal
,
672 krb5_const_realm realm
)
674 if (princ_realm(principal
))
675 free(princ_realm(principal
));
678 princ_realm(principal
) = NULL
;
679 else if ((princ_realm(principal
) = strdup(realm
)) == NULL
)
680 return krb5_enomem(context
);
684 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
685 krb5_principal_set_comp_string(krb5_context context
,
686 krb5_principal principal
,
688 const char *component
)
693 for (i
= princ_num_comp(principal
); i
<= k
; i
++)
694 append_component(context
, principal
, "", 0);
695 s
= strdup(component
);
697 return krb5_enomem(context
);
698 free(princ_ncomp(principal
, k
));
699 princ_ncomp(principal
, k
) = s
;
703 #ifndef HEIMDAL_SMALLER
705 * Build a principal using vararg style building
707 * @param context A Kerberos context.
708 * @param principal returned principal
709 * @param rlen length of realm
710 * @param realm realm name
711 * @param ... a list of components ended with NULL.
713 * @return An krb5 error code, see krb5_get_error_message().
715 * @ingroup krb5_principal
718 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
719 krb5_build_principal(krb5_context context
,
720 krb5_principal
*principal
,
722 krb5_const_realm realm
,
728 ret
= krb5_build_principal_va(context
, principal
, rlen
, realm
, ap
);
735 * Build a principal using vararg style building
737 * @param context A Kerberos context.
738 * @param principal returned principal
739 * @param realm realm name
740 * @param ... a list of components ended with NULL.
742 * @return An krb5 error code, see krb5_get_error_message().
744 * @ingroup krb5_principal
747 /* coverity[+alloc : arg-*1] */
748 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
749 krb5_make_principal(krb5_context context
,
750 krb5_principal
*principal
,
751 krb5_const_realm realm
,
758 ret
= krb5_get_default_realm(context
, &r
);
764 ret
= krb5_build_principal_va(context
, principal
, strlen(realm
), realm
, ap
);
767 krb5_free_default_realm(context
, r
);
771 static krb5_error_code
772 append_component(krb5_context context
, krb5_principal p
,
776 heim_general_string
*tmp
;
777 size_t len
= princ_num_comp(p
);
779 tmp
= realloc(princ_comp(p
), (len
+ 1) * sizeof(*tmp
));
781 return krb5_enomem(context
);
783 princ_ncomp(p
, len
) = malloc(comp_len
+ 1);
784 if (princ_ncomp(p
, len
) == NULL
)
785 return krb5_enomem(context
);
786 memcpy (princ_ncomp(p
, len
), comp
, comp_len
);
787 princ_ncomp(p
, len
)[comp_len
] = '\0';
792 static krb5_error_code
793 va_ext_princ(krb5_context context
, krb5_principal p
, va_list ap
)
795 krb5_error_code ret
= 0;
801 if ((len
= va_arg(ap
, int)) == 0)
803 s
= va_arg(ap
, const char*);
804 if ((ret
= append_component(context
, p
, s
, len
)) != 0)
810 static krb5_error_code
811 va_princ(krb5_context context
, krb5_principal p
, va_list ap
)
813 krb5_error_code ret
= 0;
818 if ((s
= va_arg(ap
, const char*)) == NULL
)
820 if ((ret
= append_component(context
, p
, s
, strlen(s
))) != 0)
826 static krb5_error_code
827 build_principal(krb5_context context
,
828 krb5_principal
*principal
,
830 krb5_const_realm realm
,
831 krb5_error_code (*func
)(krb5_context
, krb5_principal
, va_list),
838 p
= calloc(1, sizeof(*p
));
840 return krb5_enomem(context
);
842 princ_realm(p
) = strdup(realm
);
843 if (p
->realm
== NULL
) {
845 return krb5_enomem(context
);
848 ret
= func(context
, p
, ap
);
851 set_default_princ_type(p
, KRB5_NT_PRINCIPAL
);
853 krb5_free_principal(context
, p
);
857 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
858 krb5_build_principal_va(krb5_context context
,
859 krb5_principal
*principal
,
861 krb5_const_realm realm
,
864 return build_principal(context
, principal
, rlen
, realm
, va_princ
, ap
);
867 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
868 krb5_build_principal_va_ext(krb5_context context
,
869 krb5_principal
*principal
,
871 krb5_const_realm realm
,
874 return build_principal(context
, principal
, rlen
, realm
, va_ext_princ
, ap
);
878 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
879 krb5_build_principal_ext(krb5_context context
,
880 krb5_principal
*principal
,
882 krb5_const_realm realm
,
888 ret
= krb5_build_principal_va_ext(context
, principal
, rlen
, realm
, ap
);
896 * @param context A Kerberos context.
897 * @param inprinc principal to copy
898 * @param outprinc copied principal, free with krb5_free_principal()
900 * @return An krb5 error code, see krb5_get_error_message().
902 * @ingroup krb5_principal
906 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
907 krb5_copy_principal(krb5_context context
,
908 krb5_const_principal inprinc
,
909 krb5_principal
*outprinc
)
911 krb5_principal p
= malloc(sizeof(*p
));
913 return krb5_enomem(context
);
914 if(copy_Principal(inprinc
, p
)) {
916 return krb5_enomem(context
);
923 * Return TRUE iff princ1 == princ2 (without considering the realm)
925 * @param context Kerberos 5 context
926 * @param princ1 first principal to compare
927 * @param princ2 second principal to compare
929 * @return non zero if equal, 0 if not
931 * @ingroup krb5_principal
932 * @see krb5_principal_compare()
933 * @see krb5_realm_compare()
936 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
937 krb5_principal_compare_any_realm(krb5_context context
,
938 krb5_const_principal princ1
,
939 krb5_const_principal princ2
)
942 if(princ_num_comp(princ1
) != princ_num_comp(princ2
))
944 for(i
= 0; i
< princ_num_comp(princ1
); i
++){
945 if(strcmp(princ_ncomp(princ1
, i
), princ_ncomp(princ2
, i
)) != 0)
951 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
952 _krb5_principal_compare_PrincipalName(krb5_context context
,
953 krb5_const_principal princ1
,
954 PrincipalName
*princ2
)
957 if (princ_num_comp(princ1
) != princ2
->name_string
.len
)
959 for(i
= 0; i
< princ_num_comp(princ1
); i
++){
960 if(strcmp(princ_ncomp(princ1
, i
), princ2
->name_string
.val
[i
]) != 0)
968 * Compares the two principals, including realm of the principals and returns
969 * TRUE if they are the same and FALSE if not.
971 * @param context Kerberos 5 context
972 * @param princ1 first principal to compare
973 * @param princ2 second principal to compare
975 * @ingroup krb5_principal
976 * @see krb5_principal_compare_any_realm()
977 * @see krb5_realm_compare()
981 * return TRUE iff princ1 == princ2
984 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
985 krb5_principal_compare(krb5_context context
,
986 krb5_const_principal princ1
,
987 krb5_const_principal princ2
)
989 if (!krb5_realm_compare(context
, princ1
, princ2
))
991 return krb5_principal_compare_any_realm(context
, princ1
, princ2
);
995 * return TRUE iff realm(princ1) == realm(princ2)
997 * @param context Kerberos 5 context
998 * @param princ1 first principal to compare
999 * @param princ2 second principal to compare
1001 * @ingroup krb5_principal
1002 * @see krb5_principal_compare_any_realm()
1003 * @see krb5_principal_compare()
1006 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1007 krb5_realm_compare(krb5_context context
,
1008 krb5_const_principal princ1
,
1009 krb5_const_principal princ2
)
1011 return strcmp(princ_realm(princ1
), princ_realm(princ2
)) == 0;
1015 * return TRUE iff princ matches pattern
1017 * @ingroup krb5_principal
1020 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1021 krb5_principal_match(krb5_context context
,
1022 krb5_const_principal princ
,
1023 krb5_const_principal pattern
)
1026 if(princ_num_comp(princ
) != princ_num_comp(pattern
))
1028 if(fnmatch(princ_realm(pattern
), princ_realm(princ
), 0) != 0)
1030 for(i
= 0; i
< princ_num_comp(princ
); i
++){
1031 if(fnmatch(princ_ncomp(pattern
, i
), princ_ncomp(princ
, i
), 0) != 0)
1038 * This is the original krb5_sname_to_principal(), renamed to be a
1039 * helper of the new one.
1041 static krb5_error_code
1042 krb5_sname_to_principal_old(krb5_context context
,
1044 const char *hostname
,
1047 krb5_principal
*ret_princ
)
1049 krb5_error_code ret
;
1050 char localhost
[MAXHOSTNAMELEN
];
1051 char **realms
= NULL
, *host
= NULL
;
1053 if(type
!= KRB5_NT_SRV_HST
&& type
!= KRB5_NT_UNKNOWN
) {
1054 krb5_set_error_message(context
, KRB5_SNAME_UNSUPP_NAMETYPE
,
1055 N_("unsupported name type %d", ""),
1057 return KRB5_SNAME_UNSUPP_NAMETYPE
;
1059 if(hostname
== NULL
) {
1060 ret
= gethostname(localhost
, sizeof(localhost
) - 1);
1063 krb5_set_error_message(context
, ret
,
1064 N_("Failed to get local hostname", ""));
1067 localhost
[sizeof(localhost
) - 1] = '\0';
1068 hostname
= localhost
;
1072 if(type
== KRB5_NT_SRV_HST
) {
1074 ret
= krb5_expand_hostname(context
, hostname
, &host
);
1076 ret
= krb5_expand_hostname_realms(context
, hostname
,
1084 } else if (!realm
) {
1085 ret
= krb5_get_host_realm(context
, hostname
, &realms
);
1091 ret
= krb5_make_principal(context
, ret_princ
, realm
, sname
,
1096 krb5_free_host_realm(context
, realms
);
1100 static const struct {
1104 { "UNKNOWN", KRB5_NT_UNKNOWN
},
1105 { "PRINCIPAL", KRB5_NT_PRINCIPAL
},
1106 { "SRV_INST", KRB5_NT_SRV_INST
},
1107 { "SRV_HST", KRB5_NT_SRV_HST
},
1108 { "SRV_XHST", KRB5_NT_SRV_XHST
},
1109 { "UID", KRB5_NT_UID
},
1110 { "X500_PRINCIPAL", KRB5_NT_X500_PRINCIPAL
},
1111 { "SMTP_NAME", KRB5_NT_SMTP_NAME
},
1112 { "ENTERPRISE_PRINCIPAL", KRB5_NT_ENTERPRISE_PRINCIPAL
},
1113 { "WELLKNOWN", KRB5_NT_WELLKNOWN
},
1114 { "SRV_HST_DOMAIN", KRB5_NT_SRV_HST_DOMAIN
},
1115 { "ENT_PRINCIPAL_AND_ID", KRB5_NT_ENT_PRINCIPAL_AND_ID
},
1116 { "MS_PRINCIPAL", KRB5_NT_MS_PRINCIPAL
},
1117 { "MS_PRINCIPAL_AND_ID", KRB5_NT_MS_PRINCIPAL_AND_ID
},
1118 { "SRV_HST_NEEDS_CANON", KRB5_NT_SRV_HST_NEEDS_CANON
},
1123 * Parse nametype string and return a nametype integer
1125 * @ingroup krb5_principal
1128 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1129 krb5_parse_nametype(krb5_context context
, const char *str
, int32_t *nametype
)
1133 for(i
= 0; nametypes
[i
].type
; i
++) {
1134 if (strcasecmp(nametypes
[i
].type
, str
) == 0) {
1135 *nametype
= nametypes
[i
].value
;
1139 krb5_set_error_message(context
, KRB5_PARSE_MALFORMED
,
1140 N_("Failed to find name type %s", ""), str
);
1141 return KRB5_PARSE_MALFORMED
;
1145 * Returns true if name is Kerberos NULL name
1147 * @ingroup krb5_principal
1150 krb5_boolean KRB5_LIB_FUNCTION
1151 krb5_principal_is_null(krb5_context context
, krb5_const_principal principal
)
1153 if (principal
->name
.name_type
== KRB5_NT_WELLKNOWN
&&
1154 principal
->name
.name_string
.len
== 2 &&
1155 strcmp(principal
->name
.name_string
.val
[0], "WELLKNOWN") == 0 &&
1156 strcmp(principal
->name
.name_string
.val
[1], "NULL") == 0)
1161 const char _krb5_wellknown_lkdc
[] = "WELLKNOWN:COM.APPLE.LKDC";
1162 static const char lkdc_prefix
[] = "LKDC:";
1165 * Returns true if name is Kerberos an LKDC realm
1167 * @ingroup krb5_principal
1170 krb5_boolean KRB5_LIB_FUNCTION
1171 krb5_realm_is_lkdc(const char *realm
)
1174 return strncmp(realm
, lkdc_prefix
, sizeof(lkdc_prefix
)-1) == 0 ||
1175 strncmp(realm
, _krb5_wellknown_lkdc
, sizeof(_krb5_wellknown_lkdc
) - 1) == 0;
1179 * Returns true if name is Kerberos an LKDC realm
1181 * @ingroup krb5_principal
1184 krb5_boolean KRB5_LIB_FUNCTION
1185 krb5_principal_is_lkdc(krb5_context context
, krb5_const_principal principal
)
1187 return krb5_realm_is_lkdc(principal
->realm
);
1191 * Returns true if name is Kerberos an LKDC realm
1193 * @ingroup krb5_principal
1196 krb5_boolean KRB5_LIB_FUNCTION
1197 krb5_principal_is_pku2u(krb5_context context
, krb5_const_principal principal
)
1199 return strcmp(principal
->realm
, KRB5_PKU2U_REALM_NAME
) == 0;
1203 * Check if the cname part of the principal is a krbtgt principal
1205 * @ingroup krb5_principal
1208 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1209 krb5_principal_is_krbtgt(krb5_context context
, krb5_const_principal p
)
1211 return p
->name
.name_string
.len
== 2 &&
1212 strcmp(p
->name
.name_string
.val
[0], KRB5_TGS_NAME
) == 0;
1216 * Returns true iff name is an WELLKNOWN:ORG.H5L.HOSTBASED-SERVICE
1218 * @ingroup krb5_principal
1221 krb5_boolean KRB5_LIB_FUNCTION
1222 krb5_principal_is_gss_hostbased_service(krb5_context context
,
1223 krb5_const_principal principal
)
1225 if (principal
== NULL
)
1227 if (principal
->name
.name_string
.len
!= 2)
1229 if (strcmp(principal
->name
.name_string
.val
[1], KRB5_GSS_HOSTBASED_SERVICE_NAME
) != 0)
1235 * Check if the cname part of the principal is a initial or renewed krbtgt principal
1237 * @ingroup krb5_principal
1240 krb5_boolean KRB5_LIB_FUNCTION
1241 krb5_principal_is_root_krbtgt(krb5_context context
, krb5_const_principal p
)
1243 return p
->name
.name_string
.len
== 2 &&
1244 strcmp(p
->name
.name_string
.val
[0], KRB5_TGS_NAME
) == 0 &&
1245 strcmp(p
->name
.name_string
.val
[1], p
->realm
) == 0;
1249 tolower_ascii(int c
)
1251 if (c
>= 'A' || c
<= 'Z')
1252 return 'a' + (c
- 'A');
1256 typedef enum krb5_name_canon_rule_type
{
1257 KRB5_NCRT_BOGUS
= 0,
1261 } krb5_name_canon_rule_type
;
1264 #define MAXDOTS UINT8_MAX
1266 #define MAXDOTS (255U)
1269 #define MAXORDER UINT16_MAX
1271 #define MAXORDER (65535U)
1274 struct krb5_name_canon_rule_data
{
1275 krb5_name_canon_rule_type type
;
1276 krb5_name_canon_rule_options options
;
1277 uint8_t mindots
; /* match this many dots or more */
1278 uint8_t maxdots
; /* match no more than this many dots */
1279 uint16_t explicit_order
; /* given order */
1280 uint16_t order
; /* actual order */
1281 char *match_domain
; /* match this stem */
1282 char *match_realm
; /* match this realm */
1283 char *domain
; /* qualify with this domain */
1284 char *realm
; /* qualify with this realm */
1288 * Create a principal for the given service running on the given
1289 * hostname. If KRB5_NT_SRV_HST is used, the hostname is canonicalized
1290 * according the configured name canonicalization rules, with
1291 * canonicalization delayed in some cases. One rule involves DNS, which
1292 * is insecure unless DNSSEC is used, but we don't use DNSSEC-capable
1293 * resolver APIs here, so that if DNSSEC is used we wouldn't know it.
1295 * Canonicalization is immediate (not delayed) only when there is only
1296 * one canonicalization rule and that rule indicates that we should do a
1297 * host lookup by name (i.e., DNS).
1299 * @param context A Kerberos context.
1300 * @param hostname hostname to use
1301 * @param sname Service name to use
1302 * @param type name type of principal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN.
1303 * @param ret_princ return principal, free with krb5_free_principal().
1305 * @return An krb5 error code, see krb5_get_error_message().
1307 * @ingroup krb5_principal
1310 /* coverity[+alloc : arg-*4] */
1311 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1312 krb5_sname_to_principal(krb5_context context
,
1313 const char *hostname
,
1316 krb5_principal
*ret_princ
)
1318 char *realm
, *remote_host
;
1319 krb5_error_code ret
;
1321 char localname
[MAXHOSTNAMELEN
];
1325 if ((type
!= KRB5_NT_UNKNOWN
) &&
1326 (type
!= KRB5_NT_SRV_HST
))
1327 return KRB5_SNAME_UNSUPP_NAMETYPE
;
1329 /* if hostname is NULL, use local hostname */
1330 if (hostname
== NULL
) {
1331 if (gethostname(localname
, MAXHOSTNAMELEN
))
1333 hostname
= localname
;
1336 /* if sname is NULL, use "host" */
1340 remote_host
= strdup(hostname
);
1341 if (remote_host
== NULL
)
1342 return krb5_enomem(context
);
1344 if (type
== KRB5_NT_SRV_HST
) {
1345 krb5_name_canon_rule rules
;
1347 /* Lower-case the hostname, because that's the convention */
1348 for (cp
= remote_host
; *cp
; cp
++)
1349 if (isupper((int) (*cp
)))
1350 *cp
= tolower((int) (*cp
));
1353 * If there is only one name canon rule and it says to
1354 * canonicalize the old way, do that now, as we used to.
1356 ret
= _krb5_get_name_canon_rules(context
, &rules
);
1358 _krb5_debug(context
, 5, "Failed to get name canon rules: ret = %d",
1363 if (rules
[0].type
== KRB5_NCRT_NSS
&&
1364 rules
[1].type
== KRB5_NCRT_BOGUS
) {
1365 _krb5_debug(context
, 5, "Using nss for name canon immediately");
1366 ret
= krb5_sname_to_principal_old(context
, rules
[0].realm
,
1368 KRB5_NT_SRV_HST
, ret_princ
);
1374 /* Remove trailing dots */
1375 if (remote_host
[0]) {
1376 for (cp
= remote_host
+ strlen(remote_host
)-1;
1377 *cp
== '.' && cp
> remote_host
;
1383 realm
= ""; /* "Referral realm" */
1385 ret
= krb5_build_principal(context
, ret_princ
, strlen(realm
),
1386 realm
, sname
, remote_host
,
1389 if (ret
== 0 && type
== KRB5_NT_SRV_HST
) {
1391 * Hostname canonicalization is done elsewhere (in
1392 * krb5_get_credentials() and krb5_kt_get_entry()).
1394 * We overload the name type to indicate to those functions that
1395 * this principal name requires canonicalization.
1397 * We can't use the empty realm to denote the need to
1398 * canonicalize the hostname too: it would mean that users who
1399 * want to assert knowledge of a service's realm must also know
1400 * the canonical hostname, but in practice they don't.
1402 (*ret_princ
)->name
.name_type
= KRB5_NT_SRV_HST_NEEDS_CANON
;
1404 _krb5_debug(context
, 5, "Building a delayed canon principal for %s/%s@",
1405 sname
, remote_host
);
1413 tolower_str(char *s
)
1415 for (; *s
!= '\0'; s
++) {
1417 *s
= tolower_ascii(*s
);
1421 static krb5_error_code
1422 rule_parse_token(krb5_context context
, krb5_name_canon_rule rule
,
1426 int needs_type
= rule
->type
== KRB5_NCRT_BOGUS
;
1429 * Rules consist of a sequence of tokens, some of which indicate
1430 * what type of rule the rule is, and some of which set rule options
1431 * or ancilliary data. Last rule type token wins.
1434 /* Rule type tokens: */
1435 if (needs_type
&& strcmp(tok
, "as-is") == 0) {
1436 rule
->type
= KRB5_NCRT_AS_IS
;
1437 } else if (needs_type
&& strcmp(tok
, "qualify") == 0) {
1438 rule
->type
= KRB5_NCRT_QUALIFY
;
1439 } else if (needs_type
&& strcmp(tok
, "nss") == 0) {
1440 rule
->type
= KRB5_NCRT_NSS
;
1442 } else if (strcmp(tok
, "use_fast") == 0) {
1443 rule
->options
|= KRB5_NCRO_USE_FAST
;
1444 } else if (strcmp(tok
, "use_dnssec") == 0) {
1445 rule
->options
|= KRB5_NCRO_USE_DNSSEC
;
1446 } else if (strcmp(tok
, "ccache_only") == 0) {
1447 rule
->options
|= KRB5_NCRO_GC_ONLY
;
1448 } else if (strcmp(tok
, "no_referrals") == 0) {
1449 rule
->options
|= KRB5_NCRO_NO_REFERRALS
;
1450 } else if (strcmp(tok
, "use_referrals") == 0) {
1451 rule
->options
&= ~KRB5_NCRO_NO_REFERRALS
;
1452 if (rule
->realm
== NULL
) {
1453 rule
->realm
= strdup("");
1454 if (rule
->realm
== NULL
)
1455 return krb5_enomem(context
);
1457 } else if (strcmp(tok
, "lookup_realm") == 0) {
1458 rule
->options
|= KRB5_NCRO_LOOKUP_REALM
;
1461 /* Rule ancilliary data: */
1462 } else if (strncmp(tok
, "domain=", strlen("domain=")) == 0) {
1464 rule
->domain
= strdup(tok
+ strlen("domain="));
1465 if (rule
->domain
== NULL
)
1466 return krb5_enomem(context
);
1467 tolower_str(rule
->domain
);
1468 } else if (strncmp(tok
, "realm=", strlen("realm=")) == 0) {
1470 rule
->realm
= strdup(tok
+ strlen("realm="));
1471 if (rule
->realm
== NULL
)
1472 return krb5_enomem(context
);
1473 } else if (strncmp(tok
, "match_domain=", strlen("match_domain=")) == 0) {
1474 free(rule
->match_domain
);
1475 rule
->match_domain
= strdup(tok
+ strlen("match_domain="));
1476 if (rule
->match_domain
== NULL
)
1477 return krb5_enomem(context
);
1478 tolower_str(rule
->match_domain
);
1479 } else if (strncmp(tok
, "match_realm=", strlen("match_realm=")) == 0) {
1480 free(rule
->match_realm
);
1481 rule
->match_realm
= strdup(tok
+ strlen("match_realm="));
1482 if (rule
->match_realm
== NULL
)
1483 return krb5_enomem(context
);
1484 } else if (strncmp(tok
, "mindots=", strlen("mindots=")) == 0) {
1486 n
= strtol(tok
+ strlen("mindots="), NULL
, 10);
1487 if (errno
== 0 && n
> 0 && n
<= MAXDOTS
)
1489 } else if (strncmp(tok
, "maxdots=", strlen("maxdots=")) == 0) {
1491 n
= strtol(tok
+ strlen("maxdots="), NULL
, 10);
1492 if (errno
== 0 && n
> 0 && n
<= MAXDOTS
)
1494 } else if (strncmp(tok
, "order=", strlen("order=")) == 0) {
1496 n
= strtol(tok
+ strlen("order="), NULL
, 10);
1497 if (errno
== 0 && n
> 0 && n
<= MAXORDER
)
1498 rule
->explicit_order
= n
;
1500 _krb5_debug(context
, 5,
1501 "Unrecognized name canonicalization rule token %s", tok
);
1508 rule_cmp(const void *a
, const void *b
)
1510 krb5_const_name_canon_rule left
= a
;
1511 krb5_const_name_canon_rule right
= b
;
1513 if (left
->type
== KRB5_NCRT_BOGUS
&&
1514 right
->type
== KRB5_NCRT_BOGUS
)
1516 if (left
->type
== KRB5_NCRT_BOGUS
)
1518 if (right
->type
== KRB5_NCRT_BOGUS
)
1520 if (left
->explicit_order
< right
->explicit_order
)
1522 if (left
->explicit_order
> right
->explicit_order
)
1524 return left
->order
- right
->order
;
1527 static krb5_error_code
1528 parse_name_canon_rules(krb5_context context
, char **rulestrs
,
1529 krb5_name_canon_rule
*rules
)
1531 krb5_error_code ret
;
1538 krb5_name_canon_rule r
;
1542 for (n
=0, cpp
= rulestrs
; cpp
!= NULL
&& *cpp
!= NULL
; cpp
++)
1545 n
+= 2; /* Always at least one rule; two for the default case */
1547 if ((r
= calloc(n
, sizeof (*r
))) == NULL
)
1548 return krb5_enomem(context
);
1550 for (k
= 0; k
< n
; k
++) {
1551 r
[k
].type
= KRB5_NCRT_BOGUS
;
1552 r
[k
].match_domain
= NULL
;
1553 r
[k
].match_realm
= NULL
;
1558 for (i
= 0, k
= 0; i
< n
&& rulestrs
!= NULL
&& rulestrs
[i
] != NULL
; i
++) {
1560 r
[k
].explicit_order
= MAXORDER
; /* mark order, see below */
1561 r
[k
].maxdots
= MAXDOTS
;
1562 r
[k
].order
= k
; /* default order */
1564 /* Tokenize and parse value */
1567 cp
= strchr(cp
, ':'); /* XXX use strtok_r() */
1569 *cp
++ = '\0'; /* delimit token */
1570 ret
= rule_parse_token(context
, &r
[k
], tok
);
1571 if (ret
== EINVAL
) {
1572 r
[k
].type
= KRB5_NCRT_BOGUS
;
1576 _krb5_free_name_canon_rules(context
, r
);
1579 } while (cp
&& *cp
);
1580 if (r
[k
].explicit_order
!= MAXORDER
)
1583 /* Validate parsed rule */
1584 if (r
[k
].type
== KRB5_NCRT_BOGUS
||
1585 (r
[k
].type
== KRB5_NCRT_QUALIFY
&& !r
[k
].domain
) ||
1586 (r
[k
].type
== KRB5_NCRT_NSS
&& r
[k
].domain
)) {
1587 /* Invalid rule; mark it so and clean up */
1588 r
[k
].type
= KRB5_NCRT_BOGUS
;
1589 free(r
[k
].match_domain
);
1590 free(r
[k
].match_realm
);
1595 r
[k
].match_domain
= NULL
;
1596 r
[k
].match_realm
= NULL
;
1597 _krb5_debug(context
, 5,
1598 "Ignoring invalid name canonicalization rule %lu",
1602 k
++; /* good rule */
1607 * Note that we make make this a stable sort by using appareance
1608 * and explicit order.
1610 qsort(r
, n
, sizeof(r
[0]), rule_cmp
);
1613 if (r
[0].type
== KRB5_NCRT_BOGUS
) {
1614 /* No rules, or no valid rules */
1615 r
[0].type
= KRB5_NCRT_NSS
;
1619 return 0; /* We don't communicate bad rule errors here */
1623 * This exists only because the hostname canonicalization behavior in Heimdal
1624 * (and other implementations of Kerberos) has been to use getaddrinfo(),
1625 * unsafe though it is, for ages. We can't fix it in one day.
1628 make_rules_safe(krb5_context context
, krb5_name_canon_rule rules
)
1631 * If the only rule were to use the name service (getaddrinfo()) then we're
1632 * bound to fail. We could try to convert that rule to an as-is rule, but
1633 * when we do get a validating resolver we'd be unhappy that we did such a
1634 * conversion. Better let the user get failures and make them think about
1635 * their naming rules.
1639 for (; rules
[0].type
!= KRB5_NCRT_BOGUS
; rules
++) {
1640 if (rules
->type
== KRB5_NCRT_NSS
)
1641 rules
->options
|= KRB5_NCRO_USE_DNSSEC
;
1643 rules
->options
|= KRB5_NCRO_USE_FAST
;
1648 * This function returns an array of host-based service name
1649 * canonicalization rules. The array of rules is organized as a list.
1650 * See the definition of krb5_name_canon_rule.
1652 * @param context A Kerberos context.
1653 * @param rules Output location for array of rules.
1655 KRB5_LIB_FUNCTION krb5_error_code
1656 _krb5_get_name_canon_rules(krb5_context context
, krb5_name_canon_rule
*rules
)
1658 krb5_error_code ret
;
1659 char **values
= NULL
;
1661 *rules
= context
->name_canon_rules
;
1665 values
= krb5_config_get_strings(context
, NULL
,
1666 "libdefaults", "name_canon_rules", NULL
);
1667 ret
= parse_name_canon_rules(context
, values
, rules
);
1668 krb5_config_free_strings(values
);
1672 if (krb5_config_get_bool_default(context
, NULL
, FALSE
,
1673 "libdefaults", "safe_name_canon", NULL
))
1674 make_rules_safe(context
, *rules
);
1676 heim_assert(rules
!= NULL
&& (*rules
)[0].type
!= KRB5_NCRT_BOGUS
,
1677 "internal error in parsing principal name "
1678 "canonicalization rules");
1681 context
->name_canon_rules
= *rules
;
1686 static krb5_error_code
1687 get_host_realm(krb5_context context
, const char *hostname
, char **realm
)
1689 krb5_error_code ret
;
1690 char **hrealms
= NULL
;
1693 ret
= krb5_get_host_realm(context
, hostname
, &hrealms
);
1696 if (hrealms
== NULL
)
1697 return KRB5_ERR_HOST_REALM_UNKNOWN
; /* krb5_set_error() already done */
1698 if (hrealms
[0] == NULL
) {
1699 krb5_free_host_realm(context
, hrealms
);
1700 return KRB5_ERR_HOST_REALM_UNKNOWN
; /* krb5_set_error() already done */
1702 *realm
= strdup(hrealms
[0]);
1703 krb5_free_host_realm(context
, hrealms
);
1705 return krb5_enomem(context
);
1710 is_domain_suffix(const char *domain
, const char *suffix
)
1712 size_t dlen
= strlen(domain
);
1713 size_t slen
= strlen(suffix
);
1715 if (dlen
< slen
+ 2)
1718 if (strcasecmp(domain
+ (dlen
- slen
), suffix
) != 0)
1721 if (domain
[(dlen
- slen
) - 1] != '.')
1727 * Applies a name canonicalization rule to a principal.
1729 * Returns zero and no out_princ if the rule does not match.
1730 * Returns zero and an out_princ if the rule does match.
1732 static krb5_error_code
1733 apply_name_canon_rule(krb5_context context
, krb5_name_canon_rule rules
,
1734 size_t rule_idx
, krb5_const_principal in_princ
,
1735 krb5_principal
*out_princ
,
1736 krb5_name_canon_rule_options
*rule_opts
)
1738 krb5_name_canon_rule rule
= &rules
[rule_idx
];
1739 krb5_error_code ret
;
1740 unsigned int ndots
= 0;
1741 krb5_principal nss
= NULL
;
1742 const char *sname
= NULL
;
1743 const char *orig_hostname
= NULL
;
1744 const char *new_hostname
= NULL
;
1745 const char *new_realm
= NULL
;
1746 const char *port
= "";
1748 char *hostname_sans_port
= NULL
;
1749 char *hostname_with_port
= NULL
;
1750 char *tmp_hostname
= NULL
;
1751 char *tmp_realm
= NULL
;
1753 *out_princ
= NULL
; /* Signal no match */
1755 if (rule_opts
!= NULL
)
1756 *rule_opts
= rule
->options
;
1758 if (rule
->type
== KRB5_NCRT_BOGUS
)
1759 return 0; /* rule doesn't apply */
1761 sname
= krb5_principal_get_comp_string(context
, in_princ
, 0);
1762 orig_hostname
= krb5_principal_get_comp_string(context
, in_princ
, 1);
1765 * Some apps want to use the very non-standard svc/hostname:port@REALM
1766 * form. We do our best to support that here :(
1768 port
= strchr(orig_hostname
, ':');
1770 hostname_sans_port
= strndup(orig_hostname
, port
- orig_hostname
);
1771 if (hostname_sans_port
== NULL
)
1772 return krb5_enomem(context
);
1773 orig_hostname
= hostname_sans_port
;
1776 _krb5_debug(context
, 5, N_("Applying a name rule (type %d) to %s", ""),
1777 rule
->type
, orig_hostname
);
1779 if (rule
->mindots
> 0 || rule
->maxdots
> 0) {
1780 for (cp
= strchr(orig_hostname
, '.'); cp
&& *cp
; cp
= strchr(cp
+ 1, '.'))
1783 if (rule
->mindots
> 0 && ndots
< rule
->mindots
)
1785 if (ndots
> rule
->maxdots
)
1788 if (rule
->match_domain
!= NULL
&&
1789 !is_domain_suffix(orig_hostname
, rule
->match_domain
))
1792 if (rule
->match_realm
!= NULL
&&
1793 strcmp(rule
->match_realm
, in_princ
->realm
) != 0)
1796 new_realm
= rule
->realm
;
1797 switch (rule
->type
) {
1798 case KRB5_NCRT_AS_IS
:
1801 case KRB5_NCRT_QUALIFY
:
1802 heim_assert(rule
->domain
!= NULL
,
1803 "missing domain for qualify name canon rule");
1804 if (asprintf(&tmp_hostname
, "%s.%s", orig_hostname
,
1805 rule
->domain
) == -1 || tmp_hostname
== NULL
) {
1806 ret
= krb5_enomem(context
);
1809 new_hostname
= tmp_hostname
;
1813 if ((rule
->options
& KRB5_NCRO_USE_DNSSEC
)) {
1814 ret
= KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
;
1815 krb5_set_error_message(context
, ret
,
1816 "Secure hostname resolution not supported");
1819 _krb5_debug(context
, 5, "Using name service lookups");
1820 ret
= krb5_sname_to_principal_old(context
, rule
->realm
,
1821 orig_hostname
, sname
,
1824 if (rules
[rule_idx
+ 1].type
!= KRB5_NCRT_BOGUS
&&
1825 (ret
== KRB5_ERR_BAD_HOSTNAME
||
1826 ret
== KRB5_ERR_HOST_REALM_UNKNOWN
)) {
1828 * Bad hostname / realm unknown -> rule inapplicable if
1829 * there's more rules. If it's the last rule then we want
1830 * to return all errors from krb5_sname_to_principal_old()
1839 new_hostname
= krb5_principal_get_comp_string(context
, nss
, 1);
1840 new_realm
= krb5_principal_get_realm(context
, nss
);
1850 * This rule applies.
1852 * Copy in_princ and mutate the copy per the matched rule.
1854 * This way we apply to principals with two or more components, such as
1855 * domain-based names.
1857 ret
= krb5_copy_principal(context
, in_princ
, out_princ
);
1861 if (new_realm
== NULL
&& (rule
->options
& KRB5_NCRO_LOOKUP_REALM
) != 0) {
1862 ret
= get_host_realm(context
, new_hostname
, &tmp_realm
);
1865 new_realm
= tmp_realm
;
1868 /* If we stripped off a :port, add it back in */
1869 if (port
!= NULL
&& new_hostname
!= NULL
) {
1870 if (asprintf(&hostname_with_port
, "%s%s", new_hostname
, port
) == -1 ||
1871 hostname_with_port
== NULL
) {
1872 ret
= krb5_enomem(context
);
1875 new_hostname
= hostname_with_port
;
1878 if (new_realm
!= NULL
)
1879 krb5_principal_set_realm(context
, *out_princ
, new_realm
);
1880 if (new_hostname
!= NULL
)
1881 krb5_principal_set_comp_string(context
, *out_princ
, 1, new_hostname
);
1882 if (princ_type(*out_princ
) == KRB5_NT_SRV_HST_NEEDS_CANON
)
1883 princ_type(*out_princ
) = KRB5_NT_SRV_HST
;
1885 /* Trace rule application */
1887 krb5_error_code ret2
;
1890 ret2
= krb5_unparse_name(context
, *out_princ
, &unparsed
);
1892 _krb5_debug(context
, 5,
1893 N_("Couldn't unparse canonicalized princicpal (%d)",
1897 _krb5_debug(context
, 5,
1898 N_("Name canon rule application yields %s", ""),
1905 free(hostname_sans_port
);
1906 free(hostname_with_port
);
1909 krb5_free_principal(context
, nss
);
1911 krb5_set_error_message(context
, ret
,
1912 N_("Name canon rule application failed", ""));
1917 * Free name canonicalization rules
1919 KRB5_LIB_FUNCTION
void
1920 _krb5_free_name_canon_rules(krb5_context context
, krb5_name_canon_rule rules
)
1927 for (k
= 0; rules
[k
].type
!= KRB5_NCRT_BOGUS
; k
++) {
1928 free(rules
[k
].match_domain
);
1929 free(rules
[k
].match_realm
);
1930 free(rules
[k
].domain
);
1931 free(rules
[k
].realm
);
1936 struct krb5_name_canon_iterator_data
{
1937 krb5_name_canon_rule rules
;
1938 krb5_const_principal in_princ
; /* given princ */
1939 krb5_const_principal out_princ
; /* princ to be output */
1940 krb5_principal tmp_princ
; /* to be freed */
1941 int is_trivial
; /* no canon to be done */
1942 int done
; /* no more rules to be applied */
1943 size_t cursor
; /* current/next rule */
1947 * Initialize name canonicalization iterator.
1949 * @param context Kerberos context
1950 * @param in_princ principal name to be canonicalized OR
1951 * @param iter output iterator object
1953 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1954 krb5_name_canon_iterator_start(krb5_context context
,
1955 krb5_const_principal in_princ
,
1956 krb5_name_canon_iterator
*iter
)
1958 krb5_error_code ret
;
1959 krb5_name_canon_iterator state
;
1963 state
= calloc(1, sizeof (*state
));
1965 return krb5_enomem(context
);
1966 state
->in_princ
= in_princ
;
1968 if (princ_type(state
->in_princ
) == KRB5_NT_SRV_HST_NEEDS_CANON
) {
1969 ret
= _krb5_get_name_canon_rules(context
, &state
->rules
);
1973 /* Name needs no canon -> trivial iterator: in_princ is canonical */
1974 state
->is_trivial
= 1;
1981 krb5_free_name_canon_iterator(context
, state
);
1982 return krb5_enomem(context
);
1986 * Helper for name canon iteration.
1988 static krb5_error_code
1989 name_canon_iterate(krb5_context context
,
1990 krb5_name_canon_iterator
*iter
,
1991 krb5_name_canon_rule_options
*rule_opts
)
1993 krb5_error_code ret
;
1994 krb5_name_canon_iterator state
= *iter
;
2003 krb5_free_name_canon_iterator(context
, state
);
2008 if (state
->is_trivial
&& !state
->done
) {
2009 state
->out_princ
= state
->in_princ
;
2014 heim_assert(state
->rules
!= NULL
&&
2015 state
->rules
[state
->cursor
].type
!= KRB5_NCRT_BOGUS
,
2016 "Internal error during name canonicalization");
2019 krb5_free_principal(context
, state
->tmp_princ
);
2020 ret
= apply_name_canon_rule(context
, state
->rules
, state
->cursor
,
2021 state
->in_princ
, &state
->tmp_princ
, rule_opts
);
2023 krb5_free_name_canon_iterator(context
, state
);
2028 } while (state
->tmp_princ
== NULL
&&
2029 state
->rules
[state
->cursor
].type
!= KRB5_NCRT_BOGUS
);
2031 if (state
->rules
[state
->cursor
].type
== KRB5_NCRT_BOGUS
)
2034 state
->out_princ
= state
->tmp_princ
;
2035 if (state
->tmp_princ
== NULL
) {
2036 krb5_free_name_canon_iterator(context
, state
);
2044 * Iteratively apply name canon rules, outputing a principal and rule
2045 * options each time. Iteration completes when the @iter is NULL on
2046 * return or when an error is returned. Callers must free the iterator
2047 * if they abandon it mid-way.
2049 * @param context Kerberos context
2050 * @param iter name canon rule iterator (input/output)
2051 * @param try_princ output principal name
2052 * @param rule_opts output rule options
2054 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2055 krb5_name_canon_iterate(krb5_context context
,
2056 krb5_name_canon_iterator
*iter
,
2057 krb5_const_principal
*try_princ
,
2058 krb5_name_canon_rule_options
*rule_opts
)
2060 krb5_error_code ret
;
2064 ret
= name_canon_iterate(context
, iter
, rule_opts
);
2066 *try_princ
= (*iter
)->out_princ
;
2071 * Free a name canonicalization rule iterator.
2073 KRB5_LIB_FUNCTION
void KRB5_LIB_CALL
2074 krb5_free_name_canon_iterator(krb5_context context
,
2075 krb5_name_canon_iterator iter
)
2079 if (iter
->tmp_princ
)
2080 krb5_free_principal(context
, iter
->tmp_princ
);