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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 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 <sys/errno.h> 35 36 #include <smbsrv/libsmb.h> 37 #include <smbsrv/libsmbrdr.h> 38 #include <smbsrv/libsmbns.h> 39 #include <smbsrv/libmlsvc.h> 40 41 #include <smbsrv/smbinfo.h> 42 #include <smbsrv/ntstatus.h> 43 #include <smbsrv/lsalib.h> 44 45 /* 46 * Local protocol flags used to indicate which version of the 47 * netlogon protocol to use when attempting to find the PDC. 48 */ 49 #define NETLOGON_PROTO_NETLOGON 0x01 50 #define NETLOGON_PROTO_SAMLOGON 0x02 51 52 /* 53 * Maximum time to wait for a domain controller (30 seconds). 54 */ 55 #define SMB_NETLOGON_TIMEOUT 30 56 57 /* 58 * Flags used in conjunction with the location and query condition 59 * variables. 60 */ 61 #define SMB_NETLF_LOCATE_DC 0x00000001 62 #define SMB_NETLF_LSA_QUERY 0x00000002 63 64 typedef struct smb_netlogon_info { 65 char snli_domain[SMB_PI_MAX_DOMAIN]; 66 unsigned snli_flags; 67 mutex_t snli_locate_mtx; 68 cond_t snli_locate_cv; 69 mutex_t snli_query_mtx; 70 cond_t snli_query_cv; 71 uint32_t snli_status; 72 } smb_netlogon_info_t; 73 74 static smb_netlogon_info_t smb_netlogon_info; 75 76 static pthread_t lsa_monitor_thr; 77 static pthread_t dc_browser_thr; 78 79 static void *smb_netlogon_lsa_monitor(void *arg); 80 static void *smb_netlogon_dc_browser(void *arg); 81 82 /* 83 * Inline convenience function to find out if the domain information is 84 * valid. The caller can decide whether or not to wait. 85 */ 86 static boolean_t 87 smb_ntdomain_is_valid(uint32_t timeout) 88 { 89 smb_ntdomain_t *info; 90 91 if ((info = smb_getdomaininfo(timeout)) != 0) { 92 if (info->ipaddr != 0) 93 return (B_TRUE); 94 } 95 96 return (B_FALSE); 97 } 98 99 /* 100 * smbd_join 101 * 102 * Joins the specified domain/workgroup 103 */ 104 uint32_t 105 smbd_join(smb_joininfo_t *info) 106 { 107 smb_ntdomain_t *pi; 108 uint32_t status; 109 unsigned char passwd_hash[SMBAUTH_HASH_SZ]; 110 char plain_passwd[PASS_LEN + 1]; 111 char plain_user[PASS_LEN + 1]; 112 113 if (info->mode == SMB_SECMODE_WORKGRP) { 114 smb_config_wrlock(); 115 (void) smb_config_set_secmode(info->mode); 116 (void) smb_config_set(SMB_CI_DOMAIN_NAME, info->domain_name); 117 smb_config_unlock(); 118 return (NT_STATUS_SUCCESS); 119 } 120 121 /* 122 * Ensure that any previous membership of this domain has 123 * been cleared from the environment before we start. This 124 * will ensure that we don't attempt a NETLOGON_SAMLOGON 125 * when attempting to find the PDC. 126 */ 127 smb_set_domain_member(0); 128 129 (void) strcpy(plain_user, info->domain_username); 130 (void) strcpy(plain_passwd, info->domain_passwd); 131 132 if (smb_auth_ntlm_hash(plain_passwd, passwd_hash) != SMBAUTH_SUCCESS) { 133 status = NT_STATUS_INTERNAL_ERROR; 134 syslog(LOG_ERR, "smbd: could not compute ntlm hash for '%s'", 135 plain_user); 136 return (status); 137 } 138 139 smbrdr_ipc_set(plain_user, passwd_hash); 140 141 if (locate_resource_pdc(info->domain_name)) { 142 if ((pi = smb_getdomaininfo(0)) == 0) { 143 status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 144 syslog(LOG_ERR, "smbd: could not get domain controller" 145 "information for '%s'", info->domain_name); 146 return (status); 147 } 148 149 /* 150 * Temporary delay before creating 151 * the workstation trust account. 152 */ 153 (void) sleep(2); 154 status = mlsvc_join(pi->server, pi->domain, 155 plain_user, plain_passwd); 156 157 if (status == NT_STATUS_SUCCESS) { 158 smb_config_wrlock(); 159 (void) smb_config_set_secmode(SMB_SECMODE_DOMAIN); 160 (void) smb_config_set(SMB_CI_DOMAIN_NAME, 161 info->domain_name); 162 smb_config_unlock(); 163 smbrdr_ipc_commit(); 164 return (status); 165 } 166 167 smbrdr_ipc_rollback(); 168 syslog(LOG_ERR, "smbd: failed joining %s (%s)", 169 info->domain_name, xlate_nt_status(status)); 170 return (status); 171 } 172 173 smbrdr_ipc_rollback(); 174 syslog(LOG_ERR, "smbd: failed locating domain controller for %s", 175 info->domain_name); 176 return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND); 177 } 178 179 /* 180 * locate_resource_pdc 181 * 182 * This is the entry point for discovering a domain controller for the 183 * specified domain. The caller may block here for around 30 seconds if 184 * the system has to go to the network and find a domain controller. 185 * Sometime it would be good to change this to smb_locate_pdc and allow 186 * the caller to specify whether or not he wants to wait for a response. 187 * 188 * The actual work of discovering a DC is handled by other threads. 189 * All we do here is signal the request and wait for a DC or a timeout. 190 * 191 * Returns B_TRUE if a domain controller is available. 192 */ 193 boolean_t 194 locate_resource_pdc(char *domain) 195 { 196 int rc; 197 timestruc_t to; 198 199 if (domain == NULL || *domain == '\0') 200 return (B_FALSE); 201 202 (void) mutex_lock(&smb_netlogon_info.snli_locate_mtx); 203 204 if ((smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) == 0) { 205 smb_netlogon_info.snli_flags |= SMB_NETLF_LOCATE_DC; 206 (void) strlcpy(smb_netlogon_info.snli_domain, domain, 207 SMB_PI_MAX_DOMAIN); 208 (void) cond_broadcast(&smb_netlogon_info.snli_locate_cv); 209 } 210 211 while (smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) { 212 to.tv_sec = SMB_NETLOGON_TIMEOUT; 213 to.tv_nsec = 0; 214 rc = cond_reltimedwait(&smb_netlogon_info.snli_locate_cv, 215 &smb_netlogon_info.snli_locate_mtx, &to); 216 217 if (rc == ETIME) 218 break; 219 } 220 221 (void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx); 222 223 return (smb_ntdomain_is_valid(0)); 224 } 225 226 /* 227 * smb_netlogon_init 228 * 229 * Initialization of the DC browser and LSA monitor threads. 230 * Returns 0 on success, an error number if thread creation fails. 231 */ 232 int 233 smb_netlogon_init(void) 234 { 235 pthread_attr_t tattr; 236 int rc; 237 238 (void) pthread_attr_init(&tattr); 239 (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); 240 rc = pthread_create(&lsa_monitor_thr, &tattr, 241 smb_netlogon_lsa_monitor, 0); 242 if (rc != 0) 243 goto nli_exit; 244 rc = pthread_create(&dc_browser_thr, &tattr, 245 smb_netlogon_dc_browser, 0); 246 if (rc != 0) { 247 (void) pthread_cancel(lsa_monitor_thr); 248 (void) pthread_join(lsa_monitor_thr, NULL); 249 } 250 251 nli_exit: 252 (void) pthread_attr_destroy(&tattr); 253 return (rc); 254 } 255 256 /* 257 * smb_netlogon_dc_browser 258 * 259 * This is the DC browser thread: it gets woken up whenever someone 260 * wants to locate a domain controller. 261 * 262 * With the introduction of Windows 2000, NetBIOS is no longer a 263 * requirement for NT domains. If NetBIOS has been disabled on the 264 * network there will be no browsers and we won't get any response 265 * to netlogon requests. So we try to find a DC controller via ADS 266 * first. If ADS is disabled or the DNS query fails, we drop back 267 * to the netlogon protocol. 268 * 269 * This function will block for up to 30 seconds waiting for the PDC 270 * to be discovered. Sometime it would be good to change this to 271 * smb_locate_pdc and allow the caller to specify whether or not he 272 * wants to wait for a response. 273 * 274 */ 275 /*ARGSUSED*/ 276 static void * 277 smb_netlogon_dc_browser(void *arg) 278 { 279 boolean_t rc; 280 char resource_domain[SMB_PI_MAX_DOMAIN]; 281 int net, smb_nc_cnt; 282 int protocol; 283 284 for (;;) { 285 (void) mutex_lock(&smb_netlogon_info.snli_locate_mtx); 286 287 while ((smb_netlogon_info.snli_flags & SMB_NETLF_LOCATE_DC) == 288 0) { 289 (void) cond_wait(&smb_netlogon_info.snli_locate_cv, 290 &smb_netlogon_info.snli_locate_mtx); 291 } 292 293 (void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx); 294 295 (void) strlcpy(resource_domain, smb_netlogon_info.snli_domain, 296 SMB_PI_MAX_DOMAIN); 297 298 smb_setdomaininfo(NULL, NULL, 0); 299 if (msdcs_lookup_ads() == 0) { 300 if (smb_is_domain_member()) 301 protocol = NETLOGON_PROTO_SAMLOGON; 302 else 303 protocol = NETLOGON_PROTO_NETLOGON; 304 305 smb_nc_cnt = smb_nic_get_num(); 306 for (net = 0; net < smb_nc_cnt; net++) { 307 smb_netlogon_request(net, protocol, 308 resource_domain); 309 } 310 } 311 312 rc = smb_ntdomain_is_valid(SMB_NETLOGON_TIMEOUT); 313 314 (void) mutex_lock(&smb_netlogon_info.snli_locate_mtx); 315 smb_netlogon_info.snli_flags &= ~SMB_NETLF_LOCATE_DC; 316 (void) cond_broadcast(&smb_netlogon_info.snli_locate_cv); 317 (void) mutex_unlock(&smb_netlogon_info.snli_locate_mtx); 318 319 if (rc != B_TRUE) { 320 /* 321 * Notify the LSA monitor to update the 322 * primary and trusted domain information. 323 */ 324 (void) mutex_lock(&smb_netlogon_info.snli_query_mtx); 325 smb_netlogon_info.snli_flags |= SMB_NETLF_LSA_QUERY; 326 (void) cond_broadcast(&smb_netlogon_info.snli_query_cv); 327 (void) mutex_unlock(&smb_netlogon_info.snli_query_mtx); 328 } 329 } 330 331 /*NOTREACHED*/ 332 return (NULL); 333 } 334 335 /* 336 * smb_netlogon_lsa_monitor 337 * 338 * This monitor should run as a separate thread. It waits on a condition 339 * variable until someone indicates that the LSA domain information needs 340 * to be refreshed. It then queries the DC for the NT domain information: 341 * primary, account and trusted domains. The condition variable should be 342 * signaled whenever a DC is selected. 343 * 344 * Note that the LSA query calls require the DC information and this task 345 * may end up blocked on the DC location protocol, which is why this 346 * monitor is run as a separate thread. This should only happen if the DC 347 * goes down immediately after we located it. 348 */ 349 /*ARGSUSED*/ 350 static void * 351 smb_netlogon_lsa_monitor(void *arg) 352 { 353 uint32_t status; 354 355 for (;;) { 356 (void) mutex_lock(&smb_netlogon_info.snli_query_mtx); 357 358 while ((smb_netlogon_info.snli_flags & SMB_NETLF_LSA_QUERY) == 359 0) { 360 (void) cond_wait(&smb_netlogon_info.snli_query_cv, 361 &smb_netlogon_info.snli_query_mtx); 362 } 363 364 smb_netlogon_info.snli_flags &= ~SMB_NETLF_LSA_QUERY; 365 (void) mutex_unlock(&smb_netlogon_info.snli_query_mtx); 366 367 /* 368 * Skip the LSA query if Authenticated IPC is supported 369 * and the credential is not yet set. 370 */ 371 if (smbrdr_ipc_skip_lsa_query() == 0) { 372 status = lsa_query_primary_domain_info(); 373 if (status == NT_STATUS_SUCCESS) { 374 if (lsa_query_account_domain_info() 375 != NT_STATUS_SUCCESS) { 376 syslog(LOG_DEBUG, 377 "NetlogonLSAMonitor: query " 378 "account info failed"); 379 } 380 if (lsa_enum_trusted_domains() 381 != NT_STATUS_SUCCESS) { 382 syslog(LOG_DEBUG, 383 "NetlogonLSAMonitor: enum " 384 "trusted domain failed"); 385 } 386 } else { 387 syslog(LOG_DEBUG, 388 "NetlogonLSAMonitor: update failed"); 389 } 390 } 391 } 392 393 /*NOTREACHED*/ 394 return (NULL); 395 } 396