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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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