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