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
55static DWORD samr_connect2(char *, char *, char *, DWORD, mlsvc_handle_t *);
56static DWORD samr_connect4(char *, char *, char *, DWORD, mlsvc_handle_t *);
57static DWORD samr_connect5(char *, char *, char *, DWORD, mlsvc_handle_t *);
58
59typedef DWORD (*samr_connop_t)(char *, char *, char *, DWORD,
60    mlsvc_handle_t *);
61
62static 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 */
77DWORD
78samr_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 */
116DWORD
117samr_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*/
157static DWORD
158samr_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*/
197static DWORD
198samr_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*/
244static DWORD
245samr_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 */
292void
293samr_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 */
321DWORD
322samr_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 */
372DWORD
373samr_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 */
414DWORD
415samr_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 */
451int
452samr_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 */
506DWORD
507samr_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 */
568smb_sid_t *
569samr_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 */
606DWORD
607samr_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 */
649uint32_t
650samr_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 */
709DWORD
710samr_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 */
744static int
745samr_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 */
810int
811samr_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 */
860DWORD
861samr_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
888DECL_FIXUP_STRUCT(samr_SetUserInfo_u);
889DECL_FIXUP_STRUCT(samr_SetUserInfo_s);
890DECL_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 */
899DWORD
900samr_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
965DWORD
966samr_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