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