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