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