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*5772Sas200622 * Copyright 2008 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]; 112*5772Sas200622 char nbt_domain[SMB_PI_MAX_DOMAIN]; 113*5772Sas200622 char fqdn[MAXHOSTNAMELEN]; 1145331Samw 1155331Samw if (info->mode == SMB_SECMODE_WORKGRP) { 116*5772Sas200622 if (smb_config_get_secmode() == SMB_SECMODE_DOMAIN) { 117*5772Sas200622 if (ads_domain_change_cleanup("") != 0) { 118*5772Sas200622 syslog(LOG_ERR, "smbd: unable to remove the" 119*5772Sas200622 " old keys from the Kerberos keytab. " 120*5772Sas200622 "Please remove the old keys for your " 121*5772Sas200622 "host principal."); 122*5772Sas200622 } 123*5772Sas200622 } 1245331Samw (void) smb_config_set_secmode(info->mode); 125*5772Sas200622 (void) smb_config_setstr(SMB_CI_DOMAIN_NAME, info->domain_name); 1265331Samw return (NT_STATUS_SUCCESS); 1275331Samw } 1285331Samw 1295331Samw /* 1305331Samw * Ensure that any previous membership of this domain has 1315331Samw * been cleared from the environment before we start. This 1325331Samw * will ensure that we don't attempt a NETLOGON_SAMLOGON 1335331Samw * when attempting to find the PDC. 1345331Samw */ 135*5772Sas200622 (void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_FALSE); 136*5772Sas200622 137*5772Sas200622 (void) strlcpy(plain_user, info->domain_username, sizeof (plain_user)); 138*5772Sas200622 (void) strlcpy(plain_passwd, info->domain_passwd, 139*5772Sas200622 sizeof (plain_passwd)); 140*5772Sas200622 (void) smb_resolve_netbiosname(info->domain_name, nbt_domain, 141*5772Sas200622 sizeof (nbt_domain)); 1425331Samw 143*5772Sas200622 if (smb_resolve_fqdn(info->domain_name, fqdn, sizeof (fqdn)) != 1) { 144*5772Sas200622 syslog(LOG_ERR, "smbd: fully-qualified domain name is unknown"); 145*5772Sas200622 return (NT_STATUS_INVALID_PARAMETER); 146*5772Sas200622 } 147*5772Sas200622 148*5772Sas200622 if (ads_domain_change_cleanup(fqdn)) { 149*5772Sas200622 syslog(LOG_ERR, "smbd: unable to remove the old keys from the" 150*5772Sas200622 " Kerberos keytab. Please remove the old keys for your " 151*5772Sas200622 "host principal."); 152*5772Sas200622 return (NT_STATUS_INTERNAL_ERROR); 153*5772Sas200622 } 1545331Samw 1555331Samw if (smb_auth_ntlm_hash(plain_passwd, passwd_hash) != SMBAUTH_SUCCESS) { 1565331Samw status = NT_STATUS_INTERNAL_ERROR; 1575331Samw syslog(LOG_ERR, "smbd: could not compute ntlm hash for '%s'", 1585331Samw plain_user); 1595331Samw return (status); 1605331Samw } 1615331Samw 1625331Samw smbrdr_ipc_set(plain_user, passwd_hash); 1635331Samw 164*5772Sas200622 if (locate_resource_pdc(nbt_domain)) { 1655331Samw if ((pi = smb_getdomaininfo(0)) == 0) { 1665331Samw status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 1675331Samw syslog(LOG_ERR, "smbd: could not get domain controller" 1685331Samw "information for '%s'", info->domain_name); 1695331Samw return (status); 1705331Samw } 1715331Samw 1725331Samw /* 1735331Samw * Temporary delay before creating 1745331Samw * the workstation trust account. 1755331Samw */ 1765331Samw (void) sleep(2); 1775521Sas200622 status = mlsvc_join(pi->server, pi->domain, 1785331Samw plain_user, plain_passwd); 1795331Samw 1805331Samw if (status == NT_STATUS_SUCCESS) { 1815331Samw (void) smb_config_set_secmode(SMB_SECMODE_DOMAIN); 182*5772Sas200622 (void) smb_config_setstr(SMB_CI_DOMAIN_NAME, 1835331Samw info->domain_name); 1845331Samw smbrdr_ipc_commit(); 1855331Samw return (status); 1865331Samw } 1875331Samw 1885331Samw smbrdr_ipc_rollback(); 1895331Samw syslog(LOG_ERR, "smbd: failed joining %s (%s)", 1905331Samw info->domain_name, xlate_nt_status(status)); 1915331Samw return (status); 1925331Samw } 1935331Samw 1945331Samw smbrdr_ipc_rollback(); 1955331Samw syslog(LOG_ERR, "smbd: failed locating domain controller for %s", 1965331Samw info->domain_name); 1975331Samw return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND); 1985331Samw } 1995331Samw 2005331Samw /* 2015331Samw * locate_resource_pdc 2025331Samw * 2035331Samw * This is the entry point for discovering a domain controller for the 2045331Samw * specified domain. The caller may block here for around 30 seconds if 2055331Samw * the system has to go to the network and find a domain controller. 2065331Samw * Sometime it would be good to change this to smb_locate_pdc and allow 2075331Samw * the caller to specify whether or not he wants to wait for a response. 2085331Samw * 2095331Samw * The actual work of discovering a DC is handled by other threads. 2105331Samw * All we do here is signal the request and wait for a DC or a timeout. 2115331Samw * 2125331Samw * Returns B_TRUE if a domain controller is available. 2135331Samw */ 2145331Samw boolean_t 2155331Samw locate_resource_pdc(char *domain) 2165331Samw { 2175331Samw int rc; 2185331Samw timestruc_t to; 2195331Samw 2205331Samw if (domain == NULL || *domain == '\0') 2215331Samw return (B_FALSE); 2225331Samw 2235331Samw (void) mutex_lock(&smb_netlogon_info.snli_locate_mtx); 2245331Samw 2255331Samw if ((smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) == 0) { 2265331Samw smb_netlogon_info.snli_flags |= SMB_NETLF_LOCATE_DC; 2275331Samw (void) strlcpy(smb_netlogon_info.snli_domain, domain, 2285331Samw SMB_PI_MAX_DOMAIN); 2295331Samw (void) cond_broadcast(&smb_netlogon_info.snli_locate_cv); 2305331Samw } 2315331Samw 2325331Samw while (smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) { 2335331Samw to.tv_sec = SMB_NETLOGON_TIMEOUT; 2345331Samw to.tv_nsec = 0; 2355331Samw rc = cond_reltimedwait(&smb_netlogon_info.snli_locate_cv, 2365331Samw &smb_netlogon_info.snli_locate_mtx, &to); 2375331Samw 2385331Samw if (rc == ETIME) 2395331Samw break; 2405331Samw } 2415331Samw 2425331Samw (void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx); 2435331Samw 2445331Samw return (smb_ntdomain_is_valid(0)); 2455331Samw } 2465331Samw 2475331Samw /* 2485331Samw * smb_netlogon_init 2495331Samw * 2505331Samw * Initialization of the DC browser and LSA monitor threads. 2515331Samw * Returns 0 on success, an error number if thread creation fails. 2525331Samw */ 2535331Samw int 2545331Samw smb_netlogon_init(void) 2555331Samw { 2565331Samw pthread_attr_t tattr; 2575331Samw int rc; 2585331Samw 2595331Samw (void) pthread_attr_init(&tattr); 2605331Samw (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); 2615331Samw rc = pthread_create(&lsa_monitor_thr, &tattr, 2625331Samw smb_netlogon_lsa_monitor, 0); 2635331Samw if (rc != 0) 2645331Samw goto nli_exit; 2655331Samw rc = pthread_create(&dc_browser_thr, &tattr, 2665331Samw smb_netlogon_dc_browser, 0); 2675331Samw if (rc != 0) { 2685331Samw (void) pthread_cancel(lsa_monitor_thr); 2695331Samw (void) pthread_join(lsa_monitor_thr, NULL); 2705331Samw } 2715331Samw 2725331Samw nli_exit: 2735331Samw (void) pthread_attr_destroy(&tattr); 2745331Samw return (rc); 2755331Samw } 2765331Samw 2775331Samw /* 2785331Samw * smb_netlogon_dc_browser 2795331Samw * 2805331Samw * This is the DC browser thread: it gets woken up whenever someone 2815331Samw * wants to locate a domain controller. 2825331Samw * 2835331Samw * With the introduction of Windows 2000, NetBIOS is no longer a 2845331Samw * requirement for NT domains. If NetBIOS has been disabled on the 2855331Samw * network there will be no browsers and we won't get any response 2865331Samw * to netlogon requests. So we try to find a DC controller via ADS 2875331Samw * first. If ADS is disabled or the DNS query fails, we drop back 2885331Samw * to the netlogon protocol. 2895331Samw * 2905331Samw * This function will block for up to 30 seconds waiting for the PDC 2915331Samw * to be discovered. Sometime it would be good to change this to 2925331Samw * smb_locate_pdc and allow the caller to specify whether or not he 2935331Samw * wants to wait for a response. 2945331Samw * 2955331Samw */ 2965331Samw /*ARGSUSED*/ 2975331Samw static void * 2985331Samw smb_netlogon_dc_browser(void *arg) 2995331Samw { 3005331Samw boolean_t rc; 3015331Samw char resource_domain[SMB_PI_MAX_DOMAIN]; 3025331Samw int net, smb_nc_cnt; 3035331Samw int protocol; 3045331Samw 3055331Samw for (;;) { 3065331Samw (void) mutex_lock(&smb_netlogon_info.snli_locate_mtx); 3075331Samw 3085331Samw while ((smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) == 3095331Samw 0) { 3105331Samw (void) cond_wait(&smb_netlogon_info.snli_locate_cv, 3115331Samw &smb_netlogon_info.snli_locate_mtx); 3125331Samw } 3135331Samw 3145331Samw (void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx); 3155331Samw 3165331Samw (void) strlcpy(resource_domain, smb_netlogon_info.snli_domain, 3175331Samw SMB_PI_MAX_DOMAIN); 3185331Samw 3195331Samw smb_setdomaininfo(NULL, NULL, 0); 320*5772Sas200622 if (msdcs_lookup_ads(resource_domain) == 0) { 321*5772Sas200622 if (smb_config_getbool(SMB_CI_DOMAIN_MEMB)) 3225331Samw protocol = NETLOGON_PROTO_SAMLOGON; 3235331Samw else 3245331Samw protocol = NETLOGON_PROTO_NETLOGON; 3255331Samw 3265331Samw smb_nc_cnt = smb_nic_get_num(); 3275331Samw for (net = 0; net < smb_nc_cnt; net++) { 3285331Samw smb_netlogon_request(net, protocol, 3295331Samw resource_domain); 3305331Samw } 3315331Samw } 3325331Samw 3335331Samw rc = smb_ntdomain_is_valid(SMB_NETLOGON_TIMEOUT); 3345331Samw 3355331Samw (void) mutex_lock(&smb_netlogon_info.snli_locate_mtx); 3365331Samw smb_netlogon_info.snli_flags &= ~SMB_NETLF_LOCATE_DC; 3375331Samw (void) cond_broadcast(&smb_netlogon_info.snli_locate_cv); 3385331Samw (void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx); 3395331Samw 3405331Samw if (rc != B_TRUE) { 3415331Samw /* 3425331Samw * Notify the LSA monitor to update the 3435331Samw * primary and trusted domain information. 3445331Samw */ 3455331Samw (void) mutex_lock(&smb_netlogon_info.snli_query_mtx); 3465331Samw smb_netlogon_info.snli_flags |= SMB_NETLF_LSA_QUERY; 3475331Samw (void) cond_broadcast(&smb_netlogon_info.snli_query_cv); 3485331Samw (void) mutex_unlock(&smb_netlogon_info.snli_query_mtx); 3495331Samw } 3505331Samw } 3515331Samw 3525331Samw /*NOTREACHED*/ 3535331Samw return (NULL); 3545331Samw } 3555331Samw 3565331Samw /* 3575331Samw * smb_netlogon_lsa_monitor 3585331Samw * 3595331Samw * This monitor should run as a separate thread. It waits on a condition 3605331Samw * variable until someone indicates that the LSA domain information needs 3615331Samw * to be refreshed. It then queries the DC for the NT domain information: 3625331Samw * primary, account and trusted domains. The condition variable should be 3635331Samw * signaled whenever a DC is selected. 3645331Samw * 3655331Samw * Note that the LSA query calls require the DC information and this task 3665331Samw * may end up blocked on the DC location protocol, which is why this 3675331Samw * monitor is run as a separate thread. This should only happen if the DC 3685331Samw * goes down immediately after we located it. 3695331Samw */ 3705331Samw /*ARGSUSED*/ 3715331Samw static void * 3725331Samw smb_netlogon_lsa_monitor(void *arg) 3735331Samw { 3745331Samw uint32_t status; 3755331Samw 3765331Samw for (;;) { 3775331Samw (void) mutex_lock(&smb_netlogon_info.snli_query_mtx); 3785331Samw 3795331Samw while ((smb_netlogon_info.snli_flags & SMB_NETLF_LSA_QUERY) == 3805331Samw 0) { 3815331Samw (void) cond_wait(&smb_netlogon_info.snli_query_cv, 3825331Samw &smb_netlogon_info.snli_query_mtx); 3835331Samw } 3845331Samw 3855331Samw smb_netlogon_info.snli_flags &= ~SMB_NETLF_LSA_QUERY; 3865331Samw (void) mutex_unlock(&smb_netlogon_info.snli_query_mtx); 3875331Samw 3885331Samw /* 3895331Samw * Skip the LSA query if Authenticated IPC is supported 3905331Samw * and the credential is not yet set. 3915331Samw */ 3925331Samw if (smbrdr_ipc_skip_lsa_query() == 0) { 3935331Samw status = lsa_query_primary_domain_info(); 3945331Samw if (status == NT_STATUS_SUCCESS) { 3955331Samw if (lsa_query_account_domain_info() 3965331Samw != NT_STATUS_SUCCESS) { 3975331Samw syslog(LOG_DEBUG, 3985331Samw "NetlogonLSAMonitor: query " 3995331Samw "account info failed"); 4005331Samw } 4015331Samw if (lsa_enum_trusted_domains() 4025331Samw != NT_STATUS_SUCCESS) { 4035331Samw syslog(LOG_DEBUG, 4045331Samw "NetlogonLSAMonitor: enum " 4055331Samw "trusted domain failed"); 4065331Samw } 4075331Samw } else { 4085331Samw syslog(LOG_DEBUG, 4095331Samw "NetlogonLSAMonitor: update failed"); 4105331Samw } 4115331Samw } 4125331Samw } 4135331Samw 4145331Samw /*NOTREACHED*/ 4155331Samw return (NULL); 4165331Samw } 417