1/*
2 * Copyright (c) 1997-2007 Kungliga Tekniska H��gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
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.
16 *
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.
20 *
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.
32 */
33
34/**
35 * @page krb5_principal_intro The principal handing functions.
36 *
37 * A Kerberos principal is a email address looking string that
38 * contains to parts separeted by a @.  The later part is the kerbero
39 * realm the principal belongs to and the former is a list of 0 or
40 * more components. For example
41 * @verbatim
42lha@SU.SE
43host/hummel.it.su.se@SU.SE
44host/admin@H5L.ORG
45@endverbatim
46 *
47 * See the library functions here: @ref krb5_principal
48 */
49
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"
59
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)
65
66/**
67 * Frees a Kerberos principal allocated by the library with
68 * krb5_parse_name(), krb5_make_principal() or any other related
69 * principal functions.
70 *
71 * @param context A Kerberos context.
72 * @param p a principal to free.
73 *
74 * @return An krb5 error code, see krb5_get_error_message().
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 * @return An krb5 error code, see krb5_get_error_message().
97 *
98 * @ingroup krb5_principal
99 */
100
101KRB5_LIB_FUNCTION void KRB5_LIB_CALL
102krb5_principal_set_type(krb5_context context,
103			krb5_principal principal,
104			int type)
105{
106    princ_type(principal) = type;
107}
108
109/**
110 * Get the type of the principal
111 *
112 * @param context A Kerberos context.
113 * @param principal principal to get the type for
114 *
115 * @return the type of principal
116 *
117 * @ingroup krb5_principal
118 */
119
120KRB5_LIB_FUNCTION int KRB5_LIB_CALL
121krb5_principal_get_type(krb5_context context,
122			krb5_const_principal principal)
123{
124    return princ_type(principal);
125}
126
127/**
128 * Get the realm of the principal
129 *
130 * @param context A Kerberos context.
131 * @param principal principal to get the realm for
132 *
133 * @return realm of the principal, don't free or use after krb5_principal is freed
134 *
135 * @ingroup krb5_principal
136 */
137
138KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
139krb5_principal_get_realm(krb5_context context,
140			 krb5_const_principal principal)
141{
142    return princ_realm(principal);
143}
144
145KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
146krb5_principal_get_comp_string(krb5_context context,
147			       krb5_const_principal principal,
148			       unsigned int component)
149{
150    if(component >= princ_num_comp(principal))
151       return NULL;
152    return princ_ncomp(principal, component);
153}
154
155/**
156 * Get number of component is principal.
157 *
158 * @param context Kerberos 5 context
159 * @param principal principal to query
160 *
161 * @return number of components in string
162 *
163 * @ingroup krb5_principal
164 */
165
166KRB5_LIB_FUNCTION unsigned int KRB5_LIB_CALL
167krb5_principal_get_num_comp(krb5_context context,
168			    krb5_const_principal principal)
169{
170    return princ_num_comp(principal);
171}
172
173/**
174 * Parse a name into a krb5_principal structure, flags controls the behavior.
175 *
176 * @param context Kerberos 5 context
177 * @param name name to parse into a Kerberos principal
178 * @param flags flags to control the behavior
179 * @param principal returned principal, free with krb5_free_principal().
180 *
181 * @return An krb5 error code, see krb5_get_error_message().
182 *
183 * @ingroup krb5_principal
184 */
185
186KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
187krb5_parse_name_flags(krb5_context context,
188		      const char *name,
189		      int flags,
190		      krb5_principal *principal)
191{
192    krb5_error_code ret;
193    heim_general_string *comp;
194    heim_general_string realm = NULL;
195    int ncomp;
196
197    const char *p;
198    char *q;
199    char *s;
200    char *start;
201
202    int n;
203    char c;
204    int got_realm = 0;
205    int first_at = 1;
206    int enterprise = (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE);
207
208    *principal = NULL;
209
210#define RFLAGS (KRB5_PRINCIPAL_PARSE_NO_REALM|KRB5_PRINCIPAL_PARSE_REQUIRE_REALM)
211
212    if ((flags & RFLAGS) == RFLAGS) {
213	krb5_set_error_message(context, KRB5_ERR_NO_SERVICE,
214			       N_("Can't require both realm and "
215				  "no realm at the same time", ""));
216	return KRB5_ERR_NO_SERVICE;
217    }
218#undef RFLAGS
219
220    /* count number of component,
221     * enterprise names only have one component
222     */
223    ncomp = 1;
224    if (!enterprise) {
225	for(p = name; *p; p++){
226	    if(*p=='\\'){
227		if(!p[1]) {
228		    krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
229					   N_("trailing \\ in principal name", ""));
230		    return KRB5_PARSE_MALFORMED;
231		}
232		p++;
233	    } else if(*p == '/')
234		ncomp++;
235	    else if(*p == '@')
236		break;
237	}
238    }
239    comp = calloc(ncomp, sizeof(*comp));
240    if (comp == NULL) {
241	krb5_set_error_message(context, ENOMEM,
242			       N_("malloc: out of memory", ""));
243	return ENOMEM;
244    }
245
246    n = 0;
247    p = start = q = s = strdup(name);
248    if (start == NULL) {
249	free (comp);
250	krb5_set_error_message(context, ENOMEM,
251			       N_("malloc: out of memory", ""));
252	return ENOMEM;
253    }
254    while(*p){
255	c = *p++;
256	if(c == '\\'){
257	    c = *p++;
258	    if(c == 'n')
259		c = '\n';
260	    else if(c == 't')
261		c = '\t';
262	    else if(c == 'b')
263		c = '\b';
264	    else if(c == '0')
265		c = '\0';
266	    else if(c == '\0') {
267		ret = KRB5_PARSE_MALFORMED;
268		krb5_set_error_message(context, ret,
269				       N_("trailing \\ in principal name", ""));
270		goto exit;
271	    }
272	}else if(enterprise && first_at) {
273	    if (c == '@')
274		first_at = 0;
275	}else if((c == '/' && !enterprise) || c == '@'){
276	    if(got_realm){
277		ret = KRB5_PARSE_MALFORMED;
278		krb5_set_error_message(context, ret,
279				       N_("part after realm in principal name", ""));
280		goto exit;
281	    }else{
282		comp[n] = malloc(q - start + 1);
283		if (comp[n] == NULL) {
284		    ret = ENOMEM;
285		    krb5_set_error_message(context, ret,
286					   N_("malloc: out of memory", ""));
287		    goto exit;
288		}
289		memcpy(comp[n], start, q - start);
290		comp[n][q - start] = 0;
291		n++;
292	    }
293	    if(c == '@')
294		got_realm = 1;
295	    start = q;
296	    continue;
297	}
298	if(got_realm && (c == '/' || c == '\0')) {
299	    ret = KRB5_PARSE_MALFORMED;
300	    krb5_set_error_message(context, ret,
301				   N_("part after realm in principal name", ""));
302	    goto exit;
303	}
304	*q++ = c;
305    }
306    if(got_realm){
307	if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) {
308	    ret = KRB5_PARSE_MALFORMED;
309	    krb5_set_error_message(context, ret,
310				   N_("realm found in 'short' principal "
311				      "expected to be without one", ""));
312	    goto exit;
313	}
314	realm = malloc(q - start + 1);
315	if (realm == NULL) {
316	    ret = ENOMEM;
317	    krb5_set_error_message(context, ret,
318				   N_("malloc: out of memory", ""));
319	    goto exit;
320	}
321	memcpy(realm, start, q - start);
322	realm[q - start] = 0;
323    }else{
324	if (flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM) {
325	    ret = KRB5_PARSE_MALFORMED;
326	    krb5_set_error_message(context, ret,
327				   N_("realm NOT found in principal "
328				      "expected to be with one", ""));
329	    goto exit;
330	} else if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) {
331	    realm = NULL;
332	} else {
333	    ret = krb5_get_default_realm (context, &realm);
334	    if (ret)
335		goto exit;
336	}
337
338	comp[n] = malloc(q - start + 1);
339	if (comp[n] == NULL) {
340	    ret = ENOMEM;
341	    krb5_set_error_message(context, ret,
342				   N_("malloc: out of memory", ""));
343	    goto exit;
344	}
345	memcpy(comp[n], start, q - start);
346	comp[n][q - start] = 0;
347	n++;
348    }
349    *principal = malloc(sizeof(**principal));
350    if (*principal == NULL) {
351	ret = ENOMEM;
352	krb5_set_error_message(context, ret,
353			       N_("malloc: out of memory", ""));
354	goto exit;
355    }
356    if (enterprise)
357	(*principal)->name.name_type = KRB5_NT_ENTERPRISE_PRINCIPAL;
358    else
359	(*principal)->name.name_type = KRB5_NT_PRINCIPAL;
360    (*principal)->name.name_string.val = comp;
361    princ_num_comp(*principal) = n;
362    (*principal)->realm = realm;
363    free(s);
364    return 0;
365exit:
366    while(n>0){
367	free(comp[--n]);
368    }
369    free(comp);
370    free(realm);
371    free(s);
372    return ret;
373}
374
375/**
376 * Parse a name into a krb5_principal structure
377 *
378 * @param context Kerberos 5 context
379 * @param name name to parse into a Kerberos principal
380 * @param principal returned principal, free with krb5_free_principal().
381 *
382 * @return An krb5 error code, see krb5_get_error_message().
383 *
384 * @ingroup krb5_principal
385 */
386
387KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
388krb5_parse_name(krb5_context context,
389		const char *name,
390		krb5_principal *principal)
391{
392    return krb5_parse_name_flags(context, name, 0, principal);
393}
394
395static const char quotable_chars[] = " \n\t\b\\/@";
396static const char replace_chars[] = " ntb\\/@";
397static const char nq_chars[] = "    \\/@";
398
399#define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0);
400
401static size_t
402quote_string(const char *s, char *out, size_t idx, size_t len, int display)
403{
404    const char *p, *q;
405    for(p = s; *p && idx < len; p++){
406	q = strchr(quotable_chars, *p);
407	if (q && display) {
408	    add_char(out, idx, len, replace_chars[q - quotable_chars]);
409	} else if (q) {
410	    add_char(out, idx, len, '\\');
411	    add_char(out, idx, len, replace_chars[q - quotable_chars]);
412	}else
413	    add_char(out, idx, len, *p);
414    }
415    if(idx < len)
416	out[idx] = '\0';
417    return idx;
418}
419
420
421static krb5_error_code
422unparse_name_fixed(krb5_context context,
423		   krb5_const_principal principal,
424		   char *name,
425		   size_t len,
426		   int flags)
427{
428    size_t idx = 0;
429    size_t i;
430    int short_form = (flags & KRB5_PRINCIPAL_UNPARSE_SHORT) != 0;
431    int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) != 0;
432    int display = (flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) != 0;
433
434    if (!no_realm && princ_realm(principal) == NULL) {
435	krb5_set_error_message(context, ERANGE,
436			       N_("Realm missing from principal, "
437				  "can't unparse", ""));
438	return ERANGE;
439    }
440
441    for(i = 0; i < princ_num_comp(principal); i++){
442	if(i)
443	    add_char(name, idx, len, '/');
444	idx = quote_string(princ_ncomp(principal, i), name, idx, len, display);
445	if(idx == len) {
446	    krb5_set_error_message(context, ERANGE,
447				   N_("Out of space printing principal", ""));
448	    return ERANGE;
449	}
450    }
451    /* add realm if different from default realm */
452    if(short_form && !no_realm) {
453	krb5_realm r;
454	krb5_error_code ret;
455	ret = krb5_get_default_realm(context, &r);
456	if(ret)
457	    return ret;
458	if(strcmp(princ_realm(principal), r) != 0)
459	    short_form = 0;
460	free(r);
461    }
462    if(!short_form && !no_realm) {
463	add_char(name, idx, len, '@');
464	idx = quote_string(princ_realm(principal), name, idx, len, display);
465	if(idx == len) {
466	    krb5_set_error_message(context, ERANGE,
467				   N_("Out of space printing "
468				      "realm of principal", ""));
469	    return ERANGE;
470	}
471    }
472    return 0;
473}
474
475/**
476 * Unparse the principal name to a fixed buffer
477 *
478 * @param context A Kerberos context.
479 * @param principal principal to unparse
480 * @param name buffer to write name to
481 * @param len length of buffer
482 *
483 * @return An krb5 error code, see krb5_get_error_message().
484 *
485 * @ingroup krb5_principal
486 */
487
488KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
489krb5_unparse_name_fixed(krb5_context context,
490			krb5_const_principal principal,
491			char *name,
492			size_t len)
493{
494    return unparse_name_fixed(context, principal, name, len, 0);
495}
496
497/**
498 * Unparse the principal name to a fixed buffer. The realm is skipped
499 * if its a default realm.
500 *
501 * @param context A Kerberos context.
502 * @param principal principal to unparse
503 * @param name buffer to write name to
504 * @param len length of buffer
505 *
506 * @return An krb5 error code, see krb5_get_error_message().
507 *
508 * @ingroup krb5_principal
509 */
510
511KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
512krb5_unparse_name_fixed_short(krb5_context context,
513			      krb5_const_principal principal,
514			      char *name,
515			      size_t len)
516{
517    return unparse_name_fixed(context, principal, name, len,
518			      KRB5_PRINCIPAL_UNPARSE_SHORT);
519}
520
521/**
522 * Unparse the principal name with unparse flags to a fixed buffer.
523 *
524 * @param context A Kerberos context.
525 * @param principal principal to unparse
526 * @param flags unparse flags
527 * @param name buffer to write name to
528 * @param len length of buffer
529 *
530 * @return An krb5 error code, see krb5_get_error_message().
531 *
532 * @ingroup krb5_principal
533 */
534
535KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
536krb5_unparse_name_fixed_flags(krb5_context context,
537			      krb5_const_principal principal,
538			      int flags,
539			      char *name,
540			      size_t len)
541{
542    return unparse_name_fixed(context, principal, name, len, flags);
543}
544
545static krb5_error_code
546unparse_name(krb5_context context,
547	     krb5_const_principal principal,
548	     char **name,
549	     int flags)
550{
551    size_t len = 0, plen;
552    size_t i;
553    krb5_error_code ret;
554    /* count length */
555    if (princ_realm(principal)) {
556	plen = strlen(princ_realm(principal));
557
558	if(strcspn(princ_realm(principal), quotable_chars) == plen)
559	    len += plen;
560	else
561	    len += 2*plen;
562	len++; /* '@' */
563    }
564    for(i = 0; i < princ_num_comp(principal); i++){
565	plen = strlen(princ_ncomp(principal, i));
566	if(strcspn(princ_ncomp(principal, i), quotable_chars) == plen)
567	    len += plen;
568	else
569	    len += 2*plen;
570	len++;
571    }
572    len++; /* '\0' */
573    *name = malloc(len);
574    if(*name == NULL) {
575	krb5_set_error_message(context, ENOMEM,
576			       N_("malloc: out of memory", ""));
577	return ENOMEM;
578    }
579    ret = unparse_name_fixed(context, principal, *name, len, flags);
580    if(ret) {
581	free(*name);
582	*name = NULL;
583    }
584    return ret;
585}
586
587/**
588 * Unparse the Kerberos name into a string
589 *
590 * @param context Kerberos 5 context
591 * @param principal principal to query
592 * @param name resulting string, free with krb5_xfree()
593 *
594 * @return An krb5 error code, see krb5_get_error_message().
595 *
596 * @ingroup krb5_principal
597 */
598
599KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
600krb5_unparse_name(krb5_context context,
601		  krb5_const_principal principal,
602		  char **name)
603{
604    return unparse_name(context, principal, name, 0);
605}
606
607/**
608 * Unparse the Kerberos name into a string
609 *
610 * @param context Kerberos 5 context
611 * @param principal principal to query
612 * @param flags flag to determine the behavior
613 * @param name resulting string, free with krb5_xfree()
614 *
615 * @return An krb5 error code, see krb5_get_error_message().
616 *
617 * @ingroup krb5_principal
618 */
619
620KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
621krb5_unparse_name_flags(krb5_context context,
622			krb5_const_principal principal,
623			int flags,
624			char **name)
625{
626    return unparse_name(context, principal, name, flags);
627}
628
629/**
630 * Unparse the principal name to a allocated buffer. The realm is
631 * skipped if its a default realm.
632 *
633 * @param context A Kerberos context.
634 * @param principal principal to unparse
635 * @param name returned buffer, free with krb5_xfree()
636 *
637 * @return An krb5 error code, see krb5_get_error_message().
638 *
639 * @ingroup krb5_principal
640 */
641
642KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
643krb5_unparse_name_short(krb5_context context,
644			krb5_const_principal principal,
645			char **name)
646{
647    return unparse_name(context, principal, name, KRB5_PRINCIPAL_UNPARSE_SHORT);
648}
649
650/**
651 * Set a new realm for a principal, and as a side-effect free the
652 * previous realm.
653 *
654 * @param context A Kerberos context.
655 * @param principal principal set the realm for
656 * @param realm the new realm to set
657 *
658 * @return An krb5 error code, see krb5_get_error_message().
659 *
660 * @ingroup krb5_principal
661 */
662
663KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
664krb5_principal_set_realm(krb5_context context,
665			 krb5_principal principal,
666			 krb5_const_realm realm)
667{
668    if (princ_realm(principal))
669	free(princ_realm(principal));
670
671    princ_realm(principal) = strdup(realm);
672    if (princ_realm(principal) == NULL) {
673	krb5_set_error_message(context, ENOMEM,
674			       N_("malloc: out of memory", ""));
675	return ENOMEM;
676    }
677    return 0;
678}
679
680#ifndef HEIMDAL_SMALLER
681/**
682 * Build a principal using vararg style building
683 *
684 * @param context A Kerberos context.
685 * @param principal returned principal
686 * @param rlen length of realm
687 * @param realm realm name
688 * @param ... a list of components ended with NULL.
689 *
690 * @return An krb5 error code, see krb5_get_error_message().
691 *
692 * @ingroup krb5_principal
693 */
694
695KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
696krb5_build_principal(krb5_context context,
697		     krb5_principal *principal,
698		     int rlen,
699		     krb5_const_realm realm,
700		     ...)
701{
702    krb5_error_code ret;
703    va_list ap;
704    va_start(ap, realm);
705    ret = krb5_build_principal_va(context, principal, rlen, realm, ap);
706    va_end(ap);
707    return ret;
708}
709#endif
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, 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, *host = NULL;
1042
1043    if(type != KRB5_NT_SRV_HST && type != KRB5_NT_UNKNOWN) {
1044	krb5_set_error_message(context, KRB5_SNAME_UNSUPP_NAMETYPE,
1045			       N_("unsupported name type %d", ""),
1046			       (int)type);
1047	return KRB5_SNAME_UNSUPP_NAMETYPE;
1048    }
1049    if(hostname == NULL) {
1050	ret = gethostname(localhost, sizeof(localhost) - 1);
1051	if (ret != 0) {
1052	    ret = errno;
1053	    krb5_set_error_message(context, ret,
1054				   N_("Failed to get local hostname", ""));
1055	    return ret;
1056	}
1057	localhost[sizeof(localhost) - 1] = '\0';
1058	hostname = localhost;
1059    }
1060    if(sname == NULL)
1061	sname = "host";
1062    if(type == KRB5_NT_SRV_HST) {
1063	ret = krb5_expand_hostname_realms (context, hostname,
1064					   &host, &realms);
1065	if (ret)
1066	    return ret;
1067	strlwr(host);
1068	hostname = host;
1069    } else {
1070	ret = krb5_get_host_realm(context, hostname, &realms);
1071	if(ret)
1072	    return ret;
1073    }
1074
1075    ret = krb5_make_principal(context, ret_princ, realms[0], sname,
1076			      hostname, NULL);
1077    if(host)
1078	free(host);
1079    krb5_free_host_realm(context, realms);
1080    return ret;
1081}
1082
1083static const struct {
1084    const char *type;
1085    int32_t value;
1086} nametypes[] = {
1087    { "UNKNOWN", KRB5_NT_UNKNOWN },
1088    { "PRINCIPAL", KRB5_NT_PRINCIPAL },
1089    { "SRV_INST", KRB5_NT_SRV_INST },
1090    { "SRV_HST", KRB5_NT_SRV_HST },
1091    { "SRV_XHST", KRB5_NT_SRV_XHST },
1092    { "UID", KRB5_NT_UID },
1093    { "X500_PRINCIPAL", KRB5_NT_X500_PRINCIPAL },
1094    { "SMTP_NAME", KRB5_NT_SMTP_NAME },
1095    { "ENTERPRISE_PRINCIPAL", KRB5_NT_ENTERPRISE_PRINCIPAL },
1096    { "ENT_PRINCIPAL_AND_ID", KRB5_NT_ENT_PRINCIPAL_AND_ID },
1097    { "MS_PRINCIPAL", KRB5_NT_MS_PRINCIPAL },
1098    { "MS_PRINCIPAL_AND_ID", KRB5_NT_MS_PRINCIPAL_AND_ID },
1099    { NULL, 0 }
1100};
1101
1102/**
1103 * Parse nametype string and return a nametype integer
1104 *
1105 * @ingroup krb5_principal
1106 */
1107
1108KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1109krb5_parse_nametype(krb5_context context, const char *str, int32_t *nametype)
1110{
1111    size_t i;
1112
1113    for(i = 0; nametypes[i].type; i++) {
1114	if (strcasecmp(nametypes[i].type, str) == 0) {
1115	    *nametype = nametypes[i].value;
1116	    return 0;
1117	}
1118    }
1119    krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
1120			   N_("Failed to find name type %s", ""), str);
1121    return KRB5_PARSE_MALFORMED;
1122}
1123
1124/**
1125 * Check if the cname part of the principal is a krbtgt principal
1126 *
1127 * @ingroup krb5_principal
1128 */
1129
1130KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1131krb5_principal_is_krbtgt(krb5_context context, krb5_const_principal p)
1132{
1133    return p->name.name_string.len == 2 &&
1134	strcmp(p->name.name_string.val[0], KRB5_TGS_NAME) == 0;
1135
1136}
1137