1 /*
2  * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
3  */
4 
5 /*
6  * lib/kad5/kadm_host_srv_names.c
7  */
8 
9 #include <k5-int.h>
10 #include "admin.h"
11 #include <stdio.h>
12 #include <os-proto.h>
13 
14 
15 #define	KADM5_MASTER "admin_server"
16 #define	KADM5_KPASSWD "kpasswd_server"
17 
18 /*
19  * Find the admin server for the given realm. If the realm is null or
20  * the empty string, find the admin server for the default realm.
21  * Returns 0 on succsess (KADM5_OK). It is the callers responsibility to
22  * free the storage allocated to the admin server, master.
23  */
24 kadm5_ret_t
kadm5_get_master(krb5_context context,const char * realm,char ** master)25 kadm5_get_master(krb5_context context, const char *realm, char **master)
26 {
27 	/* Solaris Kerberos */
28 	char *def_realm = NULL;
29 
30 	char *delim;
31 #ifdef KRB5_DNS_LOOKUP
32 	struct sockaddr *addrs;
33 	int naddrs;
34 	unsigned short dns_portno;
35 	char dns_host[MAX_DNS_NAMELEN];
36 	krb5_data dns_realm;
37 	krb5_error_code dns_ret = 1;
38 #endif /* KRB5_DNS_LOOKUP */
39 
40 	if (realm == 0 || *realm == '\0')
41 		krb5_get_default_realm(context, &def_realm);
42 
43 	(void) profile_get_string(context->profile, "realms",
44 	    realm ? realm : def_realm,
45 	    KADM5_MASTER, 0, master);
46 
47 	if ((*master != NULL) && ((delim = strchr(*master, ':')) != NULL))
48 		*delim = '\0';
49 #ifdef KRB5_DNS_LOOKUP
50 	if (*master == NULL) {
51 		/*
52 		 * Initialize realm info for (possible) DNS lookups.
53 		 */
54 		dns_realm.data = strdup(realm ? realm : def_realm);
55 		dns_realm.length = strlen(realm ? realm : def_realm);
56 		dns_realm.magic = 0;
57 
58 		dns_ret = krb5_get_servername(context, &dns_realm,
59 		    "_kerberos-adm", "_udp",
60 		    dns_host, &dns_portno);
61 		if (dns_ret == 0)
62 			*master = strdup(dns_host);
63 
64 		if (dns_realm.data)
65 			free(dns_realm.data);
66 	}
67 #endif /* KRB5_DNS_LOOKUP */
68 
69 	/* Solaris Kerberos */
70 	if (def_realm != NULL)
71 		krb5_free_default_realm(context, def_realm);
72 
73 	return (*master ? KADM5_OK : KADM5_NO_SRV);
74 }
75 
76 /*
77  * Find the kpasswd server for the given realm. If the realm is null or
78  * the empty string, find the admin server for the default realm.
79  * Returns 0 on succsess (KADM5_OK). It is the callers responsibility to
80  * free the storage allocated to the admin server, master.
81  */
82 kadm5_ret_t
kadm5_get_kpasswd(krb5_context context,const char * realm,char ** kpasswd)83 kadm5_get_kpasswd(krb5_context context, const char *realm, char **kpasswd)
84 {
85 	char *def_realm = NULL;
86 	char *delim;
87 #ifdef KRB5_DNS_LOOKUP
88 	struct sockaddr *addrs;
89 	int naddrs;
90 	unsigned short dns_portno;
91 	char dns_host[MAX_DNS_NAMELEN];
92 	krb5_data dns_realm;
93 	krb5_error_code dns_ret = 1, ret;
94 #endif /* KRB5_DNS_LOOKUP */
95 
96 	if (realm == 0 || *realm == '\0') {
97 		ret = krb5_get_default_realm(context, &def_realm);
98 		if (ret != 0)
99 			return (ret);
100 	}
101 
102 	(void) profile_get_string(context->profile, "realms",
103 	    realm ? realm : def_realm,
104 	    KADM5_KPASSWD, 0, kpasswd);
105 
106 	if ((*kpasswd != NULL) && ((delim = strchr(*kpasswd, ':')) != NULL))
107 		*delim = '\0';
108 #ifdef KRB5_DNS_LOOKUP
109 	if (*kpasswd == NULL) {
110 		/*
111 		 * Initialize realm info for (possible) DNS lookups.
112 		 */
113 		dns_realm.data = strdup(realm ? realm : def_realm);
114 		if (dns_realm.data == NULL) {
115 			if (def_realm != NULL)
116 				free(def_realm);
117 			return (ENOMEM);
118 		}
119 		dns_realm.length = strlen(realm ? realm : def_realm);
120 		dns_realm.magic = 0;
121 
122 		dns_ret = krb5_get_servername(context, &dns_realm,
123 		    "_kpasswd", "_tcp",
124 		    dns_host, &dns_portno);
125 		if (dns_ret == 0) {
126 			*kpasswd = strdup(dns_host);
127 
128 			if (*kpasswd == NULL) {
129 				free(dns_realm.data);
130 				if (def_realm != NULL)
131 					free(def_realm);
132 				return (ENOMEM);
133 			}
134 		}
135 
136 		free(dns_realm.data);
137 	}
138 #endif /* KRB5_DNS_LOOKUP */
139 
140 	if (def_realm != NULL)
141 		free(def_realm);
142 	return (*kpasswd ? KADM5_OK : KADM5_NO_SRV);
143 }
144 
145 /*
146  * Get the host base service name for the admin principal. Returns
147  * KADM5_OK on success. Caller must free the storage allocated for
148  * host_service_name.
149  */
150 kadm5_ret_t
kadm5_get_adm_host_srv_name(krb5_context context,const char * realm,char ** host_service_name)151 kadm5_get_adm_host_srv_name(krb5_context context,
152 			    const char *realm, char **host_service_name)
153 {
154 	kadm5_ret_t ret;
155 	char *name;
156 	char *host;
157 
158 
159 	if (ret = kadm5_get_master(context, realm, &host))
160 		return (ret);
161 
162 	name = malloc(strlen(KADM5_ADMIN_HOST_SERVICE)+ strlen(host) + 2);
163 	if (name == NULL) {
164 		free(host);
165 		return (ENOMEM);
166 	}
167 	sprintf(name, "%s@%s", KADM5_ADMIN_HOST_SERVICE, host);
168 	free(host);
169 	*host_service_name = name;
170 
171 	return (KADM5_OK);
172 }
173 
174 /*
175  * Get the host base service name for the changepw principal. Returns
176  * KADM5_OK on success. Caller must free the storage allocated for
177  * host_service_name.
178  */
179 kadm5_ret_t
kadm5_get_cpw_host_srv_name(krb5_context context,const char * realm,char ** host_service_name)180 kadm5_get_cpw_host_srv_name(krb5_context context,
181 			    const char *realm, char **host_service_name)
182 {
183 	kadm5_ret_t ret;
184 	char *name;
185 	char *host;
186 
187 	/*
188 	 * First try to find the kpasswd server, after all we are about to
189 	 * try to change our password.  If this fails then try admin_server.
190 	 */
191 	if (ret = kadm5_get_kpasswd(context, realm, &host)) {
192 		if (ret = kadm5_get_master(context, realm, &host))
193 			return (ret);
194 	}
195 
196 	name = malloc(strlen(KADM5_CHANGEPW_HOST_SERVICE) + strlen(host) + 2);
197 	if (name == NULL) {
198 		free(host);
199 		return (ENOMEM);
200 	}
201 	sprintf(name, "%s@%s", KADM5_CHANGEPW_HOST_SERVICE, host);
202 	free(host);
203 	*host_service_name = name;
204 
205 	return (KADM5_OK);
206 }
207 
208 /*
209  * Get the host base service name for the kiprop principal. Returns
210  * KADM5_OK on success. Caller must free the storage allocated
211  * for host_service_name.
212  */
kadm5_get_kiprop_host_srv_name(krb5_context context,const char * realm,char ** host_service_name)213 kadm5_ret_t kadm5_get_kiprop_host_srv_name(krb5_context context,
214 				    const char *realm,
215 				    char **host_service_name) {
216 	kadm5_ret_t ret;
217 	char *name;
218 	char *host;
219 
220 
221 	if (ret = kadm5_get_master(context, realm, &host))
222 		return (ret);
223 
224 	name = malloc(strlen(KADM5_KIPROP_HOST_SERVICE) + strlen(host) + 2);
225 	if (name == NULL) {
226 		free(host);
227 		return (ENOMEM);
228 	}
229 	sprintf(name, "%s@%s", KADM5_KIPROP_HOST_SERVICE, host);
230 	free(host);
231 	*host_service_name = name;
232 
233 	return (KADM5_OK);
234 }
235 
236 /*
237  * Solaris Kerberos:
238  * Try to determine if this is the master KDC for a given realm
239  */
kadm5_is_master(krb5_context context,const char * realm,krb5_boolean * is_master)240 kadm5_ret_t kadm5_is_master(krb5_context context, const char *realm,
241     krb5_boolean *is_master) {
242 
243 	kadm5_ret_t ret;
244 	char *admin_host = NULL;
245 	krb5_address **tmp_addr, **master_addr = NULL;
246 	krb5_address **local_addr = NULL;
247 
248 	if (is_master)
249 		*is_master = FALSE;
250 	else
251 		return (KADM5_FAILURE);
252 
253 	/* Locate the master KDC */
254 	if (ret = kadm5_get_master(context, realm, &admin_host))
255 		return (ret);
256 
257 	if (ret = krb5_os_hostaddr(context, admin_host, &master_addr)) {
258 		free(admin_host);
259 		return (ret);
260 	}
261 
262 	/* Get the local addresses */
263 	if (ret = krb5_os_localaddr(context, &local_addr)) {
264 		krb5_free_addresses(context, master_addr);
265 		free(admin_host);
266 		return (ret);
267 	}
268 
269 	/* Compare them */
270 	for (tmp_addr = master_addr; *tmp_addr; tmp_addr++) {
271 		if (krb5_address_search(context, *tmp_addr, local_addr)) {
272 			*is_master = TRUE;
273 			break;
274 		}
275 	}
276 
277 	krb5_free_addresses(context, local_addr);
278 	krb5_free_addresses(context, master_addr);
279 	free(admin_host);
280 
281 	return (KADM5_OK);
282 }
283