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