principal.c revision 78527
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
44RCSID("$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
52void
53krb5_free_principal(krb5_context context,
54		    krb5_principal p)
55{
56    if(p){
57	free_Principal(p);
58	free(p);
59    }
60}
61
62krb5_error_code
63krb5_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;
185exit:
186    while(n>0){
187	free(comp[--n]);
188    }
189    free(comp);
190    free(s);
191    return ret;
192}
193
194static const char quotable_chars[] = " \n\t\b\\/@";
195static const char replace_chars[] = " ntb\\/@";
196
197#define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0);
198
199static size_t
200quote_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
216static krb5_error_code
217unparse_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
252krb5_error_code
253krb5_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
261krb5_error_code
262krb5_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
270static krb5_error_code
271unparse_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
305krb5_error_code
306krb5_unparse_name(krb5_context context,
307		  krb5_const_principal principal,
308		  char **name)
309{
310    return unparse_name(context, principal, name, FALSE);
311}
312
313krb5_error_code
314krb5_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
323krb5_error_code
324krb5_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
334krb5_realm*
335krb5_princ_realm(krb5_context context,
336		 krb5_principal principal)
337{
338    return &princ_realm(principal);
339}
340
341
342void
343krb5_princ_set_realm(krb5_context context,
344		     krb5_principal principal,
345		     krb5_realm *realm)
346{
347    princ_realm(principal) = *realm;
348}
349
350
351krb5_error_code
352krb5_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
366static krb5_error_code
367append_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
391static void
392va_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
405static void
406va_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
418static krb5_error_code
419build_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
447krb5_error_code
448krb5_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
470krb5_error_code
471krb5_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
480krb5_error_code
481krb5_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
491krb5_error_code
492krb5_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
507krb5_error_code
508krb5_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
530krb5_boolean
531krb5_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
549krb5_boolean
550krb5_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
563krb5_boolean
564krb5_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
575krb5_boolean
576krb5_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
593struct 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
611static const char*
612get_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
655krb5_error_code
656krb5_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;
779no_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
807krb5_error_code
808krb5_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
825static int
826check_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
840static int
841name_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
900krb5_error_code
901krb5_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
971krb5_error_code
972krb5_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