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