1 /*	$NetBSD: principal.c,v 1.1.1.1 2011/04/13 18:15:37 elric Exp $	*/
2 
3 /*
4  * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * 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
44 lha@SU.SE
45 host/hummel.it.su.se@SU.SE
46 host/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 <krb5/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 
81 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
82 krb5_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 
103 KRB5_LIB_FUNCTION void KRB5_LIB_CALL
104 krb5_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 
122 KRB5_LIB_FUNCTION int KRB5_LIB_CALL
123 krb5_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 
140 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
141 krb5_principal_get_realm(krb5_context context,
142 			 krb5_const_principal principal)
143 {
144     return princ_realm(principal);
145 }
146 
147 KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
148 krb5_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 
168 KRB5_LIB_FUNCTION unsigned int KRB5_LIB_CALL
169 krb5_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 
188 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
189 krb5_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;
367 exit:
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 
389 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
390 krb5_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 
397 static const char quotable_chars[] = " \n\t\b\\/@";
398 static const char replace_chars[] = " ntb\\/@";
399 static const char nq_chars[] = "    \\/@";
400 
401 #define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0);
402 
403 static size_t
404 quote_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 
423 static krb5_error_code
424 unparse_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     int 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 
490 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
491 krb5_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 
513 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
514 krb5_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 
537 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
538 krb5_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 
547 static krb5_error_code
548 unparse_name(krb5_context context,
549 	     krb5_const_principal principal,
550 	     char **name,
551 	     int flags)
552 {
553     size_t len = 0, plen;
554     int 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 
601 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
602 krb5_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 
622 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
623 krb5_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 
644 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
645 krb5_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 
665 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
666 krb5_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 #ifndef HEIMDAL_SMALLER
683 /**
684  * Build a principal using vararg style building
685  *
686  * @param context A Kerberos context.
687  * @param principal returned principal
688  * @param rlen length of realm
689  * @param realm realm name
690  * @param ... a list of components ended with NULL.
691  *
692  * @return An krb5 error code, see krb5_get_error_message().
693  *
694  * @ingroup krb5_principal
695  */
696 
697 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
698 krb5_build_principal(krb5_context context,
699 		     krb5_principal *principal,
700 		     int rlen,
701 		     krb5_const_realm realm,
702 		     ...)
703 {
704     krb5_error_code ret;
705     va_list ap;
706     va_start(ap, realm);
707     ret = krb5_build_principal_va(context, principal, rlen, realm, ap);
708     va_end(ap);
709     return ret;
710 }
711 #endif
712 
713 /**
714  * Build a principal using vararg style building
715  *
716  * @param context A Kerberos context.
717  * @param principal returned principal
718  * @param realm realm name
719  * @param ... a list of components ended with NULL.
720  *
721  * @return An krb5 error code, see krb5_get_error_message().
722  *
723  * @ingroup krb5_principal
724  */
725 
726 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
727 krb5_make_principal(krb5_context context,
728 		    krb5_principal *principal,
729 		    krb5_const_realm realm,
730 		    ...)
731 {
732     krb5_error_code ret;
733     krb5_realm r = NULL;
734     va_list ap;
735     if(realm == NULL) {
736 	ret = krb5_get_default_realm(context, &r);
737 	if(ret)
738 	    return ret;
739 	realm = r;
740     }
741     va_start(ap, realm);
742     ret = krb5_build_principal_va(context, principal, strlen(realm), realm, ap);
743     va_end(ap);
744     if(r)
745 	free(r);
746     return ret;
747 }
748 
749 static krb5_error_code
750 append_component(krb5_context context, krb5_principal p,
751 		 const char *comp,
752 		 size_t comp_len)
753 {
754     heim_general_string *tmp;
755     size_t len = princ_num_comp(p);
756 
757     tmp = realloc(princ_comp(p), (len + 1) * sizeof(*tmp));
758     if(tmp == NULL) {
759 	krb5_set_error_message(context, ENOMEM,
760 			       N_("malloc: out of memory", ""));
761 	return ENOMEM;
762     }
763     princ_comp(p) = tmp;
764     princ_ncomp(p, len) = malloc(comp_len + 1);
765     if (princ_ncomp(p, len) == NULL) {
766 	krb5_set_error_message(context, ENOMEM,
767 			       N_("malloc: out of memory", ""));
768 	return ENOMEM;
769     }
770     memcpy (princ_ncomp(p, len), comp, comp_len);
771     princ_ncomp(p, len)[comp_len] = '\0';
772     princ_num_comp(p)++;
773     return 0;
774 }
775 
776 static void
777 va_ext_princ(krb5_context context, krb5_principal p, va_list ap)
778 {
779     while(1){
780 	const char *s;
781 	int len;
782 	len = va_arg(ap, int);
783 	if(len == 0)
784 	    break;
785 	s = va_arg(ap, const char*);
786 	append_component(context, p, s, len);
787     }
788 }
789 
790 static void
791 va_princ(krb5_context context, krb5_principal p, va_list ap)
792 {
793     while(1){
794 	const char *s;
795 	s = va_arg(ap, const char*);
796 	if(s == NULL)
797 	    break;
798 	append_component(context, p, s, strlen(s));
799     }
800 }
801 
802 static krb5_error_code
803 build_principal(krb5_context context,
804 		krb5_principal *principal,
805 		int rlen,
806 		krb5_const_realm realm,
807 		void (*func)(krb5_context, krb5_principal, va_list),
808 		va_list ap)
809 {
810     krb5_principal p;
811 
812     p = calloc(1, sizeof(*p));
813     if (p == NULL) {
814 	krb5_set_error_message(context, ENOMEM,
815 			       N_("malloc: out of memory", ""));
816 	return ENOMEM;
817     }
818     princ_type(p) = KRB5_NT_PRINCIPAL;
819 
820     princ_realm(p) = strdup(realm);
821     if(p->realm == NULL){
822 	free(p);
823 	krb5_set_error_message(context, ENOMEM,
824 			       N_("malloc: out of memory", ""));
825 	return ENOMEM;
826     }
827 
828     (*func)(context, p, ap);
829     *principal = p;
830     return 0;
831 }
832 
833 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
834 krb5_build_principal_va(krb5_context context,
835 			krb5_principal *principal,
836 			int rlen,
837 			krb5_const_realm realm,
838 			va_list ap)
839 {
840     return build_principal(context, principal, rlen, realm, va_princ, ap);
841 }
842 
843 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
844 krb5_build_principal_va_ext(krb5_context context,
845 			    krb5_principal *principal,
846 			    int rlen,
847 			    krb5_const_realm realm,
848 			    va_list ap)
849 {
850     return build_principal(context, principal, rlen, realm, va_ext_princ, ap);
851 }
852 
853 
854 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
855 krb5_build_principal_ext(krb5_context context,
856 			 krb5_principal *principal,
857 			 int rlen,
858 			 krb5_const_realm realm,
859 			 ...)
860 {
861     krb5_error_code ret;
862     va_list ap;
863     va_start(ap, realm);
864     ret = krb5_build_principal_va_ext(context, principal, rlen, realm, ap);
865     va_end(ap);
866     return ret;
867 }
868 
869 /**
870  * Copy a principal
871  *
872  * @param context A Kerberos context.
873  * @param inprinc principal to copy
874  * @param outprinc copied principal, free with krb5_free_principal()
875  *
876  * @return An krb5 error code, see krb5_get_error_message().
877  *
878  * @ingroup krb5_principal
879  */
880 
881 
882 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
883 krb5_copy_principal(krb5_context context,
884 		    krb5_const_principal inprinc,
885 		    krb5_principal *outprinc)
886 {
887     krb5_principal p = malloc(sizeof(*p));
888     if (p == NULL) {
889 	krb5_set_error_message(context, ENOMEM,
890 			       N_("malloc: out of memory", ""));
891 	return ENOMEM;
892     }
893     if(copy_Principal(inprinc, p)) {
894 	free(p);
895 	krb5_set_error_message(context, ENOMEM,
896 			       N_("malloc: out of memory", ""));
897 	return ENOMEM;
898     }
899     *outprinc = p;
900     return 0;
901 }
902 
903 /**
904  * Return TRUE iff princ1 == princ2 (without considering the realm)
905  *
906  * @param context Kerberos 5 context
907  * @param princ1 first principal to compare
908  * @param princ2 second principal to compare
909  *
910  * @return non zero if equal, 0 if not
911  *
912  * @ingroup krb5_principal
913  * @see krb5_principal_compare()
914  * @see krb5_realm_compare()
915  */
916 
917 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
918 krb5_principal_compare_any_realm(krb5_context context,
919 				 krb5_const_principal princ1,
920 				 krb5_const_principal princ2)
921 {
922     int i;
923     if(princ_num_comp(princ1) != princ_num_comp(princ2))
924 	return FALSE;
925     for(i = 0; i < princ_num_comp(princ1); i++){
926 	if(strcmp(princ_ncomp(princ1, i), princ_ncomp(princ2, i)) != 0)
927 	    return FALSE;
928     }
929     return TRUE;
930 }
931 
932 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
933 _krb5_principal_compare_PrincipalName(krb5_context context,
934 				      krb5_const_principal princ1,
935 				      PrincipalName *princ2)
936 {
937     int i;
938     if (princ_num_comp(princ1) != princ2->name_string.len)
939 	return FALSE;
940     for(i = 0; i < princ_num_comp(princ1); i++){
941 	if(strcmp(princ_ncomp(princ1, i), princ2->name_string.val[i]) != 0)
942 	    return FALSE;
943     }
944     return TRUE;
945 }
946 
947 
948 /**
949  * Compares the two principals, including realm of the principals and returns
950  * TRUE if they are the same and FALSE if not.
951  *
952  * @param context Kerberos 5 context
953  * @param princ1 first principal to compare
954  * @param princ2 second principal to compare
955  *
956  * @ingroup krb5_principal
957  * @see krb5_principal_compare_any_realm()
958  * @see krb5_realm_compare()
959  */
960 
961 /*
962  * return TRUE iff princ1 == princ2
963  */
964 
965 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
966 krb5_principal_compare(krb5_context context,
967 		       krb5_const_principal princ1,
968 		       krb5_const_principal princ2)
969 {
970     if(!krb5_realm_compare(context, princ1, princ2))
971 	return FALSE;
972     return krb5_principal_compare_any_realm(context, princ1, princ2);
973 }
974 
975 /**
976  * return TRUE iff realm(princ1) == realm(princ2)
977  *
978  * @param context Kerberos 5 context
979  * @param princ1 first principal to compare
980  * @param princ2 second principal to compare
981  *
982  * @ingroup krb5_principal
983  * @see krb5_principal_compare_any_realm()
984  * @see krb5_principal_compare()
985  */
986 
987 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
988 krb5_realm_compare(krb5_context context,
989 		   krb5_const_principal princ1,
990 		   krb5_const_principal princ2)
991 {
992     return strcmp(princ_realm(princ1), princ_realm(princ2)) == 0;
993 }
994 
995 /**
996  * return TRUE iff princ matches pattern
997  *
998  * @ingroup krb5_principal
999  */
1000 
1001 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1002 krb5_principal_match(krb5_context context,
1003 		     krb5_const_principal princ,
1004 		     krb5_const_principal pattern)
1005 {
1006     int i;
1007     if(princ_num_comp(princ) != princ_num_comp(pattern))
1008 	return FALSE;
1009     if(fnmatch(princ_realm(pattern), princ_realm(princ), 0) != 0)
1010 	return FALSE;
1011     for(i = 0; i < princ_num_comp(princ); i++){
1012 	if(fnmatch(princ_ncomp(pattern, i), princ_ncomp(princ, i), 0) != 0)
1013 	    return FALSE;
1014     }
1015     return TRUE;
1016 }
1017 
1018 /**
1019  * Create a principal for the service running on hostname. If
1020  * KRB5_NT_SRV_HST is used, the hostname is canonization using DNS (or
1021  * some other service), this is potentially insecure.
1022  *
1023  * @param context A Kerberos context.
1024  * @param hostname hostname to use
1025  * @param sname Service name to use
1026  * @param type name type of pricipal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN.
1027  * @param ret_princ return principal, free with krb5_free_principal().
1028  *
1029  * @return An krb5 error code, see krb5_get_error_message().
1030  *
1031  * @ingroup krb5_principal
1032  */
1033 
1034 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1035 krb5_sname_to_principal (krb5_context context,
1036 			 const char *hostname,
1037 			 const char *sname,
1038 			 int32_t type,
1039 			 krb5_principal *ret_princ)
1040 {
1041     krb5_error_code ret;
1042     char localhost[MAXHOSTNAMELEN];
1043     char **realms, *host = NULL;
1044 
1045     if(type != KRB5_NT_SRV_HST && type != KRB5_NT_UNKNOWN) {
1046 	krb5_set_error_message(context, KRB5_SNAME_UNSUPP_NAMETYPE,
1047 			       N_("unsupported name type %d", ""),
1048 			       (int)type);
1049 	return KRB5_SNAME_UNSUPP_NAMETYPE;
1050     }
1051     if(hostname == NULL) {
1052 	ret = gethostname(localhost, sizeof(localhost) - 1);
1053 	if (ret != 0) {
1054 	    ret = errno;
1055 	    krb5_set_error_message(context, ret,
1056 				   N_("Failed to get local hostname", ""));
1057 	    return ret;
1058 	}
1059 	localhost[sizeof(localhost) - 1] = '\0';
1060 	hostname = localhost;
1061     }
1062     if(sname == NULL)
1063 	sname = "host";
1064     if(type == KRB5_NT_SRV_HST) {
1065 	ret = krb5_expand_hostname_realms (context, hostname,
1066 					   &host, &realms);
1067 	if (ret)
1068 	    return ret;
1069 	strlwr(host);
1070 	hostname = host;
1071     } else {
1072 	ret = krb5_get_host_realm(context, hostname, &realms);
1073 	if(ret)
1074 	    return ret;
1075     }
1076 
1077     ret = krb5_make_principal(context, ret_princ, realms[0], sname,
1078 			      hostname, NULL);
1079     if(host)
1080 	free(host);
1081     krb5_free_host_realm(context, realms);
1082     return ret;
1083 }
1084 
1085 static const struct {
1086     const char *type;
1087     int32_t value;
1088 } nametypes[] = {
1089     { "UNKNOWN", KRB5_NT_UNKNOWN },
1090     { "PRINCIPAL", KRB5_NT_PRINCIPAL },
1091     { "SRV_INST", KRB5_NT_SRV_INST },
1092     { "SRV_HST", KRB5_NT_SRV_HST },
1093     { "SRV_XHST", KRB5_NT_SRV_XHST },
1094     { "UID", KRB5_NT_UID },
1095     { "X500_PRINCIPAL", KRB5_NT_X500_PRINCIPAL },
1096     { "SMTP_NAME", KRB5_NT_SMTP_NAME },
1097     { "ENTERPRISE_PRINCIPAL", KRB5_NT_ENTERPRISE_PRINCIPAL },
1098     { "ENT_PRINCIPAL_AND_ID", KRB5_NT_ENT_PRINCIPAL_AND_ID },
1099     { "MS_PRINCIPAL", KRB5_NT_MS_PRINCIPAL },
1100     { "MS_PRINCIPAL_AND_ID", KRB5_NT_MS_PRINCIPAL_AND_ID },
1101     { NULL }
1102 };
1103 
1104 /**
1105  * Parse nametype string and return a nametype integer
1106  *
1107  * @ingroup krb5_principal
1108  */
1109 
1110 KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
1111 krb5_parse_nametype(krb5_context context, const char *str, int32_t *nametype)
1112 {
1113     size_t i;
1114 
1115     for(i = 0; nametypes[i].type; i++) {
1116 	if (strcasecmp(nametypes[i].type, str) == 0) {
1117 	    *nametype = nametypes[i].value;
1118 	    return 0;
1119 	}
1120     }
1121     krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
1122 			   N_("Failed to find name type %s", ""), str);
1123     return KRB5_PARSE_MALFORMED;
1124 }
1125 
1126 /**
1127  * Check if the cname part of the principal is a krbtgt principal
1128  *
1129  * @ingroup krb5_principal
1130  */
1131 
1132 KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
1133 krb5_principal_is_krbtgt(krb5_context context, krb5_const_principal p)
1134 {
1135     return p->name.name_string.len == 2 &&
1136 	strcmp(p->name.name_string.val[0], KRB5_TGS_NAME) == 0;
1137 
1138 }
1139