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