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