xref: /onnv-gate/usr/src/cmd/smbsrv/smbd/smbd_join.c (revision 12508:edb7861a1533)
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  */
21*12508Samw@Sun.COM 
225331Samw /*
23*12508Samw@Sun.COM  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
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/libsmbns.h>
365331Samw #include <smbsrv/libmlsvc.h>
375331Samw #include <smbsrv/smbinfo.h>
388334SJose.Borrego@Sun.COM #include "smbd.h"
398334SJose.Borrego@Sun.COM 
405331Samw 
415331Samw /*
428334SJose.Borrego@Sun.COM  * This is a short-lived thread that triggers the initial DC discovery
438334SJose.Borrego@Sun.COM  * at startup.
445331Samw  */
458334SJose.Borrego@Sun.COM static pthread_t smb_locate_dc_thr;
468334SJose.Borrego@Sun.COM 
478334SJose.Borrego@Sun.COM static void *smbd_locate_dc_thread(void *);
488334SJose.Borrego@Sun.COM static int smbd_get_kpasswd_srv(char *, size_t);
498334SJose.Borrego@Sun.COM static uint32_t smbd_join_workgroup(smb_joininfo_t *);
508334SJose.Borrego@Sun.COM static uint32_t smbd_join_domain(smb_joininfo_t *);
515331Samw 
525331Samw /*
538334SJose.Borrego@Sun.COM  * smbd_join
548334SJose.Borrego@Sun.COM  *
558334SJose.Borrego@Sun.COM  * Joins the specified domain/workgroup.
568334SJose.Borrego@Sun.COM  *
578334SJose.Borrego@Sun.COM  * If the security mode or domain name is being changed,
588334SJose.Borrego@Sun.COM  * the caller must restart the service.
595331Samw  */
608334SJose.Borrego@Sun.COM uint32_t
618334SJose.Borrego@Sun.COM smbd_join(smb_joininfo_t *info)
628334SJose.Borrego@Sun.COM {
638334SJose.Borrego@Sun.COM 	uint32_t status;
646139Sjb150015 
658334SJose.Borrego@Sun.COM 	dssetup_clear_domain_info();
668334SJose.Borrego@Sun.COM 	if (info->mode == SMB_SECMODE_WORKGRP)
678334SJose.Borrego@Sun.COM 		status = smbd_join_workgroup(info);
688334SJose.Borrego@Sun.COM 	else
698334SJose.Borrego@Sun.COM 		status = smbd_join_domain(info);
705331Samw 
718334SJose.Borrego@Sun.COM 	return (status);
725331Samw }
735331Samw 
745331Samw /*
758334SJose.Borrego@Sun.COM  * smbd_set_netlogon_cred
768334SJose.Borrego@Sun.COM  *
778334SJose.Borrego@Sun.COM  * If the system is joined to an AD domain via kclient, SMB daemon will need
788334SJose.Borrego@Sun.COM  * to establish the NETLOGON credential chain.
798334SJose.Borrego@Sun.COM  *
808334SJose.Borrego@Sun.COM  * Since the kclient has updated the machine password stored in SMF
818334SJose.Borrego@Sun.COM  * repository, the cached ipc_info must be updated accordingly by calling
8210717Samw@Sun.COM  * smb_ipc_commit.
838334SJose.Borrego@Sun.COM  *
848334SJose.Borrego@Sun.COM  * Due to potential replication delays in a multiple DC environment, the
858334SJose.Borrego@Sun.COM  * NETLOGON rpc request must be sent to the DC, to which the KPASSWD request
868334SJose.Borrego@Sun.COM  * is sent. If the DC discovered by the SMB daemon is different than the
878334SJose.Borrego@Sun.COM  * kpasswd server, the current connection with the DC will be torn down
888334SJose.Borrego@Sun.COM  * and a DC discovery process will be triggered to locate the kpasswd
898334SJose.Borrego@Sun.COM  * server.
908334SJose.Borrego@Sun.COM  *
918334SJose.Borrego@Sun.COM  * If joining a new domain, the domain_name property must be set after a
928334SJose.Borrego@Sun.COM  * successful credential chain setup.
938334SJose.Borrego@Sun.COM  */
948334SJose.Borrego@Sun.COM boolean_t
958334SJose.Borrego@Sun.COM smbd_set_netlogon_cred(void)
968334SJose.Borrego@Sun.COM {
978334SJose.Borrego@Sun.COM 	char kpasswd_srv[MAXHOSTNAMELEN];
988334SJose.Borrego@Sun.COM 	char kpasswd_domain[MAXHOSTNAMELEN];
998334SJose.Borrego@Sun.COM 	char sam_acct[SMB_SAMACCT_MAXLEN];
10010717Samw@Sun.COM 	char ipc_usr[SMB_USERNAME_MAXLEN];
10110717Samw@Sun.COM 	char *dom;
1028334SJose.Borrego@Sun.COM 	boolean_t new_domain = B_FALSE;
10310717Samw@Sun.COM 	smb_domainex_t dxi;
10410717Samw@Sun.COM 	smb_domain_t *di;
1058334SJose.Borrego@Sun.COM 
1068334SJose.Borrego@Sun.COM 	if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
1078334SJose.Borrego@Sun.COM 		return (B_FALSE);
1088334SJose.Borrego@Sun.COM 
1098334SJose.Borrego@Sun.COM 	if (smb_match_netlogon_seqnum())
1108334SJose.Borrego@Sun.COM 		return (B_FALSE);
1118334SJose.Borrego@Sun.COM 
1128334SJose.Borrego@Sun.COM 	(void) smb_config_getstr(SMB_CI_KPASSWD_SRV, kpasswd_srv,
1138334SJose.Borrego@Sun.COM 	    sizeof (kpasswd_srv));
1148334SJose.Borrego@Sun.COM 
1158334SJose.Borrego@Sun.COM 	if (*kpasswd_srv == '\0')
1168334SJose.Borrego@Sun.COM 		return (B_FALSE);
1178334SJose.Borrego@Sun.COM 
1188334SJose.Borrego@Sun.COM 	/*
1198334SJose.Borrego@Sun.COM 	 * If the domain join initiated by smbadm join CLI is in
1208334SJose.Borrego@Sun.COM 	 * progress, don't do anything.
1218334SJose.Borrego@Sun.COM 	 */
1228334SJose.Borrego@Sun.COM 	(void) smb_getsamaccount(sam_acct, sizeof (sam_acct));
12310717Samw@Sun.COM 	smb_ipc_get_user(ipc_usr, SMB_USERNAME_MAXLEN);
12410966SJordan.Brown@Sun.COM 	if (smb_strcasecmp(ipc_usr, sam_acct, 0))
1258334SJose.Borrego@Sun.COM 		return (B_FALSE);
1268334SJose.Borrego@Sun.COM 
12710717Samw@Sun.COM 	di = &dxi.d_primary;
12810717Samw@Sun.COM 	if (!smb_domain_getinfo(&dxi))
1299832Samw@Sun.COM 		(void) smb_getfqdomainname(di->di_fqname, MAXHOSTNAMELEN);
1308334SJose.Borrego@Sun.COM 
1318334SJose.Borrego@Sun.COM 	(void) smb_config_getstr(SMB_CI_KPASSWD_DOMAIN, kpasswd_domain,
1328334SJose.Borrego@Sun.COM 	    sizeof (kpasswd_domain));
1338334SJose.Borrego@Sun.COM 
1348334SJose.Borrego@Sun.COM 	if (*kpasswd_domain != '\0' &&
13510966SJordan.Brown@Sun.COM 	    smb_strcasecmp(kpasswd_domain, di->di_fqname, 0)) {
1368334SJose.Borrego@Sun.COM 		dom = kpasswd_domain;
1378334SJose.Borrego@Sun.COM 		new_domain = B_TRUE;
1388334SJose.Borrego@Sun.COM 	} else {
1399832Samw@Sun.COM 		dom = di->di_fqname;
1408334SJose.Borrego@Sun.COM 	}
1418334SJose.Borrego@Sun.COM 
1428334SJose.Borrego@Sun.COM 	/*
1438334SJose.Borrego@Sun.COM 	 * DC discovery will be triggered if the domain info is not
1448334SJose.Borrego@Sun.COM 	 * currently cached or the SMB daemon has previously discovered a DC
1458334SJose.Borrego@Sun.COM 	 * that is different than the kpasswd server.
1468334SJose.Borrego@Sun.COM 	 */
14710966SJordan.Brown@Sun.COM 	if (new_domain || smb_strcasecmp(dxi.d_dc, kpasswd_srv, 0) != 0) {
14810717Samw@Sun.COM 		if (*dxi.d_dc != '\0')
14910717Samw@Sun.COM 			mlsvc_disconnect(dxi.d_dc);
1508334SJose.Borrego@Sun.COM 
15110717Samw@Sun.COM 		if (!smb_locate_dc(dom, kpasswd_srv, &dxi)) {
15210717Samw@Sun.COM 			if (!smb_locate_dc(di->di_fqname, "", &dxi)) {
15310717Samw@Sun.COM 				smb_ipc_commit();
1548334SJose.Borrego@Sun.COM 				return (B_FALSE);
1558334SJose.Borrego@Sun.COM 			}
1568334SJose.Borrego@Sun.COM 		}
1578334SJose.Borrego@Sun.COM 	}
1588334SJose.Borrego@Sun.COM 
15910717Samw@Sun.COM 	smb_ipc_commit();
16010717Samw@Sun.COM 	if (mlsvc_netlogon(dxi.d_dc, di->di_nbname)) {
1618334SJose.Borrego@Sun.COM 		syslog(LOG_ERR,
1628334SJose.Borrego@Sun.COM 		    "failed to establish NETLOGON credential chain");
1638334SJose.Borrego@Sun.COM 		return (B_TRUE);
1648334SJose.Borrego@Sun.COM 	} else {
1658334SJose.Borrego@Sun.COM 		if (new_domain) {
1669832Samw@Sun.COM 			smb_config_setdomaininfo(di->di_nbname, di->di_fqname,
1679832Samw@Sun.COM 			    di->di_sid,
1689832Samw@Sun.COM 			    di->di_u.di_dns.ddi_forest,
1699832Samw@Sun.COM 			    di->di_u.di_dns.ddi_guid);
1708334SJose.Borrego@Sun.COM 			(void) smb_config_setstr(SMB_CI_KPASSWD_DOMAIN, "");
1718334SJose.Borrego@Sun.COM 		}
1728334SJose.Borrego@Sun.COM 	}
1738334SJose.Borrego@Sun.COM 
1748334SJose.Borrego@Sun.COM 	return (new_domain);
1758334SJose.Borrego@Sun.COM }
1768334SJose.Borrego@Sun.COM 
1778334SJose.Borrego@Sun.COM /*
1788334SJose.Borrego@Sun.COM  * smbd_locate_dc_start()
1798334SJose.Borrego@Sun.COM  *
1808334SJose.Borrego@Sun.COM  * Initialization of the thread that triggers the initial DC discovery
1818334SJose.Borrego@Sun.COM  * when SMB daemon starts up.
1828334SJose.Borrego@Sun.COM  * Returns 0 on success, an error number if thread creation fails.
1838334SJose.Borrego@Sun.COM  */
1848334SJose.Borrego@Sun.COM int
1858334SJose.Borrego@Sun.COM smbd_locate_dc_start(void)
1868334SJose.Borrego@Sun.COM {
1878334SJose.Borrego@Sun.COM 	pthread_attr_t tattr;
1888334SJose.Borrego@Sun.COM 	int rc;
1898334SJose.Borrego@Sun.COM 
1908334SJose.Borrego@Sun.COM 	(void) pthread_attr_init(&tattr);
1918334SJose.Borrego@Sun.COM 	(void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1928334SJose.Borrego@Sun.COM 	rc = pthread_create(&smb_locate_dc_thr, &tattr, smbd_locate_dc_thread,
1938334SJose.Borrego@Sun.COM 	    NULL);
1948334SJose.Borrego@Sun.COM 	(void) pthread_attr_destroy(&tattr);
1958334SJose.Borrego@Sun.COM 	return (rc);
1968334SJose.Borrego@Sun.COM }
1978334SJose.Borrego@Sun.COM 
1988334SJose.Borrego@Sun.COM /*
1998334SJose.Borrego@Sun.COM  * smbd_locate_dc_thread()
2008334SJose.Borrego@Sun.COM  *
2018334SJose.Borrego@Sun.COM  * If necessary, set up Netlogon credential chain and locate a
2028334SJose.Borrego@Sun.COM  * domain controller in the given resource domain.
2038334SJose.Borrego@Sun.COM  *
2048334SJose.Borrego@Sun.COM  * The domain configuration will be updated upon a successful DC discovery.
2058334SJose.Borrego@Sun.COM  */
2068334SJose.Borrego@Sun.COM /*ARGSUSED*/
2078334SJose.Borrego@Sun.COM static void *
2088334SJose.Borrego@Sun.COM smbd_locate_dc_thread(void *arg)
2098334SJose.Borrego@Sun.COM {
2108334SJose.Borrego@Sun.COM 	char domain[MAXHOSTNAMELEN];
21110717Samw@Sun.COM 	smb_domainex_t new_domain;
21210717Samw@Sun.COM 	smb_domain_t *di;
2138334SJose.Borrego@Sun.COM 
2148334SJose.Borrego@Sun.COM 	if (!smb_match_netlogon_seqnum()) {
2158334SJose.Borrego@Sun.COM 		(void) smbd_set_netlogon_cred();
2168334SJose.Borrego@Sun.COM 	} else {
2178334SJose.Borrego@Sun.COM 		if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) {
2188334SJose.Borrego@Sun.COM 			(void) smb_getdomainname(domain, MAXHOSTNAMELEN);
21910966SJordan.Brown@Sun.COM 			(void) smb_strupr(domain);
2208334SJose.Borrego@Sun.COM 		}
2218334SJose.Borrego@Sun.COM 
2229832Samw@Sun.COM 		if (smb_locate_dc(domain, "", &new_domain)) {
22310717Samw@Sun.COM 			di = &new_domain.d_primary;
2249832Samw@Sun.COM 			smb_config_setdomaininfo(di->di_nbname, di->di_fqname,
2259832Samw@Sun.COM 			    di->di_sid,
2269832Samw@Sun.COM 			    di->di_u.di_dns.ddi_forest,
2279832Samw@Sun.COM 			    di->di_u.di_dns.ddi_guid);
2289832Samw@Sun.COM 		}
2298334SJose.Borrego@Sun.COM 	}
2308334SJose.Borrego@Sun.COM 
2318334SJose.Borrego@Sun.COM 	return (NULL);
2328334SJose.Borrego@Sun.COM }
2338334SJose.Borrego@Sun.COM 
2348334SJose.Borrego@Sun.COM 
2358334SJose.Borrego@Sun.COM /*
2366139Sjb150015  * Retrieve the kpasswd server from krb5.conf.
2378334SJose.Borrego@Sun.COM  *
2388334SJose.Borrego@Sun.COM  * Initialization of the locate dc thread.
2398334SJose.Borrego@Sun.COM  * Returns 0 on success, an error number if thread creation fails.
2406139Sjb150015  */
2416139Sjb150015 static int
2426139Sjb150015 smbd_get_kpasswd_srv(char *srv, size_t len)
2436139Sjb150015 {
2446139Sjb150015 	FILE *fp;
2456139Sjb150015 	static char buf[512];
2466139Sjb150015 	char *p;
2476139Sjb150015 
2486139Sjb150015 	*srv = '\0';
2496139Sjb150015 	p = getenv("KRB5_CONFIG");
2506139Sjb150015 	if (p == NULL || *p == '\0')
2516139Sjb150015 		p = "/etc/krb5/krb5.conf";
2526139Sjb150015 
2536139Sjb150015 	if ((fp = fopen(p, "r")) == NULL)
2546139Sjb150015 		return (-1);
2556139Sjb150015 
2566139Sjb150015 	while (fgets(buf, sizeof (buf), fp)) {
2576139Sjb150015 
2586139Sjb150015 		/* Weed out any comment text */
2596139Sjb150015 		(void) trim_whitespace(buf);
2606139Sjb150015 		if (*buf == '#')
2616139Sjb150015 			continue;
2626139Sjb150015 
2636139Sjb150015 		if ((p = strstr(buf, "kpasswd_server")) != NULL) {
2646139Sjb150015 			if ((p = strchr(p, '=')) != NULL) {
2656139Sjb150015 				(void) trim_whitespace(++p);
2666139Sjb150015 				(void) strlcpy(srv, p, len);
2676139Sjb150015 			}
2686139Sjb150015 			break;
2696139Sjb150015 		}
2706139Sjb150015 	}
2716139Sjb150015 
2728334SJose.Borrego@Sun.COM 
2736139Sjb150015 	(void) fclose(fp);
2746139Sjb150015 	return ((*srv == '\0') ? -1 : 0);
2756139Sjb150015 }
2766139Sjb150015 
2778334SJose.Borrego@Sun.COM static uint32_t
2788334SJose.Borrego@Sun.COM smbd_join_workgroup(smb_joininfo_t *info)
2795331Samw {
2808334SJose.Borrego@Sun.COM 	char nb_domain[SMB_PI_MAX_DOMAIN];
2818334SJose.Borrego@Sun.COM 
2828334SJose.Borrego@Sun.COM 	(void) smb_config_getstr(SMB_CI_DOMAIN_NAME, nb_domain,
2838334SJose.Borrego@Sun.COM 	    sizeof (nb_domain));
2848334SJose.Borrego@Sun.COM 
2858334SJose.Borrego@Sun.COM 	smbd_set_secmode(SMB_SECMODE_WORKGRP);
2869832Samw@Sun.COM 	smb_config_setdomaininfo(info->domain_name, "", "", "", "");
2878334SJose.Borrego@Sun.COM 
2888334SJose.Borrego@Sun.COM 	if (strcasecmp(nb_domain, info->domain_name))
2898334SJose.Borrego@Sun.COM 		smb_browser_reconfig();
2908334SJose.Borrego@Sun.COM 
2918334SJose.Borrego@Sun.COM 	return (NT_STATUS_SUCCESS);
2928334SJose.Borrego@Sun.COM }
2938334SJose.Borrego@Sun.COM 
2948334SJose.Borrego@Sun.COM static uint32_t
2958334SJose.Borrego@Sun.COM smbd_join_domain(smb_joininfo_t *info)
2968334SJose.Borrego@Sun.COM {
2975331Samw 	uint32_t status;
2985331Samw 	unsigned char passwd_hash[SMBAUTH_HASH_SZ];
2996139Sjb150015 	char dc[MAXHOSTNAMELEN];
30010717Samw@Sun.COM 	smb_domainex_t dxi;
30110717Samw@Sun.COM 	smb_domain_t *di;
3025331Samw 
3035331Samw 	/*
3045331Samw 	 * Ensure that any previous membership of this domain has
3055331Samw 	 * been cleared from the environment before we start. This
3065331Samw 	 * will ensure that we don't attempt a NETLOGON_SAMLOGON
3075331Samw 	 * when attempting to find the PDC.
3085331Samw 	 */
3098334SJose.Borrego@Sun.COM 
3105772Sas200622 	(void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_FALSE);
3115772Sas200622 
3128334SJose.Borrego@Sun.COM 	if (smb_auth_ntlm_hash(info->domain_passwd, passwd_hash)
3138334SJose.Borrego@Sun.COM 	    != SMBAUTH_SUCCESS) {
3148334SJose.Borrego@Sun.COM 		syslog(LOG_ERR, "smbd: could not compute ntlm hash for '%s'",
3158334SJose.Borrego@Sun.COM 		    info->domain_username);
3165772Sas200622 		return (NT_STATUS_INTERNAL_ERROR);
3175772Sas200622 	}
3185331Samw 
31910717Samw@Sun.COM 	smb_ipc_set(info->domain_username, passwd_hash);
3205331Samw 
3216139Sjb150015 	(void) smbd_get_kpasswd_srv(dc, sizeof (dc));
3228334SJose.Borrego@Sun.COM 	/* info->domain_name could either be NetBIOS domain name or FQDN */
32310717Samw@Sun.COM 	if (smb_locate_dc(info->domain_name, dc, &dxi)) {
32410717Samw@Sun.COM 		status = mlsvc_join(&dxi, info->domain_username,
3258334SJose.Borrego@Sun.COM 		    info->domain_passwd);
3265331Samw 
3275331Samw 		if (status == NT_STATUS_SUCCESS) {
32810717Samw@Sun.COM 			di = &dxi.d_primary;
3298334SJose.Borrego@Sun.COM 			smbd_set_secmode(SMB_SECMODE_DOMAIN);
3309832Samw@Sun.COM 			smb_config_setdomaininfo(di->di_nbname, di->di_fqname,
3319832Samw@Sun.COM 			    di->di_sid,
3329832Samw@Sun.COM 			    di->di_u.di_dns.ddi_forest,
3339832Samw@Sun.COM 			    di->di_u.di_dns.ddi_guid);
33410717Samw@Sun.COM 			smb_ipc_commit();
3355331Samw 			return (status);
3365331Samw 		}
3375331Samw 
33810717Samw@Sun.COM 		smb_ipc_rollback();
3395331Samw 		syslog(LOG_ERR, "smbd: failed joining %s (%s)",
3405331Samw 		    info->domain_name, xlate_nt_status(status));
3415331Samw 		return (status);
3425331Samw 	}
3435331Samw 
34410717Samw@Sun.COM 	smb_ipc_rollback();
3455331Samw 	syslog(LOG_ERR, "smbd: failed locating domain controller for %s",
3465331Samw 	    info->domain_name);
3475331Samw 	return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND);
3485331Samw }
349