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