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