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