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