xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/samlib.c (revision a194faf8907a6722dcf10ad16c6ca72c9b7bd0ba)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * This module provides the high level interface to the SAM RPC
30  * functions.
31  */
32 
33 #include <unistd.h>
34 #include <netdb.h>
35 #include <alloca.h>
36 
37 #include <smbsrv/libsmb.h>
38 #include <smbsrv/libsmbrdr.h>
39 #include <smbsrv/libmlsvc.h>
40 #include <smbsrv/ntstatus.h>
41 #include <smbsrv/ntaccess.h>
42 #include <smbsrv/ntsid.h>
43 #include <smbsrv/lsalib.h>
44 #include <smbsrv/samlib.h>
45 
46 /*
47  * Valid values for the OEM OWF password encryption.
48  */
49 #define	SAM_PASSWORD_516	516
50 #define	SAM_KEYLEN		16
51 
52 extern DWORD samr_set_user_info(mlsvc_handle_t *, smb_auth_info_t *);
53 static int get_user_group_info(mlsvc_handle_t *, smb_userinfo_t *);
54 
55 /*
56  * sam_lookup_user_info
57  *
58  * Lookup user information in the SAM database on the specified server
59  * (domain controller). The LSA interface is used to obtain the user
60  * RID, the domain name and the domain SID (user privileges are TBD).
61  * Then the various SAM layers are opened using the domain SID and the
62  * user RID to obtain the users's group membership information.
63  *
64  * The information is returned in the user_info structure. The caller
65  * is responsible for allocating and releasing this structure. If the
66  * lookup is successful, sid_name_use will be set to SidTypeUser.
67  *
68  * On success 0 is returned. Otherwise a -ve error code.
69  */
70 int
71 sam_lookup_user_info(char *server, char *domain_name,
72     char *account_name, smb_userinfo_t *user_info)
73 {
74 	mlsvc_handle_t samr_handle;
75 	mlsvc_handle_t domain_handle;
76 	mlsvc_handle_t user_handle;
77 	struct samr_sid *sid;
78 	int rc;
79 	DWORD access_mask;
80 	DWORD status;
81 
82 	if (lsa_lookup_name(server, domain_name, account_name, user_info) != 0)
83 		return (-1);
84 
85 	if (user_info->sid_name_use != SidTypeUser ||
86 	    user_info->rid == 0 || user_info->domain_sid == 0) {
87 		return (-1);
88 	}
89 
90 	rc = samr_open(server, domain_name, account_name,
91 	    SAM_LOOKUP_INFORMATION, &samr_handle);
92 	if (rc != 0)
93 		return (-1);
94 	sid = (struct samr_sid *)user_info->domain_sid;
95 
96 	status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION,
97 	    sid, &domain_handle);
98 	if (status == 0) {
99 		access_mask = STANDARD_RIGHTS_EXECUTE | SAM_ACCESS_USER_READ;
100 
101 		status = samr_open_user(&domain_handle, access_mask,
102 		    user_info->rid, &user_handle);
103 
104 		if (status == NT_STATUS_SUCCESS) {
105 			(void) get_user_group_info(&user_handle, user_info);
106 			(void) samr_close_handle(&user_handle);
107 		} else {
108 			rc = -1;
109 		}
110 
111 		(void) samr_close_handle(&domain_handle);
112 	} else {
113 		rc = -1;
114 	}
115 
116 	(void) samr_close_handle(&samr_handle);
117 	return (rc);
118 }
119 
120 /*
121  * get_user_group_info
122  *
123  * This is a private function to obtain the primary group and group
124  * memberships for the user specified by the user_handle. This function
125  * should only be called from sam_lookup_user_info.
126  *
127  * On success 0 is returned. Otherwise -1 is returned.
128  */
129 static int
130 get_user_group_info(mlsvc_handle_t *user_handle, smb_userinfo_t *user_info)
131 {
132 	union samr_user_info sui;
133 	int rc;
134 
135 	rc = samr_query_user_info(user_handle, SAMR_QUERY_USER_GROUPRID, &sui);
136 	if (rc != 0)
137 		return (-1);
138 
139 	rc = samr_query_user_groups(user_handle, user_info);
140 	if (rc != 0)
141 		return (-1);
142 
143 	user_info->primary_group_rid = sui.info9.group_rid;
144 	return (0);
145 }
146 
147 /*
148  * sam_create_trust_account
149  *
150  * Create a trust account for this system.
151  *
152  *	SAMR_AF_WORKSTATION_TRUST_ACCOUNT: servers and workstations.
153  *	SAMR_AF_SERVER_TRUST_ACCOUNT: domain controllers.
154  *
155  * Returns NT status codes.
156  */
157 DWORD
158 sam_create_trust_account(char *server, char *domain, smb_auth_info_t *auth)
159 {
160 	smb_userinfo_t *user_info;
161 	char account_name[MAXHOSTNAMELEN];
162 	DWORD status;
163 
164 	if (smb_gethostname(account_name, MAXHOSTNAMELEN - 2, 1) != 0)
165 		return (NT_STATUS_NO_MEMORY);
166 
167 	(void) strlcat(account_name, "$", MAXHOSTNAMELEN);
168 
169 	if ((user_info = mlsvc_alloc_user_info()) == 0)
170 		return (NT_STATUS_NO_MEMORY);
171 
172 	/*
173 	 * The trust account value here should match
174 	 * the value that will be used when the user
175 	 * information is set on this account.
176 	 */
177 	status = sam_create_account(server, domain, account_name,
178 	    auth, SAMR_AF_WORKSTATION_TRUST_ACCOUNT, user_info);
179 
180 	mlsvc_free_user_info(user_info);
181 
182 
183 	/*
184 	 * Based on network traces, a Windows 2000 client will
185 	 * always try to create the computer account first.
186 	 * If it existed, then check the user permission to join
187 	 * the domain.
188 	 */
189 
190 	if (status == NT_STATUS_USER_EXISTS)
191 		status = sam_check_user(server, domain, account_name);
192 
193 	return (status);
194 }
195 
196 
197 /*
198  * sam_create_account
199  *
200  * Create the specified domain account in the SAM database on the
201  * domain controller.
202  *
203  * Account flags:
204  *		SAMR_AF_NORMAL_ACCOUNT
205  *		SAMR_AF_WORKSTATION_TRUST_ACCOUNT
206  *		SAMR_AF_SERVER_TRUST_ACCOUNT
207  *
208  * Returns NT status codes.
209  */
210 DWORD
211 sam_create_account(char *server, char *domain_name, char *account_name,
212     smb_auth_info_t *auth, DWORD account_flags, smb_userinfo_t *user_info)
213 {
214 	mlsvc_handle_t samr_handle;
215 	mlsvc_handle_t domain_handle;
216 	mlsvc_handle_t user_handle;
217 	union samr_user_info sui;
218 	struct samr_sid *sid;
219 	DWORD rid;
220 	DWORD status;
221 	int rc;
222 	char *user = smbrdr_ipc_get_user();
223 
224 	rc = samr_open(server, domain_name, user, SAM_CONNECT_CREATE_ACCOUNT,
225 	    &samr_handle);
226 
227 	if (rc != 0) {
228 		status = NT_STATUS_OPEN_FAILED;
229 		smb_tracef("SamCreateAccount[%s\\%s]: %s",
230 		    domain_name, account_name, xlate_nt_status(status));
231 		return (status);
232 	}
233 
234 	if (samr_handle.context->server_os == NATIVE_OS_WIN2000) {
235 		nt_domain_t *ntdp;
236 
237 		if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) {
238 			(void) lsa_query_account_domain_info();
239 			if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) {
240 				(void) samr_close_handle(&samr_handle);
241 				status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
242 				smb_tracef("SamCreateAccount[%s\\%s]: %s",
243 				    domain_name, account_name,
244 				    xlate_nt_status(status));
245 				return (status);
246 			}
247 		}
248 
249 		sid = (struct samr_sid *)ntdp->sid;
250 	} else {
251 		if (samr_lookup_domain(&samr_handle,
252 		    domain_name, user_info) != 0) {
253 			(void) samr_close_handle(&samr_handle);
254 			smb_tracef("SamCreateAccount[%s]: lookup failed",
255 			    account_name);
256 
257 			return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
258 		}
259 
260 		sid = (struct samr_sid *)user_info->domain_sid;
261 	}
262 
263 	status = samr_open_domain(&samr_handle,
264 	    SAM_DOMAIN_CREATE_ACCOUNT, sid, &domain_handle);
265 
266 	if (status == NT_STATUS_SUCCESS) {
267 		status = samr_create_user(&domain_handle, account_name,
268 		    account_flags, &rid, &user_handle);
269 
270 		if (status == NT_STATUS_SUCCESS) {
271 			(void) samr_query_user_info(&user_handle,
272 			    SAMR_QUERY_USER_UNKNOWN16, &sui);
273 
274 			(void) samr_get_user_pwinfo(&user_handle);
275 			(void) samr_set_user_info(&user_handle, auth);
276 			(void) samr_close_handle(&user_handle);
277 		} else if (status == NT_STATUS_USER_EXISTS) {
278 			mlsvc_release_user_info(user_info);
279 
280 			rc = lsa_lookup_name(server, domain_name, account_name,
281 			    user_info);
282 			if (rc == 0)
283 				rid = user_info->rid;
284 		} else {
285 			smb_tracef("SamCreateAccount[%s]: %s",
286 			    account_name, xlate_nt_status(status));
287 		}
288 
289 		(void) samr_close_handle(&domain_handle);
290 	} else {
291 		smb_tracef("SamCreateAccount[%s]: open domain failed",
292 		    account_name);
293 		status = (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
294 	}
295 
296 	(void) samr_close_handle(&samr_handle);
297 	return (status);
298 }
299 
300 
301 /*
302  * sam_remove_trust_account
303  *
304  * Attempt to remove the workstation trust account for this system.
305  * Administrator access is required to perform this operation.
306  *
307  * Returns NT status codes.
308  */
309 DWORD
310 sam_remove_trust_account(char *server, char *domain)
311 {
312 	char account_name[MAXHOSTNAMELEN];
313 
314 	if (smb_gethostname(account_name, MAXHOSTNAMELEN - 2, 1) != 0)
315 		return (NT_STATUS_NO_MEMORY);
316 
317 	(void) strcat(account_name, "$");
318 
319 	return (sam_delete_account(server, domain, account_name));
320 }
321 
322 
323 /*
324  * sam_delete_account
325  *
326  * Attempt to remove an account from the SAM database on the specified
327  * server.
328  *
329  * Returns NT status codes.
330  */
331 DWORD
332 sam_delete_account(char *server, char *domain_name, char *account_name)
333 {
334 	mlsvc_handle_t samr_handle;
335 	mlsvc_handle_t domain_handle;
336 	mlsvc_handle_t user_handle;
337 	smb_userinfo_t *user_info;
338 	struct samr_sid *sid;
339 	DWORD rid;
340 	DWORD access_mask;
341 	DWORD status;
342 	int rc;
343 	char *user = smbrdr_ipc_get_user();
344 
345 	if ((user_info = mlsvc_alloc_user_info()) == 0)
346 		return (NT_STATUS_NO_MEMORY);
347 
348 	rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION,
349 	    &samr_handle);
350 
351 	if (rc != 0) {
352 		mlsvc_free_user_info(user_info);
353 		return (NT_STATUS_OPEN_FAILED);
354 	}
355 
356 	if (samr_handle.context->server_os == NATIVE_OS_WIN2000) {
357 		nt_domain_t *ntdp;
358 
359 		if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) {
360 			(void) lsa_query_account_domain_info();
361 			if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) {
362 
363 				(void) samr_close_handle(&samr_handle);
364 				return (NT_STATUS_NO_SUCH_DOMAIN);
365 			}
366 		}
367 
368 		sid = (struct samr_sid *)ntdp->sid;
369 	} else {
370 		if (samr_lookup_domain(
371 		    &samr_handle, domain_name, user_info) != 0) {
372 			(void) samr_close_handle(&samr_handle);
373 			mlsvc_free_user_info(user_info);
374 			return (NT_STATUS_NO_SUCH_DOMAIN);
375 		}
376 
377 		sid = (struct samr_sid *)user_info->domain_sid;
378 	}
379 
380 	status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION,
381 	    sid, &domain_handle);
382 	if (status == 0) {
383 		mlsvc_release_user_info(user_info);
384 		status = samr_lookup_domain_names(&domain_handle,
385 		    account_name, user_info);
386 
387 		if (status == 0) {
388 			rid = user_info->rid;
389 			access_mask = STANDARD_RIGHTS_EXECUTE | DELETE;
390 
391 			status = samr_open_user(&domain_handle, access_mask,
392 			    rid, &user_handle);
393 			if (status == NT_STATUS_SUCCESS) {
394 				if (samr_delete_user(&user_handle) != 0)
395 					(void) samr_close_handle(&user_handle);
396 			}
397 		}
398 
399 		(void) samr_close_handle(&domain_handle);
400 	}
401 
402 	(void) samr_close_handle(&samr_handle);
403 	mlsvc_free_user_info(user_info);
404 	return (status);
405 }
406 
407 /*
408  * sam_check_user
409  *
410  * Check to see if user have permission to access computer account.
411  * The user being checked is the specified user for joining the Solaris
412  * host to the domain.
413  */
414 DWORD
415 sam_check_user(char *server, char *domain_name, char *account_name)
416 {
417 	mlsvc_handle_t samr_handle;
418 	mlsvc_handle_t domain_handle;
419 	mlsvc_handle_t user_handle;
420 	smb_userinfo_t *user_info;
421 	struct samr_sid *sid;
422 	DWORD rid;
423 	DWORD access_mask;
424 	DWORD status;
425 	int rc;
426 	char *user = smbrdr_ipc_get_user();
427 
428 	if ((user_info = mlsvc_alloc_user_info()) == 0)
429 		return (NT_STATUS_NO_MEMORY);
430 
431 	rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION,
432 	    &samr_handle);
433 
434 	if (rc != 0) {
435 		mlsvc_free_user_info(user_info);
436 		return (NT_STATUS_OPEN_FAILED);
437 	}
438 
439 	if (samr_handle.context->server_os == NATIVE_OS_WIN2000) {
440 		nt_domain_t *ntdp;
441 
442 		if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) {
443 			(void) lsa_query_account_domain_info();
444 			if ((ntdp = nt_domain_lookup_name(domain_name)) == 0) {
445 				(void) samr_close_handle(&samr_handle);
446 				return (NT_STATUS_NO_SUCH_DOMAIN);
447 			}
448 		}
449 
450 		sid = (struct samr_sid *)ntdp->sid;
451 	} else {
452 		if (samr_lookup_domain(&samr_handle, domain_name, user_info)
453 		    != 0) {
454 			(void) samr_close_handle(&samr_handle);
455 			mlsvc_free_user_info(user_info);
456 			return (NT_STATUS_NO_SUCH_DOMAIN);
457 		}
458 
459 		sid = (struct samr_sid *)user_info->domain_sid;
460 	}
461 
462 	status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, sid,
463 	    &domain_handle);
464 	if (status == 0) {
465 		mlsvc_release_user_info(user_info);
466 		status = samr_lookup_domain_names(&domain_handle, account_name,
467 		    user_info);
468 
469 		if (status == 0) {
470 			rid = user_info->rid;
471 
472 			/*
473 			 * Win2000 client uses this access mask.  The
474 			 * following SAMR user specific rights bits are
475 			 * set: set password, set attributes, and get
476 			 * attributes.
477 			 */
478 
479 			access_mask = 0xb0;
480 
481 			status = samr_open_user(&domain_handle,
482 			    access_mask, rid, &user_handle);
483 			if (status == NT_STATUS_SUCCESS)
484 				(void) samr_close_handle(&user_handle);
485 		}
486 
487 		(void) samr_close_handle(&domain_handle);
488 	}
489 
490 	(void) samr_close_handle(&samr_handle);
491 	mlsvc_free_user_info(user_info);
492 	return (status);
493 }
494 
495 /*
496  * sam_lookup_name
497  *
498  * Lookup an account name in the SAM database on the specified domain
499  * controller. Provides the account RID on success.
500  *
501  * Returns NT status codes.
502  */
503 DWORD
504 sam_lookup_name(char *server, char *domain_name, char *account_name,
505     DWORD *rid_ret)
506 {
507 	mlsvc_handle_t samr_handle;
508 	mlsvc_handle_t domain_handle;
509 	smb_userinfo_t *user_info;
510 	struct samr_sid *domain_sid;
511 	int rc;
512 	DWORD status;
513 	char *user = smbrdr_ipc_get_user();
514 
515 	*rid_ret = 0;
516 
517 	if ((user_info = mlsvc_alloc_user_info()) == 0)
518 		return (NT_STATUS_NO_MEMORY);
519 
520 	rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION,
521 	    &samr_handle);
522 
523 	if (rc != 0) {
524 		mlsvc_free_user_info(user_info);
525 		return (NT_STATUS_OPEN_FAILED);
526 	}
527 
528 	rc = samr_lookup_domain(&samr_handle, domain_name, user_info);
529 	if (rc != 0) {
530 		(void) samr_close_handle(&samr_handle);
531 		mlsvc_free_user_info(user_info);
532 		return (NT_STATUS_NO_SUCH_DOMAIN);
533 	}
534 
535 	domain_sid = (struct samr_sid *)user_info->domain_sid;
536 
537 	status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION,
538 	    domain_sid, &domain_handle);
539 	if (status == 0) {
540 		mlsvc_release_user_info(user_info);
541 
542 		status = samr_lookup_domain_names(&domain_handle,
543 		    account_name, user_info);
544 		if (status == 0)
545 			*rid_ret = user_info->rid;
546 
547 		(void) samr_close_handle(&domain_handle);
548 	}
549 
550 	(void) samr_close_handle(&samr_handle);
551 	mlsvc_free_user_info(user_info);
552 	return (status);
553 }
554 
555 
556 /*
557  * sam_get_local_domains
558  *
559  * Query a remote server to get the list of local domains that it
560  * supports.
561  *
562  * Returns NT status codes.
563  */
564 DWORD
565 sam_get_local_domains(char *server, char *domain_name)
566 {
567 	mlsvc_handle_t samr_handle;
568 	DWORD status;
569 	int rc;
570 	char *user = smbrdr_ipc_get_user();
571 
572 	rc = samr_open(server, domain_name, user, SAM_ENUM_LOCAL_DOMAIN,
573 	    &samr_handle);
574 	if (rc != 0)
575 		return (NT_STATUS_OPEN_FAILED);
576 
577 	status = samr_enum_local_domains(&samr_handle);
578 	(void) samr_close_handle(&samr_handle);
579 	return (status);
580 }
581 
582 /*
583  * sam_oem_password
584  *
585  * Generate an OEM password.
586  */
587 int sam_oem_password(oem_password_t *oem_password, unsigned char *new_password,
588     unsigned char *old_password)
589 {
590 	mts_wchar_t *unicode_password;
591 	int length;
592 
593 #ifdef PBSHORTCUT
594 	assert(sizeof (oem_password_t) == SAM_PASSWORD_516);
595 #endif /* PBSHORTCUT */
596 
597 	length = strlen((char const *)new_password);
598 	unicode_password = alloca((length + 1) * sizeof (mts_wchar_t));
599 
600 	length = smb_auth_qnd_unicode((unsigned short *)unicode_password,
601 	    (char *)new_password, length);
602 	oem_password->length = length;
603 
604 	(void) memcpy(&oem_password->data[512 - length],
605 	    unicode_password, length);
606 
607 	rand_hash((unsigned char *)oem_password, sizeof (oem_password_t),
608 	    old_password, SAM_KEYLEN);
609 
610 	return (0);
611 }
612