Fix -O3 -Werror=unused-result build in dcache.c (#420)
[heimdal.git] / lib / krb5 / principal.c
blob206cde1d63bc4cb30938ea798cddb7613a8f003a
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 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
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 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) > 1 && strcmp(princ_ncomp(p, 0), "kca_service") == 0)
74 princ_type(p) = KRB5_NT_SRV_HST;
75 else if (princ_num_comp(p) == 2 &&
76 strcmp(princ_ncomp(p, 0), KRB5_WELLKNOWN_NAME) == 0)
77 princ_type(p) = KRB5_NT_WELLKNOWN;
78 else if (princ_num_comp(p) == 1 && strchr(princ_ncomp(p, 0), '@') != NULL)
79 princ_type(p) = KRB5_NT_SMTP_NAME;
80 else
81 princ_type(p) = defnt;
82 return 0;
85 static krb5_error_code append_component(krb5_context, krb5_principal,
86 const char *, size_t);
88 /**
89 * Frees a Kerberos principal allocated by the library with
90 * krb5_parse_name(), krb5_make_principal() or any other related
91 * principal functions.
93 * @param context A Kerberos context.
94 * @param p a principal to free.
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_free_principal(krb5_context context,
103 krb5_principal p)
105 if(p){
106 free_Principal(p);
107 free(p);
112 * Set the type of the principal
114 * @param context A Kerberos context.
115 * @param principal principal to set the type for
116 * @param type the new type
118 * @return An krb5 error code, see krb5_get_error_message().
120 * @ingroup krb5_principal
123 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
124 krb5_principal_set_type(krb5_context context,
125 krb5_principal principal,
126 int type)
128 princ_type(principal) = type;
132 * Get the type of the principal
134 * @param context A Kerberos context.
135 * @param principal principal to get the type for
137 * @return the type of principal
139 * @ingroup krb5_principal
142 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
143 krb5_principal_get_type(krb5_context context,
144 krb5_const_principal principal)
146 return princ_type(principal);
150 * Get the realm of the principal
152 * @param context A Kerberos context.
153 * @param principal principal to get the realm for
155 * @return realm of the principal, don't free or use after krb5_principal is freed
157 * @ingroup krb5_principal
160 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
161 krb5_principal_get_realm(krb5_context context,
162 krb5_const_principal principal)
164 return princ_realm(principal);
167 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
168 krb5_principal_get_comp_string(krb5_context context,
169 krb5_const_principal principal,
170 unsigned int component)
172 if(component >= princ_num_comp(principal))
173 return NULL;
174 return princ_ncomp(principal, component);
178 * Get number of component is principal.
180 * @param context Kerberos 5 context
181 * @param principal principal to query
183 * @return number of components in string
185 * @ingroup krb5_principal
188 KRB5_LIB_FUNCTION unsigned int KRB5_LIB_CALL
189 krb5_principal_get_num_comp(krb5_context context,
190 krb5_const_principal principal)
192 return princ_num_comp(principal);
196 * Parse a name into a krb5_principal structure, flags controls the behavior.
198 * @param context Kerberos 5 context
199 * @param name name to parse into a Kerberos principal
200 * @param flags flags to control the behavior
201 * @param principal returned principal, free with krb5_free_principal().
203 * @return An krb5 error code, see krb5_get_error_message().
205 * @ingroup krb5_principal
208 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
209 krb5_parse_name_flags(krb5_context context,
210 const char *name,
211 int flags,
212 krb5_principal *principal)
214 krb5_error_code ret;
215 heim_general_string *comp;
216 heim_general_string realm = NULL;
217 int ncomp;
219 const char *p;
220 char *q;
221 char *s;
222 char *start;
224 int n;
225 char c;
226 int got_realm = 0;
227 int first_at = 1;
228 int no_realm = flags & KRB5_PRINCIPAL_PARSE_NO_REALM;
229 int require_realm = flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM;
230 int enterprise = flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE;
231 int ignore_realm = flags & KRB5_PRINCIPAL_PARSE_IGNORE_REALM;
232 int no_def_realm = flags & KRB5_PRINCIPAL_PARSE_NO_DEF_REALM;
234 *principal = NULL;
236 if (no_realm && require_realm) {
237 krb5_set_error_message(context, KRB5_ERR_NO_SERVICE,
238 N_("Can't require both realm and "
239 "no realm at the same time", ""));
240 return KRB5_ERR_NO_SERVICE;
243 /* count number of component,
244 * enterprise names only have one component
246 ncomp = 1;
247 if (!enterprise) {
248 for (p = name; *p; p++) {
249 if (*p=='\\') {
250 if (!p[1]) {
251 krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
252 N_("trailing \\ in principal name", ""));
253 return KRB5_PARSE_MALFORMED;
255 p++;
256 } else if (*p == '/')
257 ncomp++;
258 else if (*p == '@')
259 break;
262 comp = calloc(ncomp, sizeof(*comp));
263 if (comp == NULL)
264 return krb5_enomem(context);
266 n = 0;
267 p = start = q = s = strdup(name);
268 if (start == NULL) {
269 free(comp);
270 return krb5_enomem(context);
272 while (*p) {
273 c = *p++;
274 if (c == '\\') {
275 c = *p++;
276 if (c == 'n')
277 c = '\n';
278 else if (c == 't')
279 c = '\t';
280 else if (c == 'b')
281 c = '\b';
282 else if (c == '0')
283 c = '\0';
284 else if (c == '\0') {
285 ret = KRB5_PARSE_MALFORMED;
286 krb5_set_error_message(context, ret,
287 N_("trailing \\ in principal name", ""));
288 goto exit;
290 } else if (enterprise && first_at) {
291 if (c == '@')
292 first_at = 0;
293 } else if ((c == '/' && !enterprise) || c == '@') {
294 if (got_realm) {
295 ret = KRB5_PARSE_MALFORMED;
296 krb5_set_error_message(context, ret,
297 N_("part after realm in principal name", ""));
298 goto exit;
299 } else {
300 comp[n] = malloc(q - start + 1);
301 if (comp[n] == NULL) {
302 ret = krb5_enomem(context);
303 goto exit;
305 memcpy(comp[n], start, q - start);
306 comp[n][q - start] = 0;
307 n++;
309 if (c == '@')
310 got_realm = 1;
311 start = q;
312 continue;
314 if (got_realm && (c == '/' || c == '\0')) {
315 ret = KRB5_PARSE_MALFORMED;
316 krb5_set_error_message(context, ret,
317 N_("part after realm in principal name", ""));
318 goto exit;
320 *q++ = c;
322 if (got_realm) {
323 if (no_realm) {
324 ret = KRB5_PARSE_MALFORMED;
325 krb5_set_error_message(context, ret,
326 N_("realm found in 'short' principal "
327 "expected to be without one", ""));
328 goto exit;
330 if (!ignore_realm) {
331 realm = malloc(q - start + 1);
332 if (realm == NULL) {
333 ret = krb5_enomem(context);
334 goto exit;
336 memcpy(realm, start, q - start);
337 realm[q - start] = 0;
339 } else {
340 if (require_realm) {
341 ret = KRB5_PARSE_MALFORMED;
342 krb5_set_error_message(context, ret,
343 N_("realm NOT found in principal "
344 "expected to be with one", ""));
345 goto exit;
346 } else if (no_realm || no_def_realm) {
347 realm = NULL;
348 } else {
349 ret = krb5_get_default_realm(context, &realm);
350 if (ret)
351 goto exit;
354 comp[n] = malloc(q - start + 1);
355 if (comp[n] == NULL) {
356 ret = krb5_enomem(context);
357 goto exit;
359 memcpy(comp[n], start, q - start);
360 comp[n][q - start] = 0;
361 n++;
363 *principal = calloc(1, sizeof(**principal));
364 if (*principal == NULL) {
365 ret = krb5_enomem(context);
366 goto exit;
368 (*principal)->name.name_string.val = comp;
369 princ_num_comp(*principal) = n;
370 (*principal)->realm = realm;
371 if (enterprise)
372 princ_type(*principal) = KRB5_NT_ENTERPRISE_PRINCIPAL;
373 else
374 set_default_princ_type(*principal, KRB5_NT_PRINCIPAL);
375 free(s);
376 return 0;
377 exit:
378 while (n>0) {
379 free(comp[--n]);
381 free(comp);
382 krb5_free_default_realm(context, realm);
383 free(s);
384 return ret;
388 * Parse a name into a krb5_principal structure
390 * @param context Kerberos 5 context
391 * @param name name to parse into a Kerberos principal
392 * @param principal returned principal, free with krb5_free_principal().
394 * @return An krb5 error code, see krb5_get_error_message().
396 * @ingroup krb5_principal
399 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
400 krb5_parse_name(krb5_context context,
401 const char *name,
402 krb5_principal *principal)
404 return krb5_parse_name_flags(context, name, 0, principal);
407 static const char quotable_chars[] = " \n\t\b\\/@";
408 static const char replace_chars[] = " ntb\\/@";
410 #define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0);
412 static size_t
413 quote_string(const char *s, char *out, size_t idx, size_t len, int display)
415 const char *p, *q;
416 for(p = s; *p && idx < len; p++){
417 q = strchr(quotable_chars, *p);
418 if (q && display) {
419 add_char(out, idx, len, replace_chars[q - quotable_chars]);
420 } else if (q) {
421 add_char(out, idx, len, '\\');
422 add_char(out, idx, len, replace_chars[q - quotable_chars]);
423 }else
424 add_char(out, idx, len, *p);
426 if(idx < len)
427 out[idx] = '\0';
428 return idx;
432 static krb5_error_code
433 unparse_name_fixed(krb5_context context,
434 krb5_const_principal principal,
435 char *name,
436 size_t len,
437 int flags)
439 size_t idx = 0;
440 size_t i;
441 int short_form = (flags & KRB5_PRINCIPAL_UNPARSE_SHORT) != 0;
442 int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) != 0;
443 int display = (flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) != 0;
445 if (!no_realm && princ_realm(principal) == NULL) {
446 krb5_set_error_message(context, ERANGE,
447 N_("Realm missing from principal, "
448 "can't unparse", ""));
449 return ERANGE;
452 for(i = 0; i < princ_num_comp(principal); i++){
453 if(i)
454 add_char(name, idx, len, '/');
455 idx = quote_string(princ_ncomp(principal, i), name, idx, len, display);
456 if(idx == len) {
457 krb5_set_error_message(context, ERANGE,
458 N_("Out of space printing principal", ""));
459 return ERANGE;
462 /* add realm if different from default realm */
463 if(short_form && !no_realm) {
464 krb5_realm r;
465 krb5_error_code ret;
466 ret = krb5_get_default_realm(context, &r);
467 if(ret)
468 return ret;
469 if(strcmp(princ_realm(principal), r) != 0)
470 short_form = 0;
471 krb5_free_default_realm(context, r);
473 if(!short_form && !no_realm) {
474 add_char(name, idx, len, '@');
475 idx = quote_string(princ_realm(principal), name, idx, len, display);
476 if(idx == len) {
477 krb5_set_error_message(context, ERANGE,
478 N_("Out of space printing "
479 "realm of principal", ""));
480 return ERANGE;
483 return 0;
487 * Unparse the principal name to a fixed buffer
489 * @param context A Kerberos context.
490 * @param principal principal to unparse
491 * @param name buffer to write name to
492 * @param len length of buffer
494 * @return An krb5 error code, see krb5_get_error_message().
496 * @ingroup krb5_principal
499 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
500 krb5_unparse_name_fixed(krb5_context context,
501 krb5_const_principal principal,
502 char *name,
503 size_t len)
505 return unparse_name_fixed(context, principal, name, len, 0);
509 * Unparse the principal name to a fixed buffer. The realm is skipped
510 * if its a default realm.
512 * @param context A Kerberos context.
513 * @param principal principal to unparse
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_short(krb5_context context,
524 krb5_const_principal principal,
525 char *name,
526 size_t len)
528 return unparse_name_fixed(context, principal, name, len,
529 KRB5_PRINCIPAL_UNPARSE_SHORT);
533 * Unparse the principal name with unparse flags to a fixed buffer.
535 * @param context A Kerberos context.
536 * @param principal principal to unparse
537 * @param flags unparse flags
538 * @param name buffer to write name to
539 * @param len length of buffer
541 * @return An krb5 error code, see krb5_get_error_message().
543 * @ingroup krb5_principal
546 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
547 krb5_unparse_name_fixed_flags(krb5_context context,
548 krb5_const_principal principal,
549 int flags,
550 char *name,
551 size_t len)
553 return unparse_name_fixed(context, principal, name, len, flags);
556 static krb5_error_code
557 unparse_name(krb5_context context,
558 krb5_const_principal principal,
559 char **name,
560 int flags)
562 size_t len = 0, plen;
563 size_t i;
564 krb5_error_code ret;
565 /* count length */
566 if (princ_realm(principal)) {
567 plen = strlen(princ_realm(principal));
569 if(strcspn(princ_realm(principal), quotable_chars) == plen)
570 len += plen;
571 else
572 len += 2*plen;
573 len++; /* '@' */
575 for(i = 0; i < princ_num_comp(principal); i++){
576 plen = strlen(princ_ncomp(principal, i));
577 if(strcspn(princ_ncomp(principal, i), quotable_chars) == plen)
578 len += plen;
579 else
580 len += 2*plen;
581 len++;
583 len++; /* '\0' */
584 *name = malloc(len);
585 if(*name == NULL)
586 return krb5_enomem(context);
587 ret = unparse_name_fixed(context, principal, *name, len, flags);
588 if(ret) {
589 free(*name);
590 *name = NULL;
592 return ret;
596 * Unparse the Kerberos name into a string
598 * @param context Kerberos 5 context
599 * @param principal principal to query
600 * @param name resulting string, free with krb5_xfree()
602 * @return An krb5 error code, see krb5_get_error_message().
604 * @ingroup krb5_principal
607 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
608 krb5_unparse_name(krb5_context context,
609 krb5_const_principal principal,
610 char **name)
612 return unparse_name(context, principal, name, 0);
616 * Unparse the Kerberos name into a string
618 * @param context Kerberos 5 context
619 * @param principal principal to query
620 * @param flags flag to determine the behavior
621 * @param name resulting string, free with krb5_xfree()
623 * @return An krb5 error code, see krb5_get_error_message().
625 * @ingroup krb5_principal
628 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
629 krb5_unparse_name_flags(krb5_context context,
630 krb5_const_principal principal,
631 int flags,
632 char **name)
634 return unparse_name(context, principal, name, flags);
638 * Unparse the principal name to a allocated buffer. The realm is
639 * skipped if its a default realm.
641 * @param context A Kerberos context.
642 * @param principal principal to unparse
643 * @param name returned buffer, free with krb5_xfree()
645 * @return An krb5 error code, see krb5_get_error_message().
647 * @ingroup krb5_principal
650 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
651 krb5_unparse_name_short(krb5_context context,
652 krb5_const_principal principal,
653 char **name)
655 return unparse_name(context, principal, name, KRB5_PRINCIPAL_UNPARSE_SHORT);
659 * Set a new realm for a principal, and as a side-effect free the
660 * previous realm.
662 * @param context A Kerberos context.
663 * @param principal principal set the realm for
664 * @param realm the new realm to set
666 * @return An krb5 error code, see krb5_get_error_message().
668 * @ingroup krb5_principal
671 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
672 krb5_principal_set_realm(krb5_context context,
673 krb5_principal principal,
674 krb5_const_realm realm)
676 if (princ_realm(principal))
677 free(princ_realm(principal));
679 if (realm == NULL)
680 princ_realm(principal) = NULL;
681 else if ((princ_realm(principal) = strdup(realm)) == NULL)
682 return krb5_enomem(context);
683 return 0;
686 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
687 krb5_principal_set_comp_string(krb5_context context,
688 krb5_principal principal,
689 unsigned int k,
690 const char *component)
692 char *s;
693 size_t i;
695 for (i = princ_num_comp(principal); i <= k; i++)
696 append_component(context, principal, "", 0);
697 s = strdup(component);
698 if (s == NULL)
699 return krb5_enomem(context);
700 free(princ_ncomp(principal, k));
701 princ_ncomp(principal, k) = s;
702 return 0;
705 #ifndef HEIMDAL_SMALLER
707 * Build a principal using vararg style building
709 * @param context A Kerberos context.
710 * @param principal returned principal
711 * @param rlen length of realm
712 * @param realm realm name
713 * @param ... a list of components ended with NULL.
715 * @return An krb5 error code, see krb5_get_error_message().
717 * @ingroup krb5_principal
720 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
721 krb5_build_principal(krb5_context context,
722 krb5_principal *principal,
723 int rlen,
724 krb5_const_realm realm,
725 ...)
727 krb5_error_code ret;
728 va_list ap;
729 va_start(ap, realm);
730 ret = krb5_build_principal_va(context, principal, rlen, realm, ap);
731 va_end(ap);
732 return ret;
734 #endif
737 * Build a principal using vararg style building
739 * @param context A Kerberos context.
740 * @param principal returned principal
741 * @param realm realm name
742 * @param ... a list of components ended with NULL.
744 * @return An krb5 error code, see krb5_get_error_message().
746 * @ingroup krb5_principal
749 /* coverity[+alloc : arg-*1] */
750 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
751 krb5_make_principal(krb5_context context,
752 krb5_principal *principal,
753 krb5_const_realm realm,
754 ...)
756 krb5_error_code ret;
757 krb5_realm r = NULL;
758 va_list ap;
759 if(realm == NULL) {
760 ret = krb5_get_default_realm(context, &r);
761 if(ret)
762 return ret;
763 realm = r;
765 va_start(ap, realm);
766 ret = krb5_build_principal_va(context, principal, strlen(realm), realm, ap);
767 va_end(ap);
768 if(r)
769 krb5_free_default_realm(context, r);
770 return ret;
773 static krb5_error_code
774 append_component(krb5_context context, krb5_principal p,
775 const char *comp,
776 size_t comp_len)
778 heim_general_string *tmp;
779 size_t len = princ_num_comp(p);
781 tmp = realloc(princ_comp(p), (len + 1) * sizeof(*tmp));
782 if(tmp == NULL)
783 return krb5_enomem(context);
784 princ_comp(p) = tmp;
785 princ_ncomp(p, len) = malloc(comp_len + 1);
786 if (princ_ncomp(p, len) == NULL)
787 return krb5_enomem(context);
788 memcpy (princ_ncomp(p, len), comp, comp_len);
789 princ_ncomp(p, len)[comp_len] = '\0';
790 princ_num_comp(p)++;
791 return 0;
794 static krb5_error_code
795 va_ext_princ(krb5_context context, krb5_principal p, va_list ap)
797 krb5_error_code ret = 0;
799 while (1){
800 const char *s;
801 int len;
803 if ((len = va_arg(ap, int)) == 0)
804 break;
805 s = va_arg(ap, const char*);
806 if ((ret = append_component(context, p, s, len)) != 0)
807 break;
809 return ret;
812 static krb5_error_code
813 va_princ(krb5_context context, krb5_principal p, va_list ap)
815 krb5_error_code ret = 0;
817 while (1){
818 const char *s;
820 if ((s = va_arg(ap, const char*)) == NULL)
821 break;
822 if ((ret = append_component(context, p, s, strlen(s))) != 0)
823 break;
825 return ret;
828 static krb5_error_code
829 build_principal(krb5_context context,
830 krb5_principal *principal,
831 int rlen,
832 krb5_const_realm realm,
833 krb5_error_code (*func)(krb5_context, krb5_principal, va_list),
834 va_list ap)
836 krb5_error_code ret;
837 krb5_principal p;
839 *principal = NULL;
840 p = calloc(1, sizeof(*p));
841 if (p == NULL)
842 return krb5_enomem(context);
844 princ_realm(p) = strdup(realm);
845 if (p->realm == NULL) {
846 free(p);
847 return krb5_enomem(context);
850 ret = func(context, p, ap);
851 if (ret == 0) {
852 *principal = p;
853 set_default_princ_type(p, KRB5_NT_PRINCIPAL);
854 } else
855 krb5_free_principal(context, p);
856 return ret;
859 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
860 krb5_build_principal_va(krb5_context context,
861 krb5_principal *principal,
862 int rlen,
863 krb5_const_realm realm,
864 va_list ap)
866 return build_principal(context, principal, rlen, realm, va_princ, ap);
869 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
870 krb5_build_principal_va_ext(krb5_context context,
871 krb5_principal *principal,
872 int rlen,
873 krb5_const_realm realm,
874 va_list ap)
876 return build_principal(context, principal, rlen, realm, va_ext_princ, ap);
880 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
881 krb5_build_principal_ext(krb5_context context,
882 krb5_principal *principal,
883 int rlen,
884 krb5_const_realm realm,
885 ...)
887 krb5_error_code ret;
888 va_list ap;
889 va_start(ap, realm);
890 ret = krb5_build_principal_va_ext(context, principal, rlen, realm, ap);
891 va_end(ap);
892 return ret;
896 * Copy a principal
898 * @param context A Kerberos context.
899 * @param inprinc principal to copy
900 * @param outprinc copied principal, free with krb5_free_principal()
902 * @return An krb5 error code, see krb5_get_error_message().
904 * @ingroup krb5_principal
908 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
909 krb5_copy_principal(krb5_context context,
910 krb5_const_principal inprinc,
911 krb5_principal *outprinc)
913 krb5_principal p = malloc(sizeof(*p));
914 if (p == NULL)
915 return krb5_enomem(context);
916 if(copy_Principal(inprinc, p)) {
917 free(p);
918 return krb5_enomem(context);
920 *outprinc = p;
921 return 0;
925 * Return TRUE iff princ1 == princ2 (without considering the realm)
927 * @param context Kerberos 5 context
928 * @param princ1 first principal to compare
929 * @param princ2 second principal to compare
931 * @return non zero if equal, 0 if not
933 * @ingroup krb5_principal
934 * @see krb5_principal_compare()
935 * @see krb5_realm_compare()
938 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
939 krb5_principal_compare_any_realm(krb5_context context,
940 krb5_const_principal princ1,
941 krb5_const_principal princ2)
943 size_t i;
944 if(princ_num_comp(princ1) != princ_num_comp(princ2))
945 return FALSE;
946 for(i = 0; i < princ_num_comp(princ1); i++){
947 if(strcmp(princ_ncomp(princ1, i), princ_ncomp(princ2, i)) != 0)
948 return FALSE;
950 return TRUE;
953 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
954 _krb5_principal_compare_PrincipalName(krb5_context context,
955 krb5_const_principal princ1,
956 PrincipalName *princ2)
958 size_t i;
959 if (princ_num_comp(princ1) != princ2->name_string.len)
960 return FALSE;
961 for(i = 0; i < princ_num_comp(princ1); i++){
962 if(strcmp(princ_ncomp(princ1, i), princ2->name_string.val[i]) != 0)
963 return FALSE;
965 return TRUE;
970 * Compares the two principals, including realm of the principals and returns
971 * TRUE if they are the same and FALSE if not.
973 * @param context Kerberos 5 context
974 * @param princ1 first principal to compare
975 * @param princ2 second principal to compare
977 * @ingroup krb5_principal
978 * @see krb5_principal_compare_any_realm()
979 * @see krb5_realm_compare()
983 * return TRUE iff princ1 == princ2
986 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
987 krb5_principal_compare(krb5_context context,
988 krb5_const_principal princ1,
989 krb5_const_principal princ2)
991 if (!krb5_realm_compare(context, princ1, princ2))
992 return FALSE;
993 return krb5_principal_compare_any_realm(context, princ1, princ2);
997 * return TRUE iff realm(princ1) == realm(princ2)
999 * @param context Kerberos 5 context
1000 * @param princ1 first principal to compare
1001 * @param princ2 second principal to compare
1003 * @ingroup krb5_principal
1004 * @see krb5_principal_compare_any_realm()
1005 * @see krb5_principal_compare()
1008 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1009 krb5_realm_compare(krb5_context context,
1010 krb5_const_principal princ1,
1011 krb5_const_principal princ2)
1013 return strcmp(princ_realm(princ1), princ_realm(princ2)) == 0;
1017 * return TRUE iff princ matches pattern
1019 * @ingroup krb5_principal
1022 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1023 krb5_principal_match(krb5_context context,
1024 krb5_const_principal princ,
1025 krb5_const_principal pattern)
1027 size_t i;
1028 if(princ_num_comp(princ) != princ_num_comp(pattern))
1029 return FALSE;
1030 if(fnmatch(princ_realm(pattern), princ_realm(princ), 0) != 0)
1031 return FALSE;
1032 for(i = 0; i < princ_num_comp(princ); i++){
1033 if(fnmatch(princ_ncomp(pattern, i), princ_ncomp(princ, i), 0) != 0)
1034 return FALSE;
1036 return TRUE;
1040 * This is the original krb5_sname_to_principal(), renamed to be a
1041 * helper of the new one.
1043 static krb5_error_code
1044 krb5_sname_to_principal_old(krb5_context context,
1045 const char *realm,
1046 const char *hostname,
1047 const char *sname,
1048 int32_t type,
1049 krb5_principal *ret_princ)
1051 krb5_error_code ret;
1052 char localhost[MAXHOSTNAMELEN];
1053 char **realms = NULL, *host = NULL;
1055 if(type != KRB5_NT_SRV_HST && type != KRB5_NT_UNKNOWN) {
1056 krb5_set_error_message(context, KRB5_SNAME_UNSUPP_NAMETYPE,
1057 N_("unsupported name type %d", ""),
1058 (int)type);
1059 return KRB5_SNAME_UNSUPP_NAMETYPE;
1061 if(hostname == NULL) {
1062 ret = gethostname(localhost, sizeof(localhost) - 1);
1063 if (ret != 0) {
1064 ret = errno;
1065 krb5_set_error_message(context, ret,
1066 N_("Failed to get local hostname", ""));
1067 return ret;
1069 localhost[sizeof(localhost) - 1] = '\0';
1070 hostname = localhost;
1072 if(sname == NULL)
1073 sname = "host";
1074 if(type == KRB5_NT_SRV_HST) {
1075 if (realm)
1076 ret = krb5_expand_hostname(context, hostname, &host);
1077 else
1078 ret = krb5_expand_hostname_realms(context, hostname,
1079 &host, &realms);
1080 if (ret)
1081 return ret;
1082 strlwr(host);
1083 hostname = host;
1084 if (!realm)
1085 realm = realms[0];
1086 } else if (!realm) {
1087 ret = krb5_get_host_realm(context, hostname, &realms);
1088 if(ret)
1089 return ret;
1090 realm = realms[0];
1093 ret = krb5_make_principal(context, ret_princ, realm, sname,
1094 hostname, NULL);
1095 if(host)
1096 free(host);
1097 if (realms)
1098 krb5_free_host_realm(context, realms);
1099 return ret;
1102 static const struct {
1103 const char *type;
1104 int32_t value;
1105 } nametypes[] = {
1106 { "UNKNOWN", KRB5_NT_UNKNOWN },
1107 { "PRINCIPAL", KRB5_NT_PRINCIPAL },
1108 { "SRV_INST", KRB5_NT_SRV_INST },
1109 { "SRV_HST", KRB5_NT_SRV_HST },
1110 { "SRV_XHST", KRB5_NT_SRV_XHST },
1111 { "UID", KRB5_NT_UID },
1112 { "X500_PRINCIPAL", KRB5_NT_X500_PRINCIPAL },
1113 { "SMTP_NAME", KRB5_NT_SMTP_NAME },
1114 { "ENTERPRISE_PRINCIPAL", KRB5_NT_ENTERPRISE_PRINCIPAL },
1115 { "WELLKNOWN", KRB5_NT_WELLKNOWN },
1116 { "SRV_HST_DOMAIN", KRB5_NT_SRV_HST_DOMAIN },
1117 { "ENT_PRINCIPAL_AND_ID", KRB5_NT_ENT_PRINCIPAL_AND_ID },
1118 { "MS_PRINCIPAL", KRB5_NT_MS_PRINCIPAL },
1119 { "MS_PRINCIPAL_AND_ID", KRB5_NT_MS_PRINCIPAL_AND_ID },
1120 { "SRV_HST_NEEDS_CANON", KRB5_NT_SRV_HST_NEEDS_CANON },
1121 { NULL, 0 }
1125 * Parse nametype string and return a nametype integer
1127 * @ingroup krb5_principal
1130 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1131 krb5_parse_nametype(krb5_context context, const char *str, int32_t *nametype)
1133 size_t i;
1135 for(i = 0; nametypes[i].type; i++) {
1136 if (strcasecmp(nametypes[i].type, str) == 0) {
1137 *nametype = nametypes[i].value;
1138 return 0;
1141 krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
1142 N_("Failed to find name type %s", ""), str);
1143 return KRB5_PARSE_MALFORMED;
1147 * Returns true if name is Kerberos NULL name
1149 * @ingroup krb5_principal
1152 krb5_boolean KRB5_LIB_FUNCTION
1153 krb5_principal_is_null(krb5_context context, krb5_const_principal principal)
1155 if (principal->name.name_type == KRB5_NT_WELLKNOWN &&
1156 principal->name.name_string.len == 2 &&
1157 strcmp(principal->name.name_string.val[0], "WELLKNOWN") == 0 &&
1158 strcmp(principal->name.name_string.val[1], "NULL") == 0)
1159 return TRUE;
1160 return FALSE;
1163 const char _krb5_wellknown_lkdc[] = "WELLKNOWN:COM.APPLE.LKDC";
1164 static const char lkdc_prefix[] = "LKDC:";
1167 * Returns true if name is Kerberos an LKDC realm
1169 * @ingroup krb5_principal
1172 krb5_boolean KRB5_LIB_FUNCTION
1173 krb5_realm_is_lkdc(const char *realm)
1176 return strncmp(realm, lkdc_prefix, sizeof(lkdc_prefix)-1) == 0 ||
1177 strncmp(realm, _krb5_wellknown_lkdc, sizeof(_krb5_wellknown_lkdc) - 1) == 0;
1181 * Returns true if name is Kerberos an LKDC realm
1183 * @ingroup krb5_principal
1186 krb5_boolean KRB5_LIB_FUNCTION
1187 krb5_principal_is_lkdc(krb5_context context, krb5_const_principal principal)
1189 return krb5_realm_is_lkdc(principal->realm);
1193 * Returns true if name is Kerberos an LKDC realm
1195 * @ingroup krb5_principal
1198 krb5_boolean KRB5_LIB_FUNCTION
1199 krb5_principal_is_pku2u(krb5_context context, krb5_const_principal principal)
1201 return strcmp(principal->realm, KRB5_PKU2U_REALM_NAME) == 0;
1205 * Check if the cname part of the principal is a krbtgt principal
1207 * @ingroup krb5_principal
1210 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1211 krb5_principal_is_krbtgt(krb5_context context, krb5_const_principal p)
1213 return p->name.name_string.len == 2 &&
1214 strcmp(p->name.name_string.val[0], KRB5_TGS_NAME) == 0;
1218 * Returns true iff name is an WELLKNOWN:ORG.H5L.HOSTBASED-SERVICE
1220 * @ingroup krb5_principal
1223 krb5_boolean KRB5_LIB_FUNCTION
1224 krb5_principal_is_gss_hostbased_service(krb5_context context,
1225 krb5_const_principal principal)
1227 if (principal == NULL)
1228 return FALSE;
1229 if (principal->name.name_string.len != 2)
1230 return FALSE;
1231 if (strcmp(principal->name.name_string.val[1], KRB5_GSS_HOSTBASED_SERVICE_NAME) != 0)
1232 return FALSE;
1233 return TRUE;
1237 * Check if the cname part of the principal is a initial or renewed krbtgt principal
1239 * @ingroup krb5_principal
1242 krb5_boolean KRB5_LIB_FUNCTION
1243 krb5_principal_is_root_krbtgt(krb5_context context, krb5_const_principal p)
1245 return p->name.name_string.len == 2 &&
1246 strcmp(p->name.name_string.val[0], KRB5_TGS_NAME) == 0 &&
1247 strcmp(p->name.name_string.val[1], p->realm) == 0;
1250 static int
1251 tolower_ascii(int c)
1253 if (c >= 'A' && c <= 'Z')
1254 return 'a' + (c - 'A');
1255 return c;
1258 typedef enum krb5_name_canon_rule_type {
1259 KRB5_NCRT_BOGUS = 0,
1260 KRB5_NCRT_AS_IS,
1261 KRB5_NCRT_QUALIFY,
1262 KRB5_NCRT_NSS
1263 } krb5_name_canon_rule_type;
1265 #ifdef UINT8_MAX
1266 #define MAXDOTS UINT8_MAX
1267 #else
1268 #define MAXDOTS (255U)
1269 #endif
1270 #ifdef UINT16_MAX
1271 #define MAXORDER UINT16_MAX
1272 #else
1273 #define MAXORDER (65535U)
1274 #endif
1276 struct krb5_name_canon_rule_data {
1277 krb5_name_canon_rule_type type;
1278 krb5_name_canon_rule_options options;
1279 uint8_t mindots; /* match this many dots or more */
1280 uint8_t maxdots; /* match no more than this many dots */
1281 uint16_t explicit_order; /* given order */
1282 uint16_t order; /* actual order */
1283 char *match_domain; /* match this stem */
1284 char *match_realm; /* match this realm */
1285 char *domain; /* qualify with this domain */
1286 char *realm; /* qualify with this realm */
1290 * Create a principal for the given service running on the given
1291 * hostname. If KRB5_NT_SRV_HST is used, the hostname is canonicalized
1292 * according the configured name canonicalization rules, with
1293 * canonicalization delayed in some cases. One rule involves DNS, which
1294 * is insecure unless DNSSEC is used, but we don't use DNSSEC-capable
1295 * resolver APIs here, so that if DNSSEC is used we wouldn't know it.
1297 * Canonicalization is immediate (not delayed) only when there is only
1298 * one canonicalization rule and that rule indicates that we should do a
1299 * host lookup by name (i.e., DNS).
1301 * @param context A Kerberos context.
1302 * @param hostname hostname to use
1303 * @param sname Service name to use
1304 * @param type name type of principal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN.
1305 * @param ret_princ return principal, free with krb5_free_principal().
1307 * @return An krb5 error code, see krb5_get_error_message().
1309 * @ingroup krb5_principal
1312 /* coverity[+alloc : arg-*4] */
1313 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1314 krb5_sname_to_principal(krb5_context context,
1315 const char *hostname,
1316 const char *sname,
1317 int32_t type,
1318 krb5_principal *ret_princ)
1320 char *realm, *remote_host;
1321 krb5_error_code ret;
1322 register char *cp;
1323 char localname[MAXHOSTNAMELEN];
1325 *ret_princ = NULL;
1327 if ((type != KRB5_NT_UNKNOWN) &&
1328 (type != KRB5_NT_SRV_HST))
1329 return KRB5_SNAME_UNSUPP_NAMETYPE;
1331 /* if hostname is NULL, use local hostname */
1332 if (hostname == NULL) {
1333 if (gethostname(localname, MAXHOSTNAMELEN))
1334 return errno;
1335 hostname = localname;
1338 /* if sname is NULL, use "host" */
1339 if (sname == NULL)
1340 sname = "host";
1342 remote_host = strdup(hostname);
1343 if (remote_host == NULL)
1344 return krb5_enomem(context);
1346 if (type == KRB5_NT_SRV_HST) {
1347 krb5_name_canon_rule rules;
1349 /* Lower-case the hostname, because that's the convention */
1350 for (cp = remote_host; *cp; cp++)
1351 if (isupper((int) (*cp)))
1352 *cp = tolower((int) (*cp));
1355 * If there is only one name canon rule and it says to
1356 * canonicalize the old way, do that now, as we used to.
1358 ret = _krb5_get_name_canon_rules(context, &rules);
1359 if (ret) {
1360 _krb5_debug(context, 5, "Failed to get name canon rules: ret = %d",
1361 ret);
1362 free(remote_host);
1363 return ret;
1365 if (rules[0].type == KRB5_NCRT_NSS &&
1366 rules[1].type == KRB5_NCRT_BOGUS) {
1367 _krb5_debug(context, 5, "Using nss for name canon immediately");
1368 ret = krb5_sname_to_principal_old(context, rules[0].realm,
1369 remote_host, sname,
1370 KRB5_NT_SRV_HST, ret_princ);
1371 free(remote_host);
1372 return ret;
1376 /* Remove trailing dots */
1377 if (remote_host[0]) {
1378 for (cp = remote_host + strlen(remote_host)-1;
1379 *cp == '.' && cp > remote_host;
1380 cp--) {
1381 *cp = '\0';
1385 realm = ""; /* "Referral realm" */
1387 ret = krb5_build_principal(context, ret_princ, strlen(realm),
1388 realm, sname, remote_host,
1389 (char *)0);
1391 if (ret == 0 && type == KRB5_NT_SRV_HST) {
1393 * Hostname canonicalization is done elsewhere (in
1394 * krb5_get_credentials() and krb5_kt_get_entry()).
1396 * We overload the name type to indicate to those functions that
1397 * this principal name requires canonicalization.
1399 * We can't use the empty realm to denote the need to
1400 * canonicalize the hostname too: it would mean that users who
1401 * want to assert knowledge of a service's realm must also know
1402 * the canonical hostname, but in practice they don't.
1404 (*ret_princ)->name.name_type = KRB5_NT_SRV_HST_NEEDS_CANON;
1406 _krb5_debug(context, 5, "Building a delayed canon principal for %s/%s@",
1407 sname, remote_host);
1410 free(remote_host);
1411 return ret;
1414 static void
1415 tolower_str(char *s)
1417 for (; *s != '\0'; s++) {
1418 if (isupper(*s))
1419 *s = tolower_ascii(*s);
1423 static krb5_error_code
1424 rule_parse_token(krb5_context context, krb5_name_canon_rule rule,
1425 const char *tok)
1427 long int n;
1428 int needs_type = rule->type == KRB5_NCRT_BOGUS;
1431 * Rules consist of a sequence of tokens, some of which indicate
1432 * what type of rule the rule is, and some of which set rule options
1433 * or ancilliary data. Last rule type token wins.
1436 /* Rule type tokens: */
1437 if (needs_type && strcmp(tok, "as-is") == 0) {
1438 rule->type = KRB5_NCRT_AS_IS;
1439 } else if (needs_type && strcmp(tok, "qualify") == 0) {
1440 rule->type = KRB5_NCRT_QUALIFY;
1441 } else if (needs_type && strcmp(tok, "nss") == 0) {
1442 rule->type = KRB5_NCRT_NSS;
1443 /* Rule options: */
1444 } else if (strcmp(tok, "use_fast") == 0) {
1445 rule->options |= KRB5_NCRO_USE_FAST;
1446 } else if (strcmp(tok, "use_dnssec") == 0) {
1447 rule->options |= KRB5_NCRO_USE_DNSSEC;
1448 } else if (strcmp(tok, "ccache_only") == 0) {
1449 rule->options |= KRB5_NCRO_GC_ONLY;
1450 } else if (strcmp(tok, "no_referrals") == 0) {
1451 rule->options |= KRB5_NCRO_NO_REFERRALS;
1452 } else if (strcmp(tok, "use_referrals") == 0) {
1453 rule->options &= ~KRB5_NCRO_NO_REFERRALS;
1454 if (rule->realm == NULL) {
1455 rule->realm = strdup("");
1456 if (rule->realm == NULL)
1457 return krb5_enomem(context);
1459 } else if (strcmp(tok, "lookup_realm") == 0) {
1460 rule->options |= KRB5_NCRO_LOOKUP_REALM;
1461 free(rule->realm);
1462 rule->realm = NULL;
1463 /* Rule ancilliary data: */
1464 } else if (strncmp(tok, "domain=", strlen("domain=")) == 0) {
1465 free(rule->domain);
1466 rule->domain = strdup(tok + strlen("domain="));
1467 if (rule->domain == NULL)
1468 return krb5_enomem(context);
1469 tolower_str(rule->domain);
1470 } else if (strncmp(tok, "realm=", strlen("realm=")) == 0) {
1471 free(rule->realm);
1472 rule->realm = strdup(tok + strlen("realm="));
1473 if (rule->realm == NULL)
1474 return krb5_enomem(context);
1475 } else if (strncmp(tok, "match_domain=", strlen("match_domain=")) == 0) {
1476 free(rule->match_domain);
1477 rule->match_domain = strdup(tok + strlen("match_domain="));
1478 if (rule->match_domain == NULL)
1479 return krb5_enomem(context);
1480 tolower_str(rule->match_domain);
1481 } else if (strncmp(tok, "match_realm=", strlen("match_realm=")) == 0) {
1482 free(rule->match_realm);
1483 rule->match_realm = strdup(tok + strlen("match_realm="));
1484 if (rule->match_realm == NULL)
1485 return krb5_enomem(context);
1486 } else if (strncmp(tok, "mindots=", strlen("mindots=")) == 0) {
1487 errno = 0;
1488 n = strtol(tok + strlen("mindots="), NULL, 10);
1489 if (errno == 0 && n > 0 && n <= MAXDOTS)
1490 rule->mindots = n;
1491 } else if (strncmp(tok, "maxdots=", strlen("maxdots=")) == 0) {
1492 errno = 0;
1493 n = strtol(tok + strlen("maxdots="), NULL, 10);
1494 if (errno == 0 && n > 0 && n <= MAXDOTS)
1495 rule->maxdots = n;
1496 } else if (strncmp(tok, "order=", strlen("order=")) == 0) {
1497 errno = 0;
1498 n = strtol(tok + strlen("order="), NULL, 10);
1499 if (errno == 0 && n > 0 && n <= MAXORDER)
1500 rule->explicit_order = n;
1501 } else {
1502 _krb5_debug(context, 5,
1503 "Unrecognized name canonicalization rule token %s", tok);
1504 return EINVAL;
1506 return 0;
1509 static int
1510 rule_cmp(const void *a, const void *b)
1512 krb5_const_name_canon_rule left = a;
1513 krb5_const_name_canon_rule right = b;
1515 if (left->type == KRB5_NCRT_BOGUS &&
1516 right->type == KRB5_NCRT_BOGUS)
1517 return 0;
1518 if (left->type == KRB5_NCRT_BOGUS)
1519 return 1;
1520 if (right->type == KRB5_NCRT_BOGUS)
1521 return -1;
1522 if (left->explicit_order < right->explicit_order)
1523 return -1;
1524 if (left->explicit_order > right->explicit_order)
1525 return 1;
1526 return left->order - right->order;
1529 static krb5_error_code
1530 parse_name_canon_rules(krb5_context context, char **rulestrs,
1531 krb5_name_canon_rule *rules)
1533 krb5_error_code ret;
1534 char *tok;
1535 char *cp;
1536 char **cpp;
1537 size_t n;
1538 size_t i, k;
1539 int do_sort = 0;
1540 krb5_name_canon_rule r;
1542 *rules = NULL;
1544 for (n =0, cpp = rulestrs; cpp != NULL && *cpp != NULL; cpp++)
1545 n++;
1547 n += 2; /* Always at least one rule; two for the default case */
1549 if ((r = calloc(n, sizeof (*r))) == NULL)
1550 return krb5_enomem(context);
1552 for (k = 0; k < n; k++) {
1553 r[k].type = KRB5_NCRT_BOGUS;
1554 r[k].match_domain = NULL;
1555 r[k].match_realm = NULL;
1556 r[k].domain = NULL;
1557 r[k].realm = NULL;
1560 for (i = 0, k = 0; i < n && rulestrs != NULL && rulestrs[i] != NULL; i++) {
1561 cp = rulestrs[i];
1562 r[k].explicit_order = MAXORDER; /* mark order, see below */
1563 r[k].maxdots = MAXDOTS;
1564 r[k].order = k; /* default order */
1566 /* Tokenize and parse value */
1567 do {
1568 tok = cp;
1569 cp = strchr(cp, ':'); /* XXX use strtok_r() */
1570 if (cp)
1571 *cp++ = '\0'; /* delimit token */
1572 ret = rule_parse_token(context, &r[k], tok);
1573 if (ret == EINVAL) {
1574 r[k].type = KRB5_NCRT_BOGUS;
1575 break;
1577 if (ret) {
1578 _krb5_free_name_canon_rules(context, r);
1579 return ret;
1581 } while (cp && *cp);
1582 if (r[k].explicit_order != MAXORDER)
1583 do_sort = 1;
1585 /* Validate parsed rule */
1586 if (r[k].type == KRB5_NCRT_BOGUS ||
1587 (r[k].type == KRB5_NCRT_QUALIFY && !r[k].domain) ||
1588 (r[k].type == KRB5_NCRT_NSS && r[k].domain)) {
1589 /* Invalid rule; mark it so and clean up */
1590 r[k].type = KRB5_NCRT_BOGUS;
1591 free(r[k].match_domain);
1592 free(r[k].match_realm);
1593 free(r[k].domain);
1594 free(r[k].realm);
1595 r[k].realm = NULL;
1596 r[k].domain = NULL;
1597 r[k].match_domain = NULL;
1598 r[k].match_realm = NULL;
1599 _krb5_debug(context, 5,
1600 "Ignoring invalid name canonicalization rule %lu",
1601 (unsigned long)i);
1602 continue;
1604 k++; /* good rule */
1607 if (do_sort) {
1609 * Note that we make make this a stable sort by using appareance
1610 * and explicit order.
1612 qsort(r, n, sizeof(r[0]), rule_cmp);
1615 if (r[0].type == KRB5_NCRT_BOGUS) {
1616 /* No rules, or no valid rules */
1617 r[0].type = KRB5_NCRT_NSS;
1620 *rules = r;
1621 return 0; /* We don't communicate bad rule errors here */
1625 * This exists only because the hostname canonicalization behavior in Heimdal
1626 * (and other implementations of Kerberos) has been to use getaddrinfo(),
1627 * unsafe though it is, for ages. We can't fix it in one day.
1629 static void
1630 make_rules_safe(krb5_context context, krb5_name_canon_rule rules)
1633 * If the only rule were to use the name service (getaddrinfo()) then we're
1634 * bound to fail. We could try to convert that rule to an as-is rule, but
1635 * when we do get a validating resolver we'd be unhappy that we did such a
1636 * conversion. Better let the user get failures and make them think about
1637 * their naming rules.
1639 if (rules == NULL)
1640 return;
1641 for (; rules[0].type != KRB5_NCRT_BOGUS; rules++) {
1642 if (rules->type == KRB5_NCRT_NSS)
1643 rules->options |= KRB5_NCRO_USE_DNSSEC;
1644 else
1645 rules->options |= KRB5_NCRO_USE_FAST;
1650 * This function returns an array of host-based service name
1651 * canonicalization rules. The array of rules is organized as a list.
1652 * See the definition of krb5_name_canon_rule.
1654 * @param context A Kerberos context.
1655 * @param rules Output location for array of rules.
1657 KRB5_LIB_FUNCTION krb5_error_code
1658 _krb5_get_name_canon_rules(krb5_context context, krb5_name_canon_rule *rules)
1660 krb5_error_code ret;
1661 char **values = NULL;
1663 *rules = context->name_canon_rules;
1664 if (*rules != NULL)
1665 return 0;
1667 values = krb5_config_get_strings(context, NULL,
1668 "libdefaults", "name_canon_rules", NULL);
1669 ret = parse_name_canon_rules(context, values, rules);
1670 krb5_config_free_strings(values);
1671 if (ret)
1672 return ret;
1674 if (krb5_config_get_bool_default(context, NULL, FALSE,
1675 "libdefaults", "safe_name_canon", NULL))
1676 make_rules_safe(context, *rules);
1678 heim_assert(rules != NULL && (*rules)[0].type != KRB5_NCRT_BOGUS,
1679 "internal error in parsing principal name "
1680 "canonicalization rules");
1682 /* Memoize */
1683 context->name_canon_rules = *rules;
1685 return 0;
1688 static krb5_error_code
1689 get_host_realm(krb5_context context, const char *hostname, char **realm)
1691 krb5_error_code ret;
1692 char **hrealms = NULL;
1694 *realm = NULL;
1695 ret = krb5_get_host_realm(context, hostname, &hrealms);
1696 if (ret)
1697 return ret;
1698 if (hrealms == NULL)
1699 return KRB5_ERR_HOST_REALM_UNKNOWN; /* krb5_set_error() already done */
1700 if (hrealms[0] == NULL) {
1701 krb5_free_host_realm(context, hrealms);
1702 return KRB5_ERR_HOST_REALM_UNKNOWN; /* krb5_set_error() already done */
1704 *realm = strdup(hrealms[0]);
1705 krb5_free_host_realm(context, hrealms);
1706 if (*realm == NULL)
1707 return krb5_enomem(context);
1708 return 0;
1711 static int
1712 is_domain_suffix(const char *domain, const char *suffix)
1714 size_t dlen = strlen(domain);
1715 size_t slen = strlen(suffix);
1717 if (dlen < slen + 2)
1718 return 0;
1720 if (strcasecmp(domain + (dlen - slen), suffix) != 0)
1721 return 0;
1723 if (domain[(dlen - slen) - 1] != '.')
1724 return 0;
1725 return 1;
1729 * Applies a name canonicalization rule to a principal.
1731 * Returns zero and no out_princ if the rule does not match.
1732 * Returns zero and an out_princ if the rule does match.
1734 static krb5_error_code
1735 apply_name_canon_rule(krb5_context context, krb5_name_canon_rule rules,
1736 size_t rule_idx, krb5_const_principal in_princ,
1737 krb5_principal *out_princ,
1738 krb5_name_canon_rule_options *rule_opts)
1740 krb5_name_canon_rule rule = &rules[rule_idx];
1741 krb5_error_code ret;
1742 unsigned int ndots = 0;
1743 krb5_principal nss = NULL;
1744 const char *sname = NULL;
1745 const char *orig_hostname = NULL;
1746 const char *new_hostname = NULL;
1747 const char *new_realm = NULL;
1748 const char *port = "";
1749 const char *cp;
1750 char *hostname_sans_port = NULL;
1751 char *hostname_with_port = NULL;
1752 char *tmp_hostname = NULL;
1753 char *tmp_realm = NULL;
1755 *out_princ = NULL; /* Signal no match */
1757 if (rule_opts != NULL)
1758 *rule_opts = rule->options;
1760 if (rule->type == KRB5_NCRT_BOGUS)
1761 return 0; /* rule doesn't apply */
1763 sname = krb5_principal_get_comp_string(context, in_princ, 0);
1764 orig_hostname = krb5_principal_get_comp_string(context, in_princ, 1);
1767 * Some apps want to use the very non-standard svc/hostname:port@REALM
1768 * form. We do our best to support that here :(
1770 port = strchr(orig_hostname, ':');
1771 if (port != NULL) {
1772 hostname_sans_port = strndup(orig_hostname, port - orig_hostname);
1773 if (hostname_sans_port == NULL)
1774 return krb5_enomem(context);
1775 orig_hostname = hostname_sans_port;
1778 _krb5_debug(context, 5, N_("Applying a name rule (type %d) to %s", ""),
1779 rule->type, orig_hostname);
1781 if (rule->mindots > 0 || rule->maxdots > 0) {
1782 for (cp = strchr(orig_hostname, '.'); cp && *cp; cp = strchr(cp + 1, '.'))
1783 ndots++;
1785 if (rule->mindots > 0 && ndots < rule->mindots)
1786 return 0;
1787 if (ndots > rule->maxdots)
1788 return 0;
1790 if (rule->match_domain != NULL &&
1791 !is_domain_suffix(orig_hostname, rule->match_domain))
1792 return 0;
1794 if (rule->match_realm != NULL &&
1795 strcmp(rule->match_realm, in_princ->realm) != 0)
1796 return 0;
1798 new_realm = rule->realm;
1799 switch (rule->type) {
1800 case KRB5_NCRT_AS_IS:
1801 break;
1803 case KRB5_NCRT_QUALIFY:
1804 heim_assert(rule->domain != NULL,
1805 "missing domain for qualify name canon rule");
1806 if (asprintf(&tmp_hostname, "%s.%s", orig_hostname,
1807 rule->domain) == -1 || tmp_hostname == NULL) {
1808 ret = krb5_enomem(context);
1809 goto out;
1811 new_hostname = tmp_hostname;
1812 break;
1814 case KRB5_NCRT_NSS:
1815 if ((rule->options & KRB5_NCRO_USE_DNSSEC)) {
1816 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
1817 krb5_set_error_message(context, ret,
1818 "Secure hostname resolution not supported");
1819 goto out;
1821 _krb5_debug(context, 5, "Using name service lookups");
1822 ret = krb5_sname_to_principal_old(context, rule->realm,
1823 orig_hostname, sname,
1824 KRB5_NT_SRV_HST,
1825 &nss);
1826 if (rules[rule_idx + 1].type != KRB5_NCRT_BOGUS &&
1827 (ret == KRB5_ERR_BAD_HOSTNAME ||
1828 ret == KRB5_ERR_HOST_REALM_UNKNOWN)) {
1830 * Bad hostname / realm unknown -> rule inapplicable if
1831 * there's more rules. If it's the last rule then we want
1832 * to return all errors from krb5_sname_to_principal_old()
1833 * here.
1835 ret = 0;
1836 goto out;
1838 if (ret)
1839 goto out;
1841 new_hostname = krb5_principal_get_comp_string(context, nss, 1);
1842 new_realm = krb5_principal_get_realm(context, nss);
1843 break;
1845 default:
1846 /* Can't happen */
1847 ret = 0;
1848 goto out;
1852 * This rule applies.
1854 * Copy in_princ and mutate the copy per the matched rule.
1856 * This way we apply to principals with two or more components, such as
1857 * domain-based names.
1859 ret = krb5_copy_principal(context, in_princ, out_princ);
1860 if (ret)
1861 goto out;
1863 if (new_realm == NULL && (rule->options & KRB5_NCRO_LOOKUP_REALM) != 0) {
1864 ret = get_host_realm(context, new_hostname, &tmp_realm);
1865 if (ret)
1866 goto out;
1867 new_realm = tmp_realm;
1870 /* If we stripped off a :port, add it back in */
1871 if (port != NULL && new_hostname != NULL) {
1872 if (asprintf(&hostname_with_port, "%s%s", new_hostname, port) == -1 ||
1873 hostname_with_port == NULL) {
1874 ret = krb5_enomem(context);
1875 goto out;
1877 new_hostname = hostname_with_port;
1880 if (new_realm != NULL)
1881 krb5_principal_set_realm(context, *out_princ, new_realm);
1882 if (new_hostname != NULL)
1883 krb5_principal_set_comp_string(context, *out_princ, 1, new_hostname);
1884 if (princ_type(*out_princ) == KRB5_NT_SRV_HST_NEEDS_CANON)
1885 princ_type(*out_princ) = KRB5_NT_SRV_HST;
1887 /* Trace rule application */
1889 krb5_error_code ret2;
1890 char *unparsed;
1892 ret2 = krb5_unparse_name(context, *out_princ, &unparsed);
1893 if (ret2) {
1894 _krb5_debug(context, 5,
1895 N_("Couldn't unparse canonicalized princicpal (%d)",
1896 ""),
1897 ret);
1898 } else {
1899 _krb5_debug(context, 5,
1900 N_("Name canon rule application yields %s", ""),
1901 unparsed);
1902 free(unparsed);
1906 out:
1907 free(hostname_sans_port);
1908 free(hostname_with_port);
1909 free(tmp_hostname);
1910 free(tmp_realm);
1911 krb5_free_principal(context, nss);
1912 if (ret)
1913 krb5_set_error_message(context, ret,
1914 N_("Name canon rule application failed", ""));
1915 return ret;
1919 * Free name canonicalization rules
1921 KRB5_LIB_FUNCTION void
1922 _krb5_free_name_canon_rules(krb5_context context, krb5_name_canon_rule rules)
1924 size_t k;
1926 if (rules == NULL)
1927 return;
1929 for (k = 0; rules[k].type != KRB5_NCRT_BOGUS; k++) {
1930 free(rules[k].match_domain);
1931 free(rules[k].match_realm);
1932 free(rules[k].domain);
1933 free(rules[k].realm);
1935 free(rules);
1938 struct krb5_name_canon_iterator_data {
1939 krb5_name_canon_rule rules;
1940 krb5_const_principal in_princ; /* given princ */
1941 krb5_const_principal out_princ; /* princ to be output */
1942 krb5_principal tmp_princ; /* to be freed */
1943 int is_trivial; /* no canon to be done */
1944 int done; /* no more rules to be applied */
1945 size_t cursor; /* current/next rule */
1949 * Initialize name canonicalization iterator.
1951 * @param context Kerberos context
1952 * @param in_princ principal name to be canonicalized OR
1953 * @param iter output iterator object
1955 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1956 krb5_name_canon_iterator_start(krb5_context context,
1957 krb5_const_principal in_princ,
1958 krb5_name_canon_iterator *iter)
1960 krb5_error_code ret;
1961 krb5_name_canon_iterator state;
1963 *iter = NULL;
1965 state = calloc(1, sizeof (*state));
1966 if (state == NULL)
1967 return krb5_enomem(context);
1968 state->in_princ = in_princ;
1970 if (princ_type(state->in_princ) == KRB5_NT_SRV_HST_NEEDS_CANON) {
1971 ret = _krb5_get_name_canon_rules(context, &state->rules);
1972 if (ret)
1973 goto out;
1974 } else {
1975 /* Name needs no canon -> trivial iterator: in_princ is canonical */
1976 state->is_trivial = 1;
1979 *iter = state;
1980 return 0;
1982 out:
1983 krb5_free_name_canon_iterator(context, state);
1984 return krb5_enomem(context);
1988 * Helper for name canon iteration.
1990 static krb5_error_code
1991 name_canon_iterate(krb5_context context,
1992 krb5_name_canon_iterator *iter,
1993 krb5_name_canon_rule_options *rule_opts)
1995 krb5_error_code ret;
1996 krb5_name_canon_iterator state = *iter;
1998 if (rule_opts)
1999 *rule_opts = 0;
2001 if (state == NULL)
2002 return 0;
2004 if (state->done) {
2005 krb5_free_name_canon_iterator(context, state);
2006 *iter = NULL;
2007 return 0;
2010 if (state->is_trivial && !state->done) {
2011 state->out_princ = state->in_princ;
2012 state->done = 1;
2013 return 0;
2016 heim_assert(state->rules != NULL &&
2017 state->rules[state->cursor].type != KRB5_NCRT_BOGUS,
2018 "Internal error during name canonicalization");
2020 do {
2021 krb5_free_principal(context, state->tmp_princ);
2022 ret = apply_name_canon_rule(context, state->rules, state->cursor,
2023 state->in_princ, &state->tmp_princ, rule_opts);
2024 if (ret) {
2025 krb5_free_name_canon_iterator(context, state);
2026 *iter = NULL;
2027 return ret;
2029 state->cursor++;
2030 } while (state->tmp_princ == NULL &&
2031 state->rules[state->cursor].type != KRB5_NCRT_BOGUS);
2033 if (state->rules[state->cursor].type == KRB5_NCRT_BOGUS)
2034 state->done = 1;
2036 state->out_princ = state->tmp_princ;
2037 if (state->tmp_princ == NULL) {
2038 krb5_free_name_canon_iterator(context, state);
2039 *iter = NULL;
2040 return 0;
2042 return 0;
2046 * Iteratively apply name canon rules, outputing a principal and rule
2047 * options each time. Iteration completes when the @iter is NULL on
2048 * return or when an error is returned. Callers must free the iterator
2049 * if they abandon it mid-way.
2051 * @param context Kerberos context
2052 * @param iter name canon rule iterator (input/output)
2053 * @param try_princ output principal name
2054 * @param rule_opts output rule options
2056 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
2057 krb5_name_canon_iterate(krb5_context context,
2058 krb5_name_canon_iterator *iter,
2059 krb5_const_principal *try_princ,
2060 krb5_name_canon_rule_options *rule_opts)
2062 krb5_error_code ret;
2064 *try_princ = NULL;
2066 ret = name_canon_iterate(context, iter, rule_opts);
2067 if (*iter)
2068 *try_princ = (*iter)->out_princ;
2069 return ret;
2073 * Free a name canonicalization rule iterator.
2075 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
2076 krb5_free_name_canon_iterator(krb5_context context,
2077 krb5_name_canon_iterator iter)
2079 if (iter == NULL)
2080 return;
2081 if (iter->tmp_princ)
2082 krb5_free_principal(context, iter->tmp_princ);
2083 free(iter);