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 */
25
26 /*
27 * This module provides the high level interface to the SAM RPC
28 * functions.
29 */
30
31 #include <alloca.h>
32
33 #include <smbsrv/libsmb.h>
34 #include <smbsrv/libmlsvc.h>
35 #include <smbsrv/ntaccess.h>
36 #include <lsalib.h>
37 #include <samlib.h>
38
39 /*
40 * Valid values for the OEM OWF password encryption.
41 */
42 #define SAM_PASSWORD_516 516
43 #define SAM_KEYLEN 16
44
45 extern DWORD samr_set_user_info(mlsvc_handle_t *);
46 static struct samr_sid *sam_get_domain_sid(mlsvc_handle_t *, char *, char *);
47
48 /*
49 * sam_create_trust_account
50 *
51 * Create a trust account for this system.
52 *
53 * SAMR_AF_WORKSTATION_TRUST_ACCOUNT: servers and workstations.
54 * SAMR_AF_SERVER_TRUST_ACCOUNT: domain controllers.
55 *
56 * Returns NT status codes.
57 */
58 DWORD
sam_create_trust_account(char * server,char * domain)59 sam_create_trust_account(char *server, char *domain)
60 {
61 char account_name[SMB_SAMACCT_MAXLEN];
62 DWORD status;
63
64 if (smb_getsamaccount(account_name, SMB_SAMACCT_MAXLEN) != 0)
65 return (NT_STATUS_INTERNAL_ERROR);
66
67 /*
68 * The trust account value here should match
69 * the value that will be used when the user
70 * information is set on this account.
71 */
72 status = sam_create_account(server, domain, account_name,
73 SAMR_AF_WORKSTATION_TRUST_ACCOUNT);
74
75 /*
76 * Based on network traces, a Windows 2000 client will
77 * always try to create the computer account first.
78 * If it existed, then check the user permission to join
79 * the domain.
80 */
81
82 if (status == NT_STATUS_USER_EXISTS)
83 status = sam_check_user(server, domain, account_name);
84
85 return (status);
86 }
87
88
89 /*
90 * sam_create_account
91 *
92 * Create the specified domain account in the SAM database on the
93 * domain controller.
94 *
95 * Account flags:
96 * SAMR_AF_NORMAL_ACCOUNT
97 * SAMR_AF_WORKSTATION_TRUST_ACCOUNT
98 * SAMR_AF_SERVER_TRUST_ACCOUNT
99 *
100 * Returns NT status codes.
101 */
102 DWORD
sam_create_account(char * server,char * domain_name,char * account_name,DWORD account_flags)103 sam_create_account(char *server, char *domain_name, char *account_name,
104 DWORD account_flags)
105 {
106 mlsvc_handle_t samr_handle;
107 mlsvc_handle_t domain_handle;
108 mlsvc_handle_t user_handle;
109 union samr_user_info sui;
110 struct samr_sid *sid;
111 DWORD rid;
112 DWORD status;
113 int rc;
114 char user[SMB_USERNAME_MAXLEN];
115
116 smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
117
118 rc = samr_open(server, domain_name, user, SAM_CONNECT_CREATE_ACCOUNT,
119 &samr_handle);
120
121 if (rc != 0) {
122 status = NT_STATUS_OPEN_FAILED;
123 smb_tracef("SamCreateAccount[%s\\%s]: %s",
124 domain_name, account_name, xlate_nt_status(status));
125 return (status);
126 }
127
128 sid = sam_get_domain_sid(&samr_handle, server, domain_name);
129
130 status = samr_open_domain(&samr_handle,
131 SAM_DOMAIN_CREATE_ACCOUNT, sid, &domain_handle);
132
133 if (status == NT_STATUS_SUCCESS) {
134 status = samr_create_user(&domain_handle, account_name,
135 account_flags, &rid, &user_handle);
136
137 if (status == NT_STATUS_SUCCESS) {
138 (void) samr_query_user_info(&user_handle,
139 SAMR_QUERY_USER_CONTROL_INFO, &sui);
140
141 (void) samr_get_user_pwinfo(&user_handle);
142 (void) samr_set_user_info(&user_handle);
143 (void) samr_close_handle(&user_handle);
144 } else if (status != NT_STATUS_USER_EXISTS) {
145 smb_tracef("SamCreateAccount[%s]: %s",
146 account_name, xlate_nt_status(status));
147 }
148
149 (void) samr_close_handle(&domain_handle);
150 } else {
151 smb_tracef("SamCreateAccount[%s]: open domain failed",
152 account_name);
153 status = (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
154 }
155
156 (void) samr_close_handle(&samr_handle);
157 free(sid);
158 return (status);
159 }
160
161
162 /*
163 * sam_remove_trust_account
164 *
165 * Attempt to remove the workstation trust account for this system.
166 * Administrator access is required to perform this operation.
167 *
168 * Returns NT status codes.
169 */
170 DWORD
sam_remove_trust_account(char * server,char * domain)171 sam_remove_trust_account(char *server, char *domain)
172 {
173 char account_name[SMB_SAMACCT_MAXLEN];
174
175 if (smb_getsamaccount(account_name, SMB_SAMACCT_MAXLEN) != 0)
176 return (NT_STATUS_INTERNAL_ERROR);
177
178 return (sam_delete_account(server, domain, account_name));
179 }
180
181
182 /*
183 * sam_delete_account
184 *
185 * Attempt to remove an account from the SAM database on the specified
186 * server.
187 *
188 * Returns NT status codes.
189 */
190 DWORD
sam_delete_account(char * server,char * domain_name,char * account_name)191 sam_delete_account(char *server, char *domain_name, char *account_name)
192 {
193 mlsvc_handle_t samr_handle;
194 mlsvc_handle_t domain_handle;
195 mlsvc_handle_t user_handle;
196 smb_account_t ainfo;
197 struct samr_sid *sid;
198 DWORD access_mask;
199 DWORD status;
200 int rc;
201 char user[SMB_USERNAME_MAXLEN];
202
203 smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
204
205 rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION,
206 &samr_handle);
207
208 if (rc != 0)
209 return (NT_STATUS_OPEN_FAILED);
210
211 sid = sam_get_domain_sid(&samr_handle, server, domain_name);
212 status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, sid,
213 &domain_handle);
214 free(sid);
215 if (status != NT_STATUS_SUCCESS) {
216 (void) samr_close_handle(&samr_handle);
217 return (status);
218 }
219
220 status = samr_lookup_domain_names(&domain_handle, account_name, &ainfo);
221 if (status == NT_STATUS_SUCCESS) {
222 access_mask = STANDARD_RIGHTS_EXECUTE | DELETE;
223 status = samr_open_user(&domain_handle, access_mask,
224 ainfo.a_rid, &user_handle);
225 if (status == NT_STATUS_SUCCESS) {
226 if (samr_delete_user(&user_handle) != 0)
227 (void) samr_close_handle(&user_handle);
228 }
229 }
230
231 (void) samr_close_handle(&domain_handle);
232 (void) samr_close_handle(&samr_handle);
233 return (status);
234 }
235
236 /*
237 * sam_check_user
238 *
239 * Check to see if user have permission to access computer account.
240 * The user being checked is the specified user for joining the Solaris
241 * host to the domain.
242 */
243 DWORD
sam_check_user(char * server,char * domain_name,char * account_name)244 sam_check_user(char *server, char *domain_name, char *account_name)
245 {
246 mlsvc_handle_t samr_handle;
247 mlsvc_handle_t domain_handle;
248 mlsvc_handle_t user_handle;
249 smb_account_t ainfo;
250 struct samr_sid *sid;
251 DWORD access_mask;
252 DWORD status;
253 int rc;
254 char user[SMB_USERNAME_MAXLEN];
255
256 smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
257
258 rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION,
259 &samr_handle);
260
261 if (rc != 0)
262 return (NT_STATUS_OPEN_FAILED);
263
264 sid = sam_get_domain_sid(&samr_handle, server, domain_name);
265 status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, sid,
266 &domain_handle);
267 free(sid);
268 if (status != NT_STATUS_SUCCESS) {
269 (void) samr_close_handle(&samr_handle);
270 return (status);
271 }
272
273 status = samr_lookup_domain_names(&domain_handle, account_name, &ainfo);
274 if (status == NT_STATUS_SUCCESS) {
275 /*
276 * Win2000 client uses this access mask. The
277 * following SAMR user specific rights bits are
278 * set: set password, set attributes, and get
279 * attributes.
280 */
281
282 access_mask = 0xb0;
283 status = samr_open_user(&domain_handle,
284 access_mask, ainfo.a_rid, &user_handle);
285 if (status == NT_STATUS_SUCCESS)
286 (void) samr_close_handle(&user_handle);
287 }
288
289 (void) samr_close_handle(&domain_handle);
290 (void) samr_close_handle(&samr_handle);
291 return (status);
292 }
293
294 /*
295 * sam_lookup_name
296 *
297 * Lookup an account name in the SAM database on the specified domain
298 * controller. Provides the account RID on success.
299 *
300 * Returns NT status codes.
301 */
302 DWORD
sam_lookup_name(char * server,char * domain_name,char * account_name,DWORD * rid_ret)303 sam_lookup_name(char *server, char *domain_name, char *account_name,
304 DWORD *rid_ret)
305 {
306 mlsvc_handle_t samr_handle;
307 mlsvc_handle_t domain_handle;
308 smb_account_t ainfo;
309 struct samr_sid *domain_sid;
310 int rc;
311 DWORD status;
312 char user[SMB_USERNAME_MAXLEN];
313
314 smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
315
316 *rid_ret = 0;
317
318 rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION,
319 &samr_handle);
320
321 if (rc != 0)
322 return (NT_STATUS_OPEN_FAILED);
323
324 domain_sid = (struct samr_sid *)samr_lookup_domain(&samr_handle,
325 domain_name);
326 if (domain_sid == NULL) {
327 (void) samr_close_handle(&samr_handle);
328 return (NT_STATUS_NO_SUCH_DOMAIN);
329 }
330
331 status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION,
332 domain_sid, &domain_handle);
333 if (status == NT_STATUS_SUCCESS) {
334 status = samr_lookup_domain_names(&domain_handle,
335 account_name, &ainfo);
336 if (status == NT_STATUS_SUCCESS)
337 *rid_ret = ainfo.a_rid;
338
339 (void) samr_close_handle(&domain_handle);
340 }
341
342 (void) samr_close_handle(&samr_handle);
343 return (status);
344 }
345
346 /*
347 * sam_get_local_domains
348 *
349 * Query a remote server to get the list of local domains that it
350 * supports.
351 *
352 * Returns NT status codes.
353 */
354 DWORD
sam_get_local_domains(char * server,char * domain_name)355 sam_get_local_domains(char *server, char *domain_name)
356 {
357 mlsvc_handle_t samr_handle;
358 DWORD status;
359 int rc;
360 char user[SMB_USERNAME_MAXLEN];
361
362 smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
363
364 rc = samr_open(server, domain_name, user, SAM_ENUM_LOCAL_DOMAIN,
365 &samr_handle);
366 if (rc != 0)
367 return (NT_STATUS_OPEN_FAILED);
368
369 status = samr_enum_local_domains(&samr_handle);
370 (void) samr_close_handle(&samr_handle);
371 return (status);
372 }
373
374 /*
375 * sam_oem_password
376 *
377 * Generate an OEM password.
378 */
379 int
sam_oem_password(oem_password_t * oem_password,unsigned char * new_password,unsigned char * old_password)380 sam_oem_password(oem_password_t *oem_password, unsigned char *new_password,
381 unsigned char *old_password)
382 {
383 smb_wchar_t *unicode_password;
384 int length;
385
386 #ifdef PBSHORTCUT
387 assert(sizeof (oem_password_t) == SAM_PASSWORD_516);
388 #endif /* PBSHORTCUT */
389
390 length = strlen((char const *)new_password);
391 unicode_password = alloca((length + 1) * sizeof (smb_wchar_t));
392
393 length = smb_auth_qnd_unicode((unsigned short *)unicode_password,
394 (char *)new_password, length);
395 oem_password->length = length;
396
397 (void) memcpy(&oem_password->data[512 - length],
398 unicode_password, length);
399
400 rand_hash((unsigned char *)oem_password, sizeof (oem_password_t),
401 old_password, SAM_KEYLEN);
402
403 return (0);
404 }
405
406 static struct samr_sid *
sam_get_domain_sid(mlsvc_handle_t * samr_handle,char * server,char * domain_name)407 sam_get_domain_sid(mlsvc_handle_t *samr_handle, char *server, char *domain_name)
408 {
409 smb_sid_t *sid = NULL;
410 smb_domainex_t domain;
411
412 if (ndr_rpc_server_os(samr_handle) == NATIVE_OS_WIN2000) {
413 if (!smb_domain_getinfo(&domain)) {
414 if (lsa_query_account_domain_info(server, domain_name,
415 &domain.d_primary) != NT_STATUS_SUCCESS)
416 return (NULL);
417 }
418
419 sid = smb_sid_fromstr(domain.d_primary.di_sid);
420 } else {
421 sid = samr_lookup_domain(samr_handle, domain_name);
422 }
423
424 return ((struct samr_sid *)sid);
425 }
426