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