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