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