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