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