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