1/*
2 * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36/**
37 * @page krb5_principal_intro The principal handing functions.
38 *
39 * A Kerberos principal is a email address looking string that
40 * contains to parts separeted by a @.  The later part is the kerbero
41 * realm the principal belongs to and the former is a list of 0 or
42 * more components. For example
43 * @verbatim
44lha@SU.SE
45host/hummel.it.su.se@SU.SE
46host/admin@H5L.ORG
47@endverbatim
48 *
49 * See the library functions here: @ref krb5_principal
50 */
51
52#include "krb5_locl.h"
53#ifdef HAVE_RES_SEARCH
54#define USE_RESOLVER
55#endif
56#ifdef HAVE_ARPA_NAMESER_H
57#include <arpa/nameser.h>
58#endif
59#include <fnmatch.h>
60#include "resolve.h"
61
62#define princ_num_comp(P) ((P)->name.name_string.len)
63#define princ_type(P) ((P)->name.name_type)
64#define princ_comp(P) ((P)->name.name_string.val)
65#define princ_ncomp(P, N) ((P)->name.name_string.val[(N)])
66#define princ_realm(P) ((P)->realm)
67
68/**
69 * Frees a Kerberos principal allocated by the library with
70 * krb5_parse_name(), krb5_make_principal() or any other related
71 * principal functions.
72 *
73 * @param context A Kerberos context.
74 * @param p a principal to free.
75 *
76 * @ingroup krb5_principal
77 */
78
79KRB5_LIB_FUNCTION void KRB5_LIB_CALL
80krb5_free_principal(krb5_context context,
81		    krb5_principal p)
82{
83    if(p){
84	free_Principal(p);
85	free(p);
86    }
87}
88
89/**
90 * Set the type of the principal
91 *
92 * @param context A Kerberos context.
93 * @param principal principal to set the type for
94 * @param type the new type
95 *
96 * @ingroup krb5_principal
97 */
98
99KRB5_LIB_FUNCTION void KRB5_LIB_CALL
100krb5_principal_set_type(krb5_context context,
101			krb5_principal principal,
102			int type)
103{
104    princ_type(principal) = type;
105}
106
107/**
108 * Get the type of the principal
109 *
110 * @param context A Kerberos context.
111 * @param principal principal to get the type for
112 *
113 * @return the type of principal
114 *
115 * @ingroup krb5_principal
116 */
117
118KRB5_LIB_FUNCTION int KRB5_LIB_CALL
119krb5_principal_get_type(krb5_context context,
120			krb5_const_principal principal)
121{
122    return princ_type(principal);
123}
124
125/**
126 * Get the realm of the principal
127 *
128 * @param context A Kerberos context.
129 * @param principal principal to get the realm for
130 *
131 * @return realm of the principal, don't free or use after krb5_principal is freed
132 *
133 * @ingroup krb5_principal
134 */
135
136KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
137krb5_principal_get_realm(krb5_context context,
138			 krb5_const_principal principal)
139{
140    return princ_realm(principal);
141}
142
143KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
144krb5_principal_get_comp_string(krb5_context context,
145			       krb5_const_principal principal,
146			       unsigned int component)
147{
148    if(component >= princ_num_comp(principal))
149       return NULL;
150    return princ_ncomp(principal, component);
151}
152
153/**
154 * Get number of component is principal.
155 *
156 * @param context Kerberos 5 context
157 * @param principal principal to query
158 *
159 * @return number of components in string
160 *
161 * @ingroup krb5_principal
162 */
163
164KRB5_LIB_FUNCTION unsigned int KRB5_LIB_CALL
165krb5_principal_get_num_comp(krb5_context context,
166			    krb5_const_principal principal)
167{
168    return princ_num_comp(principal);
169}
170
171/**
172 * Parse a name into a krb5_principal structure, flags controls the behavior.
173 *
174 * @param context Kerberos 5 context
175 * @param name name to parse into a Kerberos principal
176 * @param flags flags to control the behavior
177 * @param principal returned principal, free with krb5_free_principal().
178 *
179 * @return An krb5 error code, see krb5_get_error_message().
180 *
181 * @ingroup krb5_principal
182 */
183
184KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
185krb5_parse_name_flags(krb5_context context,
186		      const char *name,
187		      int flags,
188		      krb5_principal *principal)
189{
190    krb5_error_code ret;
191    heim_general_string *comp;
192    heim_general_string realm = NULL;
193    int ncomp;
194
195    const char *p;
196    char *q;
197    char *s;
198    char *start;
199
200    int n;
201    char c;
202    int got_realm = 0;
203    int first_at = 1;
204    int enterprise = (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE);
205
206    *principal = NULL;
207
208#define RFLAGS (KRB5_PRINCIPAL_PARSE_NO_REALM|KRB5_PRINCIPAL_PARSE_REQUIRE_REALM)
209
210    if ((flags & RFLAGS) == RFLAGS) {
211	krb5_set_error_message(context, KRB5_ERR_NO_SERVICE,
212			       N_("Can't require both realm and "
213				  "no realm at the same time", ""));
214	return KRB5_ERR_NO_SERVICE;
215    }
216#undef RFLAGS
217
218    /* count number of component,
219     * enterprise names only have one component
220     */
221    ncomp = 1;
222    if (!enterprise) {
223	for(p = name; *p; p++){
224	    if(*p=='\\'){
225		if(!p[1]) {
226		    krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
227					   N_("trailing \\ in principal name", ""));
228		    return KRB5_PARSE_MALFORMED;
229		}
230		p++;
231	    } else if(*p == '/')
232		ncomp++;
233	    else if(*p == '@')
234		break;
235	}
236    }
237    comp = calloc(ncomp, sizeof(*comp));
238    if (comp == NULL) {
239	krb5_set_error_message(context, ENOMEM,
240			       N_("malloc: out of memory", ""));
241	return ENOMEM;
242    }
243
244    n = 0;
245    p = start = q = s = strdup(name);
246    if (start == NULL) {
247	free (comp);
248	krb5_set_error_message(context, ENOMEM,
249			       N_("malloc: out of memory", ""));
250	return ENOMEM;
251    }
252    while(*p){
253	c = *p++;
254	if(c == '\\'){
255	    c = *p++;
256	    if(c == 'n')
257		c = '\n';
258	    else if(c == 't')
259		c = '\t';
260	    else if(c == 'b')
261		c = '\b';
262	    else if(c == '0')
263		c = '\0';
264	    else if(c == '\0') {
265		ret = KRB5_PARSE_MALFORMED;
266		krb5_set_error_message(context, ret,
267				       N_("trailing \\ in principal name", ""));
268		goto exit;
269	    }
270	}else if(enterprise && first_at) {
271	    if (c == '@')
272		first_at = 0;
273	}else if((c == '/' && !enterprise) || c == '@'){
274	    if(got_realm){
275		ret = KRB5_PARSE_MALFORMED;
276		krb5_set_error_message(context, ret,
277				       N_("part after realm in principal name", ""));
278		goto exit;
279	    }else{
280		comp[n] = malloc(q - start + 1);
281		if (comp[n] == NULL) {
282		    ret = ENOMEM;
283		    krb5_set_error_message(context, ret,
284					   N_("malloc: out of memory", ""));
285		    goto exit;
286		}
287		memcpy(comp[n], start, q - start);
288		comp[n][q - start] = 0;
289		n++;
290	    }
291	    if(c == '@')
292		got_realm = 1;
293	    start = q;
294	    continue;
295	}
296	if(got_realm && (c == '/' || c == '\0')) {
297	    ret = KRB5_PARSE_MALFORMED;
298	    krb5_set_error_message(context, ret,
299				   N_("part after realm in principal name", ""));
300	    goto exit;
301	}
302	*q++ = c;
303    }
304    if(got_realm){
305	if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) {
306	    ret = KRB5_PARSE_MALFORMED;
307	    krb5_set_error_message(context, ret,
308				   N_("realm found in 'short' principal "
309				      "expected to be without one", ""));
310	    goto exit;
311	}
312	realm = malloc(q - start + 1);
313	if (realm == NULL) {
314	    ret = ENOMEM;
315	    krb5_set_error_message(context, ret,
316				   N_("malloc: out of memory", ""));
317	    goto exit;
318	}
319	memcpy(realm, start, q - start);
320	realm[q - start] = 0;
321    }else{
322	if (flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM) {
323	    ret = KRB5_PARSE_MALFORMED;
324	    krb5_set_error_message(context, ret,
325				   N_("realm NOT found in principal "
326				      "expected to be with one", ""));
327	    goto exit;
328	} else if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) {
329	    realm = NULL;
330	} else {
331	    ret = krb5_get_default_realm (context, &realm);
332	    if (ret)
333		goto exit;
334	}
335
336	comp[n] = malloc(q - start + 1);
337	if (comp[n] == NULL) {
338	    ret = ENOMEM;
339	    krb5_set_error_message(context, ret,
340				   N_("malloc: out of memory", ""));
341	    goto exit;
342	}
343	memcpy(comp[n], start, q - start);
344	comp[n][q - start] = 0;
345	n++;
346    }
347    *principal = malloc(sizeof(**principal));
348    if (*principal == NULL) {
349	ret = ENOMEM;
350	krb5_set_error_message(context, ret,
351			       N_("malloc: out of memory", ""));
352	goto exit;
353    }
354    if (enterprise)
355	(*principal)->name.name_type = KRB5_NT_ENTERPRISE_PRINCIPAL;
356    else
357	(*principal)->name.name_type = KRB5_NT_PRINCIPAL;
358    (*principal)->name.name_string.val = comp;
359    princ_num_comp(*principal) = n;
360    (*principal)->realm = realm;
361    free(s);
362    return 0;
363exit:
364    while(n>0){
365	free(comp[--n]);
366    }
367    free(comp);
368    free(realm);
369    free(s);
370    return ret;
371}
372
373/**
374 * Parse a name into a krb5_principal structure
375 *
376 * @param context Kerberos 5 context
377 * @param name name to parse into a Kerberos principal
378 * @param principal returned principal, free with krb5_free_principal().
379 *
380 * @return An krb5 error code, see krb5_get_error_message().
381 *
382 * @ingroup krb5_principal
383 */
384
385KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
386krb5_parse_name(krb5_context context,
387		const char *name,
388		krb5_principal *principal)
389{
390    return krb5_parse_name_flags(context, name, 0, principal);
391}
392
393static const char quotable_chars[] = " \n\t\b\\/@";
394static const char replace_chars[] = " ntb\\/@";
395
396#define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0);
397
398static size_t
399quote_string(const char *s, char *out, size_t idx, size_t len, int display)
400{
401    const char *p, *q;
402    for(p = s; *p && idx < len; p++){
403	q = strchr(quotable_chars, *p);
404	if (q && display) {
405	    add_char(out, idx, len, replace_chars[q - quotable_chars]);
406	} else if (q) {
407	    add_char(out, idx, len, '\\');
408	    add_char(out, idx, len, replace_chars[q - quotable_chars]);
409	}else
410	    add_char(out, idx, len, *p);
411    }
412    if(idx < len)
413	out[idx] = '\0';
414    return idx;
415}
416
417
418static krb5_error_code
419unparse_name_fixed(krb5_context context,
420		   krb5_const_principal principal,
421		   char *name,
422		   size_t len,
423		   int flags)
424{
425    size_t idx = 0;
426    size_t i;
427    int short_form = (flags & KRB5_PRINCIPAL_UNPARSE_SHORT) != 0;
428    int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) != 0;
429    int display = (flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) != 0;
430
431    if (!no_realm && princ_realm(principal) == NULL) {
432	krb5_set_error_message(context, ERANGE,
433			       N_("Realm missing from principal, "
434				  "can't unparse", ""));
435	return ERANGE;
436    }
437
438    for(i = 0; i < princ_num_comp(principal); i++){
439	if(i)
440	    add_char(name, idx, len, '/');
441	idx = quote_string(princ_ncomp(principal, i), name, idx, len, display);
442	if(idx == len) {
443	    krb5_set_error_message(context, ERANGE,
444				   N_("Out of space printing principal", ""));
445	    return ERANGE;
446	}
447    }
448    /* add realm if different from default realm */
449    if(short_form && !no_realm) {
450	krb5_realm r;
451	krb5_error_code ret;
452	ret = krb5_get_default_realm(context, &r);
453	if(ret)
454	    return ret;
455	if(strcmp(princ_realm(principal), r) != 0)
456	    short_form = 0;
457	free(r);
458    }
459    if(!short_form && !no_realm) {
460	add_char(name, idx, len, '@');
461	idx = quote_string(princ_realm(principal), name, idx, len, display);
462	if(idx == len) {
463	    krb5_set_error_message(context, ERANGE,
464				   N_("Out of space printing "
465				      "realm of principal", ""));
466	    return ERANGE;
467	}
468    }
469    return 0;
470}
471
472/**
473 * Unparse the principal name to a fixed buffer
474 *
475 * @param context A Kerberos context.
476 * @param principal principal to unparse
477 * @param name buffer to write name to
478 * @param len length of buffer
479 *
480 * @return An krb5 error code, see krb5_get_error_message().
481 *
482 * @ingroup krb5_principal
483 */
484
485KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
486krb5_unparse_name_fixed(krb5_context context,
487			krb5_const_principal principal,
488			char *name,
489			size_t len)
490{
491    return unparse_name_fixed(context, principal, name, len, 0);
492}
493
494/**
495 * Unparse the principal name to a fixed buffer. The realm is skipped
496 * if its a default realm.
497 *
498 * @param context A Kerberos context.
499 * @param principal principal to unparse
500 * @param name buffer to write name to
501 * @param len length of buffer
502 *
503 * @return An krb5 error code, see krb5_get_error_message().
504 *
505 * @ingroup krb5_principal
506 */
507
508KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
509krb5_unparse_name_fixed_short(krb5_context context,
510			      krb5_const_principal principal,
511			      char *name,
512			      size_t len)
513{
514    return unparse_name_fixed(context, principal, name, len,
515			      KRB5_PRINCIPAL_UNPARSE_SHORT);
516}
517
518/**
519 * Unparse the principal name with unparse flags to a fixed buffer.
520 *
521 * @param context A Kerberos context.
522 * @param principal principal to unparse
523 * @param flags unparse flags
524 * @param name buffer to write name to
525 * @param len length of buffer
526 *
527 * @return An krb5 error code, see krb5_get_error_message().
528 *
529 * @ingroup krb5_principal
530 */
531
532KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
533krb5_unparse_name_fixed_flags(krb5_context context,
534			      krb5_const_principal principal,
535			      int flags,
536			      char *name,
537			      size_t len)
538{
539    return unparse_name_fixed(context, principal, name, len, flags);
540}
541
542static krb5_error_code
543unparse_name(krb5_context context,
544	     krb5_const_principal principal,
545	     char **name,
546	     int flags)
547{
548    size_t len = 0, plen;
549    size_t i;
550    krb5_error_code ret;
551    /* count length */
552    if (princ_realm(principal)) {
553	plen = strlen(princ_realm(principal));
554
555	if(strcspn(princ_realm(principal), quotable_chars) == plen)
556	    len += plen;
557	else
558	    len += 2*plen;
559	len++; /* '@' */
560    }
561    for(i = 0; i < princ_num_comp(principal); i++){
562	plen = strlen(princ_ncomp(principal, i));
563	if(strcspn(princ_ncomp(principal, i), quotable_chars) == plen)
564	    len += plen;
565	else
566	    len += 2*plen;
567	len++;
568    }
569    len++; /* '\0' */
570    *name = malloc(len);
571    if(*name == NULL) {
572	krb5_set_error_message(context, ENOMEM,
573			       N_("malloc: out of memory", ""));
574	return ENOMEM;
575    }
576    ret = unparse_name_fixed(context, principal, *name, len, flags);
577    if(ret) {
578	free(*name);
579	*name = NULL;
580    }
581    return ret;
582}
583
584/**
585 * Unparse the Kerberos name into a string
586 *
587 * @param context Kerberos 5 context
588 * @param principal principal to query
589 * @param name resulting string, free with krb5_xfree()
590 *
591 * @return An krb5 error code, see krb5_get_error_message().
592 *
593 * @ingroup krb5_principal
594 */
595
596KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
597krb5_unparse_name(krb5_context context,
598		  krb5_const_principal principal,
599		  char **name)
600{
601    return unparse_name(context, principal, name, 0);
602}
603
604/**
605 * Unparse the Kerberos name into a string
606 *
607 * @param context Kerberos 5 context
608 * @param principal principal to query
609 * @param flags flag to determine the behavior
610 * @param name resulting string, free with krb5_xfree()
611 *
612 * @return An krb5 error code, see krb5_get_error_message().
613 *
614 * @ingroup krb5_principal
615 */
616
617KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
618krb5_unparse_name_flags(krb5_context context,
619			krb5_const_principal principal,
620			int flags,
621			char **name)
622{
623    return unparse_name(context, principal, name, flags);
624}
625
626/**
627 * Unparse the principal name to a allocated buffer. The realm is
628 * skipped if its a default realm.
629 *
630 * @param context A Kerberos context.
631 * @param principal principal to unparse
632 * @param name returned buffer, free with krb5_xfree()
633 *
634 * @return An krb5 error code, see krb5_get_error_message().
635 *
636 * @ingroup krb5_principal
637 */
638
639KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
640krb5_unparse_name_short(krb5_context context,
641			krb5_const_principal principal,
642			char **name)
643{
644    return unparse_name(context, principal, name, KRB5_PRINCIPAL_UNPARSE_SHORT);
645}
646
647/**
648 * Set a new realm for a principal, and as a side-effect free the
649 * previous realm.
650 *
651 * @param context A Kerberos context.
652 * @param principal principal set the realm for
653 * @param realm the new realm to set
654 *
655 * @return An krb5 error code, see krb5_get_error_message().
656 *
657 * @ingroup krb5_principal
658 */
659
660KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
661krb5_principal_set_realm(krb5_context context,
662			 krb5_principal principal,
663			 krb5_const_realm realm)
664{
665    if (princ_realm(principal))
666	free(princ_realm(principal));
667
668    princ_realm(principal) = strdup(realm);
669    if (princ_realm(principal) == NULL) {
670	krb5_set_error_message(context, ENOMEM,
671			       N_("malloc: out of memory", ""));
672	return ENOMEM;
673    }
674    return 0;
675}
676
677/**
678 * Build a principal using vararg style building
679 *
680 * @param context A Kerberos context.
681 * @param principal returned principal
682 * @param rlen length of realm
683 * @param realm realm name
684 * @param ... a list of components ended with NULL.
685 *
686 * @return An krb5 error code, see krb5_get_error_message().
687 *
688 * @ingroup krb5_principal
689 */
690
691KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
692krb5_build_principal(krb5_context context,
693		     krb5_principal *principal,
694		     int rlen,
695		     krb5_const_realm realm,
696		     ...)
697{
698    krb5_error_code ret;
699    va_list ap;
700    va_start(ap, realm);
701    ret = krb5_build_principal_va(context, principal, rlen, realm, ap);
702    va_end(ap);
703    return ret;
704}
705
706/**
707 * Build a principal using vararg style building
708 *
709 * @param context A Kerberos context.
710 * @param principal returned principal
711 * @param realm realm name
712 * @param ... a list of components ended with NULL.
713 *
714 * @return An krb5 error code, see krb5_get_error_message().
715 *
716 * @ingroup krb5_principal
717 */
718
719KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
720krb5_make_principal(krb5_context context,
721		    krb5_principal *principal,
722		    krb5_const_realm realm,
723		    ...)
724{
725    krb5_error_code ret;
726    krb5_realm r = NULL;
727    va_list ap;
728    if(realm == NULL) {
729	ret = krb5_get_default_realm(context, &r);
730	if(ret)
731	    return ret;
732	realm = r;
733    }
734    va_start(ap, realm);
735    ret = krb5_build_principal_va(context, principal, (int)strlen(realm), realm, ap);
736    va_end(ap);
737    if(r)
738	free(r);
739    return ret;
740}
741
742static krb5_error_code
743append_component(krb5_context context, krb5_principal p,
744		 const char *comp,
745		 size_t comp_len)
746{
747    heim_general_string *tmp;
748    size_t len = princ_num_comp(p);
749
750    tmp = realloc(princ_comp(p), (len + 1) * sizeof(*tmp));
751    if(tmp == NULL) {
752	krb5_set_error_message(context, ENOMEM,
753			       N_("malloc: out of memory", ""));
754	return ENOMEM;
755    }
756    princ_comp(p) = tmp;
757    princ_ncomp(p, len) = malloc(comp_len + 1);
758    if (princ_ncomp(p, len) == NULL) {
759	krb5_set_error_message(context, ENOMEM,
760			       N_("malloc: out of memory", ""));
761	return ENOMEM;
762    }
763    memcpy (princ_ncomp(p, len), comp, comp_len);
764    princ_ncomp(p, len)[comp_len] = '\0';
765    princ_num_comp(p)++;
766    return 0;
767}
768
769static void
770va_ext_princ(krb5_context context, krb5_principal p, va_list ap)
771{
772    while(1){
773	const char *s;
774	int len;
775	len = va_arg(ap, int);
776	if(len == 0)
777	    break;
778	s = va_arg(ap, const char*);
779	append_component(context, p, s, len);
780    }
781}
782
783static void
784va_princ(krb5_context context, krb5_principal p, va_list ap)
785{
786    while(1){
787	const char *s;
788	s = va_arg(ap, const char*);
789	if(s == NULL)
790	    break;
791	append_component(context, p, s, strlen(s));
792    }
793}
794
795static krb5_error_code
796build_principal(krb5_context context,
797		krb5_principal *principal,
798		int rlen,
799		krb5_const_realm realm,
800		void (*func)(krb5_context, krb5_principal, va_list),
801		va_list ap)
802{
803    krb5_principal p;
804
805    p = calloc(1, sizeof(*p));
806    if (p == NULL) {
807	krb5_set_error_message(context, ENOMEM,
808			       N_("malloc: out of memory", ""));
809	return ENOMEM;
810    }
811    princ_type(p) = KRB5_NT_PRINCIPAL;
812
813    princ_realm(p) = strdup(realm);
814    if(p->realm == NULL){
815	free(p);
816	krb5_set_error_message(context, ENOMEM,
817			       N_("malloc: out of memory", ""));
818	return ENOMEM;
819    }
820
821    (*func)(context, p, ap);
822    *principal = p;
823    return 0;
824}
825
826KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
827krb5_build_principal_va(krb5_context context,
828			krb5_principal *principal,
829			int rlen,
830			krb5_const_realm realm,
831			va_list ap)
832{
833    return build_principal(context, principal, rlen, realm, va_princ, ap);
834}
835
836KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
837krb5_build_principal_va_ext(krb5_context context,
838			    krb5_principal *principal,
839			    int rlen,
840			    krb5_const_realm realm,
841			    va_list ap)
842{
843    return build_principal(context, principal, rlen, realm, va_ext_princ, ap);
844}
845
846
847KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
848krb5_build_principal_ext(krb5_context context,
849			 krb5_principal *principal,
850			 int rlen,
851			 krb5_const_realm realm,
852			 ...)
853{
854    krb5_error_code ret;
855    va_list ap;
856    va_start(ap, realm);
857    ret = krb5_build_principal_va_ext(context, principal, rlen, realm, ap);
858    va_end(ap);
859    return ret;
860}
861
862/**
863 * Copy a principal
864 *
865 * @param context A Kerberos context.
866 * @param inprinc principal to copy
867 * @param outprinc copied principal, free with krb5_free_principal()
868 *
869 * @return An krb5 error code, see krb5_get_error_message().
870 *
871 * @ingroup krb5_principal
872 */
873
874
875KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
876krb5_copy_principal(krb5_context context,
877		    krb5_const_principal inprinc,
878		    krb5_principal *outprinc)
879{
880    krb5_principal p = malloc(sizeof(*p));
881    if (p == NULL) {
882	krb5_set_error_message(context, ENOMEM,
883			       N_("malloc: out of memory", ""));
884	return ENOMEM;
885    }
886    if(copy_Principal(inprinc, p)) {
887	free(p);
888	krb5_set_error_message(context, ENOMEM,
889			       N_("malloc: out of memory", ""));
890	return ENOMEM;
891    }
892    *outprinc = p;
893    return 0;
894}
895
896/**
897 * Return TRUE iff princ1 == princ2 (without considering the realm)
898 *
899 * @param context Kerberos 5 context
900 * @param princ1 first principal to compare
901 * @param princ2 second principal to compare
902 *
903 * @return non zero if equal, 0 if not
904 *
905 * @ingroup krb5_principal
906 * @see krb5_principal_compare()
907 * @see krb5_realm_compare()
908 */
909
910KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
911krb5_principal_compare_any_realm(krb5_context context,
912				 krb5_const_principal princ1,
913				 krb5_const_principal princ2)
914{
915    size_t i;
916    if(princ_num_comp(princ1) != princ_num_comp(princ2))
917	return FALSE;
918    for(i = 0; i < princ_num_comp(princ1); i++){
919	if(strcmp(princ_ncomp(princ1, i), princ_ncomp(princ2, i)) != 0)
920	    return FALSE;
921    }
922    return TRUE;
923}
924
925KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
926_krb5_principal_compare_PrincipalName(krb5_context context,
927				      krb5_const_principal princ1,
928				      PrincipalName *princ2)
929{
930    size_t i;
931    if (princ_num_comp(princ1) != princ2->name_string.len)
932	return FALSE;
933    for(i = 0; i < princ_num_comp(princ1); i++){
934	if(strcmp(princ_ncomp(princ1, i), princ2->name_string.val[i]) != 0)
935	    return FALSE;
936    }
937    return TRUE;
938}
939
940
941/**
942 * Compares the two principals, including realm of the principals and returns
943 * TRUE if they are the same and FALSE if not.
944 *
945 * @param context Kerberos 5 context
946 * @param princ1 first principal to compare
947 * @param princ2 second principal to compare
948 *
949 * @ingroup krb5_principal
950 * @see krb5_principal_compare_any_realm()
951 * @see krb5_realm_compare()
952 */
953
954/*
955 * return TRUE iff princ1 == princ2
956 */
957
958KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
959krb5_principal_compare(krb5_context context,
960		       krb5_const_principal princ1,
961		       krb5_const_principal princ2)
962{
963    if(!krb5_realm_compare(context, princ1, princ2))
964	return FALSE;
965    return krb5_principal_compare_any_realm(context, princ1, princ2);
966}
967
968/**
969 * return TRUE iff realm(princ1) == realm(princ2)
970 *
971 * @param context Kerberos 5 context
972 * @param princ1 first principal to compare
973 * @param princ2 second principal to compare
974 *
975 * @ingroup krb5_principal
976 * @see krb5_principal_compare_any_realm()
977 * @see krb5_principal_compare()
978 */
979
980KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
981krb5_realm_compare(krb5_context context,
982		   krb5_const_principal princ1,
983		   krb5_const_principal princ2)
984{
985    return strcmp(princ_realm(princ1), princ_realm(princ2)) == 0;
986}
987
988/**
989 * return TRUE iff princ matches pattern
990 *
991 * @ingroup krb5_principal
992 */
993
994KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
995krb5_principal_match(krb5_context context,
996		     krb5_const_principal princ,
997		     krb5_const_principal pattern)
998{
999    size_t i;
1000    if(princ_num_comp(princ) != princ_num_comp(pattern))
1001	return FALSE;
1002    if(fnmatch(princ_realm(pattern), princ_realm(princ), 0) != 0)
1003	return FALSE;
1004    for(i = 0; i < princ_num_comp(princ); i++){
1005	if(fnmatch(princ_ncomp(pattern, i), princ_ncomp(princ, i), 0) != 0)
1006	    return FALSE;
1007    }
1008    return TRUE;
1009}
1010
1011/**
1012 * Create a principal for the service running on hostname. If
1013 * KRB5_NT_SRV_HST is used, the hostname is canonization using DNS (or
1014 * some other service), this is potentially insecure.
1015 *
1016 * @param context A Kerberos context.
1017 * @param hostname hostname to use
1018 * @param sname Service name to use
1019 * @param type name type of pricipal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN.
1020 * @param ret_princ return principal, free with krb5_free_principal().
1021 *
1022 * @return An krb5 error code, see krb5_get_error_message().
1023 *
1024 * @ingroup krb5_principal
1025 */
1026
1027KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1028krb5_sname_to_principal (krb5_context context,
1029			 const char *hostname,
1030			 const char *sname,
1031			 int32_t type,
1032			 krb5_principal *ret_princ)
1033{
1034    krb5_error_code ret;
1035    char localhost[MAXHOSTNAMELEN];
1036    char **realms;
1037    size_t len;
1038
1039    if(type != KRB5_NT_SRV_HST && type != KRB5_NT_UNKNOWN) {
1040	krb5_set_error_message(context, KRB5_SNAME_UNSUPP_NAMETYPE,
1041			       N_("unsupported name type %d", ""),
1042			       (int)type);
1043	return KRB5_SNAME_UNSUPP_NAMETYPE;
1044    }
1045    if(hostname == NULL) {
1046	ret = gethostname(localhost, sizeof(localhost) - 1);
1047	if (ret != 0) {
1048	    ret = errno;
1049	    krb5_set_error_message(context, ret,
1050				   N_("Failed to get local hostname", ""));
1051	    return ret;
1052	}
1053	localhost[sizeof(localhost) - 1] = '\0';
1054    } else {
1055	strlcpy(localhost, hostname, sizeof(localhost));
1056    }
1057    if(sname == NULL)
1058	sname = "host";
1059    if(type == KRB5_NT_SRV_HST) {
1060	char *host;
1061
1062	ret = krb5_expand_hostname_realms (context, localhost,
1063					   &host, &realms);
1064	if (ret)
1065	    return ret;
1066	strlwr(host);
1067	strlcpy(localhost, host, sizeof(localhost));
1068	free(host);
1069    } else {
1070	ret = krb5_get_host_realm(context, hostname, &realms);
1071	if(ret)
1072	    return ret;
1073    }
1074
1075    /*
1076     * Squash any trailing . on the hostname since that is jolly good
1077     * to have when looking up a DNS name (qualified), but its no good
1078     * to have in the kerberos principal since those are supposed to
1079     * be in qualified format already.
1080     */
1081
1082    len = strlen(localhost);
1083    if (len > 0 && localhost[len - 1] == '.')
1084	localhost[len - 1] = '\0';
1085
1086    ret = krb5_make_principal(context, ret_princ, realms[0], sname,
1087			      localhost, NULL);
1088    krb5_free_host_realm(context, realms);
1089    return ret;
1090}
1091
1092static const struct {
1093    const char *type;
1094    int32_t value;
1095} nametypes[] = {
1096    { "UNKNOWN", KRB5_NT_UNKNOWN },
1097    { "PRINCIPAL", KRB5_NT_PRINCIPAL },
1098    { "SRV_INST", KRB5_NT_SRV_INST },
1099    { "SRV_HST", KRB5_NT_SRV_HST },
1100    { "SRV_XHST", KRB5_NT_SRV_XHST },
1101    { "UID", KRB5_NT_UID },
1102    { "X500_PRINCIPAL", KRB5_NT_X500_PRINCIPAL },
1103    { "SMTP_NAME", KRB5_NT_SMTP_NAME },
1104    { "ENTERPRISE_PRINCIPAL", KRB5_NT_ENTERPRISE_PRINCIPAL },
1105    { "ENT_PRINCIPAL_AND_ID", KRB5_NT_ENT_PRINCIPAL_AND_ID },
1106    { "MS_PRINCIPAL", KRB5_NT_MS_PRINCIPAL },
1107    { "MS_PRINCIPAL_AND_ID", KRB5_NT_MS_PRINCIPAL_AND_ID },
1108    { NULL, 0 }
1109};
1110
1111/**
1112 * Parse nametype string and return a nametype integer
1113 *
1114 * @ingroup krb5_principal
1115 */
1116
1117KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1118krb5_parse_nametype(krb5_context context, const char *str, int32_t *nametype)
1119{
1120    size_t i;
1121
1122    for(i = 0; nametypes[i].type; i++) {
1123	if (strcasecmp(nametypes[i].type, str) == 0) {
1124	    *nametype = nametypes[i].value;
1125	    return 0;
1126	}
1127    }
1128    krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
1129			   N_("Failed to find name type %s", ""), str);
1130    return KRB5_PARSE_MALFORMED;
1131}
1132
1133/**
1134 * Returns true if name is Kerberos NULL name
1135 *
1136 * @ingroup krb5_principal
1137 */
1138
1139krb5_boolean KRB5_LIB_FUNCTION
1140krb5_principal_is_null(krb5_context context, krb5_const_principal principal)
1141{
1142    if (principal->name.name_type == KRB5_NT_WELLKNOWN &&
1143	principal->name.name_string.len == 2 &&
1144	strcmp(principal->name.name_string.val[0], "WELLKNOWN") == 0 &&
1145	strcmp(principal->name.name_string.val[1], "NULL") == 0)
1146	return TRUE;
1147    return FALSE;
1148}
1149
1150const char _krb5_wellknown_lkdc[] = "WELLKNOWN:COM.APPLE.LKDC";
1151static const char lkdc_prefix[] = "LKDC:";
1152
1153/**
1154 * Returns true if name is Kerberos an LKDC realm
1155 *
1156 * @ingroup krb5_principal
1157 */
1158
1159krb5_boolean KRB5_LIB_FUNCTION
1160krb5_realm_is_lkdc(const char *realm)
1161{
1162
1163    return strncmp(realm, lkdc_prefix, sizeof(lkdc_prefix)-1) == 0 ||
1164	strncmp(realm, _krb5_wellknown_lkdc, sizeof(_krb5_wellknown_lkdc) - 1) == 0;
1165}
1166
1167/**
1168 * Returns true if name is Kerberos an LKDC realm
1169 *
1170 * @ingroup krb5_principal
1171 */
1172
1173krb5_boolean KRB5_LIB_FUNCTION
1174krb5_principal_is_lkdc(krb5_context context, krb5_const_principal principal)
1175{
1176    return krb5_realm_is_lkdc(principal->realm);
1177}
1178
1179/**
1180 * Returns true if name is Kerberos an LKDC realm
1181 *
1182 * @ingroup krb5_principal
1183 */
1184
1185krb5_boolean KRB5_LIB_FUNCTION
1186krb5_principal_is_pku2u(krb5_context context, krb5_const_principal principal)
1187{
1188    return strcmp(principal->realm, KRB5_PKU2U_REALM_NAME) == 0;
1189}
1190
1191/**
1192 * Check if the cname part of the principal is a krbtgt principal
1193 *
1194 * @ingroup krb5_principal
1195 */
1196
1197KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1198krb5_principal_is_krbtgt(krb5_context context, krb5_const_principal p)
1199{
1200    return p->name.name_string.len == 2 &&
1201	strcmp(p->name.name_string.val[0], KRB5_TGS_NAME) == 0;
1202}
1203
1204/**
1205 * Returns true iff name is an WELLKNOWN:ORG.H5L.HOSTBASED-SERVICE
1206 *
1207 * @ingroup krb5_principal
1208 */
1209
1210krb5_boolean KRB5_LIB_FUNCTION
1211krb5_principal_is_gss_hostbased_service(krb5_context context,
1212					krb5_const_principal principal)
1213{
1214    if (principal == NULL)
1215	return FALSE;
1216    if (principal->name.name_string.len != 2)
1217	return FALSE;
1218    if (strcmp(principal->name.name_string.val[1], KRB5_GSS_HOSTBASED_SERVICE_NAME) != 0)
1219	return FALSE;
1220    return TRUE;
1221}
1222
1223/**
1224 * Check if the cname part of the principal is a initial or renewed krbtgt principal
1225 *
1226 * @ingroup krb5_principal
1227 */
1228
1229
1230krb5_boolean KRB5_LIB_FUNCTION
1231krb5_principal_is_root_krbtgt(krb5_context context, krb5_const_principal p)
1232{
1233    return p->name.name_string.len == 2 &&
1234	strcmp(p->name.name_string.val[0], KRB5_TGS_NAME) == 0 &&
1235	strcmp(p->name.name_string.val[1], p->realm) == 0;
1236}
1237