xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/samr_clnt.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
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) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
25  */
26 
27 /*
28  * Security Accounts Manager RPC (SAMR) client-side interface.
29  *
30  * The SAM is a hierarchical database:
31  * - If you want to talk to the SAM you need a SAM handle.
32  * - If you want to work with a domain, use the SAM handle.
33  *   to obtain a domain handle.
34  * - Use domain handles to obtain user handles etc.
35  *
36  * Be careful about returning null handles to the application.  Use of a
37  * null handle may crash the domain controller if you attempt to use it.
38  */
39 
40 #include <stdio.h>
41 #include <strings.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 #include <netdb.h>
45 #include <sys/param.h>
46 
47 #include <libmlrpc/libmlrpc.h>
48 #include <smbsrv/libsmb.h>
49 #include <smbsrv/libmlsvc.h>
50 #include <smbsrv/smbinfo.h>
51 #include <smb/ntaccess.h>
52 #include <smbsrv/smb_sid.h>
53 #include <samlib.h>
54 
55 static DWORD samr_connect2(char *, char *, char *, DWORD, mlsvc_handle_t *);
56 static DWORD samr_connect4(char *, char *, char *, DWORD, mlsvc_handle_t *);
57 static DWORD samr_connect5(char *, char *, char *, DWORD, mlsvc_handle_t *);
58 
59 typedef DWORD (*samr_connop_t)(char *, char *, char *, DWORD,
60     mlsvc_handle_t *);
61 
62 static int samr_setup_user_info(WORD, struct samr_QueryUserInfo *,
63     union samr_user_info *);
64 
65 /*
66  * samr_open
67  *
68  * Wrapper round samr_connect to ensure that we connect using the server
69  * and domain.  We default to the resource domain if the caller doesn't
70  * supply a server name and a domain name.
71  *
72  * If username argument is NULL, an anonymous connection will be established.
73  * Otherwise, an authenticated connection will be established.
74  *
75  * On success 0 is returned. Otherwise a -ve error code.
76  */
77 DWORD
78 samr_open(char *server, char *domain, char *username, DWORD access_mask,
79     mlsvc_handle_t *samr_handle)
80 {
81 	smb_domainex_t di;
82 	DWORD status;
83 
84 	if (server == NULL || domain == NULL) {
85 		if (!smb_domain_getinfo(&di))
86 			return (NT_STATUS_INTERNAL_ERROR);
87 		server = di.d_dci.dc_name;
88 		domain = di.d_primary.di_nbname;
89 	}
90 
91 	if (username == NULL)
92 		username = MLSVC_ANON_USER;
93 
94 	status = samr_connect(server, domain, username, access_mask,
95 	    samr_handle);
96 
97 	return (status);
98 }
99 
100 
101 /*
102  * samr_connect
103  *
104  * Connect to the SAMR service on the specified server (domain controller).
105  * New SAM connect calls have been added to Windows over time:
106  *
107  *	Windows NT3.x:	SamrConnect
108  *	Windows NT4.0:	SamrConnect2
109  *	Windows 2000:	SamrConnect4
110  *	Windows XP:	SamrConnect5
111  *
112  * Try the calls from most recent to oldest until the server responds with
113  * something other than an RPC protocol error.  We don't use the original
114  * connect call because all supported servers should support SamrConnect2.
115  */
116 DWORD
117 samr_connect(char *server, char *domain, char *username, DWORD access_mask,
118     mlsvc_handle_t *samr_handle)
119 {
120 	static samr_connop_t samr_connop[] = {
121 		samr_connect5,
122 		samr_connect4,
123 		samr_connect2
124 	};
125 
126 	int	n_op = (sizeof (samr_connop) / sizeof (samr_connop[0]));
127 	DWORD	status;
128 	int	i;
129 
130 	status = ndr_rpc_bind(samr_handle, server, domain, username, "SAMR");
131 	if (status)
132 		return (status);
133 
134 	for (i = 0; i < n_op; ++i) {
135 		status = (*samr_connop[i])(server, domain, username,
136 		    access_mask, samr_handle);
137 
138 		if (status == NT_STATUS_SUCCESS)
139 			return (status);
140 	}
141 
142 	ndr_rpc_unbind(samr_handle);
143 	return (status);
144 }
145 
146 /*
147  * samr_connect2
148  *
149  * Connect to the SAM on a Windows NT 4.0 server (domain controller).
150  * We need the domain controller name and, if everything works, we
151  * return a handle.  This function adds the double backslash prefx to
152  * make it easy for applications.
153  *
154  * Returns 0 on success. Otherwise returns a -ve error code.
155  */
156 /*ARGSUSED*/
157 static DWORD
158 samr_connect2(char *server, char *domain, char *username, DWORD access_mask,
159     mlsvc_handle_t *samr_handle)
160 {
161 	struct samr_Connect2 arg;
162 	int opnum;
163 	DWORD status;
164 	int len;
165 
166 	bzero(&arg, sizeof (struct samr_Connect2));
167 	opnum = SAMR_OPNUM_Connect2;
168 	status = NT_STATUS_SUCCESS;
169 
170 	len = strlen(server) + 4;
171 	arg.servername = ndr_rpc_malloc(samr_handle, len);
172 	(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
173 	arg.access_mask = access_mask;
174 
175 	if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
176 		status = NT_STATUS_UNSUCCESSFUL;
177 	} else if (arg.status != 0) {
178 		status = NT_SC_VALUE(arg.status);
179 	} else {
180 		(void) memcpy(&samr_handle->handle, &arg.handle,
181 		    sizeof (ndr_hdid_t));
182 
183 		if (ndr_is_null_handle(samr_handle))
184 			status = NT_STATUS_INVALID_HANDLE;
185 	}
186 
187 	ndr_rpc_release(samr_handle);
188 	return (status);
189 }
190 
191 /*
192  * samr_connect4
193  *
194  * Connect to the SAM on a Windows 2000 domain controller.
195  */
196 /*ARGSUSED*/
197 static DWORD
198 samr_connect4(char *server, char *domain, char *username, DWORD access_mask,
199     mlsvc_handle_t *samr_handle)
200 {
201 	struct samr_Connect4 arg;
202 	int opnum;
203 	DWORD status;
204 	int len;
205 
206 	bzero(&arg, sizeof (struct samr_Connect4));
207 	opnum = SAMR_OPNUM_Connect4;
208 	status = NT_STATUS_SUCCESS;
209 
210 	len = strlen(server) + 4;
211 	arg.servername = ndr_rpc_malloc(samr_handle, len);
212 	(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
213 	arg.revision = SAMR_REVISION_2;
214 	arg.access_mask = access_mask;
215 
216 	if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
217 		status = NT_STATUS_UNSUCCESSFUL;
218 	} else if (arg.status != 0) {
219 		status = NT_SC_VALUE(arg.status);
220 	} else {
221 		(void) memcpy(&samr_handle->handle, &arg.handle,
222 		    sizeof (ndr_hdid_t));
223 
224 		if (ndr_is_null_handle(samr_handle))
225 			status = NT_STATUS_INVALID_HANDLE;
226 	}
227 
228 	ndr_rpc_release(samr_handle);
229 	return (status);
230 }
231 
232 /*
233  * samr_connect5
234  *
235  * Connect to the SAM on a Windows XP domain controller.  On Windows
236  * XP, the server should be the fully qualified DNS domain name with
237  * a double backslash prefix.  At this point, it is assumed that we
238  * need to add the prefix and the DNS domain name here.
239  *
240  * If this call succeeds, a SAMR handle is placed in samr_handle and
241  * zero is returned. Otherwise, a -ve error code is returned.
242  */
243 /*ARGSUSED*/
244 static DWORD
245 samr_connect5(char *server, char *domain, char *username, DWORD access_mask,
246     mlsvc_handle_t *samr_handle)
247 {
248 	struct samr_Connect5 arg;
249 	int len;
250 	int opnum;
251 	DWORD status;
252 
253 	bzero(&arg, sizeof (struct samr_Connect5));
254 	opnum = SAMR_OPNUM_Connect5;
255 	status = NT_STATUS_SUCCESS;
256 
257 	len = strlen(server) + 4;
258 	arg.servername = ndr_rpc_malloc(samr_handle, len);
259 	(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
260 
261 	arg.access_mask = SAM_ENUM_LOCAL_DOMAIN;
262 	arg.unknown2_00000001 = 0x00000001;
263 	arg.unknown3_00000001 = 0x00000001;
264 	arg.unknown4_00000003 = 0x00000003;
265 	arg.unknown5_00000000 = 0x00000000;
266 
267 	if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
268 		status = NT_STATUS_UNSUCCESSFUL;
269 	} else if (arg.status != 0) {
270 		status = NT_SC_VALUE(arg.status);
271 	} else {
272 
273 		(void) memcpy(&samr_handle->handle, &arg.handle,
274 		    sizeof (ndr_hdid_t));
275 
276 		if (ndr_is_null_handle(samr_handle))
277 			status = NT_STATUS_INVALID_HANDLE;
278 	}
279 
280 	ndr_rpc_release(samr_handle);
281 	return (status);
282 }
283 
284 
285 /*
286  * samr_close_handle
287  *
288  * This is function closes any valid handle, i.e. sam, domain, user etc.
289  * If the handle being closed is the top level connect handle, we unbind.
290  * Then we zero out the handle to invalidate it.
291  */
292 void
293 samr_close_handle(mlsvc_handle_t *samr_handle)
294 {
295 	struct samr_CloseHandle arg;
296 	int opnum;
297 
298 	if (ndr_is_null_handle(samr_handle))
299 		return;
300 
301 	opnum = SAMR_OPNUM_CloseHandle;
302 	bzero(&arg, sizeof (struct samr_CloseHandle));
303 	(void) memcpy(&arg.handle, &samr_handle->handle, sizeof (ndr_hdid_t));
304 
305 	(void) ndr_rpc_call(samr_handle, opnum, &arg);
306 	ndr_rpc_release(samr_handle);
307 
308 	if (ndr_is_bind_handle(samr_handle))
309 		ndr_rpc_unbind(samr_handle);
310 
311 	bzero(samr_handle, sizeof (mlsvc_handle_t));
312 }
313 
314 /*
315  * samr_open_domain
316  *
317  * We use a SAM handle to obtain a handle for a domain, specified by
318  * the SID. The SID can be obtain via the LSA interface. A handle for
319  * the domain is returned in domain_handle.
320  */
321 DWORD
322 samr_open_domain(mlsvc_handle_t *samr_handle, DWORD access_mask,
323     struct samr_sid *sid, mlsvc_handle_t *domain_handle)
324 {
325 	struct samr_OpenDomain arg;
326 	int opnum;
327 	DWORD status;
328 
329 	if (ndr_is_null_handle(samr_handle) ||
330 	    sid == NULL || domain_handle == NULL) {
331 		return (NT_STATUS_INVALID_PARAMETER);
332 	}
333 
334 	opnum = SAMR_OPNUM_OpenDomain;
335 	bzero(&arg, sizeof (struct samr_OpenDomain));
336 	(void) memcpy(&arg.handle, &samr_handle->handle, sizeof (ndr_hdid_t));
337 
338 	arg.access_mask = access_mask;
339 	arg.sid = sid;
340 
341 	if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
342 		status = NT_STATUS_UNSUCCESSFUL;
343 	} else if (arg.status != 0) {
344 		status = arg.status;
345 	} else {
346 		status = NT_STATUS_SUCCESS;
347 		ndr_inherit_handle(domain_handle, samr_handle);
348 
349 		(void) memcpy(&domain_handle->handle, &arg.domain_handle,
350 		    sizeof (ndr_hdid_t));
351 
352 		if (ndr_is_null_handle(domain_handle))
353 			status = NT_STATUS_INVALID_HANDLE;
354 	}
355 
356 	if (status != NT_STATUS_SUCCESS)
357 		ndr_rpc_status(samr_handle, opnum, status);
358 
359 	ndr_rpc_release(samr_handle);
360 	return (status);
361 }
362 
363 /*
364  * samr_open_user
365  *
366  * Use a domain handle to obtain a handle for a user, specified by the
367  * user RID. A user RID (effectively a uid) can be obtained via the
368  * LSA interface. A handle for the user is returned in user_handle.
369  * Once you have a user handle it should be possible to query the SAM
370  * for information on that user.
371  */
372 DWORD
373 samr_open_user(mlsvc_handle_t *domain_handle, DWORD access_mask, DWORD rid,
374     mlsvc_handle_t *user_handle)
375 {
376 	struct samr_OpenUser arg;
377 	int opnum;
378 	DWORD status = NT_STATUS_SUCCESS;
379 
380 	if (ndr_is_null_handle(domain_handle) || user_handle == NULL)
381 		return (NT_STATUS_INVALID_PARAMETER);
382 
383 	opnum = SAMR_OPNUM_OpenUser;
384 	bzero(&arg, sizeof (struct samr_OpenUser));
385 	(void) memcpy(&arg.handle, &domain_handle->handle,
386 	    sizeof (ndr_hdid_t));
387 	arg.access_mask = access_mask;
388 	arg.rid = rid;
389 
390 	if (ndr_rpc_call(domain_handle, opnum, &arg) != 0) {
391 		status = NT_STATUS_UNSUCCESSFUL;
392 	} else if (arg.status != 0) {
393 		ndr_rpc_status(domain_handle, opnum, arg.status);
394 		status = NT_SC_VALUE(arg.status);
395 	} else {
396 		ndr_inherit_handle(user_handle, domain_handle);
397 
398 		(void) memcpy(&user_handle->handle, &arg.user_handle,
399 		    sizeof (ndr_hdid_t));
400 
401 		if (ndr_is_null_handle(user_handle))
402 			status = NT_STATUS_INVALID_HANDLE;
403 	}
404 
405 	ndr_rpc_release(domain_handle);
406 	return (status);
407 }
408 
409 /*
410  * samr_delete_user
411  *
412  * Delete the user specified by the user_handle.
413  */
414 DWORD
415 samr_delete_user(mlsvc_handle_t *user_handle)
416 {
417 	struct samr_DeleteUser arg;
418 	int opnum;
419 	DWORD status;
420 
421 	if (ndr_is_null_handle(user_handle))
422 		return (NT_STATUS_INVALID_PARAMETER);
423 
424 	opnum = SAMR_OPNUM_DeleteUser;
425 	bzero(&arg, sizeof (struct samr_DeleteUser));
426 	(void) memcpy(&arg.user_handle, &user_handle->handle,
427 	    sizeof (ndr_hdid_t));
428 
429 	if (ndr_rpc_call(user_handle, opnum, &arg) != 0) {
430 		status = NT_STATUS_INVALID_PARAMETER;
431 	} else if (arg.status != 0) {
432 		ndr_rpc_status(user_handle, opnum, arg.status);
433 		status = NT_SC_VALUE(arg.status);
434 	} else {
435 		status = 0;
436 	}
437 
438 	ndr_rpc_release(user_handle);
439 	return (status);
440 }
441 
442 /*
443  * samr_open_group
444  *
445  * Use a domain handle to obtain a handle for a group, specified by the
446  * group RID. A group RID (effectively a gid) can be obtained via the
447  * LSA interface. A handle for the group is returned in group_handle.
448  * Once you have a group handle it should be possible to query the SAM
449  * for information on that group.
450  */
451 int
452 samr_open_group(
453 	mlsvc_handle_t *domain_handle,
454 	DWORD rid,
455 	mlsvc_handle_t *group_handle)
456 {
457 	struct samr_OpenGroup arg;
458 	int opnum;
459 	int rc;
460 
461 	if (ndr_is_null_handle(domain_handle) || group_handle == NULL)
462 		return (-1);
463 
464 	opnum = SAMR_OPNUM_OpenGroup;
465 	bzero(&arg, sizeof (struct samr_OpenUser));
466 	(void) memcpy(&arg.handle, &domain_handle->handle,
467 	    sizeof (ndr_hdid_t));
468 	arg.access_mask = SAM_LOOKUP_INFORMATION | SAM_ACCESS_USER_READ;
469 	arg.rid = rid;
470 
471 	if ((rc = ndr_rpc_call(domain_handle, opnum, &arg)) != 0)
472 		return (-1);
473 
474 	if (arg.status != 0) {
475 		ndr_rpc_status(domain_handle, opnum, arg.status);
476 		rc = -1;
477 	} else {
478 		ndr_inherit_handle(group_handle, domain_handle);
479 
480 		(void) memcpy(&group_handle->handle, &arg.group_handle,
481 		    sizeof (ndr_hdid_t));
482 
483 		if (ndr_is_null_handle(group_handle))
484 			rc = -1;
485 	}
486 
487 	ndr_rpc_release(domain_handle);
488 	return (rc);
489 }
490 
491 /*
492  * samr_create_user
493  *
494  * Create a user in the domain specified by the domain handle. If this
495  * call is successful, the server will return the RID for the user and
496  * a user handle, which may be used to set or query the SAM.
497  *
498  * Observed status codes:
499  *	NT_STATUS_INVALID_PARAMETER
500  *	NT_STATUS_INVALID_ACCOUNT_NAME
501  *	NT_STATUS_ACCESS_DENIED
502  *	NT_STATUS_USER_EXISTS
503  *
504  * Returns 0 on success. Otherwise returns an NT status code.
505  */
506 DWORD
507 samr_create_user(mlsvc_handle_t *domain_handle, char *username,
508     DWORD account_flags, DWORD *rid, mlsvc_handle_t *user_handle)
509 {
510 	struct samr_CreateUser arg;
511 	ndr_heap_t *heap;
512 	int opnum;
513 	int rc;
514 	DWORD status = 0;
515 
516 	if (ndr_is_null_handle(domain_handle) ||
517 	    username == NULL || rid == NULL) {
518 		return (NT_STATUS_INVALID_PARAMETER);
519 	}
520 
521 	opnum = SAMR_OPNUM_CreateUser;
522 
523 	bzero(&arg, sizeof (struct samr_CreateUser));
524 	(void) memcpy(&arg.handle, &domain_handle->handle,
525 	    sizeof (ndr_hdid_t));
526 
527 	heap = ndr_rpc_get_heap(domain_handle);
528 	ndr_heap_mkvcs(heap, username, (ndr_vcstr_t *)&arg.username);
529 
530 	arg.account_flags = account_flags;
531 	arg.desired_access = 0xE00500B0;
532 
533 	rc = ndr_rpc_call(domain_handle, opnum, &arg);
534 	if (rc != 0) {
535 		status = NT_STATUS_INVALID_PARAMETER;
536 	} else if (arg.status != 0) {
537 		status = NT_SC_VALUE(arg.status);
538 
539 		if (status != NT_STATUS_USER_EXISTS) {
540 			smb_tracef("SamrCreateUser[%s]: %s",
541 			    username, xlate_nt_status(status));
542 		}
543 	} else {
544 		ndr_inherit_handle(user_handle, domain_handle);
545 
546 		(void) memcpy(&user_handle->handle, &arg.user_handle,
547 		    sizeof (ndr_hdid_t));
548 
549 		*rid = arg.rid;
550 
551 		if (ndr_is_null_handle(user_handle))
552 			status = NT_STATUS_INVALID_HANDLE;
553 		else
554 			status = 0;
555 	}
556 
557 	ndr_rpc_release(domain_handle);
558 	return (status);
559 }
560 
561 /*
562  * samr_lookup_domain
563  *
564  * Lookup up the domain SID for the specified domain name. The handle
565  * should be one returned from samr_connect. The allocated memory for
566  * the returned SID must be freed by caller.
567  */
568 smb_sid_t *
569 samr_lookup_domain(mlsvc_handle_t *samr_handle, char *domain_name)
570 {
571 	struct samr_LookupDomain	arg;
572 	smb_sid_t	*domsid = NULL;
573 	int		opnum;
574 	size_t		length;
575 
576 	if (ndr_is_null_handle(samr_handle) || domain_name == NULL)
577 		return (NULL);
578 
579 	opnum = SAMR_OPNUM_LookupDomain;
580 	bzero(&arg, sizeof (struct samr_LookupDomain));
581 
582 	(void) memcpy(&arg.handle, &samr_handle->handle,
583 	    sizeof (samr_handle_t));
584 
585 	length = smb_wcequiv_strlen(domain_name);
586 	length += sizeof (smb_wchar_t);
587 
588 	arg.domain_name.length = length;
589 	arg.domain_name.allosize = length;
590 	arg.domain_name.str = (unsigned char *)domain_name;
591 
592 	if (ndr_rpc_call(samr_handle, opnum, &arg) == 0)
593 		domsid = smb_sid_dup((smb_sid_t *)arg.sid);
594 
595 	ndr_rpc_release(samr_handle);
596 	return (domsid);
597 }
598 
599 /*
600  * samr_enum_local_domains
601  *
602  * Get the list of local domains supported by a server.
603  *
604  * Returns NT status codes.
605  */
606 DWORD
607 samr_enum_local_domains(mlsvc_handle_t *samr_handle)
608 {
609 	struct samr_EnumLocalDomain	arg;
610 	int	opnum;
611 	DWORD	status;
612 
613 	if (ndr_is_null_handle(samr_handle))
614 		return (NT_STATUS_INVALID_PARAMETER);
615 
616 	opnum = SAMR_OPNUM_EnumLocalDomains;
617 	bzero(&arg, sizeof (struct samr_EnumLocalDomain));
618 
619 	(void) memcpy(&arg.handle, &samr_handle->handle,
620 	    sizeof (samr_handle_t));
621 	arg.enum_context = 0;
622 	arg.max_length = 0x00002000;	/* Value used by NT */
623 
624 	if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
625 		status = NT_STATUS_INVALID_PARAMETER;
626 	} else {
627 		status = NT_SC_VALUE(arg.status);
628 
629 		/*
630 		 * Handle none-mapped status quietly.
631 		 */
632 		if (status != NT_STATUS_NONE_MAPPED)
633 			ndr_rpc_status(samr_handle, opnum, arg.status);
634 	}
635 
636 	ndr_rpc_release(samr_handle);
637 	return (status);
638 }
639 
640 /*
641  * samr_lookup_domain_names
642  *
643  * Lookup up the given name in the domain specified by domain_handle.
644  * Upon a successful lookup the information is returned in the account
645  * arg and caller must free allocated memories by calling smb_account_free().
646  *
647  * Returns NT status codes.
648  */
649 uint32_t
650 samr_lookup_domain_names(mlsvc_handle_t *domain_handle, char *name,
651     smb_account_t *account)
652 {
653 	struct samr_LookupNames	arg;
654 	int			opnum;
655 	uint32_t		status;
656 	size_t			length;
657 
658 	if (ndr_is_null_handle(domain_handle) ||
659 	    name == NULL || account == NULL) {
660 		return (NT_STATUS_INVALID_PARAMETER);
661 	}
662 
663 	bzero(account, sizeof (smb_account_t));
664 	opnum = SAMR_OPNUM_LookupNames;
665 	bzero(&arg, sizeof (struct samr_LookupNames));
666 
667 	(void) memcpy(&arg.handle, &domain_handle->handle,
668 	    sizeof (samr_handle_t));
669 	arg.n_entry = 1;
670 	arg.max_n_entry = 1000;
671 	arg.index = 0;
672 	arg.total = 1;
673 
674 	length = smb_wcequiv_strlen(name);
675 	length += sizeof (smb_wchar_t);
676 
677 	arg.name.length = length;
678 	arg.name.allosize = length;
679 	arg.name.str = (unsigned char *)name;
680 
681 	if (ndr_rpc_call(domain_handle, opnum, &arg) != 0) {
682 		status = NT_STATUS_INVALID_PARAMETER;
683 	} else if (arg.status != NT_STATUS_SUCCESS) {
684 		status = NT_SC_VALUE(arg.status);
685 
686 		/*
687 		 * Handle none-mapped status quietly.
688 		 */
689 		if (status != NT_STATUS_NONE_MAPPED)
690 			ndr_rpc_status(domain_handle, opnum, arg.status);
691 	} else {
692 		account->a_type = arg.rid_types.rid_type[0];
693 		account->a_rid = arg.rids.rid[0];
694 		status = NT_STATUS_SUCCESS;
695 	}
696 
697 	ndr_rpc_release(domain_handle);
698 	return (status);
699 }
700 
701 /*
702  * samr_query_user_info
703  *
704  * Query information on a specific user. The handle must be a valid
705  * user handle obtained via samr_open_user.
706  *
707  * Returns 0 on success, otherwise returns NT status code.
708  */
709 DWORD
710 samr_query_user_info(mlsvc_handle_t *user_handle, WORD switch_value,
711     union samr_user_info *user_info)
712 {
713 	struct samr_QueryUserInfo	arg;
714 	int	opnum;
715 
716 	if (ndr_is_null_handle(user_handle) || user_info == 0)
717 		return (NT_STATUS_INTERNAL_ERROR);
718 
719 	opnum = SAMR_OPNUM_QueryUserInfo;
720 	bzero(&arg, sizeof (struct samr_QueryUserInfo));
721 
722 	(void) memcpy(&arg.user_handle, &user_handle->handle,
723 	    sizeof (samr_handle_t));
724 	arg.switch_value = switch_value;
725 
726 	if (ndr_rpc_call(user_handle, opnum, &arg) != 0)
727 		arg.status = RPC_NT_CALL_FAILED;
728 
729 	if (arg.status == 0)
730 		(void) samr_setup_user_info(switch_value, &arg, user_info);
731 
732 	return (arg.status);
733 }
734 
735 /*
736  * samr_setup_user_info
737  *
738  * Private function to set up the samr_user_info data. Dependent on
739  * the switch value this function may use strdup which will malloc
740  * memory. The caller is responsible for deallocating this memory.
741  *
742  * Returns 0 on success, otherwise returns -1.
743  */
744 static int
745 samr_setup_user_info(WORD switch_value,
746     struct samr_QueryUserInfo *arg, union samr_user_info *user_info)
747 {
748 	struct samr_QueryUserInfo1	*info1;
749 	struct samr_QueryUserInfo6	*info6;
750 
751 	switch (switch_value) {
752 	case 1:
753 		info1 = &arg->ru.info1;
754 		user_info->info1.username = strdup(
755 		    (char const *)info1->username.str);
756 		user_info->info1.fullname = strdup(
757 		    (char const *)info1->fullname.str);
758 		user_info->info1.description = strdup(
759 		    (char const *)info1->description.str);
760 		user_info->info1.unknown = 0;
761 		user_info->info1.group_rid = info1->group_rid;
762 		return (0);
763 
764 	case 6:
765 		info6 = &arg->ru.info6;
766 		user_info->info6.username = strdup(
767 		    (char const *)info6->username.str);
768 		user_info->info6.fullname = strdup(
769 		    (char const *)info6->fullname.str);
770 		return (0);
771 
772 	case 7:
773 		user_info->info7.username = strdup(
774 		    (char const *)arg->ru.info7.username.str);
775 		return (0);
776 
777 	case 8:
778 		user_info->info8.fullname = strdup(
779 		    (char const *)arg->ru.info8.fullname.str);
780 		return (0);
781 
782 	case 9:
783 		user_info->info9.group_rid = arg->ru.info9.group_rid;
784 		return (0);
785 
786 	case 16:
787 		user_info->info16.acct_ctrl =
788 		    arg->ru.info16.UserAccountControl;
789 		return (0);
790 
791 	default:
792 		break;
793 	};
794 
795 	return (-1);
796 }
797 
798 /*
799  * samr_query_user_groups
800  *
801  * Query the groups for a specific user. The handle must be a valid
802  * user handle obtained via samr_open_user. The list of groups is
803  * returned in group_info. Note that group_info->groups is allocated
804  * using malloc. The caller is responsible for deallocating this
805  * memory when it is no longer required. If group_info->n_entry is 0
806  * then no memory was allocated.
807  *
808  * Returns 0 on success, otherwise returns -1.
809  */
810 int
811 samr_query_user_groups(mlsvc_handle_t *user_handle, int *n_groups,
812     struct samr_UserGroups **groups)
813 {
814 	struct samr_QueryUserGroups arg;
815 	int	opnum;
816 	int	rc;
817 	int	nbytes;
818 
819 	if (ndr_is_null_handle(user_handle))
820 		return (-1);
821 
822 	opnum = SAMR_OPNUM_QueryUserGroups;
823 	bzero(&arg, sizeof (struct samr_QueryUserGroups));
824 
825 	(void) memcpy(&arg.user_handle, &user_handle->handle,
826 	    sizeof (samr_handle_t));
827 
828 	rc = ndr_rpc_call(user_handle, opnum, &arg);
829 	if (rc == 0) {
830 		if (arg.info == 0) {
831 			rc = -1;
832 		} else {
833 			nbytes = arg.info->n_entry *
834 			    sizeof (struct samr_UserGroups);
835 
836 			if ((*groups = malloc(nbytes)) == NULL) {
837 				*n_groups = 0;
838 				rc = -1;
839 			} else {
840 				*n_groups = arg.info->n_entry;
841 				bcopy(arg.info->groups, *groups, nbytes);
842 			}
843 		}
844 	}
845 
846 	ndr_rpc_release(user_handle);
847 	return (rc);
848 }
849 
850 /*
851  * samr_get_user_pwinfo
852  *
853  * Get some user password info. I'm not sure what this is yet but it is
854  * part of the create user sequence. The handle must be a valid user
855  * handle. Since I don't know what this is returning, I haven't provided
856  * any return data yet.
857  *
858  * Returns 0 on success. Otherwise returns an NT status code.
859  */
860 DWORD
861 samr_get_user_pwinfo(mlsvc_handle_t *user_handle)
862 {
863 	struct samr_GetUserPwInfo arg;
864 	int	opnum;
865 	DWORD	status;
866 
867 	if (ndr_is_null_handle(user_handle))
868 		return (NT_STATUS_INVALID_PARAMETER);
869 
870 	opnum = SAMR_OPNUM_GetUserPwInfo;
871 	bzero(&arg, sizeof (struct samr_GetUserPwInfo));
872 	(void) memcpy(&arg.user_handle, &user_handle->handle,
873 	    sizeof (samr_handle_t));
874 
875 	if (ndr_rpc_call(user_handle, opnum, &arg) != 0) {
876 		status = NT_STATUS_INVALID_PARAMETER;
877 	} else if (arg.status != 0) {
878 		ndr_rpc_status(user_handle, opnum, arg.status);
879 		status = NT_SC_VALUE(arg.status);
880 	} else {
881 		status = 0;
882 	}
883 
884 	ndr_rpc_release(user_handle);
885 	return (status);
886 }
887 
888 DECL_FIXUP_STRUCT(samr_SetUserInfo_u);
889 DECL_FIXUP_STRUCT(samr_SetUserInfo_s);
890 DECL_FIXUP_STRUCT(samr_SetUserInfo);
891 
892 /*
893  * samr_set_user_info
894  *
895  * Returns 0 on success. Otherwise returns an NT status code.
896  * NT status codes observed so far:
897  *	NT_STATUS_WRONG_PASSWORD
898  */
899 DWORD
900 samr_set_user_info(
901 	mlsvc_handle_t *user_handle,
902 	int info_level,
903 	void *info_buf)
904 {
905 	struct samr_SetUserInfo arg;
906 	uint16_t usize, tsize;
907 	int opnum;
908 
909 	if (ndr_is_null_handle(user_handle))
910 		return (NT_STATUS_INTERNAL_ERROR);
911 
912 	/*
913 	 * Only support a few levels
914 	 * MS-SAMR: UserInternal4Information
915 	 */
916 	switch (info_level) {
917 	case 16: /* samr_SetUserInfo16 */
918 		usize = sizeof (struct samr_SetUserInfo16);
919 		break;
920 	case 21: /* samr_SetUserInfo21 */
921 		usize = sizeof (struct samr_SetUserInfo21);
922 		break;
923 	case 23: /* samr_SetUserInfo23 */
924 		usize = sizeof (struct samr_SetUserInfo23);
925 		break;
926 	case 24: /* samr_SetUserInfo24 */
927 		usize = sizeof (struct samr_SetUserInfo24);
928 		break;
929 	default:
930 		return (NT_STATUS_INVALID_LEVEL);
931 	}
932 
933 	/*
934 	 * OK, now this gets really ugly, because
935 	 * ndrgen doesn't do unions correctly.
936 	 */
937 	FIXUP_PDU_SIZE(samr_SetUserInfo_u, usize);
938 	tsize = usize + (2 * sizeof (WORD));
939 	FIXUP_PDU_SIZE(samr_SetUserInfo_s, tsize);
940 	tsize += sizeof (ndr_request_hdr_t) + sizeof (DWORD);
941 	FIXUP_PDU_SIZE(samr_SetUserInfo, tsize);
942 
943 	opnum = SAMR_OPNUM_SetUserInfo;
944 	bzero(&arg, sizeof (arg));
945 	(void) memcpy(&arg.user_handle, &user_handle->handle,
946 	    sizeof (samr_handle_t));
947 	arg.info.info_level = info_level;
948 	arg.info.switch_value = info_level;
949 	(void) memcpy(&arg.info.ru, info_buf, usize);
950 
951 	if (ndr_rpc_call(user_handle, opnum, &arg) != 0)
952 		arg.status = RPC_NT_CALL_FAILED;
953 	else if (arg.status != 0)
954 		ndr_rpc_status(user_handle, opnum, arg.status);
955 
956 	ndr_rpc_release(user_handle);
957 	return (arg.status);
958 }
959 
960 /*
961  * Client side wrapper for SamrUnicodeChangePasswordUser2
962  * [MS-SAMR 3.1.5.10.3]
963  */
964 
965 DWORD
966 samr_change_password(
967 	mlsvc_handle_t *handle,
968 	char *server,
969 	char *account,
970 	struct samr_encr_passwd *newpw,
971 	struct samr_encr_hash *oldpw)
972 {
973 	static struct samr_encr_passwd zero_newpw;
974 	static struct samr_encr_hash zero_oldpw;
975 	struct samr_ChangePasswordUser2 arg;
976 	int opnum = SAMR_OPNUM_ChangePasswordUser2;
977 	char *slashserver;
978 	int len;
979 
980 	(void) memset(&arg, 0, sizeof (arg));
981 
982 	/* Need server name with slashes */
983 	len = 2 + strlen(server) + 1;
984 	slashserver = ndr_rpc_malloc(handle, len);
985 	if (slashserver == NULL)
986 		return (NT_STATUS_NO_MEMORY);
987 	(void) snprintf(slashserver, len, "\\\\%s", server);
988 
989 	arg.servername = ndr_rpc_malloc(handle, sizeof (samr_string_t));
990 	if (arg.servername == NULL)
991 		return (NT_STATUS_NO_MEMORY);
992 	len = smb_wcequiv_strlen(slashserver);
993 	if (len < 1)
994 		return (NT_STATUS_INVALID_PARAMETER);
995 	len += 2;	/* the WC null */
996 	arg.servername->length = len;
997 	arg.servername->allosize = len;
998 	arg.servername->str = (uint8_t *)slashserver;
999 
1000 	arg.username = ndr_rpc_malloc(handle, sizeof (samr_string_t));
1001 	if (arg.username == NULL)
1002 		return (NT_STATUS_NO_MEMORY);
1003 	len = smb_wcequiv_strlen(account);
1004 	if (len < 1)
1005 		return (NT_STATUS_INVALID_PARAMETER);
1006 	len += 2;	/* the WC null */
1007 	arg.username->length = len;
1008 	arg.username->allosize = len;
1009 	arg.username->str = (uint8_t *)account;
1010 
1011 	arg.nt_newpw = newpw;
1012 	arg.nt_oldpw = oldpw;
1013 
1014 	arg.lm_newpw = &zero_newpw;
1015 	arg.lm_oldpw = &zero_oldpw;
1016 
1017 	if (ndr_rpc_call(handle, opnum, &arg) != 0)
1018 		arg.status = RPC_NT_CALL_FAILED;
1019 	else if (arg.status != 0)
1020 		ndr_rpc_status(handle, opnum, arg.status);
1021 
1022 	ndr_rpc_release(handle);
1023 	return (arg.status);
1024 }
1025