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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 24 */ 25 26 /* 27 * Utility functions to support the RPC interface library. 28 */ 29 30 #include <stdio.h> 31 #include <stdarg.h> 32 #include <strings.h> 33 #include <unistd.h> 34 #include <netdb.h> 35 #include <stdlib.h> 36 #include <sys/time.h> 37 #include <sys/systm.h> 38 #include <syslog.h> 39 40 #include <smbsrv/libsmb.h> 41 #include <smbsrv/libsmbns.h> 42 #include <smbsrv/libmlsvc.h> 43 #include <smbsrv/ntaccess.h> 44 #include <smbsrv/smbinfo.h> 45 #include <libsmbrdr.h> 46 #include <lsalib.h> 47 #include <samlib.h> 48 #include <smbsrv/netrauth.h> 49 50 extern int netr_open(char *, char *, mlsvc_handle_t *); 51 extern int netr_close(mlsvc_handle_t *); 52 extern DWORD netlogon_auth(char *, mlsvc_handle_t *, DWORD); 53 54 static DWORD 55 mlsvc_join_rpc(smb_domainex_t *dxi, 56 char *admin_user, char *admin_pw, 57 char *machine_name, char *machine_pw); 58 static DWORD 59 mlsvc_join_noauth(smb_domainex_t *dxi, 60 char *machine_name, char *machine_pw); 61 62 63 DWORD 64 mlsvc_netlogon(char *server, char *domain) 65 { 66 mlsvc_handle_t netr_handle; 67 DWORD status; 68 69 if (netr_open(server, domain, &netr_handle) == 0) { 70 if ((status = netlogon_auth(server, &netr_handle, 71 NETR_FLG_INIT)) != NT_STATUS_SUCCESS) 72 syslog(LOG_NOTICE, "Failed to establish NETLOGON " 73 "credential chain"); 74 (void) netr_close(&netr_handle); 75 } else { 76 syslog(LOG_NOTICE, "Failed to connect to %s " 77 "for domain %s", server, domain); 78 status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 79 } 80 81 return (status); 82 } 83 84 /* 85 * Join the specified domain. The method varies depending on whether 86 * we're using "secure join" (using an administrative account to join) 87 * or "unsecure join" (using a pre-created machine account). In the 88 * latter case, the machine account is created "by hand" before this 89 * machine attempts to join, and we just change the password from the 90 * (weak) default password for a new machine account to a random one. 91 * 92 * Note that the caller has already done "DC discovery" and passes the 93 * domain info. in the first arg. 94 * 95 * Returns NT status codes. 96 */ 97 DWORD 98 mlsvc_join(smb_domainex_t *dxi, char *admin_user, char *admin_pw) 99 { 100 char machine_name[SMB_SAMACCT_MAXLEN]; 101 char machine_pw[NETR_MACHINE_ACCT_PASSWD_MAX]; 102 unsigned char passwd_hash[SMBAUTH_HASH_SZ]; 103 smb_domain_t *di = &dxi->d_primary; 104 DWORD status; 105 int rc; 106 107 /* 108 * Domain join support: AD (Kerberos+LDAP) or MS-RPC? 109 * Leave the AD code path disabled until it can be 110 * fixed up so that the SMB server is in complete 111 * control of which AD server we talk to. See: 112 * NX 12427 (Re-enable Kerberos+LDAP with...) 113 */ 114 boolean_t ads_enabled = smb_config_get_ads_enable(); 115 116 if (smb_getsamaccount(machine_name, sizeof (machine_name)) != 0) 117 return (NT_STATUS_INTERNAL_ERROR); 118 119 (void) smb_gen_random_passwd(machine_pw, sizeof (machine_pw)); 120 121 /* 122 * A non-null user means we do "secure join". 123 */ 124 if (admin_user != NULL && admin_user[0] != '\0') { 125 /* 126 * Doing "secure join", so authenticate as the 127 * specified user (with admin. rights). 128 */ 129 (void) smb_auth_ntlm_hash(admin_pw, passwd_hash); 130 smb_ipc_set(admin_user, passwd_hash); 131 132 /* 133 * If enabled, try to join using AD Services. 134 * The ADS code needs work. Not enabled yet. 135 */ 136 status = NT_STATUS_UNSUCCESSFUL; 137 if (ads_enabled) { 138 smb_adjoin_status_t err; 139 err = smb_ads_join(di->di_fqname, 140 admin_user, admin_pw, machine_pw); 141 if (err != SMB_ADJOIN_SUCCESS) { 142 smb_ads_join_errmsg(err); 143 } else { 144 status = NT_STATUS_SUCCESS; 145 } 146 } 147 148 /* 149 * If ADS was disabled or gave an error, 150 * fall-back and try to join using RPC. 151 */ 152 if (status != NT_STATUS_SUCCESS) { 153 status = mlsvc_join_rpc(dxi, 154 admin_user, admin_pw, 155 machine_name, machine_pw); 156 } 157 158 } else { 159 /* 160 * Doing "Unsecure join" (pre-created account) 161 */ 162 bzero(passwd_hash, sizeof (passwd_hash)); 163 smb_ipc_set(MLSVC_ANON_USER, passwd_hash); 164 165 status = mlsvc_join_noauth(dxi, machine_name, machine_pw); 166 } 167 168 if (status != NT_STATUS_SUCCESS) 169 goto out; 170 171 /* 172 * Make sure we can authenticate using the 173 * (new, or updated) machine account. 174 */ 175 (void) smb_auth_ntlm_hash(machine_pw, passwd_hash); 176 smb_ipc_set(machine_name, passwd_hash); 177 rc = smbrdr_logon(dxi->d_dc, di->di_nbname, machine_name); 178 if (rc != 0) { 179 syslog(LOG_NOTICE, "Authenticate with " 180 "new/updated machine account: %s", 181 strerror(rc)); 182 status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 183 goto out; 184 } 185 186 /* 187 * Store the new machine account password. 188 */ 189 rc = smb_setdomainprops(NULL, dxi->d_dc, machine_pw); 190 if (rc != 0) { 191 syslog(LOG_NOTICE, 192 "Failed to save machine account password"); 193 status = NT_STATUS_INTERNAL_DB_ERROR; 194 goto out; 195 } 196 197 /* 198 * Update idmap config 199 */ 200 if (smb_config_set_idmap_domain(di->di_fqname) != 0) 201 syslog(LOG_NOTICE, "Failed to set idmap domain name"); 202 if (smb_config_refresh_idmap() != 0) 203 syslog(LOG_NOTICE, "Failed to refresh idmap service"); 204 205 /* 206 * Note: The caller (smbd) saves the "secmode" and 207 * domain info (via smb_config_setdomaininfo) and 208 * and does smb_ipc_commit (or rollback). 209 */ 210 status = 0; 211 212 out: 213 /* Avoid leaving cleartext passwords around. */ 214 bzero(machine_pw, sizeof (machine_pw)); 215 bzero(passwd_hash, sizeof (passwd_hash)); 216 217 return (status); 218 } 219 220 static DWORD 221 mlsvc_join_rpc(smb_domainex_t *dxi, 222 char *admin_user, char *admin_pw, 223 char *machine_name, char *machine_pw) 224 { 225 mlsvc_handle_t samr_handle; 226 mlsvc_handle_t domain_handle; 227 mlsvc_handle_t user_handle; 228 smb_account_t ainfo; 229 char *server = dxi->d_dc; 230 smb_domain_t *di = &dxi->d_primary; 231 DWORD account_flags; 232 DWORD rid; 233 DWORD status; 234 int rc; 235 236 /* Caller did smb_ipc_set() so we don't need the pw for now. */ 237 _NOTE(ARGUNUSED(admin_pw)); 238 239 rc = samr_open(server, di->di_nbname, admin_user, 240 MAXIMUM_ALLOWED, &samr_handle); 241 if (rc != 0) { 242 syslog(LOG_NOTICE, "sam_connect to server %s failed", server); 243 return (RPC_NT_SERVER_UNAVAILABLE); 244 } 245 /* have samr_handle */ 246 247 status = samr_open_domain(&samr_handle, MAXIMUM_ALLOWED, 248 (struct samr_sid *)di->di_binsid, &domain_handle); 249 if (status != NT_STATUS_SUCCESS) 250 goto out_samr_handle; 251 /* have domain_handle */ 252 253 account_flags = SAMR_AF_WORKSTATION_TRUST_ACCOUNT; 254 status = samr_create_user(&domain_handle, machine_name, 255 account_flags, &rid, &user_handle); 256 if (status == NT_STATUS_USER_EXISTS) { 257 status = samr_lookup_domain_names(&domain_handle, 258 machine_name, &ainfo); 259 if (status != NT_STATUS_SUCCESS) 260 goto out_domain_handle; 261 status = samr_open_user(&domain_handle, MAXIMUM_ALLOWED, 262 ainfo.a_rid, &user_handle); 263 } 264 if (status != NT_STATUS_SUCCESS) { 265 syslog(LOG_NOTICE, 266 "Create or open machine account: %s", 267 xlate_nt_status(status)); 268 goto out_domain_handle; 269 } 270 271 /* 272 * The account exists, and we have user_handle open 273 * on that account. Set the password and flags. 274 */ 275 276 status = netr_set_user_password(&user_handle, machine_pw); 277 if (status != NT_STATUS_SUCCESS) { 278 syslog(LOG_NOTICE, 279 "Set machine account password: %s", 280 xlate_nt_status(status)); 281 goto out_user_handle; 282 } 283 284 account_flags |= SAMR_AF_DONT_EXPIRE_PASSWD; 285 status = netr_set_user_control(&user_handle, account_flags); 286 if (status != NT_STATUS_SUCCESS) { 287 syslog(LOG_NOTICE, 288 "Set machine account control flags: %s", 289 xlate_nt_status(status)); 290 goto out_user_handle; 291 } 292 293 out_user_handle: 294 (void) samr_close_handle(&user_handle); 295 out_domain_handle: 296 (void) samr_close_handle(&domain_handle); 297 out_samr_handle: 298 (void) samr_close_handle(&samr_handle); 299 300 return (status); 301 } 302 303 /* 304 * Doing "Unsecure join" (using a pre-created machine account). 305 * All we need to do is change the password from the default 306 * to a random string. 307 * 308 * Note: this is a work in progres. Nexenta issue 11960 309 * (allow joining an AD domain using a pre-created computer account) 310 * It turns out that to change the machine account password, 311 * we need to use a different RPC call, performed over the 312 * NetLogon secure channel. (See netr_server_password_set2) 313 */ 314 static DWORD 315 mlsvc_join_noauth(smb_domainex_t *dxi, 316 char *machine_name, char *machine_pw) 317 { 318 char old_pw[SMB_SAMACCT_MAXLEN]; 319 DWORD status; 320 321 /* 322 * Compose the current (default) password for the 323 * pre-created machine account, which is just the 324 * account name in lower case, truncated to 14 325 * characters. 326 */ 327 if (smb_gethostname(old_pw, sizeof (old_pw), SMB_CASE_LOWER) != 0) 328 return (NT_STATUS_INTERNAL_ERROR); 329 old_pw[14] = '\0'; 330 331 status = netr_change_password(dxi->d_dc, machine_name, 332 old_pw, machine_pw); 333 if (status != NT_STATUS_SUCCESS) { 334 syslog(LOG_NOTICE, 335 "Change machine account password: %s", 336 xlate_nt_status(status)); 337 } 338 return (status); 339 } 340 341 void 342 mlsvc_disconnect(const char *server) 343 { 344 smbrdr_disconnect(server); 345 } 346