plug a memory leak, don't use strcpy/strcat
[heimdal.git] / lib / krb5 / principal.c
blob1871bde93fe97350b488aab245e990e3b6fe2f1d
1 /*
2 * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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
31 * SUCH DAMAGE.
34 /**
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
41 * @verbatim
42 lha@SU.SE
43 host/hummel.it.su.se@SU.SE
44 host/admin@H5L.ORG
45 @endverbatim
47 * See the library functions here: @ref krb5_principal
50 #include "krb5_locl.h"
51 #ifdef HAVE_RES_SEARCH
52 #define USE_RESOLVER
53 #endif
54 #ifdef HAVE_ARPA_NAMESER_H
55 #include <arpa/nameser.h>
56 #endif
57 #include <fnmatch.h>
58 #include "resolve.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 /**
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,
81 krb5_principal p)
83 if(p){
84 free_Principal(p);
85 free(p);
89 /**
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,
104 int type)
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))
151 return NULL;
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,
188 const char *name,
189 int flags,
190 krb5_principal *principal)
192 krb5_error_code ret;
193 heim_general_string *comp;
194 heim_general_string realm = NULL;
195 int ncomp;
197 const char *p;
198 char *q;
199 char *s;
200 char *start;
202 int n;
203 char c;
204 int got_realm = 0;
205 int first_at = 1;
206 int enterprise = (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE);
208 *principal = NULL;
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;
218 #undef RFLAGS
220 /* count number of component,
221 * enterprise names only have one component
223 ncomp = 1;
224 if (!enterprise) {
225 for(p = name; *p; p++){
226 if(*p=='\\'){
227 if(!p[1]) {
228 krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
229 N_("trailing \\ in principal name", ""));
230 return KRB5_PARSE_MALFORMED;
232 p++;
233 } else if(*p == '/')
234 ncomp++;
235 else if(*p == '@')
236 break;
239 comp = calloc(ncomp, sizeof(*comp));
240 if (comp == NULL)
241 return krb5_enomem(context);
243 n = 0;
244 p = start = q = s = strdup(name);
245 if (start == NULL) {
246 free (comp);
247 return krb5_enomem(context);
249 while(*p){
250 c = *p++;
251 if(c == '\\'){
252 c = *p++;
253 if(c == 'n')
254 c = '\n';
255 else if(c == 't')
256 c = '\t';
257 else if(c == 'b')
258 c = '\b';
259 else if(c == '0')
260 c = '\0';
261 else if(c == '\0') {
262 ret = KRB5_PARSE_MALFORMED;
263 krb5_set_error_message(context, ret,
264 N_("trailing \\ in principal name", ""));
265 goto exit;
267 }else if(enterprise && first_at) {
268 if (c == '@')
269 first_at = 0;
270 }else if((c == '/' && !enterprise) || c == '@'){
271 if(got_realm){
272 ret = KRB5_PARSE_MALFORMED;
273 krb5_set_error_message(context, ret,
274 N_("part after realm in principal name", ""));
275 goto exit;
276 }else{
277 comp[n] = malloc(q - start + 1);
278 if (comp[n] == NULL) {
279 ret = krb5_enomem(context);
280 goto exit;
282 memcpy(comp[n], start, q - start);
283 comp[n][q - start] = 0;
284 n++;
286 if(c == '@')
287 got_realm = 1;
288 start = q;
289 continue;
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", ""));
295 goto exit;
297 *q++ = c;
299 if(got_realm){
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", ""));
305 goto exit;
307 realm = malloc(q - start + 1);
308 if (realm == NULL) {
309 ret = krb5_enomem(context);
310 goto exit;
312 memcpy(realm, start, q - start);
313 realm[q - start] = 0;
314 }else{
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", ""));
320 goto exit;
321 } else if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) {
322 realm = NULL;
323 } else {
324 ret = krb5_get_default_realm (context, &realm);
325 if (ret)
326 goto exit;
329 comp[n] = malloc(q - start + 1);
330 if (comp[n] == NULL) {
331 ret = krb5_enomem(context);
332 goto exit;
334 memcpy(comp[n], start, q - start);
335 comp[n][q - start] = 0;
336 n++;
338 *principal = calloc(1, sizeof(**principal));
339 if (*principal == NULL) {
340 ret = krb5_enomem(context);
341 goto exit;
343 if (enterprise)
344 (*principal)->name.name_type = KRB5_NT_ENTERPRISE_PRINCIPAL;
345 else
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;
350 free(s);
351 return 0;
352 exit:
353 while(n>0){
354 free(comp[--n]);
356 free(comp);
357 free(realm);
358 free(s);
359 return ret;
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,
376 const char *name,
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);
388 static size_t
389 quote_string(const char *s, char *out, size_t idx, size_t len, int display)
391 const char *p, *q;
392 for(p = s; *p && idx < len; p++){
393 q = strchr(quotable_chars, *p);
394 if (q && display) {
395 add_char(out, idx, len, replace_chars[q - quotable_chars]);
396 } else if (q) {
397 add_char(out, idx, len, '\\');
398 add_char(out, idx, len, replace_chars[q - quotable_chars]);
399 }else
400 add_char(out, idx, len, *p);
402 if(idx < len)
403 out[idx] = '\0';
404 return idx;
408 static krb5_error_code
409 unparse_name_fixed(krb5_context context,
410 krb5_const_principal principal,
411 char *name,
412 size_t len,
413 int flags)
415 size_t idx = 0;
416 size_t i;
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", ""));
425 return ERANGE;
428 for(i = 0; i < princ_num_comp(principal); i++){
429 if(i)
430 add_char(name, idx, len, '/');
431 idx = quote_string(princ_ncomp(principal, i), name, idx, len, display);
432 if(idx == len) {
433 krb5_set_error_message(context, ERANGE,
434 N_("Out of space printing principal", ""));
435 return ERANGE;
438 /* add realm if different from default realm */
439 if(short_form && !no_realm) {
440 krb5_realm r;
441 krb5_error_code ret;
442 ret = krb5_get_default_realm(context, &r);
443 if(ret)
444 return ret;
445 if(strcmp(princ_realm(principal), r) != 0)
446 short_form = 0;
447 free(r);
449 if(!short_form && !no_realm) {
450 add_char(name, idx, len, '@');
451 idx = quote_string(princ_realm(principal), name, idx, len, display);
452 if(idx == len) {
453 krb5_set_error_message(context, ERANGE,
454 N_("Out of space printing "
455 "realm of principal", ""));
456 return ERANGE;
459 return 0;
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,
478 char *name,
479 size_t len)
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,
501 char *name,
502 size_t len)
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,
525 int flags,
526 char *name,
527 size_t len)
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,
535 char **name,
536 int flags)
538 size_t len = 0, plen;
539 size_t i;
540 krb5_error_code ret;
541 /* count length */
542 if (princ_realm(principal)) {
543 plen = strlen(princ_realm(principal));
545 if(strcspn(princ_realm(principal), quotable_chars) == plen)
546 len += plen;
547 else
548 len += 2*plen;
549 len++; /* '@' */
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)
554 len += plen;
555 else
556 len += 2*plen;
557 len++;
559 len++; /* '\0' */
560 *name = malloc(len);
561 if(*name == NULL)
562 return krb5_enomem(context);
563 ret = unparse_name_fixed(context, principal, *name, len, flags);
564 if(ret) {
565 free(*name);
566 *name = NULL;
568 return ret;
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,
586 char **name)
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,
607 int flags,
608 char **name)
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,
629 char **name)
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
636 * previous realm.
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);
658 return 0;
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,
679 int rlen,
680 krb5_const_realm realm,
681 ...)
683 krb5_error_code ret;
684 va_list ap;
685 va_start(ap, realm);
686 ret = krb5_build_principal_va(context, principal, rlen, realm, ap);
687 va_end(ap);
688 return ret;
690 #endif
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,
710 ...)
712 krb5_error_code ret;
713 krb5_realm r = NULL;
714 va_list ap;
715 if(realm == NULL) {
716 ret = krb5_get_default_realm(context, &r);
717 if(ret)
718 return ret;
719 realm = r;
721 va_start(ap, realm);
722 ret = krb5_build_principal_va(context, principal, strlen(realm), realm, ap);
723 va_end(ap);
724 if(r)
725 free(r);
726 return ret;
729 static krb5_error_code
730 append_component(krb5_context context, krb5_principal p,
731 const char *comp,
732 size_t comp_len)
734 heim_general_string *tmp;
735 size_t len = princ_num_comp(p);
737 tmp = realloc(princ_comp(p), (len + 1) * sizeof(*tmp));
738 if(tmp == NULL)
739 return krb5_enomem(context);
740 princ_comp(p) = tmp;
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';
746 princ_num_comp(p)++;
747 return 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;
755 while (1){
756 const char *s;
757 int len;
759 if ((len = va_arg(ap, int)) == 0)
760 break;
761 s = va_arg(ap, const char*);
762 if ((ret = append_component(context, p, s, len)) != 0)
763 break;
765 return ret;
768 static krb5_error_code
769 va_princ(krb5_context context, krb5_principal p, va_list ap)
771 krb5_error_code ret = 0;
773 while (1){
774 const char *s;
776 if ((s = va_arg(ap, const char*)) == NULL)
777 break;
778 if ((ret = append_component(context, p, s, strlen(s))) != 0)
779 break;
781 return ret;
784 static krb5_error_code
785 build_principal(krb5_context context,
786 krb5_principal *principal,
787 int rlen,
788 krb5_const_realm realm,
789 krb5_error_code (*func)(krb5_context, krb5_principal, va_list),
790 va_list ap)
792 krb5_error_code ret;
793 krb5_principal p;
795 p = calloc(1, sizeof(*p));
796 if (p == NULL)
797 return krb5_enomem(context);
798 princ_type(p) = KRB5_NT_PRINCIPAL;
800 princ_realm(p) = strdup(realm);
801 if (p->realm == NULL) {
802 free(p);
803 return krb5_enomem(context);
806 ret = func(context, p, ap);
807 if (ret == 0)
808 *principal = p;
809 else
810 krb5_free_principal(context, p);
811 return ret;
814 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
815 krb5_build_principal_va(krb5_context context,
816 krb5_principal *principal,
817 int rlen,
818 krb5_const_realm realm,
819 va_list ap)
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,
827 int rlen,
828 krb5_const_realm realm,
829 va_list ap)
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,
838 int rlen,
839 krb5_const_realm realm,
840 ...)
842 krb5_error_code ret;
843 va_list ap;
844 va_start(ap, realm);
845 ret = krb5_build_principal_va_ext(context, principal, rlen, realm, ap);
846 va_end(ap);
847 return ret;
851 * Copy a principal
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));
869 if (p == NULL)
870 return krb5_enomem(context);
871 if(copy_Principal(inprinc, p)) {
872 free(p);
873 return krb5_enomem(context);
875 *outprinc = p;
876 return 0;
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)
898 size_t i;
899 if(princ_num_comp(princ1) != princ_num_comp(princ2))
900 return FALSE;
901 for(i = 0; i < princ_num_comp(princ1); i++){
902 if(strcmp(princ_ncomp(princ1, i), princ_ncomp(princ2, i)) != 0)
903 return FALSE;
905 return TRUE;
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)
913 size_t i;
914 if (princ_num_comp(princ1) != princ2->name_string.len)
915 return FALSE;
916 for(i = 0; i < princ_num_comp(princ1); i++){
917 if(strcmp(princ_ncomp(princ1, i), princ2->name_string.val[i]) != 0)
918 return FALSE;
920 return TRUE;
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)) {
949 krb5_error_code ret;
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;
959 } else {
960 princ2canon = princ2;
961 other_princ = princ1;
964 ret = krb5_name_canon_iterator_start(context, princ2canon, NULL, &nci);
965 if (ret)
966 return FALSE;
967 do {
968 ret = krb5_name_canon_iterate_princ(context, &nci, &try_princ,
969 NULL);
970 if (ret || try_princ == NULL)
971 break;
972 princs_eq = krb5_principal_compare(context, try_princ, other_princ);
973 if (princs_eq) {
974 krb5_free_name_canon_iterator(context, nci);
975 return TRUE;
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))
989 return FALSE;
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)
1024 size_t i;
1025 if(princ_num_comp(princ) != princ_num_comp(pattern))
1026 return FALSE;
1027 if(fnmatch(princ_realm(pattern), princ_realm(princ), 0) != 0)
1028 return FALSE;
1029 for(i = 0; i < princ_num_comp(princ); i++){
1030 if(fnmatch(princ_ncomp(pattern, i), princ_ncomp(princ, i), 0) != 0)
1031 return FALSE;
1033 return TRUE;
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,
1042 const char *realm,
1043 const char *hostname,
1044 const char *sname,
1045 int32_t type,
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", ""),
1055 (int)type);
1056 return KRB5_SNAME_UNSUPP_NAMETYPE;
1058 if(hostname == NULL) {
1059 ret = gethostname(localhost, sizeof(localhost) - 1);
1060 if (ret != 0) {
1061 ret = errno;
1062 krb5_set_error_message(context, ret,
1063 N_("Failed to get local hostname", ""));
1064 return ret;
1066 localhost[sizeof(localhost) - 1] = '\0';
1067 hostname = localhost;
1069 if(sname == NULL)
1070 sname = "host";
1071 if(type == KRB5_NT_SRV_HST) {
1072 if (realm)
1073 ret = krb5_expand_hostname(context, hostname, &host);
1074 else
1075 ret = krb5_expand_hostname_realms(context, hostname,
1076 &host, &realms);
1077 if (ret)
1078 return ret;
1079 strlwr(host);
1080 hostname = host;
1081 if (!realm)
1082 realm = realms[0];
1083 } else if (!realm) {
1084 ret = krb5_get_host_realm(context, hostname, &realms);
1085 if(ret)
1086 return ret;
1087 realm = realms[0];
1090 ret = krb5_make_principal(context, ret_princ, realm, sname,
1091 hostname, NULL);
1092 if(host)
1093 free(host);
1094 if (realms)
1095 krb5_free_host_realm(context, realms);
1096 return ret;
1099 static const struct {
1100 const char *type;
1101 int32_t value;
1102 } nametypes[] = {
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 },
1116 { NULL, 0 }
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)
1128 size_t i;
1130 for(i = 0; nametypes[i].type; i++) {
1131 if (strcasecmp(nametypes[i].type, str) == 0) {
1132 *nametype = nametypes[i].value;
1133 return 0;
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)
1154 return TRUE;
1155 return FALSE;
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,
1215 KRB5_NCRT_AS_IS,
1216 KRB5_NCRT_QUALIFY,
1217 KRB5_NCRT_RES_SEARCHLIST,
1218 KRB5_NCRT_NSS
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;
1225 char *domain;
1226 char *realm;
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,
1257 const char *sname,
1258 int32_t type,
1259 krb5_principal *ret_princ)
1261 char *realm, *remote_host;
1262 krb5_error_code ret;
1263 register char *cp;
1264 char localname[MAXHOSTNAMELEN];
1266 *ret_princ = NULL;
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 */
1273 if (!hostname) {
1274 if (gethostname(localname, MAXHOSTNAMELEN))
1275 return errno;
1276 hostname = localname;
1279 /* if sname is NULL, use "host" */
1280 if (!sname)
1281 sname = "host";
1283 remote_host = strdup(hostname);
1284 if (!remote_host)
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);
1296 if (ret) {
1297 _krb5_debug(context, 5, "Failed to get name canon rules: ret = %d",
1298 ret);
1299 return ret;
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,
1306 remote_host, sname,
1307 KRB5_NT_SRV_HST,
1308 ret_princ);
1309 free(remote_host);
1310 _krb5_free_name_canon_rules(context, rules);
1311 return ret;
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;
1319 if (*cp == '.')
1320 *cp = '\0';
1323 realm = ""; /* "Referral realm" -- borrowed from newer MIT */
1325 ret = krb5_build_principal(context, ret_princ, strlen(realm),
1326 realm, sname, remote_host,
1327 (char *)0);
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);
1343 free(remote_host);
1344 return ret;
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,
1352 const char *tok)
1354 long int n;
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;
1374 /* Rule options: */
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) {
1387 free(rule->domain);
1388 rule->domain = strdup(tok + strlen("domain="));
1389 if (!rule->domain)
1390 return krb5_enomem(context);
1391 } else if (strncmp(tok, "realm=", strlen("realm=")) == 0) {
1392 free(rule->realm);
1393 rule->realm = strdup(tok + strlen("realm="));
1394 if (!rule->realm)
1395 return krb5_enomem(context);
1396 } else if (strncmp(tok, "mindots=", strlen("mindots=")) == 0) {
1397 errno = 0;
1398 n = strtol(tok + strlen("mindots="), NULL, 10);
1399 if (errno == 0 && n > 0 && n < 8)
1400 rule->mindots = n;
1402 /* ignore bogus tokens; it's not like we can print to stderr */
1403 /* XXX Trace bogus tokens! */
1404 return 0;
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;
1421 char **dnsrch;
1422 char **domains = NULL;
1423 size_t search_list_len;
1424 size_t i;
1425 int ret;
1427 /* Sanitize */
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);
1442 if (ret)
1443 return ENOENT; /* XXX Create a better error */
1444 dnsrch = statbuf.dnsrch;
1445 search_list_len = sizeof (statbuf.dnsrch) / sizeof (*statbuf.dnsrch);
1446 #else
1447 ret = res_init();
1448 if (ret)
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;
1457 break;
1461 if (search_list_len == 0) {
1462 /* Invalidate this entry and return */
1463 (*r)[insert_point].type = KRB5_NCRT_BOGUS;
1464 return 0;
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) {
1476 while (i > 0)
1477 free(domains[--i]);
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++)
1487 free(domains[i]);
1488 free(domains);
1489 return krb5_enomem(context);
1491 } else {
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",
1517 dnsrch[i]);
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;
1522 free(domains);
1524 *r = new_r;
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 */
1531 #else
1532 /* No resolver API by which to get search list -> use name service */
1533 if ((*r)[insert_point].options & KRB5_NCRO_SECURE)
1534 return ENOTSUP;
1535 (*r)[insert_point].type = KRB5_NCRT_NSS;
1536 #endif /* HAVE_RES_NINIT || HAVE_RES_SEARCH */
1538 return 0;
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;
1549 char *tok;
1550 char *cp;
1551 char **cpp;
1552 size_t n = 0;
1553 size_t i, k;
1554 krb5_name_canon_rule r;
1556 for (cpp = rulestrs; *cpp; cpp++)
1557 n++;
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++) {
1564 cp = rulestrs[i];
1565 do {
1566 tok = cp;
1567 cp = strpbrk(cp, ":");
1568 if (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;
1578 free(r[k].realm);
1579 free(r[k].domain);
1580 r[k].realm = NULL;
1581 r[k].domain = NULL;
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)
1591 continue;
1592 ret = expand_search_list(context, &r, &n, i);
1593 if (ret)
1594 return ret;
1597 /* The first rule has to be valid */
1598 k = n;
1599 for (i = 0; i < n; i++) {
1600 if (r[i].type != KRB5_NCRT_BOGUS) {
1601 k = i;
1602 break;
1605 if (k > 0 && k < n) {
1606 r[0] = r[k];
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)
1613 continue;
1614 r[k].next = &r[i];
1615 k++;
1618 *rules = r;
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;
1635 char *realm = NULL;
1637 *rules = NULL;
1638 ret = krb5_get_default_realm(context, &realm);
1639 if (ret == KRB5_CONFIG_NODEFREALM || ret == KRB5_CONFIG_CANTOPEN)
1640 realm = NULL;
1641 else if (ret)
1642 return ret;
1644 if (realm) {
1645 values = krb5_config_get_strings(context, NULL,
1646 "libdefaults",
1647 realm,
1648 "name_canon_rules", NULL);
1649 free(realm);
1651 if (!values) {
1652 values = krb5_config_get_strings(context, NULL,
1653 "libdefaults",
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;
1662 return 0;
1665 ret = parse_name_canon_rules(context, values, rules);
1666 krb5_config_free_strings(values);
1667 if (ret)
1668 return ret;
1671 size_t k;
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! */
1686 free(*rules);
1687 *rules = NULL;
1688 /* fall through to return default rule */
1689 _krb5_debug(context, 5, "All name canon rules are bogus!");
1691 return 0;
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;
1700 *realm = NULL;
1701 if ((ret = krb5_get_host_realm(context, hostname, &hrealms)))
1702 return ret;
1703 if (!hrealms)
1704 return KRB5_ERR_HOST_REALM_UNKNOWN; /* krb5_set_error() already done */
1705 if (!hrealms[0]) {
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);
1711 return 0;
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;
1730 char *realm = NULL;
1731 const char *sname = NULL;
1732 const char *hostname = NULL;
1733 char *new_hostname = NULL;
1734 const char *cp;
1736 heim_assert(in_princ->name.name_type == KRB5_NT_SRV_HST_NEEDS_CANON,
1737 "internal error: principal does not need canon");
1738 *out_princ = NULL;
1739 if (rule_opts)
1740 *rule_opts = 0;
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,
1749 hostname);
1750 if (rule_opts)
1751 *rule_opts = rule->options;
1753 ret = 0;
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, '.'))
1758 ndots++;
1759 if (ndots < rule->mindots)
1760 goto out; /* *out_princ == NULL; rule doesn't apply */
1762 if (rule->domain) {
1763 cp = strstr(hostname, rule->domain);
1764 if (cp == NULL)
1765 goto out; /* *out_princ == NULL; rule doesn't apply */
1766 if (cp != hostname && cp[-1] != '.')
1767 goto out;
1769 /* Rule matches, copy princ with hostname as-is, with normal magic */
1770 realm = rule->realm;
1771 if (!realm) {
1772 ret = get_host_realm(context, hostname, &realm);
1773 if (ret)
1774 goto out;
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,
1779 strlen(realm),
1780 realm, sname, hostname,
1781 (char *)0);
1782 goto out;
1783 break;
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);
1797 goto out;
1800 } else {
1801 size_t len;
1803 asprintf(&new_hostname, "%s%s%s", hostname,
1804 rule->domain[0] != '.' ? "." : "",
1805 rule->domain);
1806 if (new_hostname == NULL) {
1807 ret = krb5_enomem(context);
1808 goto out;
1811 realm = rule->realm;
1812 if (!realm) {
1813 ret = get_host_realm(context, new_hostname, &realm);
1814 if (ret)
1815 goto out;
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);
1822 free(new_hostname);
1823 goto out;
1824 break;
1826 case KRB5_NCRT_NSS:
1827 _krb5_debug(context, 5, "Using name service lookups (without "
1828 "reverse lookups)");
1829 ret = krb5_sname_to_principal_old(context, rule->realm,
1830 hostname, sname,
1831 KRB5_NT_SRV_HST,
1832 out_princ);
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()
1840 * here.
1842 ret = 0;
1843 goto out;
1844 break;
1846 default:
1847 /* Can't happen, but we need this to shut up gcc */
1848 break;
1851 out:
1852 if (!ret && *out_princ) {
1853 krb5_error_code ret2;
1854 char *unparsed;
1856 ret2 = krb5_unparse_name(context, *out_princ, &unparsed);
1857 if (ret2) {
1858 _krb5_debug(context, 5, "Couldn't unparse resulting princ! (%d)",
1859 ret);
1860 } else {
1861 _krb5_debug(context, 5, "Name canon rule application yields this "
1862 "unparsed princ: %s", unparsed);
1863 free(unparsed);
1865 } else if (!ret) {
1866 _krb5_debug(context, 5, "Name canon rule did not apply");
1867 } else {
1868 _krb5_debug(context, 5, "Name canon rule application error: %d", ret);
1870 if (new_hostname)
1871 free(new_hostname);
1872 if (realm != rule->realm)
1873 free(realm);
1874 if (*out_princ)
1875 (*out_princ)->name.name_type = KRB5_NT_SRV_HST;
1876 if (ret)
1877 krb5_set_error_message(context, ret,
1878 N_("Name canon rule application failed", ""));
1879 return ret;
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) {
1891 free(r->realm);
1892 free(r->domain);
1895 free(rules);
1896 rules = NULL;
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;
1904 krb5_creds *creds;
1905 int is_trivial;
1906 int done;
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;
1927 *iter = NULL;
1929 state = calloc(1, sizeof (*state));
1930 if (state == NULL)
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;
1941 } else {
1942 ret = _krb5_get_name_canon_rules(context, &state->rules);
1943 if (ret)
1944 goto out;
1945 state->rule = state->rules;
1948 state->in_princ = princ;
1949 if (in_creds) {
1950 if (!state->is_trivial) {
1951 ret = krb5_copy_creds(context, in_creds, &state->creds);
1952 if (ret) goto out;
1954 state->tmp_princ = state->creds->server; /* so we don't leak */
1957 *iter = state;
1958 return 0;
1960 out:
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;
1976 if (rule_opts)
1977 *rule_opts = 0;
1979 if (!state)
1980 return 0;
1981 if (state->done) {
1982 krb5_free_name_canon_iterator(context, state);
1983 *iter = NULL;
1984 return 0;
1987 if (state->is_trivial && !state->done) {
1988 state->done = 1;
1989 return 0;
1992 do {
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);
1996 if (ret)
1997 return ret;
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);
2003 *iter = NULL;
2004 return 0;
2006 if (state->creds)
2007 state->creds->server = state->tmp_princ;
2008 if (state->rule == NULL)
2009 state->done = 1;
2010 return 0;
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;
2032 *try_princ = NULL;
2033 ret = krb5_name_canon_iterate(context, iter, rule_opts);
2034 if (*iter)
2035 *try_princ = (*iter)->tmp_princ;
2036 return ret;
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;
2058 *try_creds = NULL;
2059 ret = krb5_name_canon_iterate(context, iter, rule_opts);
2060 if (*iter)
2061 *try_creds = (*iter)->creds;
2062 return ret;
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)
2072 if (iter == NULL)
2073 return;
2074 if (!iter->is_trivial) {
2075 if (iter->creds) {
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);
2083 free(iter);