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 /* 225331Samw * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 235331Samw * Use is subject to license terms. 245331Samw */ 255331Samw 265331Samw #pragma ident "%Z%%M% %I% %E% SMI" 275331Samw 285331Samw #include <syslog.h> 295331Samw #include <synch.h> 305331Samw #include <pthread.h> 315331Samw #include <unistd.h> 325331Samw #include <string.h> 335331Samw #include <strings.h> 345331Samw #include <sys/errno.h> 355331Samw 365331Samw #include <smbsrv/libsmb.h> 375331Samw #include <smbsrv/libsmbrdr.h> 385331Samw #include <smbsrv/libsmbns.h> 395331Samw #include <smbsrv/libmlsvc.h> 405331Samw 415331Samw #include <smbsrv/smbinfo.h> 425331Samw #include <smbsrv/ntstatus.h> 435331Samw #include <smbsrv/lsalib.h> 445331Samw 455331Samw /* 465331Samw * Local protocol flags used to indicate which version of the 475331Samw * netlogon protocol to use when attempting to find the PDC. 485331Samw */ 495331Samw #define NETLOGON_PROTO_NETLOGON 0x01 505331Samw #define NETLOGON_PROTO_SAMLOGON 0x02 515331Samw 525331Samw /* 535331Samw * Maximum time to wait for a domain controller (30 seconds). 545331Samw */ 555331Samw #define SMB_NETLOGON_TIMEOUT 30 565331Samw 575331Samw /* 585331Samw * Flags used in conjunction with the location and query condition 595331Samw * variables. 605331Samw */ 615331Samw #define SMB_NETLF_LOCATE_DC 0x00000001 625331Samw #define SMB_NETLF_LSA_QUERY 0x00000002 635331Samw 645331Samw typedef struct smb_netlogon_info { 655331Samw char snli_domain[SMB_PI_MAX_DOMAIN]; 665331Samw unsigned snli_flags; 675331Samw mutex_t snli_locate_mtx; 685331Samw cond_t snli_locate_cv; 695331Samw mutex_t snli_query_mtx; 705331Samw cond_t snli_query_cv; 715331Samw uint32_t snli_status; 725331Samw } smb_netlogon_info_t; 735331Samw 745331Samw static smb_netlogon_info_t smb_netlogon_info; 755331Samw 765331Samw static pthread_t lsa_monitor_thr; 775331Samw static pthread_t dc_browser_thr; 785331Samw 795331Samw static void *smb_netlogon_lsa_monitor(void *arg); 805331Samw static void *smb_netlogon_dc_browser(void *arg); 815331Samw 825331Samw /* 835331Samw * Inline convenience function to find out if the domain information is 845331Samw * valid. The caller can decide whether or not to wait. 855331Samw */ 865331Samw static boolean_t 875331Samw smb_ntdomain_is_valid(uint32_t timeout) 885331Samw { 895331Samw smb_ntdomain_t *info; 905331Samw 915331Samw if ((info = smb_getdomaininfo(timeout)) != 0) { 925331Samw if (info->ipaddr != 0) 935331Samw return (B_TRUE); 945331Samw } 955331Samw 965331Samw return (B_FALSE); 975331Samw } 985331Samw 995331Samw /* 1005331Samw * smbd_join 1015331Samw * 1025331Samw * Joins the specified domain/workgroup 1035331Samw */ 1045331Samw uint32_t 1055331Samw smbd_join(smb_joininfo_t *info) 1065331Samw { 1075331Samw smb_ntdomain_t *pi; 1085331Samw uint32_t status; 1095331Samw unsigned char passwd_hash[SMBAUTH_HASH_SZ]; 1105331Samw char plain_passwd[PASS_LEN + 1]; 1115331Samw char plain_user[PASS_LEN + 1]; 1125331Samw 1135331Samw if (info->mode == SMB_SECMODE_WORKGRP) { 1145331Samw smb_config_wrlock(); 1155331Samw (void) smb_config_set_secmode(info->mode); 1165331Samw (void) smb_config_set(SMB_CI_DOMAIN_NAME, info->domain_name); 1175331Samw smb_config_unlock(); 1185331Samw return (NT_STATUS_SUCCESS); 1195331Samw } 1205331Samw 1215331Samw /* 1225331Samw * Ensure that any previous membership of this domain has 1235331Samw * been cleared from the environment before we start. This 1245331Samw * will ensure that we don't attempt a NETLOGON_SAMLOGON 1255331Samw * when attempting to find the PDC. 1265331Samw */ 1275331Samw smb_set_domain_member(0); 1285331Samw 1295331Samw (void) strcpy(plain_user, info->domain_username); 1305331Samw (void) strcpy(plain_passwd, info->domain_passwd); 1315331Samw 1325331Samw if (smb_auth_ntlm_hash(plain_passwd, passwd_hash) != SMBAUTH_SUCCESS) { 1335331Samw status = NT_STATUS_INTERNAL_ERROR; 1345331Samw syslog(LOG_ERR, "smbd: could not compute ntlm hash for '%s'", 1355331Samw plain_user); 1365331Samw return (status); 1375331Samw } 1385331Samw 1395331Samw smbrdr_ipc_set(plain_user, passwd_hash); 1405331Samw 1415331Samw if (locate_resource_pdc(info->domain_name)) { 1425331Samw if ((pi = smb_getdomaininfo(0)) == 0) { 1435331Samw status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 1445331Samw syslog(LOG_ERR, "smbd: could not get domain controller" 1455331Samw "information for '%s'", info->domain_name); 1465331Samw return (status); 1475331Samw } 1485331Samw 1495331Samw /* 1505331Samw * Temporary delay before creating 1515331Samw * the workstation trust account. 1525331Samw */ 1535331Samw (void) sleep(2); 154*5521Sas200622 status = mlsvc_join(pi->server, pi->domain, 1555331Samw plain_user, plain_passwd); 1565331Samw 1575331Samw if (status == NT_STATUS_SUCCESS) { 1585331Samw smb_config_wrlock(); 1595331Samw (void) smb_config_set_secmode(SMB_SECMODE_DOMAIN); 1605331Samw (void) smb_config_set(SMB_CI_DOMAIN_NAME, 1615331Samw info->domain_name); 1625331Samw smb_config_unlock(); 1635331Samw smbrdr_ipc_commit(); 1645331Samw return (status); 1655331Samw } 1665331Samw 1675331Samw smbrdr_ipc_rollback(); 1685331Samw syslog(LOG_ERR, "smbd: failed joining %s (%s)", 1695331Samw info->domain_name, xlate_nt_status(status)); 1705331Samw return (status); 1715331Samw } 1725331Samw 1735331Samw smbrdr_ipc_rollback(); 1745331Samw syslog(LOG_ERR, "smbd: failed locating domain controller for %s", 1755331Samw info->domain_name); 1765331Samw return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND); 1775331Samw } 1785331Samw 1795331Samw /* 1805331Samw * locate_resource_pdc 1815331Samw * 1825331Samw * This is the entry point for discovering a domain controller for the 1835331Samw * specified domain. The caller may block here for around 30 seconds if 1845331Samw * the system has to go to the network and find a domain controller. 1855331Samw * Sometime it would be good to change this to smb_locate_pdc and allow 1865331Samw * the caller to specify whether or not he wants to wait for a response. 1875331Samw * 1885331Samw * The actual work of discovering a DC is handled by other threads. 1895331Samw * All we do here is signal the request and wait for a DC or a timeout. 1905331Samw * 1915331Samw * Returns B_TRUE if a domain controller is available. 1925331Samw */ 1935331Samw boolean_t 1945331Samw locate_resource_pdc(char *domain) 1955331Samw { 1965331Samw int rc; 1975331Samw timestruc_t to; 1985331Samw 1995331Samw if (domain == NULL || *domain == '\0') 2005331Samw return (B_FALSE); 2015331Samw 2025331Samw (void) mutex_lock(&smb_netlogon_info.snli_locate_mtx); 2035331Samw 2045331Samw if ((smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) == 0) { 2055331Samw smb_netlogon_info.snli_flags |= SMB_NETLF_LOCATE_DC; 2065331Samw (void) strlcpy(smb_netlogon_info.snli_domain, domain, 2075331Samw SMB_PI_MAX_DOMAIN); 2085331Samw (void) cond_broadcast(&smb_netlogon_info.snli_locate_cv); 2095331Samw } 2105331Samw 2115331Samw while (smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) { 2125331Samw to.tv_sec = SMB_NETLOGON_TIMEOUT; 2135331Samw to.tv_nsec = 0; 2145331Samw rc = cond_reltimedwait(&smb_netlogon_info.snli_locate_cv, 2155331Samw &smb_netlogon_info.snli_locate_mtx, &to); 2165331Samw 2175331Samw if (rc == ETIME) 2185331Samw break; 2195331Samw } 2205331Samw 2215331Samw (void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx); 2225331Samw 2235331Samw return (smb_ntdomain_is_valid(0)); 2245331Samw } 2255331Samw 2265331Samw /* 2275331Samw * smb_netlogon_init 2285331Samw * 2295331Samw * Initialization of the DC browser and LSA monitor threads. 2305331Samw * Returns 0 on success, an error number if thread creation fails. 2315331Samw */ 2325331Samw int 2335331Samw smb_netlogon_init(void) 2345331Samw { 2355331Samw pthread_attr_t tattr; 2365331Samw int rc; 2375331Samw 2385331Samw (void) pthread_attr_init(&tattr); 2395331Samw (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); 2405331Samw rc = pthread_create(&lsa_monitor_thr, &tattr, 2415331Samw smb_netlogon_lsa_monitor, 0); 2425331Samw if (rc != 0) 2435331Samw goto nli_exit; 2445331Samw rc = pthread_create(&dc_browser_thr, &tattr, 2455331Samw smb_netlogon_dc_browser, 0); 2465331Samw if (rc != 0) { 2475331Samw (void) pthread_cancel(lsa_monitor_thr); 2485331Samw (void) pthread_join(lsa_monitor_thr, NULL); 2495331Samw } 2505331Samw 2515331Samw nli_exit: 2525331Samw (void) pthread_attr_destroy(&tattr); 2535331Samw return (rc); 2545331Samw } 2555331Samw 2565331Samw /* 2575331Samw * smb_netlogon_dc_browser 2585331Samw * 2595331Samw * This is the DC browser thread: it gets woken up whenever someone 2605331Samw * wants to locate a domain controller. 2615331Samw * 2625331Samw * With the introduction of Windows 2000, NetBIOS is no longer a 2635331Samw * requirement for NT domains. If NetBIOS has been disabled on the 2645331Samw * network there will be no browsers and we won't get any response 2655331Samw * to netlogon requests. So we try to find a DC controller via ADS 2665331Samw * first. If ADS is disabled or the DNS query fails, we drop back 2675331Samw * to the netlogon protocol. 2685331Samw * 2695331Samw * This function will block for up to 30 seconds waiting for the PDC 2705331Samw * to be discovered. Sometime it would be good to change this to 2715331Samw * smb_locate_pdc and allow the caller to specify whether or not he 2725331Samw * wants to wait for a response. 2735331Samw * 2745331Samw */ 2755331Samw /*ARGSUSED*/ 2765331Samw static void * 2775331Samw smb_netlogon_dc_browser(void *arg) 2785331Samw { 2795331Samw boolean_t rc; 2805331Samw char resource_domain[SMB_PI_MAX_DOMAIN]; 2815331Samw int net, smb_nc_cnt; 2825331Samw int protocol; 2835331Samw 2845331Samw for (;;) { 2855331Samw (void) mutex_lock(&smb_netlogon_info.snli_locate_mtx); 2865331Samw 2875331Samw while ((smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) == 2885331Samw 0) { 2895331Samw (void) cond_wait(&smb_netlogon_info.snli_locate_cv, 2905331Samw &smb_netlogon_info.snli_locate_mtx); 2915331Samw } 2925331Samw 2935331Samw (void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx); 2945331Samw 2955331Samw (void) strlcpy(resource_domain, smb_netlogon_info.snli_domain, 2965331Samw SMB_PI_MAX_DOMAIN); 2975331Samw 2985331Samw smb_setdomaininfo(NULL, NULL, 0); 2995331Samw if (msdcs_lookup_ads() == 0) { 3005331Samw if (smb_is_domain_member()) 3015331Samw protocol = NETLOGON_PROTO_SAMLOGON; 3025331Samw else 3035331Samw protocol = NETLOGON_PROTO_NETLOGON; 3045331Samw 3055331Samw smb_nc_cnt = smb_nic_get_num(); 3065331Samw for (net = 0; net < smb_nc_cnt; net++) { 3075331Samw smb_netlogon_request(net, protocol, 3085331Samw resource_domain); 3095331Samw } 3105331Samw } 3115331Samw 3125331Samw rc = smb_ntdomain_is_valid(SMB_NETLOGON_TIMEOUT); 3135331Samw 3145331Samw (void) mutex_lock(&smb_netlogon_info.snli_locate_mtx); 3155331Samw smb_netlogon_info.snli_flags &= ~SMB_NETLF_LOCATE_DC; 3165331Samw (void) cond_broadcast(&smb_netlogon_info.snli_locate_cv); 3175331Samw (void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx); 3185331Samw 3195331Samw if (rc != B_TRUE) { 3205331Samw /* 3215331Samw * Notify the LSA monitor to update the 3225331Samw * primary and trusted domain information. 3235331Samw */ 3245331Samw (void) mutex_lock(&smb_netlogon_info.snli_query_mtx); 3255331Samw smb_netlogon_info.snli_flags |= SMB_NETLF_LSA_QUERY; 3265331Samw (void) cond_broadcast(&smb_netlogon_info.snli_query_cv); 3275331Samw (void) mutex_unlock(&smb_netlogon_info.snli_query_mtx); 3285331Samw } 3295331Samw } 3305331Samw 3315331Samw /*NOTREACHED*/ 3325331Samw return (NULL); 3335331Samw } 3345331Samw 3355331Samw /* 3365331Samw * smb_netlogon_lsa_monitor 3375331Samw * 3385331Samw * This monitor should run as a separate thread. It waits on a condition 3395331Samw * variable until someone indicates that the LSA domain information needs 3405331Samw * to be refreshed. It then queries the DC for the NT domain information: 3415331Samw * primary, account and trusted domains. The condition variable should be 3425331Samw * signaled whenever a DC is selected. 3435331Samw * 3445331Samw * Note that the LSA query calls require the DC information and this task 3455331Samw * may end up blocked on the DC location protocol, which is why this 3465331Samw * monitor is run as a separate thread. This should only happen if the DC 3475331Samw * goes down immediately after we located it. 3485331Samw */ 3495331Samw /*ARGSUSED*/ 3505331Samw static void * 3515331Samw smb_netlogon_lsa_monitor(void *arg) 3525331Samw { 3535331Samw uint32_t status; 3545331Samw 3555331Samw for (;;) { 3565331Samw (void) mutex_lock(&smb_netlogon_info.snli_query_mtx); 3575331Samw 3585331Samw while ((smb_netlogon_info.snli_flags & SMB_NETLF_LSA_QUERY) == 3595331Samw 0) { 3605331Samw (void) cond_wait(&smb_netlogon_info.snli_query_cv, 3615331Samw &smb_netlogon_info.snli_query_mtx); 3625331Samw } 3635331Samw 3645331Samw smb_netlogon_info.snli_flags &= ~SMB_NETLF_LSA_QUERY; 3655331Samw (void) mutex_unlock(&smb_netlogon_info.snli_query_mtx); 3665331Samw 3675331Samw /* 3685331Samw * Skip the LSA query if Authenticated IPC is supported 3695331Samw * and the credential is not yet set. 3705331Samw */ 3715331Samw if (smbrdr_ipc_skip_lsa_query() == 0) { 3725331Samw status = lsa_query_primary_domain_info(); 3735331Samw if (status == NT_STATUS_SUCCESS) { 3745331Samw if (lsa_query_account_domain_info() 3755331Samw != NT_STATUS_SUCCESS) { 3765331Samw syslog(LOG_DEBUG, 3775331Samw "NetlogonLSAMonitor: query " 3785331Samw "account info failed"); 3795331Samw } 3805331Samw if (lsa_enum_trusted_domains() 3815331Samw != NT_STATUS_SUCCESS) { 3825331Samw syslog(LOG_DEBUG, 3835331Samw "NetlogonLSAMonitor: enum " 3845331Samw "trusted domain failed"); 3855331Samw } 3865331Samw } else { 3875331Samw syslog(LOG_DEBUG, 3885331Samw "NetlogonLSAMonitor: update failed"); 3895331Samw } 3905331Samw } 3915331Samw } 3925331Samw 3935331Samw /*NOTREACHED*/ 3945331Samw return (NULL); 3955331Samw } 396