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 2012 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 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_dc; 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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