1 /*
2  * lib/krb5/os/def_realm.c
3  *
4  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  *
26  *
27  * krb5_get_default_realm(), krb5_set_default_realm(),
28  * krb5_free_default_realm() functions.
29  */
30 
31 /*
32  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
33  * Use is subject to license terms.
34  *
35  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
36  * Copyright (c) 2016 by Delphix. All rights reserved.
37  */
38 
39 #include "k5-int.h"
40 #include "os-proto.h"
41 #include <stdio.h>
42 
43 /*
44  * Solaris Kerberos:
45  * For krb5int_foreach_localaddr()
46  */
47 #include "foreachaddr.h"
48 
49 #ifdef KRB5_DNS_LOOKUP
50 #ifdef WSHELPER
51 #include <wshelper.h>
52 #else /* WSHELPER */
53 #ifdef HAVE_NETINET_IN_H
54 #include <netinet/in.h>
55 #endif
56 #include <arpa/inet.h>
57 #include <arpa/nameser.h>
58 #include <resolv.h>
59 #include <netdb.h>
60 #endif /* WSHELPER */
61 
62 /* for old Unixes and friends ... */
63 #ifndef MAXHOSTNAMELEN
64 #define MAXHOSTNAMELEN 64
65 #endif
66 
67 #define MAX_DNS_NAMELEN (15*(MAXHOSTNAMELEN + 1)+1)
68 
69 #endif /* KRB5_DNS_LOOKUP */
70 
71 /*
72  * Solaris Kerberos:
73  * The following prototype is needed because it is a
74  * private interface that does not have a prototype in any .h
75  */
76 extern struct hostent *res_gethostbyaddr(const char *addr, int len, int type);
77 
78 /*
79  * Solaris Kerberos:
80  * krb5int_address_get_realm() given an address (either IPv4 or IPv6) tries to
81  * find a realm based on the DNS name of that address. Assumes that its being
82  * used as a callback for krb5int_foreach_localaddr().
83  */
krb5int_address_get_realm(void * data,struct sockaddr * addr)84 static int krb5int_address_get_realm(void *data, struct sockaddr *addr) {
85 
86 	krb5_context context = data;
87 	struct hostent *he = NULL;
88 
89 	switch (addr->sa_family) {
90 		case AF_INET:
91 			he = res_gethostbyaddr((char*)(&sa2sin(addr)->sin_addr),
92 			    sizeof(sa2sin(addr)->sin_addr), AF_INET);
93 			break;
94 		case AF_INET6:
95 			he = res_gethostbyaddr(
96 			    (char*)(&sa2sin6(addr)->sin6_addr),
97 			    sizeof(sa2sin6(addr)->sin6_addr), AF_INET6);
98 			break;
99 	}
100 
101 	if (he) {
102 		/* Try to find realm using returned DNS name */
103 		krb5int_fqdn_get_realm(context, he->h_name,
104 		    &context->default_realm);
105 
106 		/* If a realm was found return 1 to immediately halt
107 		 * krb5int_foreach_localaddr()
108 		 */
109 		if (context->default_realm != 0) {
110 			return (1);
111 		}
112 	}
113 	return (0);
114 }
115 
116 
117 /*
118  * Retrieves the default realm to be used if no user-specified realm is
119  *  available.  [e.g. to interpret a user-typed principal name with the
120  *  realm omitted for convenience]
121  *
122  *  returns system errors, NOT_ENOUGH_SPACE, KV5M_CONTEXT
123 */
124 
125 /*
126  * Implementation:  the default realm is stored in a configuration file,
127  * named by krb5_config_file;  the first token in this file is taken as
128  * the default local realm name.
129  */
130 
131 krb5_error_code KRB5_CALLCONV
krb5_get_default_realm(krb5_context context,char ** lrealm)132 krb5_get_default_realm(krb5_context context, char **lrealm)
133 {
134     char *realm = 0;
135     char *cp;
136     char localhost[MAX_DNS_NAMELEN+1];
137     krb5_error_code retval;
138 
139     (void) memset(localhost, 0, sizeof(localhost));
140 
141     if (!context || (context->magic != KV5M_CONTEXT))
142 	    return KV5M_CONTEXT;
143 
144     /*
145      * Solaris Kerberos: (illumos)
146      * Another way to provide the default realm.
147      */
148     if (!context->default_realm) {
149 	if ((realm = getenv("KRB5_DEFAULT_REALM")) != NULL) {
150 	    context->default_realm = strdup(realm);
151 	    if (context->default_realm == NULL)
152 		return ENOMEM;
153 	}
154     }
155 
156     if (!context->default_realm) {
157         context->default_realm = 0;
158         if (context->profile != 0) {
159             retval = profile_get_string(context->profile, "libdefaults",
160                                         "default_realm", 0, 0,
161                                         &realm);
162 
163             if (!retval && realm) {
164                 context->default_realm = malloc(strlen(realm) + 1);
165                 if (!context->default_realm) {
166                     profile_release_string(realm);
167                     return ENOMEM;
168                 }
169                 strcpy(context->default_realm, realm);
170                 profile_release_string(realm);
171             }
172         }
173         if (context->default_realm == 0) {
174 #ifdef KRB5_DNS_LOOKUP
175             if (_krb5_use_dns_realm(context)) {
176 		/*
177 		 * Since this didn't appear in our config file, try looking
178 		 * it up via DNS.  Look for a TXT records of the form:
179 		 *
180 		 * _kerberos.<localhost>
181 		 * _kerberos.<domainname>
182 		 * _kerberos.<searchlist>
183 		 *
184 		 */
185 		char * p;
186 		krb5int_get_fq_local_hostname (localhost, sizeof(localhost));
187 
188 		if ( localhost[0] ) {
189 		    p = localhost;
190 		    do {
191 			retval = krb5_try_realm_txt_rr("_kerberos", p,
192 						       &context->default_realm);
193 			p = strchr(p,'.');
194 			if (p)
195 			    p++;
196 		    } while (retval && p && p[0]);
197 
198 		    if (retval)
199 			retval = krb5_try_realm_txt_rr("_kerberos", "",
200 						       &context->default_realm);
201 		} else {
202 		    retval = krb5_try_realm_txt_rr("_kerberos", "",
203 						   &context->default_realm);
204 		}
205 		if (retval) {
206 		    return(KRB5_CONFIG_NODEFREALM);
207 		}
208             } else
209 #endif /* KRB5_DNS_LOOKUP */
210             if (getenv("MS_INTEROP") == NULL) {
211 
212 	/*
213 	 * Solaris Kerberos:
214 	 * Try to find a realm based on one of the local IP addresses.
215 	 * Don't do this for AD, which often does _not_ support any
216 	 * DNS reverse lookup, making these queries take forever.
217 	 */
218 	(void) krb5int_foreach_localaddr(context,
219 	    krb5int_address_get_realm, 0, 0);
220 
221 	/*
222 	 * Solaris Kerberos:
223 	 * As a final fallback try to find a realm based on the resolver search
224 	 * list
225 	 */
226 	if (context->default_realm == 0) {
227 		struct __res_state res;
228 		int i;
229 
230 		(void) memset(&res, 0, sizeof (res));
231 
232 		if (res_ninit(&res) == 0) {
233 			for (i = 0; res.dnsrch[i]; i++) {
234 				krb5int_domain_get_realm(context,
235 				    res.dnsrch[i], &context->default_realm);
236 
237 				if (context->default_realm != 0)
238 					break;
239 			}
240 		res_ndestroy(&res);
241 		}
242 	}
243 
244 	}
245 	}
246 	}
247 
248     if (context->default_realm == 0)
249 	return(KRB5_CONFIG_NODEFREALM);
250     if (context->default_realm[0] == 0) {
251         free (context->default_realm);
252         context->default_realm = 0;
253         return KRB5_CONFIG_NODEFREALM;
254     }
255 
256     realm = context->default_realm;
257 
258     /*LINTED*/
259     if (!(*lrealm = cp = malloc((unsigned int) strlen(realm) + 1)))
260         return ENOMEM;
261     strcpy(cp, realm);
262     return(0);
263 }
264 
265 krb5_error_code KRB5_CALLCONV
krb5_set_default_realm(krb5_context context,const char * lrealm)266 krb5_set_default_realm(krb5_context context, const char *lrealm)
267 {
268     if (!context || (context->magic != KV5M_CONTEXT))
269 	    return KV5M_CONTEXT;
270 
271     if (context->default_realm) {
272 	    free(context->default_realm);
273 	    context->default_realm = 0;
274     }
275 
276     /* Allow the user to clear the default realm setting by passing in
277        NULL */
278     if (!lrealm) return 0;
279 
280     context->default_realm = malloc(strlen (lrealm) + 1);
281 
282     if (!context->default_realm)
283 	    return ENOMEM;
284 
285     strcpy(context->default_realm, lrealm);
286     return(0);
287 
288 }
289 
290 /*ARGSUSED*/
291 void KRB5_CALLCONV
krb5_free_default_realm(krb5_context context,char * lrealm)292 krb5_free_default_realm(krb5_context context, char *lrealm)
293 {
294 	free (lrealm);
295 }
296