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