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