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