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 2018 Nexenta Systems, Inc. All rights reserved. 25 */ 26 27 /* 28 * This module provides the high level interface to the SAM RPC 29 * functions. 30 */ 31 32 #include <sys/types.h> 33 #include <sys/isa_defs.h> 34 #include <sys/byteorder.h> 35 36 #include <alloca.h> 37 38 #include <smbsrv/libsmb.h> 39 #include <smbsrv/libmlsvc.h> 40 #include <smb/ntaccess.h> 41 #include <lsalib.h> 42 #include <samlib.h> 43 44 #ifdef _LITTLE_ENDIAN 45 /* little-endian values on little-endian */ 46 #define htolel(x) ((uint32_t)(x)) 47 #define letohl(x) ((uint32_t)(x)) 48 #else /* (BYTE_ORDER == LITTLE_ENDIAN) */ 49 /* little-endian values on big-endian (swap) */ 50 #define letohl(x) BSWAP_32(x) 51 #define htolel(x) BSWAP_32(x) 52 #endif /* (BYTE_ORDER == LITTLE_ENDIAN) */ 53 54 /* 55 * Valid values for the OEM OWF password encryption. 56 */ 57 #define SAM_KEYLEN 16 58 59 static void samr_fill_userpw(struct samr_user_password *, const char *); 60 static void samr_make_encrypted_password( 61 struct samr_encr_passwd *epw, 62 char *new_pw_clear, 63 uint8_t *crypt_key); 64 65 66 /* 67 * Todo: Implement "unjoin" domain, which would use the 68 * sam_remove_trust_account code below. 69 */ 70 71 /* 72 * sam_remove_trust_account 73 * 74 * Attempt to remove the workstation trust account for this system. 75 * Administrator access is required to perform this operation. 76 * 77 * Returns NT status codes. 78 */ 79 DWORD 80 sam_remove_trust_account(char *server, char *domain) 81 { 82 char account_name[SMB_SAMACCT_MAXLEN]; 83 84 if (smb_getsamaccount(account_name, SMB_SAMACCT_MAXLEN) != 0) 85 return (NT_STATUS_INTERNAL_ERROR); 86 87 return (sam_delete_account(server, domain, account_name)); 88 } 89 90 91 /* 92 * sam_delete_account 93 * 94 * Attempt to remove an account from the SAM database on the specified 95 * server. 96 * 97 * Returns NT status codes. 98 */ 99 DWORD 100 sam_delete_account(char *server, char *domain_name, char *account_name) 101 { 102 mlsvc_handle_t samr_handle; 103 mlsvc_handle_t domain_handle; 104 mlsvc_handle_t user_handle; 105 smb_account_t ainfo; 106 smb_sid_t *sid; 107 DWORD access_mask; 108 DWORD status; 109 int rc; 110 char user[SMB_USERNAME_MAXLEN]; 111 112 smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); 113 114 rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION, 115 &samr_handle); 116 if (rc != 0) 117 return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); 118 119 sid = samr_lookup_domain(&samr_handle, domain_name); 120 if (sid == NULL) { 121 status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 122 goto out_samr_hdl; 123 } 124 125 status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, 126 (struct samr_sid *)sid, &domain_handle); 127 if (status != NT_STATUS_SUCCESS) 128 goto out_sid_ptr; 129 130 status = samr_lookup_domain_names(&domain_handle, account_name, &ainfo); 131 if (status != NT_STATUS_SUCCESS) 132 goto out_dom_hdl; 133 134 access_mask = STANDARD_RIGHTS_EXECUTE | DELETE; 135 status = samr_open_user(&domain_handle, access_mask, 136 ainfo.a_rid, &user_handle); 137 if (status != NT_STATUS_SUCCESS) 138 goto out_dom_hdl; 139 140 status = samr_delete_user(&user_handle); 141 142 (void) samr_close_handle(&user_handle); 143 out_dom_hdl: 144 (void) samr_close_handle(&domain_handle); 145 out_sid_ptr: 146 free(sid); 147 out_samr_hdl: 148 (void) samr_close_handle(&samr_handle); 149 150 return (status); 151 } 152 153 154 /* 155 * sam_lookup_name 156 * 157 * Lookup an account name in the SAM database on the specified domain 158 * controller. Provides the account RID on success. 159 * 160 * Returns NT status codes. 161 */ 162 DWORD 163 sam_lookup_name(char *server, char *domain_name, char *account_name, 164 DWORD *rid_ret) 165 { 166 mlsvc_handle_t samr_handle; 167 mlsvc_handle_t domain_handle; 168 smb_account_t ainfo; 169 struct samr_sid *domain_sid; 170 int rc; 171 DWORD status; 172 char user[SMB_USERNAME_MAXLEN]; 173 174 smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); 175 176 *rid_ret = 0; 177 178 rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION, 179 &samr_handle); 180 181 if (rc != 0) 182 return (NT_STATUS_OPEN_FAILED); 183 184 domain_sid = (struct samr_sid *)samr_lookup_domain(&samr_handle, 185 domain_name); 186 if (domain_sid == NULL) { 187 (void) samr_close_handle(&samr_handle); 188 return (NT_STATUS_NO_SUCH_DOMAIN); 189 } 190 191 status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, 192 domain_sid, &domain_handle); 193 if (status == NT_STATUS_SUCCESS) { 194 status = samr_lookup_domain_names(&domain_handle, 195 account_name, &ainfo); 196 if (status == NT_STATUS_SUCCESS) 197 *rid_ret = ainfo.a_rid; 198 199 (void) samr_close_handle(&domain_handle); 200 } 201 202 (void) samr_close_handle(&samr_handle); 203 return (status); 204 } 205 206 /* 207 * sam_get_local_domains 208 * 209 * Query a remote server to get the list of local domains that it 210 * supports. 211 * 212 * Returns NT status codes. 213 */ 214 DWORD 215 sam_get_local_domains(char *server, char *domain_name) 216 { 217 mlsvc_handle_t samr_handle; 218 DWORD status; 219 int rc; 220 char user[SMB_USERNAME_MAXLEN]; 221 222 smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); 223 224 rc = samr_open(server, domain_name, user, SAM_ENUM_LOCAL_DOMAIN, 225 &samr_handle); 226 if (rc != 0) 227 return (NT_STATUS_OPEN_FAILED); 228 229 status = samr_enum_local_domains(&samr_handle); 230 (void) samr_close_handle(&samr_handle); 231 return (status); 232 } 233 234 /* 235 * Set the account control flags on some account for which we 236 * have already opened a SAM handle with appropriate rights, 237 * passed in here as sam_handle, along with the new flags. 238 */ 239 DWORD 240 netr_set_user_control( 241 mlsvc_handle_t *user_handle, 242 DWORD UserAccountControl) 243 { 244 struct samr_SetUserInfo16 info; 245 246 info.UserAccountControl = UserAccountControl; 247 return (samr_set_user_info(user_handle, 16, &info)); 248 } 249 250 /* 251 * Set the password on some account, for which we have already 252 * opened a SAM handle with appropriate rights, passed in here 253 * as sam_handle, along with the new password as cleartext. 254 * 255 * This builds a struct SAMPR_USER_INTERNAL5_INFORMATION [MS-SAMR] 256 * containing the new password, encrypted with our session key. 257 */ 258 DWORD 259 netr_set_user_password( 260 mlsvc_handle_t *user_handle, 261 char *new_pw_clear) 262 { 263 unsigned char ssn_key[SMBAUTH_HASH_SZ]; 264 struct samr_SetUserInfo24 info; 265 266 if (ndr_rpc_get_ssnkey(user_handle, ssn_key, SMBAUTH_HASH_SZ)) 267 return (NT_STATUS_INTERNAL_ERROR); 268 269 (void) memset(&info, 0, sizeof (info)); 270 samr_make_encrypted_password(&info.encr_pw, new_pw_clear, ssn_key); 271 272 /* Rather not leave the session key around. */ 273 (void) memset(ssn_key, 0, sizeof (ssn_key)); 274 275 return (samr_set_user_info(user_handle, 24, &info)); 276 } 277 278 /* 279 * Change a password like NetUserChangePassword(), 280 * but where we already know which AD server to use, 281 * so we don't request the domain name or search for 282 * an AD server for that domain here. 283 */ 284 DWORD 285 netr_change_password( 286 char *server, 287 char *account, 288 char *old_pw_clear, 289 char *new_pw_clear) 290 { 291 struct samr_encr_passwd epw; 292 struct samr_encr_hash old_hash; 293 uint8_t old_nt_hash[SAMR_PWHASH_LEN]; 294 uint8_t new_nt_hash[SAMR_PWHASH_LEN]; 295 mlsvc_handle_t handle; 296 DWORD status; 297 298 /* 299 * Create an RPC handle to this server, bound to SAMR. 300 * Note: the group policy option to disable anonymous access to named 301 * pipes on DCs doesn't work with this. 302 * To work with that option, we'll have to find an interface that lets 303 * us authenticate with an expired password, instead of the anonymous 304 * bind we currently perform. 305 */ 306 status = ndr_rpc_bind(&handle, server, "", "", "SAMR"); 307 if (status != NT_STATUS_SUCCESS) 308 return (status); 309 310 /* 311 * Encrypt the new p/w (plus random filler) with the 312 * old password, and send the old p/w encrypted with 313 * the new p/w hash to prove we know the old p/w. 314 * Details: [MS-SAMR 3.1.5.10.3] 315 */ 316 (void) smb_auth_ntlm_hash(old_pw_clear, old_nt_hash); 317 (void) smb_auth_ntlm_hash(new_pw_clear, new_nt_hash); 318 samr_make_encrypted_password(&epw, new_pw_clear, old_nt_hash); 319 320 (void) smb_auth_DES(old_hash.data, SAMR_PWHASH_LEN, 321 new_nt_hash, 14, /* key */ 322 old_nt_hash, SAMR_PWHASH_LEN); 323 324 /* 325 * Finally, ready to try the OtW call. 326 */ 327 status = samr_change_password( 328 &handle, server, account, 329 &epw, &old_hash); 330 331 /* Avoid leaving cleartext (or equivalent) around. */ 332 (void) memset(old_nt_hash, 0, sizeof (old_nt_hash)); 333 (void) memset(new_nt_hash, 0, sizeof (new_nt_hash)); 334 335 ndr_rpc_unbind(&handle); 336 return (status); 337 } 338 339 /* 340 * Build an encrypted password, as used by samr_set_user_info 341 * and samr_change_password. Note: This builds the unencrypted 342 * form in one union arm, and encrypts it in the other union arm. 343 */ 344 void 345 samr_make_encrypted_password( 346 struct samr_encr_passwd *epw, 347 char *new_pw_clear, 348 uint8_t *crypt_key) 349 { 350 union { 351 struct samr_user_password u; 352 struct samr_encr_passwd e; 353 } pwu; 354 355 samr_fill_userpw(&pwu.u, new_pw_clear); 356 357 (void) smb_auth_RC4(pwu.e.data, sizeof (pwu.e.data), 358 crypt_key, SAMR_PWHASH_LEN, 359 pwu.e.data, sizeof (pwu.e.data)); 360 361 (void) memcpy(epw->data, pwu.e.data, sizeof (pwu.e.data)); 362 (void) memset(pwu.e.data, 0, sizeof (pwu.e.data)); 363 } 364 365 /* 366 * This fills in a samr_user_password (a.k.a. SAMPR_USER_PASSWORD 367 * in the MS Net API) which has the new password "right justified" 368 * in the buffer, and any space on the left filled with random junk 369 * to improve the quality of the encryption that is subsequently 370 * applied to this buffer before it goes over the wire. 371 */ 372 static void 373 samr_fill_userpw(struct samr_user_password *upw, const char *new_pw) 374 { 375 smb_wchar_t *pbuf; 376 uint32_t pwlen_bytes; 377 size_t pwlen_wchars; 378 379 /* 380 * First fill the whole buffer with the random junk. 381 * (Slightly less random when debugging:) 382 */ 383 #ifdef DEBUG 384 (void) memset(upw->Buffer, '*', sizeof (upw->Buffer)); 385 #else 386 randomize((char *)upw->Buffer, sizeof (upw->Buffer)); 387 #endif 388 389 /* 390 * Now overwrite the last pwlen characters of 391 * that buffer with the password, and set the 392 * length field so the receiving end knows where 393 * the junk ends and the real password starts. 394 */ 395 pwlen_wchars = smb_wcequiv_strlen(new_pw) / 2; 396 if (pwlen_wchars > SAMR_USER_PWLEN) 397 pwlen_wchars = SAMR_USER_PWLEN; 398 pwlen_bytes = pwlen_wchars * 2; 399 400 pbuf = &upw->Buffer[SAMR_USER_PWLEN - pwlen_wchars]; 401 (void) smb_mbstowcs(pbuf, new_pw, pwlen_wchars); 402 403 /* Yes, this is in Bytes, not wchars. */ 404 upw->Length = htolel(pwlen_bytes); 405 } 406