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