17c478bd9Sstevel@tonic-gate /*
2*212bfef1SMark Phalan  * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
37c478bd9Sstevel@tonic-gate  */
47c478bd9Sstevel@tonic-gate 
57c478bd9Sstevel@tonic-gate /*
67c478bd9Sstevel@tonic-gate  * lib/kad5/kadm_host_srv_names.c
77c478bd9Sstevel@tonic-gate  */
87c478bd9Sstevel@tonic-gate 
9159d09a2SMark Phalan #include <k5-int.h>
107c478bd9Sstevel@tonic-gate #include "admin.h"
117c478bd9Sstevel@tonic-gate #include <stdio.h>
127c478bd9Sstevel@tonic-gate #include <os-proto.h>
137c478bd9Sstevel@tonic-gate 
14159d09a2SMark Phalan 
157c478bd9Sstevel@tonic-gate #define	KADM5_MASTER "admin_server"
16bd211b85Ssemery #define	KADM5_KPASSWD "kpasswd_server"
177c478bd9Sstevel@tonic-gate 
187c478bd9Sstevel@tonic-gate /*
197c478bd9Sstevel@tonic-gate  * Find the admin server for the given realm. If the realm is null or
207c478bd9Sstevel@tonic-gate  * the empty string, find the admin server for the default realm.
217c478bd9Sstevel@tonic-gate  * Returns 0 on succsess (KADM5_OK). It is the callers responsibility to
227c478bd9Sstevel@tonic-gate  * free the storage allocated to the admin server, master.
237c478bd9Sstevel@tonic-gate  */
247c478bd9Sstevel@tonic-gate kadm5_ret_t
kadm5_get_master(krb5_context context,const char * realm,char ** master)257c478bd9Sstevel@tonic-gate kadm5_get_master(krb5_context context, const char *realm, char **master)
267c478bd9Sstevel@tonic-gate {
27dc25fd74SMark Phalan 	/* Solaris Kerberos */
28dc25fd74SMark Phalan 	char *def_realm = NULL;
29dc25fd74SMark Phalan 
307c478bd9Sstevel@tonic-gate 	char *delim;
317c478bd9Sstevel@tonic-gate #ifdef KRB5_DNS_LOOKUP
327c478bd9Sstevel@tonic-gate 	struct sockaddr *addrs;
337c478bd9Sstevel@tonic-gate 	int naddrs;
347c478bd9Sstevel@tonic-gate 	unsigned short dns_portno;
357c478bd9Sstevel@tonic-gate 	char dns_host[MAX_DNS_NAMELEN];
367c478bd9Sstevel@tonic-gate 	krb5_data dns_realm;
377c478bd9Sstevel@tonic-gate 	krb5_error_code dns_ret = 1;
387c478bd9Sstevel@tonic-gate #endif /* KRB5_DNS_LOOKUP */
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate 	if (realm == 0 || *realm == '\0')
417c478bd9Sstevel@tonic-gate 		krb5_get_default_realm(context, &def_realm);
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate 	(void) profile_get_string(context->profile, "realms",
447c64d375Smp 	    realm ? realm : def_realm,
457c64d375Smp 	    KADM5_MASTER, 0, master);
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate 	if ((*master != NULL) && ((delim = strchr(*master, ':')) != NULL))
487c478bd9Sstevel@tonic-gate 		*delim = '\0';
497c478bd9Sstevel@tonic-gate #ifdef KRB5_DNS_LOOKUP
507c478bd9Sstevel@tonic-gate 	if (*master == NULL) {
517c478bd9Sstevel@tonic-gate 		/*
527c478bd9Sstevel@tonic-gate 		 * Initialize realm info for (possible) DNS lookups.
537c478bd9Sstevel@tonic-gate 		 */
547c478bd9Sstevel@tonic-gate 		dns_realm.data = strdup(realm ? realm : def_realm);
557c478bd9Sstevel@tonic-gate 		dns_realm.length = strlen(realm ? realm : def_realm);
567c478bd9Sstevel@tonic-gate 		dns_realm.magic = 0;
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate 		dns_ret = krb5_get_servername(context, &dns_realm,
597c64d375Smp 		    "_kerberos-adm", "_udp",
607c64d375Smp 		    dns_host, &dns_portno);
617c478bd9Sstevel@tonic-gate 		if (dns_ret == 0)
627c478bd9Sstevel@tonic-gate 			*master = strdup(dns_host);
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate 		if (dns_realm.data)
657c478bd9Sstevel@tonic-gate 			free(dns_realm.data);
667c478bd9Sstevel@tonic-gate 	}
677c478bd9Sstevel@tonic-gate #endif /* KRB5_DNS_LOOKUP */
68dc25fd74SMark Phalan 
69dc25fd74SMark Phalan 	/* Solaris Kerberos */
70dc25fd74SMark Phalan 	if (def_realm != NULL)
71dc25fd74SMark Phalan 		krb5_free_default_realm(context, def_realm);
72dc25fd74SMark Phalan 
737c478bd9Sstevel@tonic-gate 	return (*master ? KADM5_OK : KADM5_NO_SRV);
747c478bd9Sstevel@tonic-gate }
757c478bd9Sstevel@tonic-gate 
76bd211b85Ssemery /*
77bd211b85Ssemery  * Find the kpasswd server for the given realm. If the realm is null or
78bd211b85Ssemery  * the empty string, find the admin server for the default realm.
79bd211b85Ssemery  * Returns 0 on succsess (KADM5_OK). It is the callers responsibility to
80bd211b85Ssemery  * free the storage allocated to the admin server, master.
81bd211b85Ssemery  */
82bd211b85Ssemery kadm5_ret_t
kadm5_get_kpasswd(krb5_context context,const char * realm,char ** kpasswd)83bd211b85Ssemery kadm5_get_kpasswd(krb5_context context, const char *realm, char **kpasswd)
84bd211b85Ssemery {
85bd211b85Ssemery 	char *def_realm = NULL;
86bd211b85Ssemery 	char *delim;
87bd211b85Ssemery #ifdef KRB5_DNS_LOOKUP
88bd211b85Ssemery 	struct sockaddr *addrs;
89bd211b85Ssemery 	int naddrs;
90bd211b85Ssemery 	unsigned short dns_portno;
91bd211b85Ssemery 	char dns_host[MAX_DNS_NAMELEN];
92bd211b85Ssemery 	krb5_data dns_realm;
93bd211b85Ssemery 	krb5_error_code dns_ret = 1, ret;
94bd211b85Ssemery #endif /* KRB5_DNS_LOOKUP */
95bd211b85Ssemery 
96bd211b85Ssemery 	if (realm == 0 || *realm == '\0') {
97bd211b85Ssemery 		ret = krb5_get_default_realm(context, &def_realm);
98bd211b85Ssemery 		if (ret != 0)
99bd211b85Ssemery 			return (ret);
100bd211b85Ssemery 	}
101bd211b85Ssemery 
102bd211b85Ssemery 	(void) profile_get_string(context->profile, "realms",
103bd211b85Ssemery 	    realm ? realm : def_realm,
104bd211b85Ssemery 	    KADM5_KPASSWD, 0, kpasswd);
105bd211b85Ssemery 
106bd211b85Ssemery 	if ((*kpasswd != NULL) && ((delim = strchr(*kpasswd, ':')) != NULL))
107bd211b85Ssemery 		*delim = '\0';
108bd211b85Ssemery #ifdef KRB5_DNS_LOOKUP
109bd211b85Ssemery 	if (*kpasswd == NULL) {
110bd211b85Ssemery 		/*
111bd211b85Ssemery 		 * Initialize realm info for (possible) DNS lookups.
112bd211b85Ssemery 		 */
113bd211b85Ssemery 		dns_realm.data = strdup(realm ? realm : def_realm);
114bd211b85Ssemery 		if (dns_realm.data == NULL) {
115bd211b85Ssemery 			if (def_realm != NULL)
116bd211b85Ssemery 				free(def_realm);
117bd211b85Ssemery 			return (ENOMEM);
118bd211b85Ssemery 		}
119bd211b85Ssemery 		dns_realm.length = strlen(realm ? realm : def_realm);
120bd211b85Ssemery 		dns_realm.magic = 0;
121bd211b85Ssemery 
122bd211b85Ssemery 		dns_ret = krb5_get_servername(context, &dns_realm,
123bd211b85Ssemery 		    "_kpasswd", "_tcp",
124bd211b85Ssemery 		    dns_host, &dns_portno);
125bd211b85Ssemery 		if (dns_ret == 0) {
126bd211b85Ssemery 			*kpasswd = strdup(dns_host);
127bd211b85Ssemery 
128bd211b85Ssemery 			if (*kpasswd == NULL) {
129bd211b85Ssemery 				free(dns_realm.data);
130bd211b85Ssemery 				if (def_realm != NULL)
131bd211b85Ssemery 					free(def_realm);
132bd211b85Ssemery 				return (ENOMEM);
133bd211b85Ssemery 			}
134bd211b85Ssemery 		}
135bd211b85Ssemery 
136bd211b85Ssemery 		free(dns_realm.data);
137bd211b85Ssemery 	}
138bd211b85Ssemery #endif /* KRB5_DNS_LOOKUP */
139bd211b85Ssemery 
140bd211b85Ssemery 	if (def_realm != NULL)
141bd211b85Ssemery 		free(def_realm);
142bd211b85Ssemery 	return (*kpasswd ? KADM5_OK : KADM5_NO_SRV);
143bd211b85Ssemery }
144bd211b85Ssemery 
1457c478bd9Sstevel@tonic-gate /*
1467c478bd9Sstevel@tonic-gate  * Get the host base service name for the admin principal. Returns
1477c478bd9Sstevel@tonic-gate  * KADM5_OK on success. Caller must free the storage allocated for
1487c478bd9Sstevel@tonic-gate  * host_service_name.
1497c478bd9Sstevel@tonic-gate  */
1507c478bd9Sstevel@tonic-gate kadm5_ret_t
kadm5_get_adm_host_srv_name(krb5_context context,const char * realm,char ** host_service_name)1517c478bd9Sstevel@tonic-gate kadm5_get_adm_host_srv_name(krb5_context context,
1527c478bd9Sstevel@tonic-gate 			    const char *realm, char **host_service_name)
1537c478bd9Sstevel@tonic-gate {
1547c478bd9Sstevel@tonic-gate 	kadm5_ret_t ret;
1557c478bd9Sstevel@tonic-gate 	char *name;
1567c478bd9Sstevel@tonic-gate 	char *host;
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	if (ret = kadm5_get_master(context, realm, &host))
1607c478bd9Sstevel@tonic-gate 		return (ret);
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate 	name = malloc(strlen(KADM5_ADMIN_HOST_SERVICE)+ strlen(host) + 2);
1637c478bd9Sstevel@tonic-gate 	if (name == NULL) {
1647c478bd9Sstevel@tonic-gate 		free(host);
1657c478bd9Sstevel@tonic-gate 		return (ENOMEM);
1667c478bd9Sstevel@tonic-gate 	}
1677c478bd9Sstevel@tonic-gate 	sprintf(name, "%s@%s", KADM5_ADMIN_HOST_SERVICE, host);
1687c478bd9Sstevel@tonic-gate 	free(host);
1697c478bd9Sstevel@tonic-gate 	*host_service_name = name;
1707c478bd9Sstevel@tonic-gate 
1717c478bd9Sstevel@tonic-gate 	return (KADM5_OK);
1727c478bd9Sstevel@tonic-gate }
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate /*
1757c478bd9Sstevel@tonic-gate  * Get the host base service name for the changepw principal. Returns
1767c478bd9Sstevel@tonic-gate  * KADM5_OK on success. Caller must free the storage allocated for
1777c478bd9Sstevel@tonic-gate  * host_service_name.
1787c478bd9Sstevel@tonic-gate  */
1797c478bd9Sstevel@tonic-gate kadm5_ret_t
kadm5_get_cpw_host_srv_name(krb5_context context,const char * realm,char ** host_service_name)1807c478bd9Sstevel@tonic-gate kadm5_get_cpw_host_srv_name(krb5_context context,
1817c478bd9Sstevel@tonic-gate 			    const char *realm, char **host_service_name)
1827c478bd9Sstevel@tonic-gate {
1837c478bd9Sstevel@tonic-gate 	kadm5_ret_t ret;
1847c478bd9Sstevel@tonic-gate 	char *name;
1857c478bd9Sstevel@tonic-gate 	char *host;
1867c478bd9Sstevel@tonic-gate 
187bd211b85Ssemery 	/*
188bd211b85Ssemery 	 * First try to find the kpasswd server, after all we are about to
189bd211b85Ssemery 	 * try to change our password.  If this fails then try admin_server.
190bd211b85Ssemery 	 */
191bd211b85Ssemery 	if (ret = kadm5_get_kpasswd(context, realm, &host)) {
192bd211b85Ssemery 		if (ret = kadm5_get_master(context, realm, &host))
193bd211b85Ssemery 			return (ret);
194bd211b85Ssemery 	}
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	name = malloc(strlen(KADM5_CHANGEPW_HOST_SERVICE) + strlen(host) + 2);
1977c478bd9Sstevel@tonic-gate 	if (name == NULL) {
1987c478bd9Sstevel@tonic-gate 		free(host);
1997c478bd9Sstevel@tonic-gate 		return (ENOMEM);
2007c478bd9Sstevel@tonic-gate 	}
2017c478bd9Sstevel@tonic-gate 	sprintf(name, "%s@%s", KADM5_CHANGEPW_HOST_SERVICE, host);
2027c478bd9Sstevel@tonic-gate 	free(host);
2037c478bd9Sstevel@tonic-gate 	*host_service_name = name;
2047c478bd9Sstevel@tonic-gate 
2057c478bd9Sstevel@tonic-gate 	return (KADM5_OK);
2067c478bd9Sstevel@tonic-gate }
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate /*
2097c478bd9Sstevel@tonic-gate  * Get the host base service name for the kiprop principal. Returns
2107c478bd9Sstevel@tonic-gate  * KADM5_OK on success. Caller must free the storage allocated
2117c478bd9Sstevel@tonic-gate  * for host_service_name.
2127c478bd9Sstevel@tonic-gate  */
kadm5_get_kiprop_host_srv_name(krb5_context context,const char * realm,char ** host_service_name)2137c478bd9Sstevel@tonic-gate kadm5_ret_t kadm5_get_kiprop_host_srv_name(krb5_context context,
2147c478bd9Sstevel@tonic-gate 				    const char *realm,
2157c478bd9Sstevel@tonic-gate 				    char **host_service_name) {
2167c478bd9Sstevel@tonic-gate 	kadm5_ret_t ret;
2177c478bd9Sstevel@tonic-gate 	char *name;
2187c478bd9Sstevel@tonic-gate 	char *host;
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate 
2217c478bd9Sstevel@tonic-gate 	if (ret = kadm5_get_master(context, realm, &host))
2227c478bd9Sstevel@tonic-gate 		return (ret);
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate 	name = malloc(strlen(KADM5_KIPROP_HOST_SERVICE) + strlen(host) + 2);
2257c478bd9Sstevel@tonic-gate 	if (name == NULL) {
2267c478bd9Sstevel@tonic-gate 		free(host);
2277c478bd9Sstevel@tonic-gate 		return (ENOMEM);
2287c478bd9Sstevel@tonic-gate 	}
2297c478bd9Sstevel@tonic-gate 	sprintf(name, "%s@%s", KADM5_KIPROP_HOST_SERVICE, host);
2307c478bd9Sstevel@tonic-gate 	free(host);
2317c478bd9Sstevel@tonic-gate 	*host_service_name = name;
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 	return (KADM5_OK);
2347c478bd9Sstevel@tonic-gate }
2357c64d375Smp 
2367c64d375Smp /*
2377c64d375Smp  * Solaris Kerberos:
2387c64d375Smp  * Try to determine if this is the master KDC for a given realm
2397c64d375Smp  */
kadm5_is_master(krb5_context context,const char * realm,krb5_boolean * is_master)2407c64d375Smp kadm5_ret_t kadm5_is_master(krb5_context context, const char *realm,
2417c64d375Smp     krb5_boolean *is_master) {
2427c64d375Smp 
2437c64d375Smp 	kadm5_ret_t ret;
2447c64d375Smp 	char *admin_host = NULL;
245*212bfef1SMark Phalan 	krb5_address **tmp_addr, **master_addr = NULL;
2467c64d375Smp 	krb5_address **local_addr = NULL;
2477c64d375Smp 
2487c64d375Smp 	if (is_master)
2497c64d375Smp 		*is_master = FALSE;
2507c64d375Smp 	else
2517c64d375Smp 		return (KADM5_FAILURE);
2527c64d375Smp 
2537c64d375Smp 	/* Locate the master KDC */
2547c64d375Smp 	if (ret = kadm5_get_master(context, realm, &admin_host))
2557c64d375Smp 		return (ret);
2567c64d375Smp 
2577c64d375Smp 	if (ret = krb5_os_hostaddr(context, admin_host, &master_addr)) {
2587c64d375Smp 		free(admin_host);
2597c64d375Smp 		return (ret);
2607c64d375Smp 	}
2617c64d375Smp 
2627c64d375Smp 	/* Get the local addresses */
2637c64d375Smp 	if (ret = krb5_os_localaddr(context, &local_addr)) {
2647c64d375Smp 		krb5_free_addresses(context, master_addr);
2657c64d375Smp 		free(admin_host);
2667c64d375Smp 		return (ret);
2677c64d375Smp 	}
2687c64d375Smp 
2697c64d375Smp 	/* Compare them */
270*212bfef1SMark Phalan 	for (tmp_addr = master_addr; *tmp_addr; tmp_addr++) {
271*212bfef1SMark Phalan 		if (krb5_address_search(context, *tmp_addr, local_addr)) {
2727c64d375Smp 			*is_master = TRUE;
2737c64d375Smp 			break;
2747c64d375Smp 		}
2757c64d375Smp 	}
2767c64d375Smp 
2777c64d375Smp 	krb5_free_addresses(context, local_addr);
2787c64d375Smp 	krb5_free_addresses(context, master_addr);
2797c64d375Smp 	free(admin_host);
2807c64d375Smp 
2817c64d375Smp 	return (KADM5_OK);
2827c64d375Smp }
283