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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <syslog.h> 27 #include <synch.h> 28 #include <pthread.h> 29 #include <unistd.h> 30 #include <string.h> 31 #include <strings.h> 32 #include <sys/errno.h> 33 34 #include <smbsrv/libsmb.h> 35 #include <smbsrv/libsmbrdr.h> 36 #include <smbsrv/libsmbns.h> 37 #include <smbsrv/libmlsvc.h> 38 #include <smbsrv/smbinfo.h> 39 #include <smbsrv/ntstatus.h> 40 #include "smbd.h" 41 42 43 /* 44 * This is a short-lived thread that triggers the initial DC discovery 45 * at startup. 46 */ 47 static pthread_t smb_locate_dc_thr; 48 49 static void *smbd_locate_dc_thread(void *); 50 static int smbd_get_kpasswd_srv(char *, size_t); 51 static uint32_t smbd_join_workgroup(smb_joininfo_t *); 52 static uint32_t smbd_join_domain(smb_joininfo_t *); 53 54 /* 55 * smbd_join 56 * 57 * Joins the specified domain/workgroup. 58 * 59 * If the security mode or domain name is being changed, 60 * the caller must restart the service. 61 */ 62 uint32_t 63 smbd_join(smb_joininfo_t *info) 64 { 65 uint32_t status; 66 67 dssetup_clear_domain_info(); 68 if (info->mode == SMB_SECMODE_WORKGRP) 69 status = smbd_join_workgroup(info); 70 else 71 status = smbd_join_domain(info); 72 73 return (status); 74 } 75 76 /* 77 * smbd_set_netlogon_cred 78 * 79 * If the system is joined to an AD domain via kclient, SMB daemon will need 80 * to establish the NETLOGON credential chain. 81 * 82 * Since the kclient has updated the machine password stored in SMF 83 * repository, the cached ipc_info must be updated accordingly by calling 84 * smbrdr_ipc_commit. 85 * 86 * Due to potential replication delays in a multiple DC environment, the 87 * NETLOGON rpc request must be sent to the DC, to which the KPASSWD request 88 * is sent. If the DC discovered by the SMB daemon is different than the 89 * kpasswd server, the current connection with the DC will be torn down 90 * and a DC discovery process will be triggered to locate the kpasswd 91 * server. 92 * 93 * If joining a new domain, the domain_name property must be set after a 94 * successful credential chain setup. 95 */ 96 boolean_t 97 smbd_set_netlogon_cred(void) 98 { 99 char kpasswd_srv[MAXHOSTNAMELEN]; 100 char kpasswd_domain[MAXHOSTNAMELEN]; 101 char sam_acct[SMB_SAMACCT_MAXLEN]; 102 char *ipc_usr, *dom; 103 boolean_t new_domain = B_FALSE; 104 smb_domain_t dinfo; 105 106 if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN) 107 return (B_FALSE); 108 109 if (smb_match_netlogon_seqnum()) 110 return (B_FALSE); 111 112 (void) smb_config_getstr(SMB_CI_KPASSWD_SRV, kpasswd_srv, 113 sizeof (kpasswd_srv)); 114 115 if (*kpasswd_srv == '\0') 116 return (B_FALSE); 117 118 /* 119 * If the domain join initiated by smbadm join CLI is in 120 * progress, don't do anything. 121 */ 122 (void) smb_getsamaccount(sam_acct, sizeof (sam_acct)); 123 ipc_usr = smbrdr_ipc_get_user(); 124 if (utf8_strcasecmp(ipc_usr, sam_acct)) 125 return (B_FALSE); 126 127 if (!smb_domain_getinfo(&dinfo)) 128 (void) smb_getfqdomainname(dinfo.d_fqdomain, MAXHOSTNAMELEN); 129 130 (void) smb_config_getstr(SMB_CI_KPASSWD_DOMAIN, kpasswd_domain, 131 sizeof (kpasswd_domain)); 132 133 if (*kpasswd_domain != '\0' && 134 utf8_strcasecmp(kpasswd_domain, dinfo.d_fqdomain)) { 135 dom = kpasswd_domain; 136 new_domain = B_TRUE; 137 } else { 138 dom = dinfo.d_fqdomain; 139 } 140 141 /* 142 * DC discovery will be triggered if the domain info is not 143 * currently cached or the SMB daemon has previously discovered a DC 144 * that is different than the kpasswd server. 145 */ 146 if (new_domain || utf8_strcasecmp(dinfo.d_dc, kpasswd_srv) != 0) { 147 if (*dinfo.d_dc != '\0') 148 mlsvc_disconnect(dinfo.d_dc); 149 150 if (!smb_locate_dc(dom, kpasswd_srv, &dinfo)) { 151 if (!smb_locate_dc(dinfo.d_fqdomain, "", &dinfo)) { 152 smbrdr_ipc_commit(); 153 return (B_FALSE); 154 } 155 } 156 } 157 158 smbrdr_ipc_commit(); 159 if (mlsvc_netlogon(dinfo.d_dc, dinfo.d_nbdomain)) { 160 syslog(LOG_ERR, 161 "failed to establish NETLOGON credential chain"); 162 return (B_TRUE); 163 } else { 164 if (new_domain) { 165 smb_config_setdomaininfo(dinfo.d_nbdomain, 166 dinfo.d_fqdomain, dinfo.d_forest, dinfo.d_guid); 167 (void) smb_config_setstr(SMB_CI_KPASSWD_DOMAIN, ""); 168 } 169 } 170 171 return (new_domain); 172 } 173 174 /* 175 * smbd_locate_dc_start() 176 * 177 * Initialization of the thread that triggers the initial DC discovery 178 * when SMB daemon starts up. 179 * Returns 0 on success, an error number if thread creation fails. 180 */ 181 int 182 smbd_locate_dc_start(void) 183 { 184 pthread_attr_t tattr; 185 int rc; 186 187 (void) pthread_attr_init(&tattr); 188 (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); 189 rc = pthread_create(&smb_locate_dc_thr, &tattr, smbd_locate_dc_thread, 190 NULL); 191 (void) pthread_attr_destroy(&tattr); 192 return (rc); 193 } 194 195 /* 196 * smbd_locate_dc_thread() 197 * 198 * If necessary, set up Netlogon credential chain and locate a 199 * domain controller in the given resource domain. 200 * 201 * The domain configuration will be updated upon a successful DC discovery. 202 */ 203 /*ARGSUSED*/ 204 static void * 205 smbd_locate_dc_thread(void *arg) 206 { 207 char domain[MAXHOSTNAMELEN]; 208 smb_domain_t new_dinfo; 209 210 if (!smb_match_netlogon_seqnum()) { 211 (void) smbd_set_netlogon_cred(); 212 } else { 213 if (smb_getfqdomainname(domain, MAXHOSTNAMELEN) != 0) { 214 (void) smb_getdomainname(domain, MAXHOSTNAMELEN); 215 (void) utf8_strupr(domain); 216 } 217 218 if (smb_locate_dc(domain, "", &new_dinfo)) 219 smb_config_setdomaininfo(new_dinfo.d_nbdomain, 220 new_dinfo.d_fqdomain, new_dinfo.d_forest, 221 new_dinfo.d_guid); 222 } 223 224 return (NULL); 225 } 226 227 228 /* 229 * Retrieve the kpasswd server from krb5.conf. 230 * 231 * Initialization of the locate dc thread. 232 * Returns 0 on success, an error number if thread creation fails. 233 */ 234 static int 235 smbd_get_kpasswd_srv(char *srv, size_t len) 236 { 237 FILE *fp; 238 static char buf[512]; 239 char *p; 240 241 *srv = '\0'; 242 p = getenv("KRB5_CONFIG"); 243 if (p == NULL || *p == '\0') 244 p = "/etc/krb5/krb5.conf"; 245 246 if ((fp = fopen(p, "r")) == NULL) 247 return (-1); 248 249 while (fgets(buf, sizeof (buf), fp)) { 250 251 /* Weed out any comment text */ 252 (void) trim_whitespace(buf); 253 if (*buf == '#') 254 continue; 255 256 if ((p = strstr(buf, "kpasswd_server")) != NULL) { 257 if ((p = strchr(p, '=')) != NULL) { 258 (void) trim_whitespace(++p); 259 (void) strlcpy(srv, p, len); 260 } 261 break; 262 } 263 } 264 265 266 (void) fclose(fp); 267 return ((*srv == '\0') ? -1 : 0); 268 } 269 270 static uint32_t 271 smbd_join_workgroup(smb_joininfo_t *info) 272 { 273 char nb_domain[SMB_PI_MAX_DOMAIN]; 274 smb_domain_t dinfo; 275 276 (void) smb_config_getstr(SMB_CI_DOMAIN_NAME, nb_domain, 277 sizeof (nb_domain)); 278 279 smbd_set_secmode(SMB_SECMODE_WORKGRP); 280 281 bzero(&dinfo, sizeof (smb_domain_t)); 282 (void) strlcpy(dinfo.d_nbdomain, info->domain_name, 283 sizeof (dinfo.d_nbdomain)); 284 smb_config_setdomaininfo(dinfo.d_nbdomain, dinfo.d_fqdomain, 285 dinfo.d_forest, dinfo.d_guid); 286 287 288 if (strcasecmp(nb_domain, info->domain_name)) 289 smb_browser_reconfig(); 290 291 return (NT_STATUS_SUCCESS); 292 } 293 294 static uint32_t 295 smbd_join_domain(smb_joininfo_t *info) 296 { 297 uint32_t status; 298 unsigned char passwd_hash[SMBAUTH_HASH_SZ]; 299 char dc[MAXHOSTNAMELEN]; 300 smb_domain_t domain_info; 301 302 /* 303 * Ensure that any previous membership of this domain has 304 * been cleared from the environment before we start. This 305 * will ensure that we don't attempt a NETLOGON_SAMLOGON 306 * when attempting to find the PDC. 307 */ 308 309 (void) smb_config_setbool(SMB_CI_DOMAIN_MEMB, B_FALSE); 310 311 if (smb_auth_ntlm_hash(info->domain_passwd, passwd_hash) 312 != SMBAUTH_SUCCESS) { 313 syslog(LOG_ERR, "smbd: could not compute ntlm hash for '%s'", 314 info->domain_username); 315 return (NT_STATUS_INTERNAL_ERROR); 316 } 317 318 smbrdr_ipc_set(info->domain_username, passwd_hash); 319 320 (void) smbd_get_kpasswd_srv(dc, sizeof (dc)); 321 /* info->domain_name could either be NetBIOS domain name or FQDN */ 322 if (smb_locate_dc(info->domain_name, dc, &domain_info)) { 323 324 status = mlsvc_join(&domain_info, info->domain_username, 325 info->domain_passwd); 326 327 if (status == NT_STATUS_SUCCESS) { 328 smbd_set_secmode(SMB_SECMODE_DOMAIN); 329 smb_config_setdomaininfo(domain_info.d_nbdomain, 330 domain_info.d_fqdomain, domain_info. d_forest, 331 domain_info.d_guid); 332 smbrdr_ipc_commit(); 333 return (status); 334 } 335 336 smbrdr_ipc_rollback(); 337 syslog(LOG_ERR, "smbd: failed joining %s (%s)", 338 info->domain_name, xlate_nt_status(status)); 339 return (status); 340 } 341 342 smbrdr_ipc_rollback(); 343 syslog(LOG_ERR, "smbd: failed locating domain controller for %s", 344 info->domain_name); 345 return (NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND); 346 } 347