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