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
samr_open(char * server,char * domain,char * username,DWORD access_mask,mlsvc_handle_t * samr_handle)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
samr_connect(char * server,char * domain,char * username,DWORD access_mask,mlsvc_handle_t * samr_handle)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
samr_connect1(char * server,char * domain,char * username,DWORD access_mask,mlsvc_handle_t * samr_handle)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
samr_connect2(char * server,char * domain,char * username,DWORD access_mask,mlsvc_handle_t * samr_handle)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
samr_connect4(char * server,char * domain,char * username,DWORD access_mask,mlsvc_handle_t * samr_handle)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
samr_connect5(char * server,char * domain,char * username,DWORD access_mask,mlsvc_handle_t * samr_handle)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
samr_close_handle(mlsvc_handle_t * samr_handle)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
samr_open_domain(mlsvc_handle_t * samr_handle,DWORD access_mask,struct samr_sid * sid,mlsvc_handle_t * domain_handle)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
samr_open_user(mlsvc_handle_t * domain_handle,DWORD access_mask,DWORD rid,mlsvc_handle_t * user_handle)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
samr_delete_user(mlsvc_handle_t * user_handle)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
samr_open_group(mlsvc_handle_t * domain_handle,DWORD rid,mlsvc_handle_t * group_handle)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
samr_create_user(mlsvc_handle_t * domain_handle,char * username,DWORD account_flags,DWORD * rid,mlsvc_handle_t * user_handle)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 *
samr_lookup_domain(mlsvc_handle_t * samr_handle,char * domain_name)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
samr_enum_local_domains(mlsvc_handle_t * samr_handle)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
samr_lookup_domain_names(mlsvc_handle_t * domain_handle,char * name,smb_account_t * account)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
samr_query_user_info(mlsvc_handle_t * user_handle,WORD switch_value,union samr_user_info * user_info)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
samr_setup_user_info(WORD switch_value,struct samr_QueryUserInfo * arg,union samr_user_info * user_info)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
samr_query_user_groups(mlsvc_handle_t * user_handle,int * n_groups,struct samr_UserGroups ** groups)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
samr_get_user_pwinfo(mlsvc_handle_t * user_handle)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
samr_set_user_info(mlsvc_handle_t * user_handle)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
samr_set_user_unknowns(struct samr_SetUserInfo23 * info)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
samr_set_user_logon_hours(struct samr_SetUserInfo * sui)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
samr_set_user_password(unsigned char * nt_key,BYTE * oem_password)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