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