xref: /illumos-gate/usr/src/lib/krb5/kadm5/kadm_host_srv_names.c (revision 46b592853d0f4f11781b6b0a7533f267c6aee132)
1 /*
2  * Copyright 2008 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 	char *def_realm;
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 	return (*master ? KADM5_OK : KADM5_NO_SRV);
69 }
70 
71 /*
72  * Find the kpasswd server for the given realm. If the realm is null or
73  * the empty string, find the admin server for the default realm.
74  * Returns 0 on succsess (KADM5_OK). It is the callers responsibility to
75  * free the storage allocated to the admin server, master.
76  */
77 kadm5_ret_t
78 kadm5_get_kpasswd(krb5_context context, const char *realm, char **kpasswd)
79 {
80 	char *def_realm = NULL;
81 	char *delim;
82 #ifdef KRB5_DNS_LOOKUP
83 	struct sockaddr *addrs;
84 	int naddrs;
85 	unsigned short dns_portno;
86 	char dns_host[MAX_DNS_NAMELEN];
87 	krb5_data dns_realm;
88 	krb5_error_code dns_ret = 1, ret;
89 #endif /* KRB5_DNS_LOOKUP */
90 
91 	if (realm == 0 || *realm == '\0') {
92 		ret = krb5_get_default_realm(context, &def_realm);
93 		if (ret != 0)
94 			return (ret);
95 	}
96 
97 	(void) profile_get_string(context->profile, "realms",
98 	    realm ? realm : def_realm,
99 	    KADM5_KPASSWD, 0, kpasswd);
100 
101 	if ((*kpasswd != NULL) && ((delim = strchr(*kpasswd, ':')) != NULL))
102 		*delim = '\0';
103 #ifdef KRB5_DNS_LOOKUP
104 	if (*kpasswd == NULL) {
105 		/*
106 		 * Initialize realm info for (possible) DNS lookups.
107 		 */
108 		dns_realm.data = strdup(realm ? realm : def_realm);
109 		if (dns_realm.data == NULL) {
110 			if (def_realm != NULL)
111 				free(def_realm);
112 			return (ENOMEM);
113 		}
114 		dns_realm.length = strlen(realm ? realm : def_realm);
115 		dns_realm.magic = 0;
116 
117 		dns_ret = krb5_get_servername(context, &dns_realm,
118 		    "_kpasswd", "_tcp",
119 		    dns_host, &dns_portno);
120 		if (dns_ret == 0) {
121 			*kpasswd = strdup(dns_host);
122 
123 			if (*kpasswd == NULL) {
124 				free(dns_realm.data);
125 				if (def_realm != NULL)
126 					free(def_realm);
127 				return (ENOMEM);
128 			}
129 		}
130 
131 		free(dns_realm.data);
132 	}
133 #endif /* KRB5_DNS_LOOKUP */
134 
135 	if (def_realm != NULL)
136 		free(def_realm);
137 	return (*kpasswd ? KADM5_OK : KADM5_NO_SRV);
138 }
139 
140 /*
141  * Get the host base service name for the admin principal. Returns
142  * KADM5_OK on success. Caller must free the storage allocated for
143  * host_service_name.
144  */
145 kadm5_ret_t
146 kadm5_get_adm_host_srv_name(krb5_context context,
147 			    const char *realm, char **host_service_name)
148 {
149 	kadm5_ret_t ret;
150 	char *name;
151 	char *host;
152 
153 
154 	if (ret = kadm5_get_master(context, realm, &host))
155 		return (ret);
156 
157 	name = malloc(strlen(KADM5_ADMIN_HOST_SERVICE)+ strlen(host) + 2);
158 	if (name == NULL) {
159 		free(host);
160 		return (ENOMEM);
161 	}
162 	sprintf(name, "%s@%s", KADM5_ADMIN_HOST_SERVICE, host);
163 	free(host);
164 	*host_service_name = name;
165 
166 	return (KADM5_OK);
167 }
168 
169 /*
170  * Get the host base service name for the changepw principal. Returns
171  * KADM5_OK on success. Caller must free the storage allocated for
172  * host_service_name.
173  */
174 kadm5_ret_t
175 kadm5_get_cpw_host_srv_name(krb5_context context,
176 			    const char *realm, char **host_service_name)
177 {
178 	kadm5_ret_t ret;
179 	char *name;
180 	char *host;
181 
182 	/*
183 	 * First try to find the kpasswd server, after all we are about to
184 	 * try to change our password.  If this fails then try admin_server.
185 	 */
186 	if (ret = kadm5_get_kpasswd(context, realm, &host)) {
187 		if (ret = kadm5_get_master(context, realm, &host))
188 			return (ret);
189 	}
190 
191 	name = malloc(strlen(KADM5_CHANGEPW_HOST_SERVICE) + strlen(host) + 2);
192 	if (name == NULL) {
193 		free(host);
194 		return (ENOMEM);
195 	}
196 	sprintf(name, "%s@%s", KADM5_CHANGEPW_HOST_SERVICE, host);
197 	free(host);
198 	*host_service_name = name;
199 
200 	return (KADM5_OK);
201 }
202 
203 /*
204  * Get the host base service name for the kiprop principal. Returns
205  * KADM5_OK on success. Caller must free the storage allocated
206  * for host_service_name.
207  */
208 kadm5_ret_t kadm5_get_kiprop_host_srv_name(krb5_context context,
209 				    const char *realm,
210 				    char **host_service_name) {
211 	kadm5_ret_t ret;
212 	char *name;
213 	char *host;
214 
215 
216 	if (ret = kadm5_get_master(context, realm, &host))
217 		return (ret);
218 
219 	name = malloc(strlen(KADM5_KIPROP_HOST_SERVICE) + strlen(host) + 2);
220 	if (name == NULL) {
221 		free(host);
222 		return (ENOMEM);
223 	}
224 	sprintf(name, "%s@%s", KADM5_KIPROP_HOST_SERVICE, host);
225 	free(host);
226 	*host_service_name = name;
227 
228 	return (KADM5_OK);
229 }
230 
231 /*
232  * Solaris Kerberos:
233  * Try to determine if this is the master KDC for a given realm
234  */
235 kadm5_ret_t kadm5_is_master(krb5_context context, const char *realm,
236     krb5_boolean *is_master) {
237 
238 	kadm5_ret_t ret;
239 	char *admin_host = NULL;
240 	krb5_address **master_addr = NULL;
241 	krb5_address **local_addr = NULL;
242 
243 	if (is_master)
244 		*is_master = FALSE;
245 	else
246 		return (KADM5_FAILURE);
247 
248 	/* Locate the master KDC */
249 	if (ret = kadm5_get_master(context, realm, &admin_host))
250 		return (ret);
251 
252 	if (ret = krb5_os_hostaddr(context, admin_host, &master_addr)) {
253 		free(admin_host);
254 		return (ret);
255 	}
256 
257 	/* Get the local addresses */
258 	if (ret = krb5_os_localaddr(context, &local_addr)) {
259 		krb5_free_addresses(context, master_addr);
260 		free(admin_host);
261 		return (ret);
262 	}
263 
264 	/* Compare them */
265 	for (; *master_addr; master_addr++) {
266 		if (krb5_address_search(context, *master_addr, local_addr)) {
267 			*is_master = TRUE;
268 			break;
269 		}
270 	}
271 
272 	krb5_free_addresses(context, local_addr);
273 	krb5_free_addresses(context, master_addr);
274 	free(admin_host);
275 
276 	return (KADM5_OK);
277 }
278