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