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 */
24kadm5_ret_t
25kadm5_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 */
82kadm5_ret_t
83kadm5_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 */
150kadm5_ret_t
151kadm5_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 */
179kadm5_ret_t
180kadm5_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 */
213kadm5_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 */
240kadm5_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