xref: /onnv-gate/usr/src/cmd/smbsrv/smbd/smbd_join.c (revision 9832:3569b6c7f56c)
15331Samw /*
25331Samw  * CDDL HEADER START
35331Samw  *
45331Samw  * The contents of this file are subject to the terms of the
55331Samw  * Common Development and Distribution License (the "License").
65331Samw  * You may not use this file except in compliance with the License.
75331Samw  *
85331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95331Samw  * or http://www.opensolaris.org/os/licensing.
105331Samw  * See the License for the specific language governing permissions
115331Samw  * and limitations under the License.
125331Samw  *
135331Samw  * When distributing Covered Code, include this CDDL HEADER in each
145331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155331Samw  * If applicable, add the following below this CDDL HEADER, with the
165331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
175331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
185331Samw  *
195331Samw  * CDDL HEADER END
205331Samw  */
215331Samw /*
22*9832Samw@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235331Samw  * Use is subject to license terms.
245331Samw  */
255331Samw 
265331Samw #include <syslog.h>
275331Samw #include <synch.h>
285331Samw #include <pthread.h>
295331Samw #include <unistd.h>
305331Samw #include <string.h>
315331Samw #include <strings.h>
325331Samw #include <sys/errno.h>
335331Samw 
345331Samw #include <smbsrv/libsmb.h>
355331Samw #include <smbsrv/libsmbrdr.h>
365331Samw #include <smbsrv/libsmbns.h>
375331Samw #include <smbsrv/libmlsvc.h>
385331Samw #include <smbsrv/smbinfo.h>
395331Samw #include <smbsrv/ntstatus.h>
408334SJose.Borrego@Sun.COM #include "smbd.h"
418334SJose.Borrego@Sun.COM 
425331Samw 
435331Samw /*
448334SJose.Borrego@Sun.COM  * This is a short-lived thread that triggers the initial DC discovery
458334SJose.Borrego@Sun.COM  * at startup.
465331Samw  */
478334SJose.Borrego@Sun.COM static pthread_t smb_locate_dc_thr;
488334SJose.Borrego@Sun.COM 
498334SJose.Borrego@Sun.COM static void *smbd_locate_dc_thread(void *);
508334SJose.Borrego@Sun.COM static int smbd_get_kpasswd_srv(char *, size_t);
518334SJose.Borrego@Sun.COM static uint32_t smbd_join_workgroup(smb_joininfo_t *);
528334SJose.Borrego@Sun.COM static uint32_t smbd_join_domain(smb_joininfo_t *);
535331Samw 
545331Samw /*
558334SJose.Borrego@Sun.COM  * smbd_join
568334SJose.Borrego@Sun.COM  *
578334SJose.Borrego@Sun.COM  * Joins the specified domain/workgroup.
588334SJose.Borrego@Sun.COM  *
598334SJose.Borrego@Sun.COM  * If the security mode or domain name is being changed,
608334SJose.Borrego@Sun.COM  * the caller must restart the service.
615331Samw  */
628334SJose.Borrego@Sun.COM uint32_t
638334SJose.Borrego@Sun.COM smbd_join(smb_joininfo_t *info)
648334SJose.Borrego@Sun.COM {
658334SJose.Borrego@Sun.COM 	uint32_t status;
666139Sjb150015 
678334SJose.Borrego@Sun.COM 	dssetup_clear_domain_info();
688334SJose.Borrego@Sun.COM 	if (info->mode == SMB_SECMODE_WORKGRP)
698334SJose.Borrego@Sun.COM 		status = smbd_join_workgroup(info);
708334SJose.Borrego@Sun.COM 	else
718334SJose.Borrego@Sun.COM 		status = smbd_join_domain(info);
725331Samw 
738334SJose.Borrego@Sun.COM 	return (status);
745331Samw }
755331Samw 
765331Samw /*
778334SJose.Borrego@Sun.COM  * smbd_set_netlogon_cred
788334SJose.Borrego@Sun.COM  *
798334SJose.Borrego@Sun.COM  * If the system is joined to an AD domain via kclient, SMB daemon will need
808334SJose.Borrego@Sun.COM  * to establish the NETLOGON credential chain.
818334SJose.Borrego@Sun.COM  *
828334SJose.Borrego@Sun.COM  * Since the kclient has updated the machine password stored in SMF
838334SJose.Borrego@Sun.COM  * repository, the cached ipc_info must be updated accordingly by calling
848334SJose.Borrego@Sun.COM  * smbrdr_ipc_commit.
858334SJose.Borrego@Sun.COM  *
868334SJose.Borrego@Sun.COM  * Due to potential replication delays in a multiple DC environment, the
878334SJose.Borrego@Sun.COM  * NETLOGON rpc request must be sent to the DC, to which the KPASSWD request
888334SJose.Borrego@Sun.COM  * is sent. If the DC discovered by the SMB daemon is different than the
898334SJose.Borrego@Sun.COM  * kpasswd server, the current connection with the DC will be torn down
908334SJose.Borrego@Sun.COM  * and a DC discovery process will be triggered to locate the kpasswd
918334SJose.Borrego@Sun.COM  * server.
928334SJose.Borrego@Sun.COM  *
938334SJose.Borrego@Sun.COM  * If joining a new domain, the domain_name property must be set after a
948334SJose.Borrego@Sun.COM  * successful credential chain setup.
958334SJose.Borrego@Sun.COM  */
968334SJose.Borrego@Sun.COM boolean_t
978334SJose.Borrego@Sun.COM smbd_set_netlogon_cred(void)
988334SJose.Borrego@Sun.COM {
998334SJose.Borrego@Sun.COM 	char kpasswd_srv[MAXHOSTNAMELEN];
1008334SJose.Borrego@Sun.COM 	char kpasswd_domain[MAXHOSTNAMELEN];
1018334SJose.Borrego@Sun.COM 	char sam_acct[SMB_SAMACCT_MAXLEN];
1028334SJose.Borrego@Sun.COM 	char *ipc_usr, *dom;
1038334SJose.Borrego@Sun.COM 	boolean_t new_domain = B_FALSE;
104*9832Samw@Sun.COM 	smb_domain_t domain;
105*9832Samw@Sun.COM 	nt_domain_t *di;
1068334SJose.Borrego@Sun.COM 
1078334SJose.Borrego@Sun.COM 	if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
1088334SJose.Borrego@Sun.COM 		return (B_FALSE);
1098334SJose.Borrego@Sun.COM 
1108334SJose.Borrego@Sun.COM 	if (smb_match_netlogon_seqnum())
1118334SJose.Borrego@Sun.COM 		return (B_FALSE);
1128334SJose.Borrego@Sun.COM 
1138334SJose.Borrego@Sun.COM 	(void) smb_config_getstr(SMB_CI_KPASSWD_SRV, kpasswd_srv,
1148334SJose.Borrego@Sun.COM 	    sizeof (kpasswd_srv));
1158334SJose.Borrego@Sun.COM 
1168334SJose.Borrego@Sun.COM 	if (*kpasswd_srv == '\0')
1178334SJose.Borrego@Sun.COM 		return (B_FALSE);
1188334SJose.Borrego@Sun.COM 
1198334SJose.Borrego@Sun.COM 	/*
1208334SJose.Borrego@Sun.COM 	 * If the domain join initiated by smbadm join CLI is in
1218334SJose.Borrego@Sun.COM 	 * progress, don't do anything.
1228334SJose.Borrego@Sun.COM 	 */
1238334SJose.Borrego@Sun.COM 	(void) smb_getsamaccount(sam_acct, sizeof (sam_acct));
1248334SJose.Borrego@Sun.COM 	ipc_usr = smbrdr_ipc_get_user();
1258334SJose.Borrego@Sun.COM 	if (utf8_strcasecmp(ipc_usr, sam_acct))
1268334SJose.Borrego@Sun.COM 		return (B_FALSE);
1278334SJose.Borrego@Sun.COM 
128*9832Samw@Sun.COM 	di = &domain.d_info;
129*9832Samw@Sun.COM 	if (!smb_domain_getinfo(&domain))
130*9832Samw@Sun.COM 		(void) smb_getfqdomainname(di->di_fqname, MAXHOSTNAMELEN);
1318334SJose.Borrego@Sun.COM 
1328334SJose.Borrego@Sun.COM 	(void) smb_config_getstr(SMB_CI_KPASSWD_DOMAIN, kpasswd_domain,
1338334SJose.Borrego@Sun.COM 	    sizeof (kpasswd_domain));
1348334SJose.Borrego@Sun.COM 
1358334SJose.Borrego@Sun.COM 	if (*kpasswd_domain != '\0' &&
136*9832Samw@Sun.COM 	    utf8_strcasecmp(kpasswd_domain, di->di_fqname)) {
1378334SJose.Borrego@Sun.COM 		dom = kpasswd_domain;
1388334SJose.Borrego@Sun.COM 		new_domain = B_TRUE;
1398334SJose.Borrego@Sun.COM 	} else {
140*9832Samw@Sun.COM 		dom = di->di_fqname;
1418334SJose.Borrego@Sun.COM 	}
1428334SJose.Borrego@Sun.COM 
1438334SJose.Borrego@Sun.COM 	/*
1448334SJose.Borrego@Sun.COM 	 * DC discovery will be triggered if the domain info is not
1458334SJose.Borrego@Sun.COM 	 * currently cached or the SMB daemon has previously discovered a DC
1468334SJose.Borrego@Sun.COM 	 * that is different than the kpasswd server.
1478334SJose.Borrego@Sun.COM 	 */
148*9832Samw@Sun.COM 	if (new_domain || utf8_strcasecmp(domain.d_dc, kpasswd_srv) != 0) {
149*9832Samw@Sun.COM 		if (*domain.d_dc != '\0')
150*9832Samw@Sun.COM 			mlsvc_disconnect(domain.d_dc);
1518334SJose.Borrego@Sun.COM 
152*9832Samw@Sun.COM 		if (!smb_locate_dc(dom, kpasswd_srv, &domain)) {
153*9832Samw@Sun.COM 			if (!smb_locate_dc(di->di_fqname, "", &domain)) {
1548334SJose.Borrego@Sun.COM 				smbrdr_ipc_commit();
1558334SJose.Borrego@Sun.COM 				return (B_FALSE);
1568334SJose.Borrego@Sun.COM 			}
1578334SJose.Borrego@Sun.COM 		}
1588334SJose.Borrego@Sun.COM 	}
1598334SJose.Borrego@Sun.COM 
1608334SJose.Borrego@Sun.COM 	smbrdr_ipc_commit();
161*9832Samw@Sun.COM 	if (mlsvc_netlogon(domain.d_dc, di->di_nbname)) {
1628334SJose.Borrego@Sun.COM 		syslog(LOG_ERR,
1638334SJose.Borrego@Sun.COM 		    "failed to establish NETLOGON credential chain");
1648334SJose.Borrego@Sun.COM 		return (B_TRUE);
1658334SJose.Borrego@Sun.COM 	} else {
1668334SJose.Borrego@Sun.COM 		if (new_domain) {
167*9832Samw@Sun.COM 			smb_config_setdomaininfo(di->di_nbname, di->di_fqname,
168*9832Samw@Sun.COM 			    di->di_sid,
169*9832Samw@Sun.COM 			    di->di_u.di_dns.ddi_forest,
170*9832Samw@Sun.COM 			    di->di_u.di_dns.ddi_guid);
1718334SJose.Borrego@Sun.COM 			(void) smb_config_setstr(SMB_CI_KPASSWD_DOMAIN, "");
1728334SJose.Borrego@Sun.COM 		}
1738334SJose.Borrego@Sun.COM 	}
1748334SJose.Borrego@Sun.COM 
1758334SJose.Borrego@Sun.COM 	return (new_domain);
1768334SJose.Borrego@Sun.COM }
1778334SJose.Borrego@Sun.COM 
1788334SJose.Borrego@Sun.COM /*
1798334SJose.Borrego@Sun.COM  * smbd_locate_dc_start()
1808334SJose.Borrego@Sun.COM  *
1818334SJose.Borrego@Sun.COM  * Initialization of the thread that triggers the initial DC discovery
1828334SJose.Borrego@Sun.COM  * when SMB daemon starts up.
1838334SJose.Borrego@Sun.COM  * Returns 0 on success, an error number if thread creation fails.
1848334SJose.Borrego@Sun.COM  */
1858334SJose.Borrego@Sun.COM int
1868334SJose.Borrego@Sun.COM smbd_locate_dc_start(void)
1878334SJose.Borrego@Sun.COM {
1888334SJose.Borrego@Sun.COM 	pthread_attr_t tattr;
1898334SJose.Borrego@Sun.COM 	int rc;
1908334SJose.Borrego@Sun.COM 
1918334SJose.Borrego@Sun.COM 	(void) pthread_attr_init(&tattr);
1928334SJose.Borrego@Sun.COM 	(void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1938334SJose.Borrego@Sun.COM 	rc = pthread_create(&smb_locate_dc_thr, &tattr, smbd_locate_dc_thread,
1948334SJose.Borrego@Sun.COM 	    NULL);
1958334SJose.Borrego@Sun.COM 	(void) pthread_attr_destroy(&tattr);
1968334SJose.Borrego@Sun.COM 	return (rc);
1978334SJose.Borrego@Sun.COM }
1988334SJose.Borrego@Sun.COM 
1998334SJose.Borrego@Sun.COM /*
2008334SJose.Borrego@Sun.COM  * smbd_locate_dc_thread()
2018334SJose.Borrego@Sun.COM  *
2028334SJose.Borrego@Sun.COM  * If necessary, set up Netlogon credential chain and locate a
2038334SJose.Borrego@Sun.COM  * domain controller in the given resource domain.
2048334SJose.Borrego@Sun.COM  *
2058334SJose.Borrego@Sun.COM  * The domain configuration will be updated upon a successful DC discovery.
2068334SJose.Borrego@Sun.COM  */
2078334SJose.Borrego@Sun.COM /*ARGSUSED*/
2088334SJose.Borrego@Sun.COM static void *
2098334SJose.Borrego@Sun.COM smbd_locate_dc_thread(void *arg)
2108334SJose.Borrego@Sun.COM {
2118334SJose.Borrego@Sun.COM 	char domain[MAXHOSTNAMELEN];
212*9832Samw@Sun.COM 	smb_domain_t new_domain;
213*9832Samw@Sun.COM 	nt_domain_t *di;
2148334SJose.Borrego@Sun.COM 
2158334SJose.Borrego@Sun.COM 	if (!smb_match_netlogon_seqnum()) {
2168334SJose.Borrego@Sun.COM 		(void) smbd_set_netlogon_cred();
2178334SJose.Borrego@Sun.COM 	} else {
2188334SJose.Borrego@Sun.COM 		if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) {
2198334SJose.Borrego@Sun.COM 			(void) smb_getdomainname(domain, MAXHOSTNAMELEN);
2208334SJose.Borrego@Sun.COM 			(void) utf8_strupr(domain);
2218334SJose.Borrego@Sun.COM 		}
2228334SJose.Borrego@Sun.COM 
223*9832Samw@Sun.COM 		if (smb_locate_dc(domain, "", &new_domain)) {
224*9832Samw@Sun.COM 			di = &new_domain.d_info;
225*9832Samw@Sun.COM 			smb_config_setdomaininfo(di->di_nbname, di->di_fqname,
226*9832Samw@Sun.COM 			    di->di_sid,
227*9832Samw@Sun.COM 			    di->di_u.di_dns.ddi_forest,
228*9832Samw@Sun.COM 			    di->di_u.di_dns.ddi_guid);
229*9832Samw@Sun.COM 		}
2308334SJose.Borrego@Sun.COM 	}
2318334SJose.Borrego@Sun.COM 
2328334SJose.Borrego@Sun.COM 	return (NULL);
2338334SJose.Borrego@Sun.COM }
2348334SJose.Borrego@Sun.COM 
2358334SJose.Borrego@Sun.COM 
2368334SJose.Borrego@Sun.COM /*
2376139Sjb150015  * Retrieve the kpasswd server from krb5.conf.
2388334SJose.Borrego@Sun.COM  *
2398334SJose.Borrego@Sun.COM  * Initialization of the locate dc thread.
2408334SJose.Borrego@Sun.COM  * Returns 0 on success, an error number if thread creation fails.
2416139Sjb150015  */
2426139Sjb150015 static int
2436139Sjb150015 smbd_get_kpasswd_srv(char *srv, size_t len)
2446139Sjb150015 {
2456139Sjb150015 	FILE *fp;
2466139Sjb150015 	static char buf[512];
2476139Sjb150015 	char *p;
2486139Sjb150015 
2496139Sjb150015 	*srv = '\0';
2506139Sjb150015 	p = getenv("KRB5_CONFIG");
2516139Sjb150015 	if (p == NULL || *p == '\0')
2526139Sjb150015 		p = "/etc/krb5/krb5.conf";
2536139Sjb150015 
2546139Sjb150015 	if ((fp = fopen(p, "r")) == NULL)
2556139Sjb150015 		return (-1);
2566139Sjb150015 
2576139Sjb150015 	while (fgets(buf, sizeof (buf), fp)) {
2586139Sjb150015 
2596139Sjb150015 		/* Weed out any comment text */
2606139Sjb150015 		(void) trim_whitespace(buf);
2616139Sjb150015 		if (*buf == '#')
2626139Sjb150015 			continue;
2636139Sjb150015 
2646139Sjb150015 		if ((p = strstr(buf, "kpasswd_server")) != NULL) {
2656139Sjb150015 			if ((p = strchr(p, '=')) != NULL) {
2666139Sjb150015 				(void) trim_whitespace(++p);
2676139Sjb150015 				(void) strlcpy(srv, p, len);
2686139Sjb150015 			}
2696139Sjb150015 			break;
2706139Sjb150015 		}
2716139Sjb150015 	}
2726139Sjb150015 
2738334SJose.Borrego@Sun.COM 
2746139Sjb150015 	(void) fclose(fp);
2756139Sjb150015 	return ((*srv == '\0') ? -1 : 0);
2766139Sjb150015 }
2776139Sjb150015 
2788334SJose.Borrego@Sun.COM static uint32_t
2798334SJose.Borrego@Sun.COM smbd_join_workgroup(smb_joininfo_t *info)
2805331Samw {
2818334SJose.Borrego@Sun.COM 	char nb_domain[SMB_PI_MAX_DOMAIN];
2828334SJose.Borrego@Sun.COM 
2838334SJose.Borrego@Sun.COM 	(void) smb_config_getstr(SMB_CI_DOMAIN_NAME, nb_domain,
2848334SJose.Borrego@Sun.COM 	    sizeof (nb_domain));
2858334SJose.Borrego@Sun.COM 
2868334SJose.Borrego@Sun.COM 	smbd_set_secmode(SMB_SECMODE_WORKGRP);
287*9832Samw@Sun.COM 	smb_config_setdomaininfo(info->domain_name, "", "", "", "");
2888334SJose.Borrego@Sun.COM 
2898334SJose.Borrego@Sun.COM 	if (strcasecmp(nb_domain, info->domain_name))
2908334SJose.Borrego@Sun.COM 		smb_browser_reconfig();
2918334SJose.Borrego@Sun.COM 
2928334SJose.Borrego@Sun.COM 	return (NT_STATUS_SUCCESS);
2938334SJose.Borrego@Sun.COM }
2948334SJose.Borrego@Sun.COM 
2958334SJose.Borrego@Sun.COM static uint32_t
2968334SJose.Borrego@Sun.COM smbd_join_domain(smb_joininfo_t *info)
2978334SJose.Borrego@Sun.COM {
2985331Samw 	uint32_t status;
2995331Samw 	unsigned char passwd_hash[SMBAUTH_HASH_SZ];
3006139Sjb150015 	char dc[MAXHOSTNAMELEN];
3018334SJose.Borrego@Sun.COM 	smb_domain_t domain_info;
302*9832Samw@Sun.COM 	nt_domain_t *di;
3035331Samw 
3045331Samw 	/*
3055331Samw 	 * Ensure that any previous membership of this domain has
3065331Samw 	 * been cleared from the environment before we start. This
3075331Samw 	 * will ensure that we don't attempt a NETLOGON_SAMLOGON
3085331Samw 	 * when attempting to find the PDC.
3095331Samw 	 */
3108334SJose.Borrego@Sun.COM 
3115772Sas200622 	(void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_FALSE);
3125772Sas200622 
3138334SJose.Borrego@Sun.COM 	if (smb_auth_ntlm_hash(info->domain_passwd, passwd_hash)
3148334SJose.Borrego@Sun.COM 	    != SMBAUTH_SUCCESS) {
3158334SJose.Borrego@Sun.COM 		syslog(LOG_ERR, "smbd: could not compute ntlm hash for '%s'",
3168334SJose.Borrego@Sun.COM 		    info->domain_username);
3175772Sas200622 		return (NT_STATUS_INTERNAL_ERROR);
3185772Sas200622 	}
3195331Samw 
3208334SJose.Borrego@Sun.COM 	smbrdr_ipc_set(info->domain_username, passwd_hash);
3215331Samw 
3226139Sjb150015 	(void) smbd_get_kpasswd_srv(dc, sizeof (dc));
3238334SJose.Borrego@Sun.COM 	/* info->domain_name could either be NetBIOS domain name or FQDN */
3248334SJose.Borrego@Sun.COM 	if (smb_locate_dc(info->domain_name, dc, &domain_info)) {
3258334SJose.Borrego@Sun.COM 		status = mlsvc_join(&domain_info, info->domain_username,
3268334SJose.Borrego@Sun.COM 		    info->domain_passwd);
3275331Samw 
3285331Samw 		if (status == NT_STATUS_SUCCESS) {
329*9832Samw@Sun.COM 			di = &domain_info.d_info;
3308334SJose.Borrego@Sun.COM 			smbd_set_secmode(SMB_SECMODE_DOMAIN);
331*9832Samw@Sun.COM 			smb_config_setdomaininfo(di->di_nbname, di->di_fqname,
332*9832Samw@Sun.COM 			    di->di_sid,
333*9832Samw@Sun.COM 			    di->di_u.di_dns.ddi_forest,
334*9832Samw@Sun.COM 			    di->di_u.di_dns.ddi_guid);
3355331Samw 			smbrdr_ipc_commit();
3365331Samw 			return (status);
3375331Samw 		}
3385331Samw 
3395331Samw 		smbrdr_ipc_rollback();
3405331Samw 		syslog(LOG_ERR, "smbd: failed joining %s (%s)",
3415331Samw 		    info->domain_name, xlate_nt_status(status));
3425331Samw 		return (status);
3435331Samw 	}
3445331Samw 
3455331Samw 	smbrdr_ipc_rollback();
3465331Samw 	syslog(LOG_ERR, "smbd: failed locating domain controller for %s",
3475331Samw 	    info->domain_name);
3485331Samw 	return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND);
3495331Samw }
350