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