2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * Security Accounts Manager RPC (SAMR) client-side interface.
2N/A *
2N/A * The SAM is a hierarchical database:
2N/A * - If you want to talk to the SAM you need a SAM handle.
2N/A * - If you want to work with a domain, use the SAM handle.
2N/A * to obtain a domain handle.
2N/A * - Use domain handles to obtain user handles etc.
2N/A *
2N/A * Be careful about returning null handles to the application. Use of a
2N/A * null handle may crash the domain controller if you attempt to use it.
2N/A */
2N/A
2N/A#include <stdio.h>
2N/A#include <strings.h>
2N/A#include <stdlib.h>
2N/A#include <unistd.h>
2N/A#include <netdb.h>
2N/A#include <sys/param.h>
2N/A
2N/A#include <smbsrv/libsmb.h>
2N/A#include <smbsrv/libntsvcs.h>
2N/A#include <smbsrv/smbinfo.h>
2N/A#include <smb/smb.h>
2N/A#include <smb/smb_sid.h>
2N/A#include <samlib.h>
2N/A
2N/A/*LINTED E_STATIC_UNUSED*/
2N/Astatic DWORD samr_connect1(char *, char *, char *, DWORD, mlsvc_handle_t *);
2N/Astatic DWORD samr_connect2(char *, char *, char *, DWORD, mlsvc_handle_t *);
2N/Astatic DWORD samr_connect4(char *, char *, char *, DWORD, mlsvc_handle_t *);
2N/Astatic DWORD samr_connect5(char *, char *, char *, DWORD, mlsvc_handle_t *);
2N/A
2N/Atypedef DWORD (*samr_connop_t)(char *, char *, char *, DWORD,
2N/A mlsvc_handle_t *);
2N/A
2N/Astatic int samr_setup_user_info(WORD, struct samr_QueryUserInfo *,
2N/A union samr_user_info *);
2N/Astatic void samr_set_user_unknowns(struct samr_SetUserInfo23 *);
2N/Astatic void samr_set_user_logon_hours(struct samr_SetUserInfo *);
2N/Astatic int samr_set_user_password(unsigned char *, BYTE *);
2N/A
2N/A/*
2N/A * samr_open
2N/A *
2N/A * Wrapper round samr_connect to ensure that we connect using the server
2N/A * and domain. We default to the resource domain if the caller doesn't
2N/A * supply a server name and a domain name.
2N/A *
2N/A * If username argument is NULL, an anonymous connection will be established.
2N/A * Otherwise, an authenticated connection will be established.
2N/A *
2N/A * On success 0 is returned. Otherwise a -ve error code.
2N/A */
2N/Aint
2N/Asamr_open(char *server, char *domain, char *username, DWORD access_mask,
2N/A mlsvc_handle_t *samr_handle)
2N/A{
2N/A smb_domainex_t di;
2N/A int rc;
2N/A
2N/A if (server == NULL || domain == NULL) {
2N/A if (!smb_domain_getinfo(&di))
2N/A return (-1);
2N/A
2N/A server = di.d_dc;
2N/A domain = di.d_primary.di_nbname;
2N/A }
2N/A
2N/A rc = samr_connect(server, domain, username, access_mask, samr_handle);
2N/A return (rc);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * samr_connect
2N/A *
2N/A * Connect to the SAMR service on the specified server (domain controller).
2N/A * New SAM connect calls have been added to Windows over time:
2N/A *
2N/A * Windows NT3.x: SamrConnect
2N/A * Windows NT4.0: SamrConnect2
2N/A * Windows 2000: SamrConnect4
2N/A * Windows XP: SamrConnect5
2N/A *
2N/A * Try the calls from most recent to oldest until the server responds with
2N/A * something other than an RPC protocol error. We don't use the original
2N/A * connect call because all supported servers should support SamrConnect2.
2N/A */
2N/Aint
2N/Asamr_connect(char *server, char *domain, char *username, DWORD access_mask,
2N/A mlsvc_handle_t *samr_handle)
2N/A{
2N/A static samr_connop_t samr_connop[] = {
2N/A samr_connect5,
2N/A samr_connect4,
2N/A samr_connect2
2N/A };
2N/A
2N/A int n_op = (sizeof (samr_connop) / sizeof (samr_connop[0]));
2N/A DWORD status;
2N/A int i;
2N/A
2N/A if (ndr_rpc_bind(samr_handle, server, domain, username, "SAMR") < 0)
2N/A return (-1);
2N/A
2N/A for (i = 0; i < n_op; ++i) {
2N/A status = (*samr_connop[i])(server, domain, username,
2N/A access_mask, samr_handle);
2N/A
2N/A if (status == NT_STATUS_SUCCESS)
2N/A return (0);
2N/A }
2N/A
2N/A ndr_rpc_unbind(samr_handle);
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * samr_connect1
2N/A *
2N/A * Original SAMR connect call; probably used on Windows NT 3.51.
2N/A * Windows 95 uses this call with the srvmgr tools update.
2N/A * Servername appears to be a dword rather than a string.
2N/A * The first word contains '\' and the second word contains 0x001,
2N/A * (which is probably uninitialized junk: 0x0001005c.
2N/A */
2N/A/*ARGSUSED*/
2N/Astatic DWORD
2N/Asamr_connect1(char *server, char *domain, char *username, DWORD access_mask,
2N/A mlsvc_handle_t *samr_handle)
2N/A{
2N/A struct samr_Connect arg;
2N/A int opnum;
2N/A DWORD status;
2N/A
2N/A bzero(&arg, sizeof (struct samr_Connect));
2N/A opnum = SAMR_OPNUM_Connect;
2N/A status = NT_STATUS_SUCCESS;
2N/A
2N/A arg.servername = ndr_rpc_malloc(samr_handle, sizeof (DWORD));
2N/A *(arg.servername) = 0x0001005c;
2N/A arg.access_mask = access_mask;
2N/A
2N/A if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
2N/A status = NT_STATUS_UNSUCCESSFUL;
2N/A } else if (arg.status != 0) {
2N/A status = NT_SC_VALUE(arg.status);
2N/A } else {
2N/A (void) memcpy(&samr_handle->handle, &arg.handle,
2N/A sizeof (ndr_hdid_t));
2N/A
2N/A if (ndr_is_null_handle(samr_handle))
2N/A status = NT_STATUS_INVALID_HANDLE;
2N/A }
2N/A
2N/A ndr_rpc_release(samr_handle);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * samr_connect2
2N/A *
2N/A * Connect to the SAM on a Windows NT 4.0 server (domain controller).
2N/A * We need the domain controller name and, if everything works, we
2N/A * return a handle. This function adds the double backslash prefx to
2N/A * make it easy for applications.
2N/A *
2N/A * Returns 0 on success. Otherwise returns a -ve error code.
2N/A */
2N/A/*ARGSUSED*/
2N/Astatic DWORD
2N/Asamr_connect2(char *server, char *domain, char *username, DWORD access_mask,
2N/A mlsvc_handle_t *samr_handle)
2N/A{
2N/A struct samr_Connect2 arg;
2N/A int opnum;
2N/A DWORD status;
2N/A
2N/A bzero(&arg, sizeof (struct samr_Connect2));
2N/A opnum = SAMR_OPNUM_Connect2;
2N/A status = NT_STATUS_SUCCESS;
2N/A
2N/A arg.servername = ndr_rpc_derive_nbhandle(samr_handle, server);
2N/A arg.access_mask = access_mask;
2N/A
2N/A if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
2N/A status = NT_STATUS_UNSUCCESSFUL;
2N/A } else if (arg.status != 0) {
2N/A status = NT_SC_VALUE(arg.status);
2N/A } else {
2N/A (void) memcpy(&samr_handle->handle, &arg.handle,
2N/A sizeof (ndr_hdid_t));
2N/A
2N/A if (ndr_is_null_handle(samr_handle))
2N/A status = NT_STATUS_INVALID_HANDLE;
2N/A }
2N/A
2N/A ndr_rpc_release(samr_handle);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * samr_connect4
2N/A *
2N/A * Connect to the SAM on a Windows 2000 domain controller.
2N/A */
2N/A/*ARGSUSED*/
2N/Astatic DWORD
2N/Asamr_connect4(char *server, char *domain, char *username, DWORD access_mask,
2N/A mlsvc_handle_t *samr_handle)
2N/A{
2N/A struct samr_Connect4 arg;
2N/A int opnum;
2N/A DWORD status;
2N/A
2N/A bzero(&arg, sizeof (struct samr_Connect4));
2N/A opnum = SAMR_OPNUM_Connect4;
2N/A status = NT_STATUS_SUCCESS;
2N/A
2N/A arg.servername = ndr_rpc_derive_nbhandle(samr_handle, server);
2N/A arg.revision = SAMR_REVISION_2;
2N/A arg.access_mask = access_mask;
2N/A
2N/A if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
2N/A status = NT_STATUS_UNSUCCESSFUL;
2N/A } else if (arg.status != 0) {
2N/A status = NT_SC_VALUE(arg.status);
2N/A } else {
2N/A (void) memcpy(&samr_handle->handle, &arg.handle,
2N/A sizeof (ndr_hdid_t));
2N/A
2N/A if (ndr_is_null_handle(samr_handle))
2N/A status = NT_STATUS_INVALID_HANDLE;
2N/A }
2N/A
2N/A ndr_rpc_release(samr_handle);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * samr_connect5
2N/A *
2N/A * Connect to the SAM on a Windows XP domain controller. On Windows
2N/A * XP, the server should be the fully qualified DNS domain name with
2N/A * a double backslash prefix. At this point, it is assumed that we
2N/A * need to add the prefix and the DNS domain name here.
2N/A *
2N/A * If this call succeeds, a SAMR handle is placed in samr_handle and
2N/A * zero is returned. Otherwise, a -ve error code is returned.
2N/A */
2N/A/*ARGSUSED*/
2N/Astatic DWORD
2N/Asamr_connect5(char *server, char *domain, char *username, DWORD access_mask,
2N/A mlsvc_handle_t *samr_handle)
2N/A{
2N/A struct samr_Connect5 arg;
2N/A int opnum;
2N/A DWORD status;
2N/A smb_domainex_t dinfo;
2N/A
2N/A bzero(&arg, sizeof (struct samr_Connect5));
2N/A opnum = SAMR_OPNUM_Connect5;
2N/A status = NT_STATUS_SUCCESS;
2N/A
2N/A if (!smb_domain_getinfo(&dinfo))
2N/A return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
2N/A
2N/A arg.servername = ndr_rpc_derive_nbhandle(samr_handle, server);
2N/A arg.access_mask = SAM_ENUM_LOCAL_DOMAIN;
2N/A arg.unknown2_00000001 = 0x00000001;
2N/A arg.unknown3_00000001 = 0x00000001;
2N/A arg.unknown4_00000003 = 0x00000003;
2N/A arg.unknown5_00000000 = 0x00000000;
2N/A
2N/A if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
2N/A status = NT_STATUS_UNSUCCESSFUL;
2N/A } else if (arg.status != 0) {
2N/A status = NT_SC_VALUE(arg.status);
2N/A } else {
2N/A
2N/A (void) memcpy(&samr_handle->handle, &arg.handle,
2N/A sizeof (ndr_hdid_t));
2N/A
2N/A if (ndr_is_null_handle(samr_handle))
2N/A status = NT_STATUS_INVALID_HANDLE;
2N/A }
2N/A
2N/A ndr_rpc_release(samr_handle);
2N/A return (status);
2N/A}
2N/A
2N/A
2N/A/*
2N/A * samr_close_handle
2N/A *
2N/A * This is function closes any valid handle, i.e. sam, domain, user etc.
2N/A * If the handle being closed is the top level connect handle, we unbind.
2N/A * Then we zero out the handle to invalidate it.
2N/A */
2N/Aint
2N/Asamr_close_handle(mlsvc_handle_t *samr_handle)
2N/A{
2N/A struct samr_CloseHandle arg;
2N/A int opnum;
2N/A
2N/A if (ndr_is_null_handle(samr_handle))
2N/A return (-1);
2N/A
2N/A opnum = SAMR_OPNUM_CloseHandle;
2N/A bzero(&arg, sizeof (struct samr_CloseHandle));
2N/A (void) memcpy(&arg.handle, &samr_handle->handle, sizeof (ndr_hdid_t));
2N/A
2N/A (void) ndr_rpc_call(samr_handle, opnum, &arg);
2N/A ndr_rpc_release(samr_handle);
2N/A
2N/A if (ndr_is_bind_handle(samr_handle))
2N/A ndr_rpc_unbind(samr_handle);
2N/A
2N/A bzero(samr_handle, sizeof (mlsvc_handle_t));
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * samr_open_domain
2N/A *
2N/A * We use a SAM handle to obtain a handle for a domain, specified by
2N/A * the SID. The SID can be obtain via the LSA interface. A handle for
2N/A * the domain is returned in domain_handle.
2N/A */
2N/ADWORD
2N/Asamr_open_domain(mlsvc_handle_t *samr_handle, DWORD access_mask,
2N/A struct samr_sid *sid, mlsvc_handle_t *domain_handle)
2N/A{
2N/A struct samr_OpenDomain arg;
2N/A int opnum;
2N/A DWORD status;
2N/A
2N/A if (ndr_is_null_handle(samr_handle) ||
2N/A sid == NULL || domain_handle == NULL) {
2N/A return (NT_STATUS_INVALID_PARAMETER);
2N/A }
2N/A
2N/A opnum = SAMR_OPNUM_OpenDomain;
2N/A bzero(&arg, sizeof (struct samr_OpenDomain));
2N/A (void) memcpy(&arg.handle, &samr_handle->handle, sizeof (ndr_hdid_t));
2N/A
2N/A arg.access_mask = access_mask;
2N/A arg.sid = sid;
2N/A
2N/A if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
2N/A status = NT_STATUS_UNSUCCESSFUL;
2N/A } else if (arg.status != 0) {
2N/A status = arg.status;
2N/A } else {
2N/A status = NT_STATUS_SUCCESS;
2N/A ndr_inherit_handle(domain_handle, samr_handle);
2N/A
2N/A (void) memcpy(&domain_handle->handle, &arg.domain_handle,
2N/A sizeof (ndr_hdid_t));
2N/A
2N/A if (ndr_is_null_handle(domain_handle))
2N/A status = NT_STATUS_INVALID_HANDLE;
2N/A }
2N/A
2N/A if (status != NT_STATUS_SUCCESS)
2N/A ndr_rpc_status(samr_handle, opnum, status);
2N/A
2N/A ndr_rpc_release(samr_handle);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * samr_open_user
2N/A *
2N/A * Use a domain handle to obtain a handle for a user, specified by the
2N/A * user RID. A user RID (effectively a uid) can be obtained via the
2N/A * LSA interface. A handle for the user is returned in user_handle.
2N/A * Once you have a user handle it should be possible to query the SAM
2N/A * for information on that user.
2N/A */
2N/ADWORD
2N/Asamr_open_user(mlsvc_handle_t *domain_handle, DWORD access_mask, DWORD rid,
2N/A mlsvc_handle_t *user_handle)
2N/A{
2N/A struct samr_OpenUser arg;
2N/A int opnum;
2N/A DWORD status = NT_STATUS_SUCCESS;
2N/A
2N/A if (ndr_is_null_handle(domain_handle) || user_handle == NULL)
2N/A return (NT_STATUS_INVALID_PARAMETER);
2N/A
2N/A opnum = SAMR_OPNUM_OpenUser;
2N/A bzero(&arg, sizeof (struct samr_OpenUser));
2N/A (void) memcpy(&arg.handle, &domain_handle->handle,
2N/A sizeof (ndr_hdid_t));
2N/A arg.access_mask = access_mask;
2N/A arg.rid = rid;
2N/A
2N/A if (ndr_rpc_call(domain_handle, opnum, &arg) != 0) {
2N/A status = NT_STATUS_UNSUCCESSFUL;
2N/A } else if (arg.status != 0) {
2N/A ndr_rpc_status(domain_handle, opnum, arg.status);
2N/A status = NT_SC_VALUE(arg.status);
2N/A } else {
2N/A ndr_inherit_handle(user_handle, domain_handle);
2N/A
2N/A (void) memcpy(&user_handle->handle, &arg.user_handle,
2N/A sizeof (ndr_hdid_t));
2N/A
2N/A if (ndr_is_null_handle(user_handle))
2N/A status = NT_STATUS_INVALID_HANDLE;
2N/A }
2N/A
2N/A ndr_rpc_release(domain_handle);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * samr_delete_user
2N/A *
2N/A * Delete the user specified by the user_handle.
2N/A */
2N/ADWORD
2N/Asamr_delete_user(mlsvc_handle_t *user_handle)
2N/A{
2N/A struct samr_DeleteUser arg;
2N/A int opnum;
2N/A DWORD status;
2N/A
2N/A if (ndr_is_null_handle(user_handle))
2N/A return (NT_STATUS_INVALID_PARAMETER);
2N/A
2N/A opnum = SAMR_OPNUM_DeleteUser;
2N/A bzero(&arg, sizeof (struct samr_DeleteUser));
2N/A (void) memcpy(&arg.user_handle, &user_handle->handle,
2N/A sizeof (ndr_hdid_t));
2N/A
2N/A if (ndr_rpc_call(user_handle, opnum, &arg) != 0) {
2N/A status = NT_STATUS_INVALID_PARAMETER;
2N/A } else if (arg.status != 0) {
2N/A ndr_rpc_status(user_handle, opnum, arg.status);
2N/A status = NT_SC_VALUE(arg.status);
2N/A } else {
2N/A status = 0;
2N/A }
2N/A
2N/A ndr_rpc_release(user_handle);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * samr_open_group
2N/A *
2N/A * Use a domain handle to obtain a handle for a group, specified by the
2N/A * group RID. A group RID (effectively a gid) can be obtained via the
2N/A * LSA interface. A handle for the group is returned in group_handle.
2N/A * Once you have a group handle it should be possible to query the SAM
2N/A * for information on that group.
2N/A */
2N/Aint
2N/Asamr_open_group(
2N/A mlsvc_handle_t *domain_handle,
2N/A DWORD rid,
2N/A mlsvc_handle_t *group_handle)
2N/A{
2N/A struct samr_OpenGroup arg;
2N/A int opnum;
2N/A int rc;
2N/A
2N/A if (ndr_is_null_handle(domain_handle) || group_handle == NULL)
2N/A return (-1);
2N/A
2N/A opnum = SAMR_OPNUM_OpenGroup;
2N/A bzero(&arg, sizeof (struct samr_OpenUser));
2N/A (void) memcpy(&arg.handle, &domain_handle->handle,
2N/A sizeof (ndr_hdid_t));
2N/A arg.access_mask = SAM_LOOKUP_INFORMATION | SAM_ACCESS_USER_READ;
2N/A arg.rid = rid;
2N/A
2N/A if ((rc = ndr_rpc_call(domain_handle, opnum, &arg)) != 0)
2N/A return (-1);
2N/A
2N/A if (arg.status != 0) {
2N/A ndr_rpc_status(domain_handle, opnum, arg.status);
2N/A rc = -1;
2N/A } else {
2N/A ndr_inherit_handle(group_handle, domain_handle);
2N/A
2N/A (void) memcpy(&group_handle->handle, &arg.group_handle,
2N/A sizeof (ndr_hdid_t));
2N/A
2N/A if (ndr_is_null_handle(group_handle))
2N/A rc = -1;
2N/A }
2N/A
2N/A ndr_rpc_release(domain_handle);
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * samr_create_user
2N/A *
2N/A * Create a user in the domain specified by the domain handle. If this
2N/A * call is successful, the server will return the RID for the user and
2N/A * a user handle, which may be used to set or query the SAM.
2N/A *
2N/A * Observed status codes:
2N/A * NT_STATUS_INVALID_PARAMETER
2N/A * NT_STATUS_INVALID_ACCOUNT_NAME
2N/A * NT_STATUS_ACCESS_DENIED
2N/A * NT_STATUS_USER_EXISTS
2N/A *
2N/A * Returns 0 on success. Otherwise returns an NT status code.
2N/A */
2N/ADWORD
2N/Asamr_create_user(mlsvc_handle_t *domain_handle, char *username,
2N/A DWORD account_flags, DWORD *rid, mlsvc_handle_t *user_handle)
2N/A{
2N/A struct samr_CreateUser arg;
2N/A ndr_heap_t *heap;
2N/A int opnum;
2N/A int rc;
2N/A DWORD status = 0;
2N/A
2N/A if (ndr_is_null_handle(domain_handle) ||
2N/A username == NULL || rid == NULL) {
2N/A return (NT_STATUS_INVALID_PARAMETER);
2N/A }
2N/A
2N/A opnum = SAMR_OPNUM_CreateUser;
2N/A
2N/A bzero(&arg, sizeof (struct samr_CreateUser));
2N/A (void) memcpy(&arg.handle, &domain_handle->handle,
2N/A sizeof (ndr_hdid_t));
2N/A
2N/A heap = ndr_rpc_get_heap(domain_handle);
2N/A ndr_heap_mkvcs(heap, username, (ndr_vcstr_t *)&arg.username);
2N/A
2N/A arg.account_flags = account_flags;
2N/A arg.desired_access = 0xE00500B0;
2N/A
2N/A rc = ndr_rpc_call(domain_handle, opnum, &arg);
2N/A if (rc != 0) {
2N/A status = NT_STATUS_INVALID_PARAMETER;
2N/A } else if (arg.status != 0) {
2N/A status = NT_SC_VALUE(arg.status);
2N/A
2N/A if (status != NT_STATUS_USER_EXISTS) {
2N/A smb_tracef("SamrCreateUser[%s]: %s", username,
2N/A xlate_nt_status(status));
2N/A }
2N/A } else {
2N/A ndr_inherit_handle(user_handle, domain_handle);
2N/A
2N/A (void) memcpy(&user_handle->handle, &arg.user_handle,
2N/A sizeof (ndr_hdid_t));
2N/A
2N/A *rid = arg.rid;
2N/A
2N/A if (ndr_is_null_handle(user_handle))
2N/A status = NT_STATUS_INVALID_HANDLE;
2N/A else
2N/A status = 0;
2N/A }
2N/A
2N/A ndr_rpc_release(domain_handle);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * samr_lookup_domain
2N/A *
2N/A * Lookup up the domain SID for the specified domain name. The handle
2N/A * should be one returned from samr_connect. The allocated memory for
2N/A * the returned SID must be freed by caller.
2N/A */
2N/Asmb_sid_t *
2N/Asamr_lookup_domain(mlsvc_handle_t *samr_handle, char *domain_name)
2N/A{
2N/A struct samr_LookupDomain arg;
2N/A smb_sid_t *domsid = NULL;
2N/A int opnum;
2N/A size_t length;
2N/A
2N/A if (ndr_is_null_handle(samr_handle) || domain_name == NULL)
2N/A return (NULL);
2N/A
2N/A opnum = SAMR_OPNUM_LookupDomain;
2N/A bzero(&arg, sizeof (struct samr_LookupDomain));
2N/A
2N/A (void) memcpy(&arg.handle, &samr_handle->handle,
2N/A sizeof (samr_handle_t));
2N/A
2N/A length = smb_wcequiv_strlen(domain_name);
2N/A if (ndr_rpc_server_os(samr_handle) == NATIVE_OS_WIN2000)
2N/A length += sizeof (smb_wchar_t);
2N/A
2N/A arg.domain_name.length = length;
2N/A arg.domain_name.allosize = length;
2N/A arg.domain_name.str = (unsigned char *)domain_name;
2N/A
2N/A if (ndr_rpc_call(samr_handle, opnum, &arg) == 0)
2N/A domsid = smb_sid_dup((smb_sid_t *)arg.sid);
2N/A
2N/A ndr_rpc_release(samr_handle);
2N/A return (domsid);
2N/A}
2N/A
2N/A/*
2N/A * samr_enum_local_domains
2N/A *
2N/A * Get the list of local domains supported by a server.
2N/A *
2N/A * Returns NT status codes.
2N/A */
2N/ADWORD
2N/Asamr_enum_local_domains(mlsvc_handle_t *samr_handle)
2N/A{
2N/A struct samr_EnumLocalDomain arg;
2N/A int opnum;
2N/A DWORD status;
2N/A
2N/A if (ndr_is_null_handle(samr_handle))
2N/A return (NT_STATUS_INVALID_PARAMETER);
2N/A
2N/A opnum = SAMR_OPNUM_EnumLocalDomains;
2N/A bzero(&arg, sizeof (struct samr_EnumLocalDomain));
2N/A
2N/A (void) memcpy(&arg.handle, &samr_handle->handle,
2N/A sizeof (samr_handle_t));
2N/A arg.enum_context = 0;
2N/A arg.max_length = 0x00002000; /* Value used by NT */
2N/A
2N/A if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
2N/A status = NT_STATUS_INVALID_PARAMETER;
2N/A } else {
2N/A status = NT_SC_VALUE(arg.status);
2N/A
2N/A /*
2N/A * Handle none-mapped status quietly.
2N/A */
2N/A if (status != NT_STATUS_NONE_MAPPED)
2N/A ndr_rpc_status(samr_handle, opnum, arg.status);
2N/A }
2N/A
2N/A ndr_rpc_release(samr_handle);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * samr_lookup_domain_names
2N/A *
2N/A * Lookup up the given name in the domain specified by domain_handle.
2N/A * Upon a successful lookup the information is returned in the account
2N/A * arg and caller must free allocated memories by calling smb_account_free().
2N/A *
2N/A * Returns NT status codes.
2N/A */
2N/Auint32_t
2N/Asamr_lookup_domain_names(mlsvc_handle_t *domain_handle, char *name,
2N/A smb_account_t *account)
2N/A{
2N/A struct samr_LookupNames arg;
2N/A int opnum;
2N/A uint32_t status;
2N/A size_t length;
2N/A
2N/A if (ndr_is_null_handle(domain_handle) ||
2N/A name == NULL || account == NULL) {
2N/A return (NT_STATUS_INVALID_PARAMETER);
2N/A }
2N/A
2N/A bzero(account, sizeof (smb_account_t));
2N/A opnum = SAMR_OPNUM_LookupNames;
2N/A bzero(&arg, sizeof (struct samr_LookupNames));
2N/A
2N/A (void) memcpy(&arg.handle, &domain_handle->handle,
2N/A sizeof (samr_handle_t));
2N/A arg.n_entry = 1;
2N/A arg.max_n_entry = 1000;
2N/A arg.index = 0;
2N/A arg.total = 1;
2N/A
2N/A length = smb_wcequiv_strlen(name);
2N/A if (ndr_rpc_server_os(domain_handle) == NATIVE_OS_WIN2000)
2N/A length += sizeof (smb_wchar_t);
2N/A
2N/A arg.name.length = length;
2N/A arg.name.allosize = length;
2N/A arg.name.str = (unsigned char *)name;
2N/A
2N/A if (ndr_rpc_call(domain_handle, opnum, &arg) != 0) {
2N/A status = NT_STATUS_INVALID_PARAMETER;
2N/A } else if (arg.status != NT_STATUS_SUCCESS) {
2N/A status = NT_SC_VALUE(arg.status);
2N/A
2N/A /*
2N/A * Handle none-mapped status quietly.
2N/A */
2N/A if (status != NT_STATUS_NONE_MAPPED)
2N/A ndr_rpc_status(domain_handle, opnum, arg.status);
2N/A } else {
2N/A account->a_type = arg.rid_types.rid_type[0];
2N/A account->a_rid = arg.rids.rid[0];
2N/A status = NT_STATUS_SUCCESS;
2N/A }
2N/A
2N/A ndr_rpc_release(domain_handle);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * samr_query_user_info
2N/A *
2N/A * Query information on a specific user. The handle must be a valid
2N/A * user handle obtained via samr_open_user.
2N/A *
2N/A * Returns 0 on success, otherwise returns -ve error code.
2N/A */
2N/Aint
2N/Asamr_query_user_info(mlsvc_handle_t *user_handle, WORD switch_value,
2N/A union samr_user_info *user_info)
2N/A{
2N/A struct samr_QueryUserInfo arg;
2N/A int opnum;
2N/A int rc;
2N/A
2N/A if (ndr_is_null_handle(user_handle) || user_info == 0)
2N/A return (-1);
2N/A
2N/A opnum = SAMR_OPNUM_QueryUserInfo;
2N/A bzero(&arg, sizeof (struct samr_QueryUserInfo));
2N/A
2N/A (void) memcpy(&arg.user_handle, &user_handle->handle,
2N/A sizeof (samr_handle_t));
2N/A arg.switch_value = switch_value;
2N/A
2N/A if (ndr_rpc_call(user_handle, opnum, &arg) != 0) {
2N/A ndr_rpc_release(user_handle);
2N/A return (-1);
2N/A }
2N/A
2N/A if (arg.status != 0)
2N/A rc = -1;
2N/A else
2N/A rc = samr_setup_user_info(switch_value, &arg, user_info);
2N/A
2N/A ndr_rpc_release(user_handle);
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * samr_setup_user_info
2N/A *
2N/A * Private function to set up the samr_user_info data. Dependent on
2N/A * the switch value this function may use strdup which will malloc
2N/A * memory. The caller is responsible for deallocating this memory.
2N/A *
2N/A * Returns 0 on success, otherwise returns -1.
2N/A */
2N/Astatic int
2N/Asamr_setup_user_info(WORD switch_value,
2N/A struct samr_QueryUserInfo *arg, union samr_user_info *user_info)
2N/A{
2N/A struct samr_QueryUserInfo1 *info1;
2N/A struct samr_QueryUserInfo6 *info6;
2N/A
2N/A switch (switch_value) {
2N/A case 1:
2N/A info1 = &arg->ru.info1;
2N/A user_info->info1.username = strdup(
2N/A (char const *)info1->username.str);
2N/A user_info->info1.fullname = strdup(
2N/A (char const *)info1->fullname.str);
2N/A user_info->info1.description = strdup(
2N/A (char const *)info1->description.str);
2N/A user_info->info1.unknown = 0;
2N/A user_info->info1.group_rid = info1->group_rid;
2N/A return (0);
2N/A
2N/A case 6:
2N/A info6 = &arg->ru.info6;
2N/A user_info->info6.username = strdup(
2N/A (char const *)info6->username.str);
2N/A user_info->info6.fullname = strdup(
2N/A (char const *)info6->fullname.str);
2N/A return (0);
2N/A
2N/A case 7:
2N/A user_info->info7.username = strdup(
2N/A (char const *)arg->ru.info7.username.str);
2N/A return (0);
2N/A
2N/A case 8:
2N/A user_info->info8.fullname = strdup(
2N/A (char const *)arg->ru.info8.fullname.str);
2N/A return (0);
2N/A
2N/A case 9:
2N/A user_info->info9.group_rid = arg->ru.info9.group_rid;
2N/A return (0);
2N/A
2N/A case 16:
2N/A return (0);
2N/A
2N/A default:
2N/A break;
2N/A };
2N/A
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * samr_query_user_groups
2N/A *
2N/A * Query the groups for a specific user. The handle must be a valid
2N/A * user handle obtained via samr_open_user. The list of groups is
2N/A * returned in group_info. Note that group_info->groups is allocated
2N/A * using malloc. The caller is responsible for deallocating this
2N/A * memory when it is no longer required. If group_info->n_entry is 0
2N/A * then no memory was allocated.
2N/A *
2N/A * Returns 0 on success, otherwise returns -1.
2N/A */
2N/Aint
2N/Asamr_query_user_groups(mlsvc_handle_t *user_handle, int *n_groups,
2N/A struct samr_UserGroups **groups)
2N/A{
2N/A struct samr_QueryUserGroups arg;
2N/A int opnum;
2N/A int rc;
2N/A int nbytes;
2N/A
2N/A if (ndr_is_null_handle(user_handle))
2N/A return (-1);
2N/A
2N/A opnum = SAMR_OPNUM_QueryUserGroups;
2N/A bzero(&arg, sizeof (struct samr_QueryUserGroups));
2N/A
2N/A (void) memcpy(&arg.user_handle, &user_handle->handle,
2N/A sizeof (samr_handle_t));
2N/A
2N/A rc = ndr_rpc_call(user_handle, opnum, &arg);
2N/A if (rc == 0) {
2N/A if (arg.info == 0) {
2N/A rc = -1;
2N/A } else {
2N/A nbytes = arg.info->n_entry *
2N/A sizeof (struct samr_UserGroups);
2N/A
2N/A if ((*groups = malloc(nbytes)) == NULL) {
2N/A *n_groups = 0;
2N/A rc = -1;
2N/A } else {
2N/A *n_groups = arg.info->n_entry;
2N/A bcopy(arg.info->groups, *groups, nbytes);
2N/A }
2N/A }
2N/A }
2N/A
2N/A ndr_rpc_release(user_handle);
2N/A return (rc);
2N/A}
2N/A
2N/A/*
2N/A * samr_get_user_pwinfo
2N/A *
2N/A * Get some user password info. I'm not sure what this is yet but it is
2N/A * part of the create user sequence. The handle must be a valid user
2N/A * handle. Since I don't know what this is returning, I haven't provided
2N/A * any return data yet.
2N/A *
2N/A * Returns 0 on success. Otherwise returns an NT status code.
2N/A */
2N/ADWORD
2N/Asamr_get_user_pwinfo(mlsvc_handle_t *user_handle)
2N/A{
2N/A struct samr_GetUserPwInfo arg;
2N/A int opnum;
2N/A DWORD status;
2N/A
2N/A if (ndr_is_null_handle(user_handle))
2N/A return (NT_STATUS_INVALID_PARAMETER);
2N/A
2N/A opnum = SAMR_OPNUM_GetUserPwInfo;
2N/A bzero(&arg, sizeof (struct samr_GetUserPwInfo));
2N/A (void) memcpy(&arg.user_handle, &user_handle->handle,
2N/A sizeof (samr_handle_t));
2N/A
2N/A if (ndr_rpc_call(user_handle, opnum, &arg) != 0) {
2N/A status = NT_STATUS_INVALID_PARAMETER;
2N/A } else if (arg.status != 0) {
2N/A ndr_rpc_status(user_handle, opnum, arg.status);
2N/A status = NT_SC_VALUE(arg.status);
2N/A } else {
2N/A status = 0;
2N/A }
2N/A
2N/A ndr_rpc_release(user_handle);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * samr_set_user_info
2N/A *
2N/A * Returns 0 on success. Otherwise returns an NT status code.
2N/A * NT status codes observed so far:
2N/A * NT_STATUS_WRONG_PASSWORD
2N/A */
2N/A/*ARGSUSED*/
2N/ADWORD
2N/Asamr_set_user_info(mlsvc_handle_t *user_handle)
2N/A{
2N/A unsigned char ssn_key[SMBAUTH_SESSION_KEY_SZ];
2N/A struct samr_SetUserInfo arg;
2N/A int opnum;
2N/A DWORD status = 0;
2N/A
2N/A if (ndr_is_null_handle(user_handle))
2N/A return (NT_STATUS_INVALID_PARAMETER);
2N/A
2N/A if (ndr_rpc_get_ssnkey(user_handle, ssn_key, sizeof (ssn_key)))
2N/A return (NT_STATUS_INVALID_PARAMETER);
2N/A
2N/A opnum = SAMR_OPNUM_SetUserInfo;
2N/A bzero(&arg, sizeof (struct samr_SetUserInfo));
2N/A (void) memcpy(&arg.user_handle, &user_handle->handle,
2N/A sizeof (samr_handle_t));
2N/A
2N/A arg.info.index = SAMR_SET_USER_INFO_23;
2N/A arg.info.switch_value = SAMR_SET_USER_INFO_23;
2N/A
2N/A samr_set_user_unknowns(&arg.info.ru.info23);
2N/A samr_set_user_logon_hours(&arg);
2N/A
2N/A if (samr_set_user_password(ssn_key, arg.info.ru.info23.password) < 0)
2N/A status = NT_STATUS_INTERNAL_ERROR;
2N/A
2N/A if (ndr_rpc_call(user_handle, opnum, &arg) != 0) {
2N/A status = NT_STATUS_INVALID_PARAMETER;
2N/A } else if (arg.status != 0) {
2N/A ndr_rpc_status(user_handle, opnum, arg.status);
2N/A status = NT_SC_VALUE(arg.status);
2N/A }
2N/A
2N/A ndr_rpc_release(user_handle);
2N/A return (status);
2N/A}
2N/A
2N/Astatic void
2N/Asamr_set_user_unknowns(struct samr_SetUserInfo23 *info)
2N/A{
2N/A bzero(info, sizeof (struct samr_SetUserInfo23));
2N/A
2N/A info->sd.length = 0;
2N/A info->sd.data = 0;
2N/A info->user_rid = 0;
2N/A info->group_rid = DOMAIN_GROUP_RID_USERS;
2N/A
2N/A /*
2N/A * The trust account value used here should probably
2N/A * match the one used to create the trust account.
2N/A */
2N/A info->acct_info = SAMR_AF_WORKSTATION_TRUST_ACCOUNT;
2N/A info->flags = 0x09F827FA;
2N/A}
2N/A
2N/A/*
2N/A * samr_set_user_logon_hours
2N/A *
2N/A * SamrSetUserInfo appears to contain some logon hours information, which
2N/A * looks like a varying, conformant array. The top level contains a value
2N/A * (units), which probably indicates the how to interpret the array. The
2N/A * array definition looks like it contains a maximum size, an initial
2N/A * offset and a bit length (units/8), followed by the bitmap.
2N/A *
2N/A * (info)
2N/A * +-------+
2N/A * | units |
2N/A * +-------+
2N/A * | pad |
2N/A * +-------+ (hours)
2N/A * | hours |-->+-----------+
2N/A * +-------+ | max_is |
2N/A * +-----------+
2N/A * | first_is |
2N/A * +-----------+
2N/A * | length_is |
2N/A * +------------------------+
2N/A * | bitmap[length_is] |
2N/A * +---------+--------------+
2N/A *
2N/A * 10080 minutes/week => 10080/8 = 1260 (0x04EC) bytes.
2N/A * 168 hours/week => 168/8 = 21 (0xA8) bytes.
2N/A * In the netmon examples seen so far, all bits are set to 1, i.e.
2N/A * an array containing 0xff. This is probably the default setting.
2N/A *
2N/A * ndrgen has a problem with complex [size_is] statements (length/8).
2N/A * So, for now, we fake it using two separate components (samr_logon_info and
2N/A * samr_logon_hours NDR structures).
2N/A */
2N/Astatic void
2N/Asamr_set_user_logon_hours(struct samr_SetUserInfo *sui)
2N/A{
2N/A sui->logon_hours.size = SAMR_HOURS_MAX_SIZE;
2N/A sui->logon_hours.first = 0;
2N/A sui->logon_hours.length = SAMR_SET_USER_HOURS_SZ;
2N/A (void) memset(sui->logon_hours.bitmap, 0xFF, SAMR_SET_USER_HOURS_SZ);
2N/A
2N/A sui->info.ru.info23.logon_info.units = SAMR_HOURS_PER_WEEK;
2N/A sui->info.ru.info23.logon_info.pad = 0;
2N/A
2N/A sui->info.ru.info23.logon_info.hours =
2N/A (DWORD)(uintptr_t)sui->logon_hours.bitmap;
2N/A}
2N/A
2N/A/*
2N/A * samr_set_user_password
2N/A *
2N/A * Set the initial password for the user.
2N/A * The OEM password is generated using the machine password and the user
2N/A * session key(nt_key).
2N/A * Returns 0 if everything goes well, -1 if there is trouble generating a
2N/A * key.
2N/A */
2N/Astatic int
2N/Asamr_set_user_password(unsigned char *nt_key, BYTE *oem_password)
2N/A{
2N/A char machine_passwd[NETR_MACHINE_ACCT_PASSWD_MAX];
2N/A int rc;
2N/A
2N/A randomize((char *)oem_password, SAMR_SET_USER_DATA_SZ);
2N/A
2N/A rc = smb_get_machine_passwd(machine_passwd, sizeof (machine_passwd));
2N/A if (rc != 0)
2N/A return (-1);
2N/A
2N/A /*LINTED E_BAD_PTR_CAST_ALIGN*/
2N/A (void) sam_oem_password((oem_password_t *)oem_password,
2N/A (unsigned char *)machine_passwd, nt_key);
2N/A return (0);
2N/A}