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