1da6c28aaSamw /*
2da6c28aaSamw  * CDDL HEADER START
3da6c28aaSamw  *
4da6c28aaSamw  * The contents of this file are subject to the terms of the
5da6c28aaSamw  * Common Development and Distribution License (the "License").
6da6c28aaSamw  * You may not use this file except in compliance with the License.
7da6c28aaSamw  *
8da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw  * See the License for the specific language governing permissions
11da6c28aaSamw  * and limitations under the License.
12da6c28aaSamw  *
13da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw  *
19da6c28aaSamw  * CDDL HEADER END
20da6c28aaSamw  */
21148c5f43SAlan Wright 
22da6c28aaSamw /*
23148c5f43SAlan Wright  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24*adee6784SGordon Ross  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
25da6c28aaSamw  */
26da6c28aaSamw 
27da6c28aaSamw /*
28fe1c642dSBill Krier  * Security Accounts Manager RPC (SAMR) client-side interface.
29fe1c642dSBill Krier  *
30fe1c642dSBill Krier  * The SAM is a hierarchical database:
31fe1c642dSBill Krier  * - If you want to talk to the SAM you need a SAM handle.
32fe1c642dSBill Krier  * - If you want to work with a domain, use the SAM handle.
33fe1c642dSBill Krier  *   to obtain a domain handle.
34fe1c642dSBill Krier  * - Use domain handles to obtain user handles etc.
35fe1c642dSBill Krier  *
36fe1c642dSBill Krier  * Be careful about returning null handles to the application.  Use of a
37fe1c642dSBill Krier  * null handle may crash the domain controller if you attempt to use it.
38da6c28aaSamw  */
39da6c28aaSamw 
40da6c28aaSamw #include <stdio.h>
41da6c28aaSamw #include <strings.h>
42fe1c642dSBill Krier #include <stdlib.h>
43da6c28aaSamw #include <unistd.h>
44da6c28aaSamw #include <netdb.h>
45da6c28aaSamw #include <sys/param.h>
46da6c28aaSamw 
473299f39fSGordon Ross #include <libmlrpc/libmlrpc.h>
48da6c28aaSamw #include <smbsrv/libsmb.h>
498d7e4166Sjose borrego #include <smbsrv/libmlsvc.h>
50da6c28aaSamw #include <smbsrv/smbinfo.h>
51*adee6784SGordon Ross #include <smb/ntaccess.h>
52fe1c642dSBill Krier #include <smbsrv/smb_sid.h>
538d7e4166Sjose borrego #include <samlib.h>
54da6c28aaSamw 
55da6c28aaSamw static DWORD samr_connect2(char *, char *, char *, DWORD, mlsvc_handle_t *);
56da6c28aaSamw static DWORD samr_connect4(char *, char *, char *, DWORD, mlsvc_handle_t *);
57cb174861Sjoyce mcintosh static DWORD samr_connect5(char *, char *, char *, DWORD, mlsvc_handle_t *);
58da6c28aaSamw 
59a0aa776eSAlan Wright typedef DWORD (*samr_connop_t)(char *, char *, char *, DWORD,
60a0aa776eSAlan Wright     mlsvc_handle_t *);
61a0aa776eSAlan Wright 
62fe1c642dSBill Krier static int samr_setup_user_info(WORD, struct samr_QueryUserInfo *,
63fe1c642dSBill Krier     union samr_user_info *);
64fe1c642dSBill Krier 
65da6c28aaSamw /*
66da6c28aaSamw  * samr_open
67da6c28aaSamw  *
688d7e4166Sjose borrego  * Wrapper round samr_connect to ensure that we connect using the server
698d7e4166Sjose borrego  * and domain.  We default to the resource domain if the caller doesn't
708d7e4166Sjose borrego  * supply a server name and a domain name.
71da6c28aaSamw  *
7255bf511dSas  * If username argument is NULL, an anonymous connection will be established.
7355bf511dSas  * Otherwise, an authenticated connection will be established.
7455bf511dSas  *
75da6c28aaSamw  * On success 0 is returned. Otherwise a -ve error code.
76da6c28aaSamw  */
771ed6b69aSGordon Ross DWORD
samr_open(char * server,char * domain,char * username,DWORD access_mask,mlsvc_handle_t * samr_handle)7855bf511dSas samr_open(char *server, char *domain, char *username, DWORD access_mask,
7955bf511dSas     mlsvc_handle_t *samr_handle)
80da6c28aaSamw {
81a0aa776eSAlan Wright 	smb_domainex_t di;
821ed6b69aSGordon Ross 	DWORD status;
83da6c28aaSamw 
84da6c28aaSamw 	if (server == NULL || domain == NULL) {
858d7e4166Sjose borrego 		if (!smb_domain_getinfo(&di))
861ed6b69aSGordon Ross 			return (NT_STATUS_INTERNAL_ERROR);
87b3700b07SGordon Ross 		server = di.d_dci.dc_name;
88a0aa776eSAlan Wright 		domain = di.d_primary.di_nbname;
89da6c28aaSamw 	}
90da6c28aaSamw 
9155bf511dSas 	if (username == NULL)
9255bf511dSas 		username = MLSVC_ANON_USER;
93da6c28aaSamw 
941ed6b69aSGordon Ross 	status = samr_connect(server, domain, username, access_mask,
951ed6b69aSGordon Ross 	    samr_handle);
961ed6b69aSGordon Ross 
971ed6b69aSGordon Ross 	return (status);
98da6c28aaSamw }
99da6c28aaSamw 
100da6c28aaSamw 
101da6c28aaSamw /*
102da6c28aaSamw  * samr_connect
103da6c28aaSamw  *
104a0aa776eSAlan Wright  * Connect to the SAMR service on the specified server (domain controller).
105a0aa776eSAlan Wright  * New SAM connect calls have been added to Windows over time:
106da6c28aaSamw  *
107a0aa776eSAlan Wright  *	Windows NT3.x:	SamrConnect
108a0aa776eSAlan Wright  *	Windows NT4.0:	SamrConnect2
109cb174861Sjoyce mcintosh  *	Windows 2000:	SamrConnect4
110cb174861Sjoyce mcintosh  *	Windows XP:	SamrConnect5
111a0aa776eSAlan Wright  *
112a0aa776eSAlan Wright  * Try the calls from most recent to oldest until the server responds with
113a0aa776eSAlan Wright  * something other than an RPC protocol error.  We don't use the original
114a0aa776eSAlan Wright  * connect call because all supported servers should support SamrConnect2.
115da6c28aaSamw  */
1161ed6b69aSGordon Ross DWORD
samr_connect(char * server,char * domain,char * username,DWORD access_mask,mlsvc_handle_t * samr_handle)117da6c28aaSamw samr_connect(char *server, char *domain, char *username, DWORD access_mask,
118da6c28aaSamw     mlsvc_handle_t *samr_handle)
119da6c28aaSamw {
120a0aa776eSAlan Wright 	static samr_connop_t samr_connop[] = {
121cb174861Sjoyce mcintosh 		samr_connect5,
122a0aa776eSAlan Wright 		samr_connect4,
123a0aa776eSAlan Wright 		samr_connect2
124a0aa776eSAlan Wright 	};
125a0aa776eSAlan Wright 
126a0aa776eSAlan Wright 	int	n_op = (sizeof (samr_connop) / sizeof (samr_connop[0]));
127a0aa776eSAlan Wright 	DWORD	status;
128a0aa776eSAlan Wright 	int	i;
129da6c28aaSamw 
1301ed6b69aSGordon Ross 	status = ndr_rpc_bind(samr_handle, server, domain, username, "SAMR");
1311ed6b69aSGordon Ross 	if (status)
1321ed6b69aSGordon Ross 		return (status);
133da6c28aaSamw 
134a0aa776eSAlan Wright 	for (i = 0; i < n_op; ++i) {
135a0aa776eSAlan Wright 		status = (*samr_connop[i])(server, domain, username,
136a0aa776eSAlan Wright 		    access_mask, samr_handle);
137a0aa776eSAlan Wright 
138cb174861Sjoyce mcintosh 		if (status == NT_STATUS_SUCCESS)
1391ed6b69aSGordon Ross 			return (status);
140da6c28aaSamw 	}
141da6c28aaSamw 
142cb174861Sjoyce mcintosh 	ndr_rpc_unbind(samr_handle);
143da6c28aaSamw 	return (status);
144da6c28aaSamw }
145da6c28aaSamw 
146da6c28aaSamw /*
147da6c28aaSamw  * samr_connect2
148da6c28aaSamw  *
149da6c28aaSamw  * Connect to the SAM on a Windows NT 4.0 server (domain controller).
150da6c28aaSamw  * We need the domain controller name and, if everything works, we
151da6c28aaSamw  * return a handle.  This function adds the double backslash prefx to
152da6c28aaSamw  * make it easy for applications.
153da6c28aaSamw  *
154da6c28aaSamw  * Returns 0 on success. Otherwise returns a -ve error code.
155da6c28aaSamw  */
156da6c28aaSamw /*ARGSUSED*/
157da6c28aaSamw static DWORD
samr_connect2(char * server,char * domain,char * username,DWORD access_mask,mlsvc_handle_t * samr_handle)158da6c28aaSamw samr_connect2(char *server, char *domain, char *username, DWORD access_mask,
159da6c28aaSamw     mlsvc_handle_t *samr_handle)
160da6c28aaSamw {
161cb174861Sjoyce mcintosh 	struct samr_Connect2 arg;
162da6c28aaSamw 	int opnum;
163da6c28aaSamw 	DWORD status;
164da6c28aaSamw 	int len;
165da6c28aaSamw 
166cb174861Sjoyce mcintosh 	bzero(&arg, sizeof (struct samr_Connect2));
167cb174861Sjoyce mcintosh 	opnum = SAMR_OPNUM_Connect2;
168da6c28aaSamw 	status = NT_STATUS_SUCCESS;
169da6c28aaSamw 
170da6c28aaSamw 	len = strlen(server) + 4;
1718d7e4166Sjose borrego 	arg.servername = ndr_rpc_malloc(samr_handle, len);
172da6c28aaSamw 	(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
173da6c28aaSamw 	arg.access_mask = access_mask;
174da6c28aaSamw 
1758d7e4166Sjose borrego 	if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
176da6c28aaSamw 		status = NT_STATUS_UNSUCCESSFUL;
177da6c28aaSamw 	} else if (arg.status != 0) {
178da6c28aaSamw 		status = NT_SC_VALUE(arg.status);
179da6c28aaSamw 	} else {
180da6c28aaSamw 		(void) memcpy(&samr_handle->handle, &arg.handle,
1818d7e4166Sjose borrego 		    sizeof (ndr_hdid_t));
182da6c28aaSamw 
1838d7e4166Sjose borrego 		if (ndr_is_null_handle(samr_handle))
184da6c28aaSamw 			status = NT_STATUS_INVALID_HANDLE;
185da6c28aaSamw 	}
186da6c28aaSamw 
1878d7e4166Sjose borrego 	ndr_rpc_release(samr_handle);
188da6c28aaSamw 	return (status);
189da6c28aaSamw }
190da6c28aaSamw 
191da6c28aaSamw /*
192cb174861Sjoyce mcintosh  * samr_connect4
193da6c28aaSamw  *
194da6c28aaSamw  * Connect to the SAM on a Windows 2000 domain controller.
195da6c28aaSamw  */
196da6c28aaSamw /*ARGSUSED*/
197da6c28aaSamw static DWORD
samr_connect4(char * server,char * domain,char * username,DWORD access_mask,mlsvc_handle_t * samr_handle)198cb174861Sjoyce mcintosh samr_connect4(char *server, char *domain, char *username, DWORD access_mask,
199da6c28aaSamw     mlsvc_handle_t *samr_handle)
200da6c28aaSamw {
201cb174861Sjoyce mcintosh 	struct samr_Connect4 arg;
202da6c28aaSamw 	int opnum;
203da6c28aaSamw 	DWORD status;
204da6c28aaSamw 	int len;
205da6c28aaSamw 
206cb174861Sjoyce mcintosh 	bzero(&arg, sizeof (struct samr_Connect4));
207cb174861Sjoyce mcintosh 	opnum = SAMR_OPNUM_Connect4;
208da6c28aaSamw 	status = NT_STATUS_SUCCESS;
209da6c28aaSamw 
210da6c28aaSamw 	len = strlen(server) + 4;
2118d7e4166Sjose borrego 	arg.servername = ndr_rpc_malloc(samr_handle, len);
212da6c28aaSamw 	(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
213a0aa776eSAlan Wright 	arg.revision = SAMR_REVISION_2;
214da6c28aaSamw 	arg.access_mask = access_mask;
215da6c28aaSamw 
2168d7e4166Sjose borrego 	if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
217da6c28aaSamw 		status = NT_STATUS_UNSUCCESSFUL;
218da6c28aaSamw 	} else if (arg.status != 0) {
219da6c28aaSamw 		status = NT_SC_VALUE(arg.status);
220da6c28aaSamw 	} else {
221da6c28aaSamw 		(void) memcpy(&samr_handle->handle, &arg.handle,
2228d7e4166Sjose borrego 		    sizeof (ndr_hdid_t));
223da6c28aaSamw 
2248d7e4166Sjose borrego 		if (ndr_is_null_handle(samr_handle))
225da6c28aaSamw 			status = NT_STATUS_INVALID_HANDLE;
226da6c28aaSamw 	}
227da6c28aaSamw 
2288d7e4166Sjose borrego 	ndr_rpc_release(samr_handle);
229da6c28aaSamw 	return (status);
230da6c28aaSamw }
231da6c28aaSamw 
232da6c28aaSamw /*
233cb174861Sjoyce mcintosh  * samr_connect5
234da6c28aaSamw  *
235da6c28aaSamw  * Connect to the SAM on a Windows XP domain controller.  On Windows
236da6c28aaSamw  * XP, the server should be the fully qualified DNS domain name with
237da6c28aaSamw  * a double backslash prefix.  At this point, it is assumed that we
238da6c28aaSamw  * need to add the prefix and the DNS domain name here.
239da6c28aaSamw  *
240da6c28aaSamw  * If this call succeeds, a SAMR handle is placed in samr_handle and
241da6c28aaSamw  * zero is returned. Otherwise, a -ve error code is returned.
242da6c28aaSamw  */
243da6c28aaSamw /*ARGSUSED*/
244da6c28aaSamw static DWORD
samr_connect5(char * server,char * domain,char * username,DWORD access_mask,mlsvc_handle_t * samr_handle)245cb174861Sjoyce mcintosh samr_connect5(char *server, char *domain, char *username, DWORD access_mask,
246da6c28aaSamw     mlsvc_handle_t *samr_handle)
247da6c28aaSamw {
248cb174861Sjoyce mcintosh 	struct samr_Connect5 arg;
249da6c28aaSamw 	int len;
250da6c28aaSamw 	int opnum;
251da6c28aaSamw 	DWORD status;
252da6c28aaSamw 
253cb174861Sjoyce mcintosh 	bzero(&arg, sizeof (struct samr_Connect5));
254cb174861Sjoyce mcintosh 	opnum = SAMR_OPNUM_Connect5;
255da6c28aaSamw 	status = NT_STATUS_SUCCESS;
256da6c28aaSamw 
2571ed6b69aSGordon Ross 	len = strlen(server) + 4;
2588d7e4166Sjose borrego 	arg.servername = ndr_rpc_malloc(samr_handle, len);
2591ed6b69aSGordon Ross 	(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
260da6c28aaSamw 
261da6c28aaSamw 	arg.access_mask = SAM_ENUM_LOCAL_DOMAIN;
262da6c28aaSamw 	arg.unknown2_00000001 = 0x00000001;
263da6c28aaSamw 	arg.unknown3_00000001 = 0x00000001;
264da6c28aaSamw 	arg.unknown4_00000003 = 0x00000003;
265da6c28aaSamw 	arg.unknown5_00000000 = 0x00000000;
266da6c28aaSamw 
2678d7e4166Sjose borrego 	if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
268da6c28aaSamw 		status = NT_STATUS_UNSUCCESSFUL;
269da6c28aaSamw 	} else if (arg.status != 0) {
270da6c28aaSamw 		status = NT_SC_VALUE(arg.status);
271da6c28aaSamw 	} else {
272da6c28aaSamw 
273da6c28aaSamw 		(void) memcpy(&samr_handle->handle, &arg.handle,
2748d7e4166Sjose borrego 		    sizeof (ndr_hdid_t));
275da6c28aaSamw 
2768d7e4166Sjose borrego 		if (ndr_is_null_handle(samr_handle))
277da6c28aaSamw 			status = NT_STATUS_INVALID_HANDLE;
278da6c28aaSamw 	}
279da6c28aaSamw 
2808d7e4166Sjose borrego 	ndr_rpc_release(samr_handle);
281da6c28aaSamw 	return (status);
282da6c28aaSamw }
283da6c28aaSamw 
284da6c28aaSamw 
285da6c28aaSamw /*
286da6c28aaSamw  * samr_close_handle
287da6c28aaSamw  *
288da6c28aaSamw  * This is function closes any valid handle, i.e. sam, domain, user etc.
2898d7e4166Sjose borrego  * If the handle being closed is the top level connect handle, we unbind.
2908d7e4166Sjose borrego  * Then we zero out the handle to invalidate it.
291da6c28aaSamw  */
2921ed6b69aSGordon Ross void
samr_close_handle(mlsvc_handle_t * samr_handle)2938d7e4166Sjose borrego samr_close_handle(mlsvc_handle_t *samr_handle)
294da6c28aaSamw {
295da6c28aaSamw 	struct samr_CloseHandle arg;
296da6c28aaSamw 	int opnum;
297da6c28aaSamw 
2988d7e4166Sjose borrego 	if (ndr_is_null_handle(samr_handle))
2991ed6b69aSGordon Ross 		return;
300da6c28aaSamw 
301da6c28aaSamw 	opnum = SAMR_OPNUM_CloseHandle;
302da6c28aaSamw 	bzero(&arg, sizeof (struct samr_CloseHandle));
3038d7e4166Sjose borrego 	(void) memcpy(&arg.handle, &samr_handle->handle, sizeof (ndr_hdid_t));
304da6c28aaSamw 
3058d7e4166Sjose borrego 	(void) ndr_rpc_call(samr_handle, opnum, &arg);
3068d7e4166Sjose borrego 	ndr_rpc_release(samr_handle);
307da6c28aaSamw 
3088d7e4166Sjose borrego 	if (ndr_is_bind_handle(samr_handle))
3098d7e4166Sjose borrego 		ndr_rpc_unbind(samr_handle);
310da6c28aaSamw 
3118d7e4166Sjose borrego 	bzero(samr_handle, sizeof (mlsvc_handle_t));
312da6c28aaSamw }
313da6c28aaSamw 
314da6c28aaSamw /*
315da6c28aaSamw  * samr_open_domain
316da6c28aaSamw  *
317da6c28aaSamw  * We use a SAM handle to obtain a handle for a domain, specified by
318da6c28aaSamw  * the SID. The SID can be obtain via the LSA interface. A handle for
319da6c28aaSamw  * the domain is returned in domain_handle.
320da6c28aaSamw  */
321da6c28aaSamw DWORD
samr_open_domain(mlsvc_handle_t * samr_handle,DWORD access_mask,struct samr_sid * sid,mlsvc_handle_t * domain_handle)322da6c28aaSamw samr_open_domain(mlsvc_handle_t *samr_handle, DWORD access_mask,
323da6c28aaSamw     struct samr_sid *sid, mlsvc_handle_t *domain_handle)
324da6c28aaSamw {
325da6c28aaSamw 	struct samr_OpenDomain arg;
326da6c28aaSamw 	int opnum;
327da6c28aaSamw 	DWORD status;
328da6c28aaSamw 
3298d7e4166Sjose borrego 	if (ndr_is_null_handle(samr_handle) ||
3308d7e4166Sjose borrego 	    sid == NULL || domain_handle == NULL) {
331da6c28aaSamw 		return (NT_STATUS_INVALID_PARAMETER);
332da6c28aaSamw 	}
333da6c28aaSamw 
334da6c28aaSamw 	opnum = SAMR_OPNUM_OpenDomain;
335da6c28aaSamw 	bzero(&arg, sizeof (struct samr_OpenDomain));
3368d7e4166Sjose borrego 	(void) memcpy(&arg.handle, &samr_handle->handle, sizeof (ndr_hdid_t));
337da6c28aaSamw 
338da6c28aaSamw 	arg.access_mask = access_mask;
339da6c28aaSamw 	arg.sid = sid;
340da6c28aaSamw 
3418d7e4166Sjose borrego 	if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
342da6c28aaSamw 		status = NT_STATUS_UNSUCCESSFUL;
343da6c28aaSamw 	} else if (arg.status != 0) {
344da6c28aaSamw 		status = arg.status;
345da6c28aaSamw 	} else {
346da6c28aaSamw 		status = NT_STATUS_SUCCESS;
3478d7e4166Sjose borrego 		ndr_inherit_handle(domain_handle, samr_handle);
3488d7e4166Sjose borrego 
349da6c28aaSamw 		(void) memcpy(&domain_handle->handle, &arg.domain_handle,
3508d7e4166Sjose borrego 		    sizeof (ndr_hdid_t));
3518d7e4166Sjose borrego 
3528d7e4166Sjose borrego 		if (ndr_is_null_handle(domain_handle))
353da6c28aaSamw 			status = NT_STATUS_INVALID_HANDLE;
354da6c28aaSamw 	}
355da6c28aaSamw 
356da6c28aaSamw 	if (status != NT_STATUS_SUCCESS)
3578d7e4166Sjose borrego 		ndr_rpc_status(samr_handle, opnum, status);
358da6c28aaSamw 
3598d7e4166Sjose borrego 	ndr_rpc_release(samr_handle);
360da6c28aaSamw 	return (status);
361da6c28aaSamw }
362da6c28aaSamw 
363da6c28aaSamw /*
364da6c28aaSamw  * samr_open_user
365da6c28aaSamw  *
366da6c28aaSamw  * Use a domain handle to obtain a handle for a user, specified by the
367da6c28aaSamw  * user RID. A user RID (effectively a uid) can be obtained via the
368da6c28aaSamw  * LSA interface. A handle for the user is returned in user_handle.
369da6c28aaSamw  * Once you have a user handle it should be possible to query the SAM
370da6c28aaSamw  * for information on that user.
371da6c28aaSamw  */
37255bf511dSas DWORD
samr_open_user(mlsvc_handle_t * domain_handle,DWORD access_mask,DWORD rid,mlsvc_handle_t * user_handle)373da6c28aaSamw samr_open_user(mlsvc_handle_t *domain_handle, DWORD access_mask, DWORD rid,
374da6c28aaSamw     mlsvc_handle_t *user_handle)
375da6c28aaSamw {
376da6c28aaSamw 	struct samr_OpenUser arg;
3778d7e4166Sjose borrego 	int opnum;
37855bf511dSas 	DWORD status = NT_STATUS_SUCCESS;
379da6c28aaSamw 
3808d7e4166Sjose borrego 	if (ndr_is_null_handle(domain_handle) || user_handle == NULL)
38155bf511dSas 		return (NT_STATUS_INVALID_PARAMETER);
382da6c28aaSamw 
383da6c28aaSamw 	opnum = SAMR_OPNUM_OpenUser;
384da6c28aaSamw 	bzero(&arg, sizeof (struct samr_OpenUser));
385da6c28aaSamw 	(void) memcpy(&arg.handle, &domain_handle->handle,
3868d7e4166Sjose borrego 	    sizeof (ndr_hdid_t));
387da6c28aaSamw 	arg.access_mask = access_mask;
388da6c28aaSamw 	arg.rid = rid;
389da6c28aaSamw 
3908d7e4166Sjose borrego 	if (ndr_rpc_call(domain_handle, opnum, &arg) != 0) {
39155bf511dSas 		status = NT_STATUS_UNSUCCESSFUL;
39255bf511dSas 	} else if (arg.status != 0) {
3938d7e4166Sjose borrego 		ndr_rpc_status(domain_handle, opnum, arg.status);
39455bf511dSas 		status = NT_SC_VALUE(arg.status);
39555bf511dSas 	} else {
3968d7e4166Sjose borrego 		ndr_inherit_handle(user_handle, domain_handle);
3978d7e4166Sjose borrego 
39855bf511dSas 		(void) memcpy(&user_handle->handle, &arg.user_handle,
3998d7e4166Sjose borrego 		    sizeof (ndr_hdid_t));
40055bf511dSas 
4018d7e4166Sjose borrego 		if (ndr_is_null_handle(user_handle))
40255bf511dSas 			status = NT_STATUS_INVALID_HANDLE;
403da6c28aaSamw 	}
404da6c28aaSamw 
4058d7e4166Sjose borrego 	ndr_rpc_release(domain_handle);
40655bf511dSas 	return (status);
407da6c28aaSamw }
408da6c28aaSamw 
409da6c28aaSamw /*
410da6c28aaSamw  * samr_delete_user
411da6c28aaSamw  *
412da6c28aaSamw  * Delete the user specified by the user_handle.
413da6c28aaSamw  */
414da6c28aaSamw DWORD
samr_delete_user(mlsvc_handle_t * user_handle)415da6c28aaSamw samr_delete_user(mlsvc_handle_t *user_handle)
416da6c28aaSamw {
417da6c28aaSamw 	struct samr_DeleteUser arg;
418da6c28aaSamw 	int opnum;
419da6c28aaSamw 	DWORD status;
420da6c28aaSamw 
4218d7e4166Sjose borrego 	if (ndr_is_null_handle(user_handle))
422da6c28aaSamw 		return (NT_STATUS_INVALID_PARAMETER);
423da6c28aaSamw 
424da6c28aaSamw 	opnum = SAMR_OPNUM_DeleteUser;
425da6c28aaSamw 	bzero(&arg, sizeof (struct samr_DeleteUser));
426da6c28aaSamw 	(void) memcpy(&arg.user_handle, &user_handle->handle,
4278d7e4166Sjose borrego 	    sizeof (ndr_hdid_t));
428da6c28aaSamw 
4298d7e4166Sjose borrego 	if (ndr_rpc_call(user_handle, opnum, &arg) != 0) {
430da6c28aaSamw 		status = NT_STATUS_INVALID_PARAMETER;
431da6c28aaSamw 	} else if (arg.status != 0) {
4328d7e4166Sjose borrego 		ndr_rpc_status(user_handle, opnum, arg.status);
433da6c28aaSamw 		status = NT_SC_VALUE(arg.status);
434da6c28aaSamw 	} else {
435da6c28aaSamw 		status = 0;
436da6c28aaSamw 	}
437da6c28aaSamw 
4388d7e4166Sjose borrego 	ndr_rpc_release(user_handle);
439da6c28aaSamw 	return (status);
440da6c28aaSamw }
441da6c28aaSamw 
442da6c28aaSamw /*
443da6c28aaSamw  * samr_open_group
444da6c28aaSamw  *
445da6c28aaSamw  * Use a domain handle to obtain a handle for a group, specified by the
446da6c28aaSamw  * group RID. A group RID (effectively a gid) can be obtained via the
447da6c28aaSamw  * LSA interface. A handle for the group is returned in group_handle.
448da6c28aaSamw  * Once you have a group handle it should be possible to query the SAM
449da6c28aaSamw  * for information on that group.
450da6c28aaSamw  */
451da6c28aaSamw int
samr_open_group(mlsvc_handle_t * domain_handle,DWORD rid,mlsvc_handle_t * group_handle)452da6c28aaSamw samr_open_group(
453da6c28aaSamw 	mlsvc_handle_t *domain_handle,
454da6c28aaSamw 	DWORD rid,
455da6c28aaSamw 	mlsvc_handle_t *group_handle)
456da6c28aaSamw {
457da6c28aaSamw 	struct samr_OpenGroup arg;
458da6c28aaSamw 	int opnum;
459da6c28aaSamw 	int rc;
460da6c28aaSamw 
4618d7e4166Sjose borrego 	if (ndr_is_null_handle(domain_handle) || group_handle == NULL)
462da6c28aaSamw 		return (-1);
463da6c28aaSamw 
464da6c28aaSamw 	opnum = SAMR_OPNUM_OpenGroup;
465da6c28aaSamw 	bzero(&arg, sizeof (struct samr_OpenUser));
466da6c28aaSamw 	(void) memcpy(&arg.handle, &domain_handle->handle,
4678d7e4166Sjose borrego 	    sizeof (ndr_hdid_t));
468da6c28aaSamw 	arg.access_mask = SAM_LOOKUP_INFORMATION | SAM_ACCESS_USER_READ;
469da6c28aaSamw 	arg.rid = rid;
470da6c28aaSamw 
4718d7e4166Sjose borrego 	if ((rc = ndr_rpc_call(domain_handle, opnum, &arg)) != 0)
4728d7e4166Sjose borrego 		return (-1);
4738d7e4166Sjose borrego 
4748d7e4166Sjose borrego 	if (arg.status != 0) {
4758d7e4166Sjose borrego 		ndr_rpc_status(domain_handle, opnum, arg.status);
4768d7e4166Sjose borrego 		rc = -1;
4778d7e4166Sjose borrego 	} else {
4788d7e4166Sjose borrego 		ndr_inherit_handle(group_handle, domain_handle);
479da6c28aaSamw 
4808d7e4166Sjose borrego 		(void) memcpy(&group_handle->handle, &arg.group_handle,
4818d7e4166Sjose borrego 		    sizeof (ndr_hdid_t));
4828d7e4166Sjose borrego 
4838d7e4166Sjose borrego 		if (ndr_is_null_handle(group_handle))
484da6c28aaSamw 			rc = -1;
485da6c28aaSamw 	}
486da6c28aaSamw 
4878d7e4166Sjose borrego 	ndr_rpc_release(domain_handle);
488da6c28aaSamw 	return (rc);
489da6c28aaSamw }
490da6c28aaSamw 
491da6c28aaSamw /*
492da6c28aaSamw  * samr_create_user
493da6c28aaSamw  *
494da6c28aaSamw  * Create a user in the domain specified by the domain handle. If this
495da6c28aaSamw  * call is successful, the server will return the RID for the user and
496da6c28aaSamw  * a user handle, which may be used to set or query the SAM.
497da6c28aaSamw  *
498da6c28aaSamw  * Observed status codes:
499da6c28aaSamw  *	NT_STATUS_INVALID_PARAMETER
500da6c28aaSamw  *	NT_STATUS_INVALID_ACCOUNT_NAME
501da6c28aaSamw  *	NT_STATUS_ACCESS_DENIED
502da6c28aaSamw  *	NT_STATUS_USER_EXISTS
503da6c28aaSamw  *
504da6c28aaSamw  * Returns 0 on success. Otherwise returns an NT status code.
505da6c28aaSamw  */
506da6c28aaSamw DWORD
samr_create_user(mlsvc_handle_t * domain_handle,char * username,DWORD account_flags,DWORD * rid,mlsvc_handle_t * user_handle)507da6c28aaSamw samr_create_user(mlsvc_handle_t *domain_handle, char *username,
508da6c28aaSamw     DWORD account_flags, DWORD *rid, mlsvc_handle_t *user_handle)
509da6c28aaSamw {
510da6c28aaSamw 	struct samr_CreateUser arg;
5118d7e4166Sjose borrego 	ndr_heap_t *heap;
512da6c28aaSamw 	int opnum;
513da6c28aaSamw 	int rc;
514da6c28aaSamw 	DWORD status = 0;
515da6c28aaSamw 
5168d7e4166Sjose borrego 	if (ndr_is_null_handle(domain_handle) ||
517da6c28aaSamw 	    username == NULL || rid == NULL) {
518da6c28aaSamw 		return (NT_STATUS_INVALID_PARAMETER);
519da6c28aaSamw 	}
520da6c28aaSamw 
521da6c28aaSamw 	opnum = SAMR_OPNUM_CreateUser;
522da6c28aaSamw 
523da6c28aaSamw 	bzero(&arg, sizeof (struct samr_CreateUser));
524da6c28aaSamw 	(void) memcpy(&arg.handle, &domain_handle->handle,
5258d7e4166Sjose borrego 	    sizeof (ndr_hdid_t));
526da6c28aaSamw 
5278d7e4166Sjose borrego 	heap = ndr_rpc_get_heap(domain_handle);
5288d7e4166Sjose borrego 	ndr_heap_mkvcs(heap, username, (ndr_vcstr_t *)&arg.username);
529da6c28aaSamw 
530da6c28aaSamw 	arg.account_flags = account_flags;
531a0aa776eSAlan Wright 	arg.desired_access = 0xE00500B0;
532da6c28aaSamw 
5338d7e4166Sjose borrego 	rc = ndr_rpc_call(domain_handle, opnum, &arg);
534da6c28aaSamw 	if (rc != 0) {
535da6c28aaSamw 		status = NT_STATUS_INVALID_PARAMETER;
536da6c28aaSamw 	} else if (arg.status != 0) {
537da6c28aaSamw 		status = NT_SC_VALUE(arg.status);
538da6c28aaSamw 
539da6c28aaSamw 		if (status != NT_STATUS_USER_EXISTS) {
5401ed6b69aSGordon Ross 			smb_tracef("SamrCreateUser[%s]: %s",
5411ed6b69aSGordon Ross 			    username, xlate_nt_status(status));
542da6c28aaSamw 		}
543da6c28aaSamw 	} else {
5448d7e4166Sjose borrego 		ndr_inherit_handle(user_handle, domain_handle);
5458d7e4166Sjose borrego 
546da6c28aaSamw 		(void) memcpy(&user_handle->handle, &arg.user_handle,
5478d7e4166Sjose borrego 		    sizeof (ndr_hdid_t));
5488d7e4166Sjose borrego 
549da6c28aaSamw 		*rid = arg.rid;
550da6c28aaSamw 
5518d7e4166Sjose borrego 		if (ndr_is_null_handle(user_handle))
552da6c28aaSamw 			status = NT_STATUS_INVALID_HANDLE;
553da6c28aaSamw 		else
554da6c28aaSamw 			status = 0;
555da6c28aaSamw 	}
556da6c28aaSamw 
5578d7e4166Sjose borrego 	ndr_rpc_release(domain_handle);
558da6c28aaSamw 	return (status);
559da6c28aaSamw }
560fe1c642dSBill Krier 
561fe1c642dSBill Krier /*
562fe1c642dSBill Krier  * samr_lookup_domain
563fe1c642dSBill Krier  *
564fe1c642dSBill Krier  * Lookup up the domain SID for the specified domain name. The handle
565fe1c642dSBill Krier  * should be one returned from samr_connect. The allocated memory for
566fe1c642dSBill Krier  * the returned SID must be freed by caller.
567fe1c642dSBill Krier  */
568fe1c642dSBill Krier smb_sid_t *
samr_lookup_domain(mlsvc_handle_t * samr_handle,char * domain_name)569fe1c642dSBill Krier samr_lookup_domain(mlsvc_handle_t *samr_handle, char *domain_name)
570fe1c642dSBill Krier {
571fe1c642dSBill Krier 	struct samr_LookupDomain	arg;
572fe1c642dSBill Krier 	smb_sid_t	*domsid = NULL;
573fe1c642dSBill Krier 	int		opnum;
574fe1c642dSBill Krier 	size_t		length;
575fe1c642dSBill Krier 
576fe1c642dSBill Krier 	if (ndr_is_null_handle(samr_handle) || domain_name == NULL)
577fe1c642dSBill Krier 		return (NULL);
578fe1c642dSBill Krier 
579fe1c642dSBill Krier 	opnum = SAMR_OPNUM_LookupDomain;
580fe1c642dSBill Krier 	bzero(&arg, sizeof (struct samr_LookupDomain));
581fe1c642dSBill Krier 
582fe1c642dSBill Krier 	(void) memcpy(&arg.handle, &samr_handle->handle,
583fe1c642dSBill Krier 	    sizeof (samr_handle_t));
584fe1c642dSBill Krier 
585fe1c642dSBill Krier 	length = smb_wcequiv_strlen(domain_name);
5861ed6b69aSGordon Ross 	length += sizeof (smb_wchar_t);
587fe1c642dSBill Krier 
588fe1c642dSBill Krier 	arg.domain_name.length = length;
589fe1c642dSBill Krier 	arg.domain_name.allosize = length;
590fe1c642dSBill Krier 	arg.domain_name.str = (unsigned char *)domain_name;
591fe1c642dSBill Krier 
592fe1c642dSBill Krier 	if (ndr_rpc_call(samr_handle, opnum, &arg) == 0)
593fe1c642dSBill Krier 		domsid = smb_sid_dup((smb_sid_t *)arg.sid);
594fe1c642dSBill Krier 
595fe1c642dSBill Krier 	ndr_rpc_release(samr_handle);
596fe1c642dSBill Krier 	return (domsid);
597fe1c642dSBill Krier }
598fe1c642dSBill Krier 
599fe1c642dSBill Krier /*
600fe1c642dSBill Krier  * samr_enum_local_domains
601fe1c642dSBill Krier  *
602fe1c642dSBill Krier  * Get the list of local domains supported by a server.
603fe1c642dSBill Krier  *
604fe1c642dSBill Krier  * Returns NT status codes.
605fe1c642dSBill Krier  */
606fe1c642dSBill Krier DWORD
samr_enum_local_domains(mlsvc_handle_t * samr_handle)607fe1c642dSBill Krier samr_enum_local_domains(mlsvc_handle_t *samr_handle)
608fe1c642dSBill Krier {
609fe1c642dSBill Krier 	struct samr_EnumLocalDomain	arg;
610fe1c642dSBill Krier 	int	opnum;
611fe1c642dSBill Krier 	DWORD	status;
612fe1c642dSBill Krier 
613fe1c642dSBill Krier 	if (ndr_is_null_handle(samr_handle))
614fe1c642dSBill Krier 		return (NT_STATUS_INVALID_PARAMETER);
615fe1c642dSBill Krier 
616fe1c642dSBill Krier 	opnum = SAMR_OPNUM_EnumLocalDomains;
617fe1c642dSBill Krier 	bzero(&arg, sizeof (struct samr_EnumLocalDomain));
618fe1c642dSBill Krier 
619fe1c642dSBill Krier 	(void) memcpy(&arg.handle, &samr_handle->handle,
620fe1c642dSBill Krier 	    sizeof (samr_handle_t));
621fe1c642dSBill Krier 	arg.enum_context = 0;
622fe1c642dSBill Krier 	arg.max_length = 0x00002000;	/* Value used by NT */
623fe1c642dSBill Krier 
624fe1c642dSBill Krier 	if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
625fe1c642dSBill Krier 		status = NT_STATUS_INVALID_PARAMETER;
626fe1c642dSBill Krier 	} else {
627fe1c642dSBill Krier 		status = NT_SC_VALUE(arg.status);
628fe1c642dSBill Krier 
629fe1c642dSBill Krier 		/*
630fe1c642dSBill Krier 		 * Handle none-mapped status quietly.
631fe1c642dSBill Krier 		 */
632fe1c642dSBill Krier 		if (status != NT_STATUS_NONE_MAPPED)
633fe1c642dSBill Krier 			ndr_rpc_status(samr_handle, opnum, arg.status);
634fe1c642dSBill Krier 	}
635fe1c642dSBill Krier 
636fe1c642dSBill Krier 	ndr_rpc_release(samr_handle);
637fe1c642dSBill Krier 	return (status);
638fe1c642dSBill Krier }
639fe1c642dSBill Krier 
640fe1c642dSBill Krier /*
641fe1c642dSBill Krier  * samr_lookup_domain_names
642fe1c642dSBill Krier  *
643fe1c642dSBill Krier  * Lookup up the given name in the domain specified by domain_handle.
644fe1c642dSBill Krier  * Upon a successful lookup the information is returned in the account
645fe1c642dSBill Krier  * arg and caller must free allocated memories by calling smb_account_free().
646fe1c642dSBill Krier  *
647fe1c642dSBill Krier  * Returns NT status codes.
648fe1c642dSBill Krier  */
649fe1c642dSBill Krier uint32_t
samr_lookup_domain_names(mlsvc_handle_t * domain_handle,char * name,smb_account_t * account)650fe1c642dSBill Krier samr_lookup_domain_names(mlsvc_handle_t *domain_handle, char *name,
651fe1c642dSBill Krier     smb_account_t *account)
652fe1c642dSBill Krier {
653fe1c642dSBill Krier 	struct samr_LookupNames	arg;
654fe1c642dSBill Krier 	int			opnum;
655fe1c642dSBill Krier 	uint32_t		status;
656fe1c642dSBill Krier 	size_t			length;
657fe1c642dSBill Krier 
658fe1c642dSBill Krier 	if (ndr_is_null_handle(domain_handle) ||
659fe1c642dSBill Krier 	    name == NULL || account == NULL) {
660fe1c642dSBill Krier 		return (NT_STATUS_INVALID_PARAMETER);
661fe1c642dSBill Krier 	}
662fe1c642dSBill Krier 
663fe1c642dSBill Krier 	bzero(account, sizeof (smb_account_t));
664fe1c642dSBill Krier 	opnum = SAMR_OPNUM_LookupNames;
665fe1c642dSBill Krier 	bzero(&arg, sizeof (struct samr_LookupNames));
666fe1c642dSBill Krier 
667fe1c642dSBill Krier 	(void) memcpy(&arg.handle, &domain_handle->handle,
668fe1c642dSBill Krier 	    sizeof (samr_handle_t));
669fe1c642dSBill Krier 	arg.n_entry = 1;
670fe1c642dSBill Krier 	arg.max_n_entry = 1000;
671fe1c642dSBill Krier 	arg.index = 0;
672fe1c642dSBill Krier 	arg.total = 1;
673fe1c642dSBill Krier 
674fe1c642dSBill Krier 	length = smb_wcequiv_strlen(name);
6751ed6b69aSGordon Ross 	length += sizeof (smb_wchar_t);
676fe1c642dSBill Krier 
677fe1c642dSBill Krier 	arg.name.length = length;
678fe1c642dSBill Krier 	arg.name.allosize = length;
679fe1c642dSBill Krier 	arg.name.str = (unsigned char *)name;
680fe1c642dSBill Krier 
681fe1c642dSBill Krier 	if (ndr_rpc_call(domain_handle, opnum, &arg) != 0) {
682fe1c642dSBill Krier 		status = NT_STATUS_INVALID_PARAMETER;
683fe1c642dSBill Krier 	} else if (arg.status != NT_STATUS_SUCCESS) {
684fe1c642dSBill Krier 		status = NT_SC_VALUE(arg.status);
685fe1c642dSBill Krier 
686fe1c642dSBill Krier 		/*
687fe1c642dSBill Krier 		 * Handle none-mapped status quietly.
688fe1c642dSBill Krier 		 */
689fe1c642dSBill Krier 		if (status != NT_STATUS_NONE_MAPPED)
690fe1c642dSBill Krier 			ndr_rpc_status(domain_handle, opnum, arg.status);
691fe1c642dSBill Krier 	} else {
692fe1c642dSBill Krier 		account->a_type = arg.rid_types.rid_type[0];
693fe1c642dSBill Krier 		account->a_rid = arg.rids.rid[0];
694fe1c642dSBill Krier 		status = NT_STATUS_SUCCESS;
695fe1c642dSBill Krier 	}
696fe1c642dSBill Krier 
697fe1c642dSBill Krier 	ndr_rpc_release(domain_handle);
698fe1c642dSBill Krier 	return (status);
699fe1c642dSBill Krier }
700fe1c642dSBill Krier 
701fe1c642dSBill Krier /*
702fe1c642dSBill Krier  * samr_query_user_info
703fe1c642dSBill Krier  *
704fe1c642dSBill Krier  * Query information on a specific user. The handle must be a valid
705fe1c642dSBill Krier  * user handle obtained via samr_open_user.
706fe1c642dSBill Krier  *
7071ed6b69aSGordon Ross  * Returns 0 on success, otherwise returns NT status code.
708fe1c642dSBill Krier  */
7091ed6b69aSGordon Ross DWORD
samr_query_user_info(mlsvc_handle_t * user_handle,WORD switch_value,union samr_user_info * user_info)710fe1c642dSBill Krier samr_query_user_info(mlsvc_handle_t *user_handle, WORD switch_value,
711fe1c642dSBill Krier     union samr_user_info *user_info)
712fe1c642dSBill Krier {
713fe1c642dSBill Krier 	struct samr_QueryUserInfo	arg;
714fe1c642dSBill Krier 	int	opnum;
715fe1c642dSBill Krier 
716fe1c642dSBill Krier 	if (ndr_is_null_handle(user_handle) || user_info == 0)
7171ed6b69aSGordon Ross 		return (NT_STATUS_INTERNAL_ERROR);
718fe1c642dSBill Krier 
719fe1c642dSBill Krier 	opnum = SAMR_OPNUM_QueryUserInfo;
720fe1c642dSBill Krier 	bzero(&arg, sizeof (struct samr_QueryUserInfo));
721fe1c642dSBill Krier 
722fe1c642dSBill Krier 	(void) memcpy(&arg.user_handle, &user_handle->handle,
723fe1c642dSBill Krier 	    sizeof (samr_handle_t));
724fe1c642dSBill Krier 	arg.switch_value = switch_value;
725fe1c642dSBill Krier 
7261ed6b69aSGordon Ross 	if (ndr_rpc_call(user_handle, opnum, &arg) != 0)
7271ed6b69aSGordon Ross 		arg.status = RPC_NT_CALL_FAILED;
728fe1c642dSBill Krier 
7291ed6b69aSGordon Ross 	if (arg.status == 0)
7301ed6b69aSGordon Ross 		(void) samr_setup_user_info(switch_value, &arg, user_info);
731fe1c642dSBill Krier 
7321ed6b69aSGordon Ross 	return (arg.status);
733fe1c642dSBill Krier }
734fe1c642dSBill Krier 
735fe1c642dSBill Krier /*
736fe1c642dSBill Krier  * samr_setup_user_info
737fe1c642dSBill Krier  *
738fe1c642dSBill Krier  * Private function to set up the samr_user_info data. Dependent on
739fe1c642dSBill Krier  * the switch value this function may use strdup which will malloc
740fe1c642dSBill Krier  * memory. The caller is responsible for deallocating this memory.
741fe1c642dSBill Krier  *
742fe1c642dSBill Krier  * Returns 0 on success, otherwise returns -1.
743fe1c642dSBill Krier  */
744fe1c642dSBill Krier static int
samr_setup_user_info(WORD switch_value,struct samr_QueryUserInfo * arg,union samr_user_info * user_info)745fe1c642dSBill Krier samr_setup_user_info(WORD switch_value,
746fe1c642dSBill Krier     struct samr_QueryUserInfo *arg, union samr_user_info *user_info)
747fe1c642dSBill Krier {
748fe1c642dSBill Krier 	struct samr_QueryUserInfo1	*info1;
749fe1c642dSBill Krier 	struct samr_QueryUserInfo6	*info6;
750fe1c642dSBill Krier 
751fe1c642dSBill Krier 	switch (switch_value) {
752fe1c642dSBill Krier 	case 1:
753fe1c642dSBill Krier 		info1 = &arg->ru.info1;
754fe1c642dSBill Krier 		user_info->info1.username = strdup(
755fe1c642dSBill Krier 		    (char const *)info1->username.str);
756fe1c642dSBill Krier 		user_info->info1.fullname = strdup(
757fe1c642dSBill Krier 		    (char const *)info1->fullname.str);
758fe1c642dSBill Krier 		user_info->info1.description = strdup(
759fe1c642dSBill Krier 		    (char const *)info1->description.str);
760fe1c642dSBill Krier 		user_info->info1.unknown = 0;
761fe1c642dSBill Krier 		user_info->info1.group_rid = info1->group_rid;
762fe1c642dSBill Krier 		return (0);
763fe1c642dSBill Krier 
764fe1c642dSBill Krier 	case 6:
765fe1c642dSBill Krier 		info6 = &arg->ru.info6;
766fe1c642dSBill Krier 		user_info->info6.username = strdup(
767fe1c642dSBill Krier 		    (char const *)info6->username.str);
768fe1c642dSBill Krier 		user_info->info6.fullname = strdup(
769fe1c642dSBill Krier 		    (char const *)info6->fullname.str);
770fe1c642dSBill Krier 		return (0);
771fe1c642dSBill Krier 
772fe1c642dSBill Krier 	case 7:
773fe1c642dSBill Krier 		user_info->info7.username = strdup(
774fe1c642dSBill Krier 		    (char const *)arg->ru.info7.username.str);
775fe1c642dSBill Krier 		return (0);
776fe1c642dSBill Krier 
777fe1c642dSBill Krier 	case 8:
778fe1c642dSBill Krier 		user_info->info8.fullname = strdup(
779fe1c642dSBill Krier 		    (char const *)arg->ru.info8.fullname.str);
780fe1c642dSBill Krier 		return (0);
781fe1c642dSBill Krier 
782fe1c642dSBill Krier 	case 9:
783fe1c642dSBill Krier 		user_info->info9.group_rid = arg->ru.info9.group_rid;
784fe1c642dSBill Krier 		return (0);
785fe1c642dSBill Krier 
786fe1c642dSBill Krier 	case 16:
7871ed6b69aSGordon Ross 		user_info->info16.acct_ctrl =
7881ed6b69aSGordon Ross 		    arg->ru.info16.UserAccountControl;
789fe1c642dSBill Krier 		return (0);
790fe1c642dSBill Krier 
791fe1c642dSBill Krier 	default:
792fe1c642dSBill Krier 		break;
793fe1c642dSBill Krier 	};
794fe1c642dSBill Krier 
795fe1c642dSBill Krier 	return (-1);
796fe1c642dSBill Krier }
797fe1c642dSBill Krier 
798fe1c642dSBill Krier /*
799fe1c642dSBill Krier  * samr_query_user_groups
800fe1c642dSBill Krier  *
801fe1c642dSBill Krier  * Query the groups for a specific user. The handle must be a valid
802fe1c642dSBill Krier  * user handle obtained via samr_open_user. The list of groups is
803fe1c642dSBill Krier  * returned in group_info. Note that group_info->groups is allocated
804fe1c642dSBill Krier  * using malloc. The caller is responsible for deallocating this
805fe1c642dSBill Krier  * memory when it is no longer required. If group_info->n_entry is 0
806fe1c642dSBill Krier  * then no memory was allocated.
807fe1c642dSBill Krier  *
808fe1c642dSBill Krier  * Returns 0 on success, otherwise returns -1.
809fe1c642dSBill Krier  */
810fe1c642dSBill Krier int
samr_query_user_groups(mlsvc_handle_t * user_handle,int * n_groups,struct samr_UserGroups ** groups)811fe1c642dSBill Krier samr_query_user_groups(mlsvc_handle_t *user_handle, int *n_groups,
812fe1c642dSBill Krier     struct samr_UserGroups **groups)
813fe1c642dSBill Krier {
814fe1c642dSBill Krier 	struct samr_QueryUserGroups arg;
815fe1c642dSBill Krier 	int	opnum;
816fe1c642dSBill Krier 	int	rc;
817fe1c642dSBill Krier 	int	nbytes;
818fe1c642dSBill Krier 
819fe1c642dSBill Krier 	if (ndr_is_null_handle(user_handle))
820fe1c642dSBill Krier 		return (-1);
821fe1c642dSBill Krier 
822fe1c642dSBill Krier 	opnum = SAMR_OPNUM_QueryUserGroups;
823fe1c642dSBill Krier 	bzero(&arg, sizeof (struct samr_QueryUserGroups));
824fe1c642dSBill Krier 
825fe1c642dSBill Krier 	(void) memcpy(&arg.user_handle, &user_handle->handle,
826fe1c642dSBill Krier 	    sizeof (samr_handle_t));
827fe1c642dSBill Krier 
828fe1c642dSBill Krier 	rc = ndr_rpc_call(user_handle, opnum, &arg);
829fe1c642dSBill Krier 	if (rc == 0) {
830fe1c642dSBill Krier 		if (arg.info == 0) {
831fe1c642dSBill Krier 			rc = -1;
832fe1c642dSBill Krier 		} else {
833fe1c642dSBill Krier 			nbytes = arg.info->n_entry *
834fe1c642dSBill Krier 			    sizeof (struct samr_UserGroups);
835fe1c642dSBill Krier 
836fe1c642dSBill Krier 			if ((*groups = malloc(nbytes)) == NULL) {
837fe1c642dSBill Krier 				*n_groups = 0;
838fe1c642dSBill Krier 				rc = -1;
839fe1c642dSBill Krier 			} else {
840fe1c642dSBill Krier 				*n_groups = arg.info->n_entry;
841fe1c642dSBill Krier 				bcopy(arg.info->groups, *groups, nbytes);
842fe1c642dSBill Krier 			}
843fe1c642dSBill Krier 		}
844fe1c642dSBill Krier 	}
845fe1c642dSBill Krier 
846fe1c642dSBill Krier 	ndr_rpc_release(user_handle);
847fe1c642dSBill Krier 	return (rc);
848fe1c642dSBill Krier }
849fe1c642dSBill Krier 
850fe1c642dSBill Krier /*
851fe1c642dSBill Krier  * samr_get_user_pwinfo
852fe1c642dSBill Krier  *
853fe1c642dSBill Krier  * Get some user password info. I'm not sure what this is yet but it is
854fe1c642dSBill Krier  * part of the create user sequence. The handle must be a valid user
855fe1c642dSBill Krier  * handle. Since I don't know what this is returning, I haven't provided
856fe1c642dSBill Krier  * any return data yet.
857fe1c642dSBill Krier  *
858fe1c642dSBill Krier  * Returns 0 on success. Otherwise returns an NT status code.
859fe1c642dSBill Krier  */
860fe1c642dSBill Krier DWORD
samr_get_user_pwinfo(mlsvc_handle_t * user_handle)861fe1c642dSBill Krier samr_get_user_pwinfo(mlsvc_handle_t *user_handle)
862fe1c642dSBill Krier {
863fe1c642dSBill Krier 	struct samr_GetUserPwInfo arg;
864fe1c642dSBill Krier 	int	opnum;
865fe1c642dSBill Krier 	DWORD	status;
866fe1c642dSBill Krier 
867fe1c642dSBill Krier 	if (ndr_is_null_handle(user_handle))
868fe1c642dSBill Krier 		return (NT_STATUS_INVALID_PARAMETER);
869fe1c642dSBill Krier 
870fe1c642dSBill Krier 	opnum = SAMR_OPNUM_GetUserPwInfo;
871fe1c642dSBill Krier 	bzero(&arg, sizeof (struct samr_GetUserPwInfo));
872fe1c642dSBill Krier 	(void) memcpy(&arg.user_handle, &user_handle->handle,
873fe1c642dSBill Krier 	    sizeof (samr_handle_t));
874fe1c642dSBill Krier 
875fe1c642dSBill Krier 	if (ndr_rpc_call(user_handle, opnum, &arg) != 0) {
876fe1c642dSBill Krier 		status = NT_STATUS_INVALID_PARAMETER;
877fe1c642dSBill Krier 	} else if (arg.status != 0) {
878fe1c642dSBill Krier 		ndr_rpc_status(user_handle, opnum, arg.status);
879fe1c642dSBill Krier 		status = NT_SC_VALUE(arg.status);
880fe1c642dSBill Krier 	} else {
881fe1c642dSBill Krier 		status = 0;
882fe1c642dSBill Krier 	}
883fe1c642dSBill Krier 
884fe1c642dSBill Krier 	ndr_rpc_release(user_handle);
885fe1c642dSBill Krier 	return (status);
886fe1c642dSBill Krier }
887fe1c642dSBill Krier 
8881ed6b69aSGordon Ross DECL_FIXUP_STRUCT(samr_SetUserInfo_u);
8891ed6b69aSGordon Ross DECL_FIXUP_STRUCT(samr_SetUserInfo_s);
8901ed6b69aSGordon Ross DECL_FIXUP_STRUCT(samr_SetUserInfo);
8911ed6b69aSGordon Ross 
892fe1c642dSBill Krier /*
893fe1c642dSBill Krier  * samr_set_user_info
894fe1c642dSBill Krier  *
895fe1c642dSBill Krier  * Returns 0 on success. Otherwise returns an NT status code.
896fe1c642dSBill Krier  * NT status codes observed so far:
897fe1c642dSBill Krier  *	NT_STATUS_WRONG_PASSWORD
898fe1c642dSBill Krier  */
899fe1c642dSBill Krier DWORD
samr_set_user_info(mlsvc_handle_t * user_handle,int info_level,void * info_buf)9001ed6b69aSGordon Ross samr_set_user_info(
9011ed6b69aSGordon Ross 	mlsvc_handle_t *user_handle,
9021ed6b69aSGordon Ross 	int info_level,
9031ed6b69aSGordon Ross 	void *info_buf)
904fe1c642dSBill Krier {
905fe1c642dSBill Krier 	struct samr_SetUserInfo arg;
9061ed6b69aSGordon Ross 	uint16_t usize, tsize;
907fe1c642dSBill Krier 	int opnum;
908fe1c642dSBill Krier 
909fe1c642dSBill Krier 	if (ndr_is_null_handle(user_handle))
9101ed6b69aSGordon Ross 		return (NT_STATUS_INTERNAL_ERROR);
911fe1c642dSBill Krier 
9121ed6b69aSGordon Ross 	/*
9131ed6b69aSGordon Ross 	 * Only support a few levels
9141ed6b69aSGordon Ross 	 * MS-SAMR: UserInternal4Information
9151ed6b69aSGordon Ross 	 */
9161ed6b69aSGordon Ross 	switch (info_level) {
9171ed6b69aSGordon Ross 	case 16: /* samr_SetUserInfo16 */
9181ed6b69aSGordon Ross 		usize = sizeof (struct samr_SetUserInfo16);
9191ed6b69aSGordon Ross 		break;
9201ed6b69aSGordon Ross 	case 21: /* samr_SetUserInfo21 */
9211ed6b69aSGordon Ross 		usize = sizeof (struct samr_SetUserInfo21);
9221ed6b69aSGordon Ross 		break;
9231ed6b69aSGordon Ross 	case 23: /* samr_SetUserInfo23 */
9241ed6b69aSGordon Ross 		usize = sizeof (struct samr_SetUserInfo23);
9251ed6b69aSGordon Ross 		break;
9261ed6b69aSGordon Ross 	case 24: /* samr_SetUserInfo24 */
9271ed6b69aSGordon Ross 		usize = sizeof (struct samr_SetUserInfo24);
9281ed6b69aSGordon Ross 		break;
9291ed6b69aSGordon Ross 	default:
9301ed6b69aSGordon Ross 		return (NT_STATUS_INVALID_LEVEL);
9311ed6b69aSGordon Ross 	}
9321ed6b69aSGordon Ross 
9331ed6b69aSGordon Ross 	/*
9341ed6b69aSGordon Ross 	 * OK, now this gets really ugly, because
9351ed6b69aSGordon Ross 	 * ndrgen doesn't do unions correctly.
9361ed6b69aSGordon Ross 	 */
9371ed6b69aSGordon Ross 	FIXUP_PDU_SIZE(samr_SetUserInfo_u, usize);
9381ed6b69aSGordon Ross 	tsize = usize + (2 * sizeof (WORD));
9391ed6b69aSGordon Ross 	FIXUP_PDU_SIZE(samr_SetUserInfo_s, tsize);
9401ed6b69aSGordon Ross 	tsize += sizeof (ndr_request_hdr_t) + sizeof (DWORD);
9411ed6b69aSGordon Ross 	FIXUP_PDU_SIZE(samr_SetUserInfo, tsize);
942fe1c642dSBill Krier 
943fe1c642dSBill Krier 	opnum = SAMR_OPNUM_SetUserInfo;
9441ed6b69aSGordon Ross 	bzero(&arg, sizeof (arg));
945fe1c642dSBill Krier 	(void) memcpy(&arg.user_handle, &user_handle->handle,
946fe1c642dSBill Krier 	    sizeof (samr_handle_t));
9471ed6b69aSGordon Ross 	arg.info.info_level = info_level;
9481ed6b69aSGordon Ross 	arg.info.switch_value = info_level;
9491ed6b69aSGordon Ross 	(void) memcpy(&arg.info.ru, info_buf, usize);
950fe1c642dSBill Krier 
9511ed6b69aSGordon Ross 	if (ndr_rpc_call(user_handle, opnum, &arg) != 0)
9521ed6b69aSGordon Ross 		arg.status = RPC_NT_CALL_FAILED;
9531ed6b69aSGordon Ross 	else if (arg.status != 0)
954fe1c642dSBill Krier 		ndr_rpc_status(user_handle, opnum, arg.status);
955fe1c642dSBill Krier 
956fe1c642dSBill Krier 	ndr_rpc_release(user_handle);
9571ed6b69aSGordon Ross 	return (arg.status);
958fe1c642dSBill Krier }
959fe1c642dSBill Krier 
960fe1c642dSBill Krier /*
9611ed6b69aSGordon Ross  * Client side wrapper for SamrUnicodeChangePasswordUser2
9621ed6b69aSGordon Ross  * [MS-SAMR 3.1.5.10.3]
963fe1c642dSBill Krier  */
964fe1c642dSBill Krier 
9651ed6b69aSGordon Ross DWORD
samr_change_password(mlsvc_handle_t * handle,char * server,char * account,struct samr_encr_passwd * newpw,struct samr_encr_hash * oldpw)9661ed6b69aSGordon Ross samr_change_password(
9671ed6b69aSGordon Ross 	mlsvc_handle_t *handle,
9681ed6b69aSGordon Ross 	char *server,
9691ed6b69aSGordon Ross 	char *account,
9701ed6b69aSGordon Ross 	struct samr_encr_passwd *newpw,
9711ed6b69aSGordon Ross 	struct samr_encr_hash *oldpw)
972fe1c642dSBill Krier {
9731ed6b69aSGordon Ross 	static struct samr_encr_passwd zero_newpw;
9741ed6b69aSGordon Ross 	static struct samr_encr_hash zero_oldpw;
9751ed6b69aSGordon Ross 	struct samr_ChangePasswordUser2 arg;
9761ed6b69aSGordon Ross 	int opnum = SAMR_OPNUM_ChangePasswordUser2;
9771ed6b69aSGordon Ross 	char *slashserver;
9781ed6b69aSGordon Ross 	int len;
979fe1c642dSBill Krier 
9801ed6b69aSGordon Ross 	(void) memset(&arg, 0, sizeof (arg));
981fe1c642dSBill Krier 
9821ed6b69aSGordon Ross 	/* Need server name with slashes */
9831ed6b69aSGordon Ross 	len = 2 + strlen(server) + 1;
9841ed6b69aSGordon Ross 	slashserver = ndr_rpc_malloc(handle, len);
9851ed6b69aSGordon Ross 	if (slashserver == NULL)
9861ed6b69aSGordon Ross 		return (NT_STATUS_NO_MEMORY);
9871ed6b69aSGordon Ross 	(void) snprintf(slashserver, len, "\\\\%s", server);
988fe1c642dSBill Krier 
9891ed6b69aSGordon Ross 	arg.servername = ndr_rpc_malloc(handle, sizeof (samr_string_t));
9901ed6b69aSGordon Ross 	if (arg.servername == NULL)
9911ed6b69aSGordon Ross 		return (NT_STATUS_NO_MEMORY);
9921ed6b69aSGordon Ross 	len = smb_wcequiv_strlen(slashserver);
9931ed6b69aSGordon Ross 	if (len < 1)
9941ed6b69aSGordon Ross 		return (NT_STATUS_INVALID_PARAMETER);
9951ed6b69aSGordon Ross 	len += 2;	/* the WC null */
9961ed6b69aSGordon Ross 	arg.servername->length = len;
9971ed6b69aSGordon Ross 	arg.servername->allosize = len;
9981ed6b69aSGordon Ross 	arg.servername->str = (uint8_t *)slashserver;
9991ed6b69aSGordon Ross 
10001ed6b69aSGordon Ross 	arg.username = ndr_rpc_malloc(handle, sizeof (samr_string_t));
10011ed6b69aSGordon Ross 	if (arg.username == NULL)
10021ed6b69aSGordon Ross 		return (NT_STATUS_NO_MEMORY);
10031ed6b69aSGordon Ross 	len = smb_wcequiv_strlen(account);
10041ed6b69aSGordon Ross 	if (len < 1)
10051ed6b69aSGordon Ross 		return (NT_STATUS_INVALID_PARAMETER);
10061ed6b69aSGordon Ross 	len += 2;	/* the WC null */
10071ed6b69aSGordon Ross 	arg.username->length = len;
10081ed6b69aSGordon Ross 	arg.username->allosize = len;
10091ed6b69aSGordon Ross 	arg.username->str = (uint8_t *)account;
1010fe1c642dSBill Krier 
10111ed6b69aSGordon Ross 	arg.nt_newpw = newpw;
10121ed6b69aSGordon Ross 	arg.nt_oldpw = oldpw;
10131ed6b69aSGordon Ross 
10141ed6b69aSGordon Ross 	arg.lm_newpw = &zero_newpw;
10151ed6b69aSGordon Ross 	arg.lm_oldpw = &zero_oldpw;
10161ed6b69aSGordon Ross 
10171ed6b69aSGordon Ross 	if (ndr_rpc_call(handle, opnum, &arg) != 0)
10181ed6b69aSGordon Ross 		arg.status = RPC_NT_CALL_FAILED;
10191ed6b69aSGordon Ross 	else if (arg.status != 0)
10201ed6b69aSGordon Ross 		ndr_rpc_status(handle, opnum, arg.status);
10211ed6b69aSGordon Ross 
10221ed6b69aSGordon Ross 	ndr_rpc_release(handle);
10231ed6b69aSGordon Ross 	return (arg.status);
1024fe1c642dSBill Krier }
1025