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
44RCSID("$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
52void KRB5_LIB_FUNCTION
53krb5_free_principal(krb5_context context,
54		    krb5_principal p)
55{
56    if(p){
57	free_Principal(p);
58	free(p);
59    }
60}
61
62void KRB5_LIB_FUNCTION
63krb5_principal_set_type(krb5_context context,
64			krb5_principal principal,
65			int type)
66{
67    princ_type(principal) = type;
68}
69
70int KRB5_LIB_FUNCTION
71krb5_principal_get_type(krb5_context context,
72			krb5_const_principal principal)
73{
74    return princ_type(principal);
75}
76
77const char* KRB5_LIB_FUNCTION
78krb5_principal_get_realm(krb5_context context,
79			 krb5_const_principal principal)
80{
81    return princ_realm(principal);
82}
83
84const char* KRB5_LIB_FUNCTION
85krb5_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
94krb5_error_code KRB5_LIB_FUNCTION
95krb5_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;
264exit:
265    while(n>0){
266	free(comp[--n]);
267    }
268    free(comp);
269    free(realm);
270    free(s);
271    return ret;
272}
273
274krb5_error_code KRB5_LIB_FUNCTION
275krb5_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
282static const char quotable_chars[] = " \n\t\b\\/@";
283static const char replace_chars[] = " ntb\\/@";
284static const char nq_chars[] = "    \\/@";
285
286#define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0);
287
288static size_t
289quote_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
308static krb5_error_code
309unparse_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
359krb5_error_code KRB5_LIB_FUNCTION
360krb5_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
368krb5_error_code KRB5_LIB_FUNCTION
369krb5_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
378krb5_error_code KRB5_LIB_FUNCTION
379krb5_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
388static krb5_error_code
389unparse_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
429krb5_error_code KRB5_LIB_FUNCTION
430krb5_unparse_name(krb5_context context,
431		  krb5_const_principal principal,
432		  char **name)
433{
434    return unparse_name(context, principal, name, 0);
435}
436
437krb5_error_code KRB5_LIB_FUNCTION
438krb5_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
446krb5_error_code KRB5_LIB_FUNCTION
447krb5_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
456krb5_error_code KRB5_LIB_FUNCTION
457krb5_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
467krb5_realm * KRB5_LIB_FUNCTION
468krb5_princ_realm(krb5_context context,
469		 krb5_principal principal)
470{
471    return &princ_realm(principal);
472}
473
474
475void KRB5_LIB_FUNCTION
476krb5_princ_set_realm(krb5_context context,
477		     krb5_principal principal,
478		     krb5_realm *realm)
479{
480    princ_realm(principal) = *realm;
481}
482
483
484krb5_error_code KRB5_LIB_FUNCTION
485krb5_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
499static krb5_error_code
500append_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
524static void
525va_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
538static void
539va_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
551static krb5_error_code
552build_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
580krb5_error_code KRB5_LIB_FUNCTION
581krb5_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
603krb5_error_code KRB5_LIB_FUNCTION
604krb5_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
613krb5_error_code KRB5_LIB_FUNCTION
614krb5_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
624krb5_error_code KRB5_LIB_FUNCTION
625krb5_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
640krb5_error_code KRB5_LIB_FUNCTION
641krb5_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
663krb5_boolean KRB5_LIB_FUNCTION
664krb5_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
682krb5_boolean KRB5_LIB_FUNCTION
683krb5_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
696krb5_boolean KRB5_LIB_FUNCTION
697krb5_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
708krb5_boolean KRB5_LIB_FUNCTION
709krb5_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
726static 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
745static const char*
746get_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
789krb5_error_code KRB5_LIB_FUNCTION
790krb5_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);
950local_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;
959no_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
987static krb5_boolean
988convert_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
994krb5_error_code KRB5_LIB_FUNCTION
995krb5_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
1015krb5_error_code KRB5_LIB_FUNCTION
1016krb5_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
1033static int
1034check_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
1048static int
1049name_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
1108krb5_error_code KRB5_LIB_FUNCTION
1109krb5_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
1179krb5_error_code KRB5_LIB_FUNCTION
1180krb5_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
1222static 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
1241krb5_error_code
1242krb5_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}
1255