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