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