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