xref: /freebsd/crypto/heimdal/lib/krb5/get_host_realm.c (revision 6a068746777241722b2b32c5d0bc443a2a64d80b)
1b528cefcSMark Murray /*
2*ae771770SStanislav Sedov  * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
3b528cefcSMark Murray  * (Royal Institute of Technology, Stockholm, Sweden).
4b528cefcSMark Murray  * All rights reserved.
5b528cefcSMark Murray  *
6b528cefcSMark Murray  * Redistribution and use in source and binary forms, with or without
7b528cefcSMark Murray  * modification, are permitted provided that the following conditions
8b528cefcSMark Murray  * are met:
9b528cefcSMark Murray  *
10b528cefcSMark Murray  * 1. Redistributions of source code must retain the above copyright
11b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer.
12b528cefcSMark Murray  *
13b528cefcSMark Murray  * 2. Redistributions in binary form must reproduce the above copyright
14b528cefcSMark Murray  *    notice, this list of conditions and the following disclaimer in the
15b528cefcSMark Murray  *    documentation and/or other materials provided with the distribution.
16b528cefcSMark Murray  *
17b528cefcSMark Murray  * 3. Neither the name of the Institute nor the names of its contributors
18b528cefcSMark Murray  *    may be used to endorse or promote products derived from this software
19b528cefcSMark Murray  *    without specific prior written permission.
20b528cefcSMark Murray  *
21b528cefcSMark Murray  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22b528cefcSMark Murray  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23b528cefcSMark Murray  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24b528cefcSMark Murray  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25b528cefcSMark Murray  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26b528cefcSMark Murray  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27b528cefcSMark Murray  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28b528cefcSMark Murray  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29b528cefcSMark Murray  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30b528cefcSMark Murray  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31b528cefcSMark Murray  * SUCH DAMAGE.
32b528cefcSMark Murray  */
33b528cefcSMark Murray 
34b528cefcSMark Murray #include "krb5_locl.h"
35b528cefcSMark Murray #include <resolve.h>
36b528cefcSMark Murray 
37b528cefcSMark Murray /* To automagically find the correct realm of a host (without
38b528cefcSMark Murray  * [domain_realm] in krb5.conf) add a text record for your domain with
39b528cefcSMark Murray  * the name of your realm, like this:
40b528cefcSMark Murray  *
418373020dSJacques Vidrine  * _kerberos	IN	TXT	"FOO.SE"
42b528cefcSMark Murray  *
43b528cefcSMark Murray  * The search is recursive, so you can add entries for specific
44b528cefcSMark Murray  * hosts. To find the realm of host a.b.c, it first tries
458373020dSJacques Vidrine  * _kerberos.a.b.c, then _kerberos.b.c and so on.
46b528cefcSMark Murray  *
478373020dSJacques Vidrine  * This method is described in draft-ietf-cat-krb-dns-locate-03.txt.
48b528cefcSMark Murray  *
49b528cefcSMark Murray  */
50b528cefcSMark Murray 
51b528cefcSMark Murray static int
copy_txt_to_realms(struct rk_resource_record * head,krb5_realm ** realms)52*ae771770SStanislav Sedov copy_txt_to_realms (struct rk_resource_record *head,
53b528cefcSMark Murray 		    krb5_realm **realms)
54b528cefcSMark Murray {
55*ae771770SStanislav Sedov     struct rk_resource_record *rr;
56*ae771770SStanislav Sedov     unsigned int n, i;
57b528cefcSMark Murray 
58b528cefcSMark Murray     for(n = 0, rr = head; rr; rr = rr->next)
59*ae771770SStanislav Sedov 	if (rr->type == rk_ns_t_txt)
60b528cefcSMark Murray 	    ++n;
61b528cefcSMark Murray 
62b528cefcSMark Murray     if (n == 0)
63b528cefcSMark Murray 	return -1;
64b528cefcSMark Murray 
65b528cefcSMark Murray     *realms = malloc ((n + 1) * sizeof(krb5_realm));
66b528cefcSMark Murray     if (*realms == NULL)
67b528cefcSMark Murray 	return -1;
68b528cefcSMark Murray 
69b528cefcSMark Murray     for (i = 0; i < n + 1; ++i)
70b528cefcSMark Murray 	(*realms)[i] = NULL;
71b528cefcSMark Murray 
72b528cefcSMark Murray     for (i = 0, rr = head; rr; rr = rr->next) {
73*ae771770SStanislav Sedov 	if (rr->type == rk_ns_t_txt) {
74b528cefcSMark Murray 	    char *tmp;
75b528cefcSMark Murray 
76b528cefcSMark Murray 	    tmp = strdup(rr->u.txt);
77b528cefcSMark Murray 	    if (tmp == NULL) {
78b528cefcSMark Murray 		for (i = 0; i < n; ++i)
79b528cefcSMark Murray 		    free ((*realms)[i]);
80b528cefcSMark Murray 		free (*realms);
81b528cefcSMark Murray 		return -1;
82b528cefcSMark Murray 	    }
83b528cefcSMark Murray 	    (*realms)[i] = tmp;
84b528cefcSMark Murray 	    ++i;
85b528cefcSMark Murray 	}
86b528cefcSMark Murray     }
87b528cefcSMark Murray     return 0;
88b528cefcSMark Murray }
89b528cefcSMark Murray 
90b528cefcSMark Murray static int
dns_find_realm(krb5_context context,const char * domain,krb5_realm ** realms)91b528cefcSMark Murray dns_find_realm(krb5_context context,
92b528cefcSMark Murray 	       const char *domain,
93b528cefcSMark Murray 	       krb5_realm **realms)
94b528cefcSMark Murray {
95c19800e8SDoug Rabson     static const char *default_labels[] = { "_kerberos", NULL };
96b528cefcSMark Murray     char dom[MAXHOSTNAMELEN];
97*ae771770SStanislav Sedov     struct rk_dns_reply *r;
98c19800e8SDoug Rabson     const char **labels;
99c19800e8SDoug Rabson     char **config_labels;
1008373020dSJacques Vidrine     int i, ret;
101b528cefcSMark Murray 
102c19800e8SDoug Rabson     config_labels = krb5_config_get_strings(context, NULL, "libdefaults",
1038373020dSJacques Vidrine 					    "dns_lookup_realm_labels", NULL);
104c19800e8SDoug Rabson     if(config_labels != NULL)
105c19800e8SDoug Rabson 	labels = (const char **)config_labels;
106c19800e8SDoug Rabson     else
1078373020dSJacques Vidrine 	labels = default_labels;
108b528cefcSMark Murray     if(*domain == '.')
109b528cefcSMark Murray 	domain++;
1108373020dSJacques Vidrine     for (i = 0; labels[i] != NULL; i++) {
111c19800e8SDoug Rabson 	ret = snprintf(dom, sizeof(dom), "%s.%s.", labels[i], domain);
112*ae771770SStanislav Sedov 	if(ret < 0 || (size_t)ret >= sizeof(dom)) {
113c19800e8SDoug Rabson 	    if (config_labels)
114c19800e8SDoug Rabson 		krb5_config_free_strings(config_labels);
115b528cefcSMark Murray 	    return -1;
116c19800e8SDoug Rabson 	}
117*ae771770SStanislav Sedov     	r = rk_dns_lookup(dom, "TXT");
1188373020dSJacques Vidrine     	if(r != NULL) {
119b528cefcSMark Murray 	    ret = copy_txt_to_realms (r->head, realms);
120*ae771770SStanislav Sedov 	    rk_dns_free_data(r);
121c19800e8SDoug Rabson 	    if(ret == 0) {
122c19800e8SDoug Rabson 		if (config_labels)
123c19800e8SDoug Rabson 		    krb5_config_free_strings(config_labels);
1248373020dSJacques Vidrine 		return 0;
1258373020dSJacques Vidrine 	    }
1268373020dSJacques Vidrine 	}
127c19800e8SDoug Rabson     }
128c19800e8SDoug Rabson     if (config_labels)
129c19800e8SDoug Rabson 	krb5_config_free_strings(config_labels);
1308373020dSJacques Vidrine     return -1;
131b528cefcSMark Murray }
132b528cefcSMark Murray 
133b528cefcSMark Murray /*
134b528cefcSMark Murray  * Try to figure out what realms host in `domain' belong to from the
135b528cefcSMark Murray  * configuration file.
136b528cefcSMark Murray  */
137b528cefcSMark Murray 
138b528cefcSMark Murray static int
config_find_realm(krb5_context context,const char * domain,krb5_realm ** realms)139b528cefcSMark Murray config_find_realm(krb5_context context,
140b528cefcSMark Murray 		  const char *domain,
141b528cefcSMark Murray 		  krb5_realm **realms)
142b528cefcSMark Murray {
143b528cefcSMark Murray     char **tmp = krb5_config_get_strings (context, NULL,
144b528cefcSMark Murray 					  "domain_realm",
145b528cefcSMark Murray 					  domain,
146b528cefcSMark Murray 					  NULL);
147b528cefcSMark Murray 
148b528cefcSMark Murray     if (tmp == NULL)
149b528cefcSMark Murray 	return -1;
150b528cefcSMark Murray     *realms = tmp;
151b528cefcSMark Murray     return 0;
152b528cefcSMark Murray }
153b528cefcSMark Murray 
154b528cefcSMark Murray /*
155b528cefcSMark Murray  * This function assumes that `host' is a FQDN (and doesn't handle the
156b528cefcSMark Murray  * special case of host == NULL either).
157b528cefcSMark Murray  * Try to find mapping in the config file or DNS and it that fails,
158b528cefcSMark Murray  * fall back to guessing
159b528cefcSMark Murray  */
160b528cefcSMark Murray 
161*ae771770SStanislav Sedov KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_get_host_realm_int(krb5_context context,const char * host,krb5_boolean use_dns,krb5_realm ** realms)162c19800e8SDoug Rabson _krb5_get_host_realm_int (krb5_context context,
163b528cefcSMark Murray 			  const char *host,
164adb0ddaeSAssar Westerlund 			  krb5_boolean use_dns,
165b528cefcSMark Murray 			  krb5_realm **realms)
166b528cefcSMark Murray {
1678373020dSJacques Vidrine     const char *p, *q;
1688373020dSJacques Vidrine     krb5_boolean dns_locate_enable;
169b528cefcSMark Murray 
1708373020dSJacques Vidrine     dns_locate_enable = krb5_config_get_bool_default(context, NULL, TRUE,
1718373020dSJacques Vidrine 	"libdefaults", "dns_lookup_realm", NULL);
172b528cefcSMark Murray     for (p = host; p != NULL; p = strchr (p + 1, '.')) {
1738373020dSJacques Vidrine 	if(config_find_realm(context, p, realms) == 0) {
1748373020dSJacques Vidrine 	    if(strcasecmp(*realms[0], "dns_locate") == 0) {
1758373020dSJacques Vidrine 		if(use_dns)
1768373020dSJacques Vidrine 		    for (q = host; q != NULL; q = strchr(q + 1, '.'))
1778373020dSJacques Vidrine 			if(dns_find_realm(context, q, realms) == 0)
178b528cefcSMark Murray 			    return 0;
1798373020dSJacques Vidrine 		continue;
1808373020dSJacques Vidrine 	    } else
181b528cefcSMark Murray 	    	return 0;
1828373020dSJacques Vidrine 	}
1838373020dSJacques Vidrine 	else if(use_dns && dns_locate_enable) {
1848373020dSJacques Vidrine 	    if(dns_find_realm(context, p, realms) == 0)
185b528cefcSMark Murray 		return 0;
186b528cefcSMark Murray 	}
187adb0ddaeSAssar Westerlund     }
188b528cefcSMark Murray     p = strchr(host, '.');
189b528cefcSMark Murray     if(p != NULL) {
190b528cefcSMark Murray 	p++;
191b528cefcSMark Murray 	*realms = malloc(2 * sizeof(krb5_realm));
192adb0ddaeSAssar Westerlund 	if (*realms == NULL) {
193*ae771770SStanislav Sedov 	    krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
194b528cefcSMark Murray 	    return ENOMEM;
195adb0ddaeSAssar Westerlund 	}
196b528cefcSMark Murray 
197b528cefcSMark Murray 	(*realms)[0] = strdup(p);
198b528cefcSMark Murray 	if((*realms)[0] == NULL) {
199b528cefcSMark Murray 	    free(*realms);
200*ae771770SStanislav Sedov 	    krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
201b528cefcSMark Murray 	    return ENOMEM;
202b528cefcSMark Murray 	}
203b528cefcSMark Murray 	strupr((*realms)[0]);
204b528cefcSMark Murray 	(*realms)[1] = NULL;
205b528cefcSMark Murray 	return 0;
206b528cefcSMark Murray     }
207*ae771770SStanislav Sedov     krb5_set_error_message(context, KRB5_ERR_HOST_REALM_UNKNOWN,
208*ae771770SStanislav Sedov 			   N_("unable to find realm of host %s", ""),
209*ae771770SStanislav Sedov 			   host);
210b528cefcSMark Murray     return KRB5_ERR_HOST_REALM_UNKNOWN;
211b528cefcSMark Murray }
212b528cefcSMark Murray 
213b528cefcSMark Murray /*
214c19800e8SDoug Rabson  * Return the realm(s) of `host' as a NULL-terminated list in
215c19800e8SDoug Rabson  * `realms'. Free `realms' with krb5_free_host_realm().
216b528cefcSMark Murray  */
217b528cefcSMark Murray 
218*ae771770SStanislav Sedov KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_host_realm(krb5_context context,const char * targethost,krb5_realm ** realms)219b528cefcSMark Murray krb5_get_host_realm(krb5_context context,
220c19800e8SDoug Rabson 		    const char *targethost,
221b528cefcSMark Murray 		    krb5_realm **realms)
222b528cefcSMark Murray {
223c19800e8SDoug Rabson     const char *host = targethost;
224b528cefcSMark Murray     char hostname[MAXHOSTNAMELEN];
225c19800e8SDoug Rabson     krb5_error_code ret;
226c19800e8SDoug Rabson     int use_dns;
227b528cefcSMark Murray 
228b528cefcSMark Murray     if (host == NULL) {
229c19800e8SDoug Rabson 	if (gethostname (hostname, sizeof(hostname))) {
230c19800e8SDoug Rabson 	    *realms = NULL;
231b528cefcSMark Murray 	    return errno;
232c19800e8SDoug Rabson 	}
233b528cefcSMark Murray 	host = hostname;
234b528cefcSMark Murray     }
235b528cefcSMark Murray 
236c19800e8SDoug Rabson     /*
237c19800e8SDoug Rabson      * If our local hostname is without components, don't even try to dns.
238c19800e8SDoug Rabson      */
239c19800e8SDoug Rabson 
240c19800e8SDoug Rabson     use_dns = (strchr(host, '.') != NULL);
241c19800e8SDoug Rabson 
242c19800e8SDoug Rabson     ret = _krb5_get_host_realm_int (context, host, use_dns, realms);
243c19800e8SDoug Rabson     if (ret && targethost != NULL) {
244c19800e8SDoug Rabson 	/*
245c19800e8SDoug Rabson 	 * If there was no realm mapping for the host (and we wasn't
246c19800e8SDoug Rabson 	 * looking for ourself), guess at the local realm, maybe our
247c19800e8SDoug Rabson 	 * KDC knows better then we do and we get a referral back.
248c19800e8SDoug Rabson 	 */
249c19800e8SDoug Rabson 	ret = krb5_get_default_realms(context, realms);
250c19800e8SDoug Rabson 	if (ret) {
251*ae771770SStanislav Sedov 	    krb5_set_error_message(context, KRB5_ERR_HOST_REALM_UNKNOWN,
252*ae771770SStanislav Sedov 				   N_("Unable to find realm of host %s", ""),
253c19800e8SDoug Rabson 				   host);
254c19800e8SDoug Rabson 	    return KRB5_ERR_HOST_REALM_UNKNOWN;
255c19800e8SDoug Rabson 	}
256c19800e8SDoug Rabson     }
257c19800e8SDoug Rabson     return ret;
258b528cefcSMark Murray }
259