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