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
sam_remove_trust_account(char * server,char * domain)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
sam_delete_account(char * server,char * domain_name,char * account_name)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
sam_lookup_name(char * server,char * domain_name,char * account_name,DWORD * rid_ret)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
sam_get_local_domains(char * server,char * domain_name)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
netr_set_user_control(mlsvc_handle_t * user_handle,DWORD UserAccountControl)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
netr_set_user_password(mlsvc_handle_t * user_handle,char * new_pw_clear)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
netr_change_password(char * server,char * account,char * old_pw_clear,char * new_pw_clear)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
samr_make_encrypted_password(struct samr_encr_passwd * epw,char * new_pw_clear,uint8_t * crypt_key)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
samr_fill_userpw(struct samr_user_password * upw,const char * new_pw)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