1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 25 * Copyright 2023 RackTop Systems, Inc. 26 */ 27 28 #include <syslog.h> 29 #include <synch.h> 30 #include <pthread.h> 31 #include <unistd.h> 32 #include <string.h> 33 #include <strings.h> 34 #include <errno.h> 35 #include <netinet/in.h> 36 #include <netinet/tcp.h> 37 38 #include <smbsrv/libsmb.h> 39 #include <smbsrv/libsmbns.h> 40 #include <smbsrv/libmlsvc.h> 41 #include <smbsrv/smbinfo.h> 42 #include "smbd.h" 43 44 #define SMBD_DC_MONITOR_ATTEMPTS 3 45 #define SMBD_DC_MONITOR_RETRY_INTERVAL 3 /* seconds */ 46 #define SMBD_DC_MONITOR_INTERVAL 60 /* seconds */ 47 48 extern smbd_t smbd; 49 50 static mutex_t smbd_dc_mutex; 51 static cond_t smbd_dc_cv; 52 53 static void *smbd_dc_monitor(void *); 54 static void smbd_dc_update(void); 55 static int smbd_dc_check(smb_domainex_t *); 56 /* Todo: static boolean_t smbd_set_netlogon_cred(void); */ 57 static void smbd_join_workgroup(smb_joininfo_t *, smb_joinres_t *); 58 static void smbd_join_domain(smb_joininfo_t *, smb_joinres_t *); 59 60 /* 61 * Launch the DC discovery and monitor thread. 62 */ 63 int 64 smbd_dc_monitor_init(void) 65 { 66 pthread_attr_t attr; 67 int rc; 68 69 smb_ads_init(); 70 71 if (smbd.s_secmode != SMB_SECMODE_DOMAIN) 72 return (0); 73 74 (void) pthread_attr_init(&attr); 75 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 76 rc = pthread_create(&smbd.s_dc_monitor_tid, &attr, smbd_dc_monitor, 77 NULL); 78 (void) pthread_attr_destroy(&attr); 79 return (rc); 80 } 81 82 /* 83 * Refresh the DC monitor. Called from SMF refresh and when idmap 84 * finds a different DC from what we were using previously. 85 * Update our domain (and current DC) information. 86 */ 87 void 88 smbd_dc_monitor_refresh(void) 89 { 90 91 syslog(LOG_INFO, "smbd_dc_monitor_refresh"); 92 93 smb_ddiscover_refresh(); 94 95 (void) mutex_lock(&smbd_dc_mutex); 96 97 smbd.s_dc_changed = B_TRUE; 98 (void) cond_signal(&smbd_dc_cv); 99 100 (void) mutex_unlock(&smbd_dc_mutex); 101 } 102 103 /*ARGSUSED*/ 104 static void * 105 smbd_dc_monitor(void *arg) 106 { 107 smb_domainex_t di; 108 boolean_t ds_not_responding; 109 boolean_t ds_cfg_changed; 110 timestruc_t delay; 111 int i; 112 113 /* Wait for smb_dclocator_init() to complete. */ 114 smbd_online_wait("smbd_dc_monitor"); 115 smbd_dc_update(); 116 117 while (smbd_online()) { 118 ds_not_responding = B_FALSE; 119 ds_cfg_changed = B_FALSE; 120 delay.tv_sec = SMBD_DC_MONITOR_INTERVAL; 121 delay.tv_nsec = 0; 122 123 (void) mutex_lock(&smbd_dc_mutex); 124 (void) cond_reltimedwait(&smbd_dc_cv, &smbd_dc_mutex, &delay); 125 126 if (smbd.s_dc_changed) { 127 smbd.s_dc_changed = B_FALSE; 128 ds_cfg_changed = B_TRUE; 129 /* NB: smb_ddiscover_refresh was called. */ 130 } 131 132 (void) mutex_unlock(&smbd_dc_mutex); 133 134 if (ds_cfg_changed) { 135 syslog(LOG_DEBUG, "smbd_dc_monitor: config changed"); 136 goto rediscover; 137 } 138 139 if (!smb_domain_getinfo(&di)) { 140 syslog(LOG_DEBUG, "smbd_dc_monitor: no domain info"); 141 goto rediscover; 142 } 143 144 if (di.d_dci.dc_name[0] == '\0') { 145 syslog(LOG_DEBUG, "smbd_dc_monitor: no DC name"); 146 goto rediscover; 147 } 148 149 for (i = 0; i < SMBD_DC_MONITOR_ATTEMPTS; ++i) { 150 if (smbd_dc_check(&di) == 0) { 151 ds_not_responding = B_FALSE; 152 break; 153 } 154 155 ds_not_responding = B_TRUE; 156 (void) sleep(SMBD_DC_MONITOR_RETRY_INTERVAL); 157 } 158 159 if (ds_not_responding) { 160 syslog(LOG_NOTICE, 161 "smbd_dc_monitor: DC not responding: %s", 162 di.d_dci.dc_name); 163 smb_ddiscover_bad_dc(di.d_dci.dc_name); 164 } 165 166 if (ds_not_responding || ds_cfg_changed) { 167 rediscover: 168 /* 169 * An smb_ads_refresh will be done by the 170 * smb_ddiscover_service when necessary. 171 * Note: smbd_dc_monitor_refresh was already 172 * called if appropriate. 173 */ 174 smbd_dc_update(); 175 } 176 } 177 178 smbd.s_dc_monitor_tid = 0; 179 return (NULL); 180 } 181 182 /* 183 * Simply attempt a connection to the DC. 184 */ 185 static int 186 smbd_dc_check(smb_domainex_t *di) 187 { 188 struct sockaddr_storage sa; 189 int salen = 0; 190 int sock = -1; 191 int tmo = 5 * 1000; /* 5 sec. */ 192 int rc; 193 194 bzero(&sa, sizeof (sa)); 195 switch (di->d_dci.dc_addr.a_family) { 196 case AF_INET: { 197 struct sockaddr_in *sin = (void *)&sa; 198 sin->sin_family = AF_INET; 199 sin->sin_port = htons(IPPORT_SMB); 200 sin->sin_addr.s_addr = di->d_dci.dc_addr.a_ipv4; 201 salen = sizeof (*sin); 202 break; 203 } 204 case AF_INET6: { 205 struct sockaddr_in6 *sin6 = (void *)&sa; 206 sin6->sin6_family = AF_INET6; 207 sin6->sin6_port = htons(IPPORT_SMB); 208 (void) memcpy(&sin6->sin6_addr, 209 &di->d_dci.dc_addr.a_ipv6, 210 sizeof (in6_addr_t)); 211 salen = sizeof (*sin6); 212 break; 213 } 214 default: 215 return (-1); 216 } 217 218 sock = socket(di->d_dci.dc_addr.a_family, SOCK_STREAM, 0); 219 if (sock < 0) 220 return (errno); 221 (void) setsockopt(sock, IPPROTO_TCP, 222 TCP_CONN_ABORT_THRESHOLD, &tmo, sizeof (tmo)); 223 224 rc = connect(sock, (const struct sockaddr *)&sa, salen); 225 if (rc < 0) 226 rc = errno; 227 228 (void) close(sock); 229 return (rc); 230 } 231 232 /* 233 * Locate a domain controller in the current resource domain and Update 234 * the Netlogon credential chain. 235 * 236 * The domain configuration will be updated upon successful DC discovery. 237 */ 238 static void 239 smbd_dc_update(void) 240 { 241 char domain[MAXHOSTNAMELEN]; 242 smb_domainex_t info; 243 smb_domain_t *di; 244 DWORD status; 245 246 /* 247 * Don't want this active until we're a domain member. 248 */ 249 if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) 250 return; 251 252 if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) 253 return; 254 255 if (domain[0] == '\0') { 256 syslog(LOG_NOTICE, 257 "smbd_dc_update: no domain name set"); 258 return; 259 } 260 261 if (!smb_locate_dc(domain, &info)) { 262 syslog(LOG_NOTICE, 263 "smbd_dc_update: %s: locate failed", domain); 264 return; 265 } 266 267 di = &info.d_primary; 268 syslog(LOG_INFO, 269 "smbd_dc_update: %s: located %s", domain, info.d_dci.dc_name); 270 271 status = mlsvc_netlogon(info.d_dci.dc_name, di->di_nbname); 272 if (status != NT_STATUS_SUCCESS) { 273 syslog(LOG_NOTICE, 274 "failed to establish NETLOGON credential chain"); 275 syslog(LOG_NOTICE, " with server %s for domain %s (%s)", 276 info.d_dci.dc_name, domain, 277 xlate_nt_status(status)); 278 } 279 } 280 281 /* 282 * smbd_join 283 * 284 * Joins the specified domain/workgroup. 285 * 286 * If the security mode or domain name is being changed, 287 * the caller must restart the service. 288 */ 289 void 290 smbd_join(smb_joininfo_t *info, smb_joinres_t *res) 291 { 292 dssetup_clear_domain_info(); 293 if (info->mode == SMB_SECMODE_WORKGRP) 294 smbd_join_workgroup(info, res); 295 else 296 smbd_join_domain(info, res); 297 } 298 299 static void 300 smbd_join_workgroup(smb_joininfo_t *info, smb_joinres_t *res) 301 { 302 char nb_domain[SMB_PI_MAX_DOMAIN]; 303 304 syslog(LOG_DEBUG, "smbd: join workgroup: %s", info->domain_name); 305 306 (void) smb_config_getstr(SMB_CI_DOMAIN_NAME, nb_domain, 307 sizeof (nb_domain)); 308 309 smbd_set_secmode(SMB_SECMODE_WORKGRP); 310 smb_config_setdomaininfo(info->domain_name, "", "", "", ""); 311 (void) smb_config_set_idmap_domain(""); 312 (void) smb_config_refresh_idmap(); 313 314 if (strcasecmp(nb_domain, info->domain_name)) 315 smb_browser_reconfig(); 316 317 res->status = NT_STATUS_SUCCESS; 318 } 319 320 static void 321 smbd_join_domain(smb_joininfo_t *info, smb_joinres_t *res) 322 { 323 324 syslog(LOG_DEBUG, "smbd: join domain: %s", info->domain_name); 325 326 /* info->domain_name could either be NetBIOS domain name or FQDN */ 327 mlsvc_join(info, res); 328 if (res->status == 0) { 329 smbd_set_secmode(SMB_SECMODE_DOMAIN); 330 } else { 331 syslog(LOG_ERR, "smbd: failed joining %s (%s)", 332 info->domain_name, xlate_nt_status(res->status)); 333 } 334 } 335