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