#include <stdio.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/param.h>
#include <libmlrpc/libmlrpc.h>
#include <smbsrv/libsmb.h>
#include <smbsrv/libmlsvc.h>
#include <smbsrv/smbinfo.h>
#include <smb/ntaccess.h>
#include <smbsrv/smb_sid.h>
#include <samlib.h>
static DWORD samr_connect2(char *, char *, char *, DWORD, mlsvc_handle_t *);
static DWORD samr_connect4(char *, char *, char *, DWORD, mlsvc_handle_t *);
static DWORD samr_connect5(char *, char *, char *, DWORD, mlsvc_handle_t *);
typedef DWORD (*samr_connop_t)(char *, char *, char *, DWORD,
mlsvc_handle_t *);
static int samr_setup_user_info(WORD, struct samr_QueryUserInfo *,
union samr_user_info *);
DWORD
samr_open(char *server, char *domain, char *username, DWORD access_mask,
mlsvc_handle_t *samr_handle)
{
smb_domainex_t di;
DWORD status;
if (server == NULL || domain == NULL) {
if (!smb_domain_getinfo(&di))
return (NT_STATUS_INTERNAL_ERROR);
server = di.d_dci.dc_name;
domain = di.d_primary.di_nbname;
}
if (username == NULL)
username = MLSVC_ANON_USER;
status = samr_connect(server, domain, username, access_mask,
samr_handle);
return (status);
}
DWORD
samr_connect(char *server, char *domain, char *username, DWORD access_mask,
mlsvc_handle_t *samr_handle)
{
static samr_connop_t samr_connop[] = {
samr_connect5,
samr_connect4,
samr_connect2
};
int n_op = (sizeof (samr_connop) / sizeof (samr_connop[0]));
DWORD status;
int i;
status = ndr_rpc_bind(samr_handle, server, domain, username, "SAMR");
if (status)
return (status);
for (i = 0; i < n_op; ++i) {
status = (*samr_connop[i])(server, domain, username,
access_mask, samr_handle);
if (status == NT_STATUS_SUCCESS)
return (status);
}
ndr_rpc_unbind(samr_handle);
return (status);
}
static DWORD
samr_connect2(char *server, char *domain, char *username, DWORD access_mask,
mlsvc_handle_t *samr_handle)
{
struct samr_Connect2 arg;
int opnum;
DWORD status;
int len;
bzero(&arg, sizeof (struct samr_Connect2));
opnum = SAMR_OPNUM_Connect2;
status = NT_STATUS_SUCCESS;
len = strlen(server) + 4;
arg.servername = ndr_rpc_malloc(samr_handle, len);
(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
arg.access_mask = access_mask;
if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
status = NT_STATUS_UNSUCCESSFUL;
} else if (arg.status != 0) {
status = NT_SC_VALUE(arg.status);
} else {
(void) memcpy(&samr_handle->handle, &arg.handle,
sizeof (ndr_hdid_t));
if (ndr_is_null_handle(samr_handle))
status = NT_STATUS_INVALID_HANDLE;
}
ndr_rpc_release(samr_handle);
return (status);
}
static DWORD
samr_connect4(char *server, char *domain, char *username, DWORD access_mask,
mlsvc_handle_t *samr_handle)
{
struct samr_Connect4 arg;
int opnum;
DWORD status;
int len;
bzero(&arg, sizeof (struct samr_Connect4));
opnum = SAMR_OPNUM_Connect4;
status = NT_STATUS_SUCCESS;
len = strlen(server) + 4;
arg.servername = ndr_rpc_malloc(samr_handle, len);
(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
arg.revision = SAMR_REVISION_2;
arg.access_mask = access_mask;
if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
status = NT_STATUS_UNSUCCESSFUL;
} else if (arg.status != 0) {
status = NT_SC_VALUE(arg.status);
} else {
(void) memcpy(&samr_handle->handle, &arg.handle,
sizeof (ndr_hdid_t));
if (ndr_is_null_handle(samr_handle))
status = NT_STATUS_INVALID_HANDLE;
}
ndr_rpc_release(samr_handle);
return (status);
}
static DWORD
samr_connect5(char *server, char *domain, char *username, DWORD access_mask,
mlsvc_handle_t *samr_handle)
{
struct samr_Connect5 arg;
int len;
int opnum;
DWORD status;
bzero(&arg, sizeof (struct samr_Connect5));
opnum = SAMR_OPNUM_Connect5;
status = NT_STATUS_SUCCESS;
len = strlen(server) + 4;
arg.servername = ndr_rpc_malloc(samr_handle, len);
(void) snprintf((char *)arg.servername, len, "\\\\%s", server);
arg.access_mask = SAM_ENUM_LOCAL_DOMAIN;
arg.unknown2_00000001 = 0x00000001;
arg.unknown3_00000001 = 0x00000001;
arg.unknown4_00000003 = 0x00000003;
arg.unknown5_00000000 = 0x00000000;
if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
status = NT_STATUS_UNSUCCESSFUL;
} else if (arg.status != 0) {
status = NT_SC_VALUE(arg.status);
} else {
(void) memcpy(&samr_handle->handle, &arg.handle,
sizeof (ndr_hdid_t));
if (ndr_is_null_handle(samr_handle))
status = NT_STATUS_INVALID_HANDLE;
}
ndr_rpc_release(samr_handle);
return (status);
}
void
samr_close_handle(mlsvc_handle_t *samr_handle)
{
struct samr_CloseHandle arg;
int opnum;
if (ndr_is_null_handle(samr_handle))
return;
opnum = SAMR_OPNUM_CloseHandle;
bzero(&arg, sizeof (struct samr_CloseHandle));
(void) memcpy(&arg.handle, &samr_handle->handle, sizeof (ndr_hdid_t));
(void) ndr_rpc_call(samr_handle, opnum, &arg);
ndr_rpc_release(samr_handle);
if (ndr_is_bind_handle(samr_handle))
ndr_rpc_unbind(samr_handle);
bzero(samr_handle, sizeof (mlsvc_handle_t));
}
DWORD
samr_open_domain(mlsvc_handle_t *samr_handle, DWORD access_mask,
struct samr_sid *sid, mlsvc_handle_t *domain_handle)
{
struct samr_OpenDomain arg;
int opnum;
DWORD status;
if (ndr_is_null_handle(samr_handle) ||
sid == NULL || domain_handle == NULL) {
return (NT_STATUS_INVALID_PARAMETER);
}
opnum = SAMR_OPNUM_OpenDomain;
bzero(&arg, sizeof (struct samr_OpenDomain));
(void) memcpy(&arg.handle, &samr_handle->handle, sizeof (ndr_hdid_t));
arg.access_mask = access_mask;
arg.sid = sid;
if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
status = NT_STATUS_UNSUCCESSFUL;
} else if (arg.status != 0) {
status = arg.status;
} else {
status = NT_STATUS_SUCCESS;
ndr_inherit_handle(domain_handle, samr_handle);
(void) memcpy(&domain_handle->handle, &arg.domain_handle,
sizeof (ndr_hdid_t));
if (ndr_is_null_handle(domain_handle))
status = NT_STATUS_INVALID_HANDLE;
}
if (status != NT_STATUS_SUCCESS)
ndr_rpc_status(samr_handle, opnum, status);
ndr_rpc_release(samr_handle);
return (status);
}
DWORD
samr_open_user(mlsvc_handle_t *domain_handle, DWORD access_mask, DWORD rid,
mlsvc_handle_t *user_handle)
{
struct samr_OpenUser arg;
int opnum;
DWORD status = NT_STATUS_SUCCESS;
if (ndr_is_null_handle(domain_handle) || user_handle == NULL)
return (NT_STATUS_INVALID_PARAMETER);
opnum = SAMR_OPNUM_OpenUser;
bzero(&arg, sizeof (struct samr_OpenUser));
(void) memcpy(&arg.handle, &domain_handle->handle,
sizeof (ndr_hdid_t));
arg.access_mask = access_mask;
arg.rid = rid;
if (ndr_rpc_call(domain_handle, opnum, &arg) != 0) {
status = NT_STATUS_UNSUCCESSFUL;
} else if (arg.status != 0) {
ndr_rpc_status(domain_handle, opnum, arg.status);
status = NT_SC_VALUE(arg.status);
} else {
ndr_inherit_handle(user_handle, domain_handle);
(void) memcpy(&user_handle->handle, &arg.user_handle,
sizeof (ndr_hdid_t));
if (ndr_is_null_handle(user_handle))
status = NT_STATUS_INVALID_HANDLE;
}
ndr_rpc_release(domain_handle);
return (status);
}
DWORD
samr_delete_user(mlsvc_handle_t *user_handle)
{
struct samr_DeleteUser arg;
int opnum;
DWORD status;
if (ndr_is_null_handle(user_handle))
return (NT_STATUS_INVALID_PARAMETER);
opnum = SAMR_OPNUM_DeleteUser;
bzero(&arg, sizeof (struct samr_DeleteUser));
(void) memcpy(&arg.user_handle, &user_handle->handle,
sizeof (ndr_hdid_t));
if (ndr_rpc_call(user_handle, opnum, &arg) != 0) {
status = NT_STATUS_INVALID_PARAMETER;
} else if (arg.status != 0) {
ndr_rpc_status(user_handle, opnum, arg.status);
status = NT_SC_VALUE(arg.status);
} else {
status = 0;
}
ndr_rpc_release(user_handle);
return (status);
}
int
samr_open_group(
mlsvc_handle_t *domain_handle,
DWORD rid,
mlsvc_handle_t *group_handle)
{
struct samr_OpenGroup arg;
int opnum;
int rc;
if (ndr_is_null_handle(domain_handle) || group_handle == NULL)
return (-1);
opnum = SAMR_OPNUM_OpenGroup;
bzero(&arg, sizeof (struct samr_OpenUser));
(void) memcpy(&arg.handle, &domain_handle->handle,
sizeof (ndr_hdid_t));
arg.access_mask = SAM_LOOKUP_INFORMATION | SAM_ACCESS_USER_READ;
arg.rid = rid;
if ((rc = ndr_rpc_call(domain_handle, opnum, &arg)) != 0)
return (-1);
if (arg.status != 0) {
ndr_rpc_status(domain_handle, opnum, arg.status);
rc = -1;
} else {
ndr_inherit_handle(group_handle, domain_handle);
(void) memcpy(&group_handle->handle, &arg.group_handle,
sizeof (ndr_hdid_t));
if (ndr_is_null_handle(group_handle))
rc = -1;
}
ndr_rpc_release(domain_handle);
return (rc);
}
DWORD
samr_create_user(mlsvc_handle_t *domain_handle, char *username,
DWORD account_flags, DWORD *rid, mlsvc_handle_t *user_handle)
{
struct samr_CreateUser arg;
ndr_heap_t *heap;
int opnum;
int rc;
DWORD status = 0;
if (ndr_is_null_handle(domain_handle) ||
username == NULL || rid == NULL) {
return (NT_STATUS_INVALID_PARAMETER);
}
opnum = SAMR_OPNUM_CreateUser;
bzero(&arg, sizeof (struct samr_CreateUser));
(void) memcpy(&arg.handle, &domain_handle->handle,
sizeof (ndr_hdid_t));
heap = ndr_rpc_get_heap(domain_handle);
ndr_heap_mkvcs(heap, username, (ndr_vcstr_t *)&arg.username);
arg.account_flags = account_flags;
arg.desired_access = 0xE00500B0;
rc = ndr_rpc_call(domain_handle, opnum, &arg);
if (rc != 0) {
status = NT_STATUS_INVALID_PARAMETER;
} else if (arg.status != 0) {
status = NT_SC_VALUE(arg.status);
if (status != NT_STATUS_USER_EXISTS) {
smb_tracef("SamrCreateUser[%s]: %s",
username, xlate_nt_status(status));
}
} else {
ndr_inherit_handle(user_handle, domain_handle);
(void) memcpy(&user_handle->handle, &arg.user_handle,
sizeof (ndr_hdid_t));
*rid = arg.rid;
if (ndr_is_null_handle(user_handle))
status = NT_STATUS_INVALID_HANDLE;
else
status = 0;
}
ndr_rpc_release(domain_handle);
return (status);
}
smb_sid_t *
samr_lookup_domain(mlsvc_handle_t *samr_handle, char *domain_name)
{
struct samr_LookupDomain arg;
smb_sid_t *domsid = NULL;
int opnum;
size_t length;
if (ndr_is_null_handle(samr_handle) || domain_name == NULL)
return (NULL);
opnum = SAMR_OPNUM_LookupDomain;
bzero(&arg, sizeof (struct samr_LookupDomain));
(void) memcpy(&arg.handle, &samr_handle->handle,
sizeof (samr_handle_t));
length = smb_wcequiv_strlen(domain_name);
length += sizeof (smb_wchar_t);
arg.domain_name.length = length;
arg.domain_name.allosize = length;
arg.domain_name.str = (unsigned char *)domain_name;
if (ndr_rpc_call(samr_handle, opnum, &arg) == 0)
domsid = smb_sid_dup((smb_sid_t *)arg.sid);
ndr_rpc_release(samr_handle);
return (domsid);
}
DWORD
samr_enum_local_domains(mlsvc_handle_t *samr_handle)
{
struct samr_EnumLocalDomain arg;
int opnum;
DWORD status;
if (ndr_is_null_handle(samr_handle))
return (NT_STATUS_INVALID_PARAMETER);
opnum = SAMR_OPNUM_EnumLocalDomains;
bzero(&arg, sizeof (struct samr_EnumLocalDomain));
(void) memcpy(&arg.handle, &samr_handle->handle,
sizeof (samr_handle_t));
arg.enum_context = 0;
arg.max_length = 0x00002000;
if (ndr_rpc_call(samr_handle, opnum, &arg) != 0) {
status = NT_STATUS_INVALID_PARAMETER;
} else {
status = NT_SC_VALUE(arg.status);
if (status != NT_STATUS_NONE_MAPPED)
ndr_rpc_status(samr_handle, opnum, arg.status);
}
ndr_rpc_release(samr_handle);
return (status);
}
uint32_t
samr_lookup_domain_names(mlsvc_handle_t *domain_handle, char *name,
smb_account_t *account)
{
struct samr_LookupNames arg;
int opnum;
uint32_t status;
size_t length;
if (ndr_is_null_handle(domain_handle) ||
name == NULL || account == NULL) {
return (NT_STATUS_INVALID_PARAMETER);
}
bzero(account, sizeof (smb_account_t));
opnum = SAMR_OPNUM_LookupNames;
bzero(&arg, sizeof (struct samr_LookupNames));
(void) memcpy(&arg.handle, &domain_handle->handle,
sizeof (samr_handle_t));
arg.n_entry = 1;
arg.max_n_entry = 1000;
arg.index = 0;
arg.total = 1;
length = smb_wcequiv_strlen(name);
length += sizeof (smb_wchar_t);
arg.name.length = length;
arg.name.allosize = length;
arg.name.str = (unsigned char *)name;
if (ndr_rpc_call(domain_handle, opnum, &arg) != 0) {
status = NT_STATUS_INVALID_PARAMETER;
} else if (arg.status != NT_STATUS_SUCCESS) {
status = NT_SC_VALUE(arg.status);
if (status != NT_STATUS_NONE_MAPPED)
ndr_rpc_status(domain_handle, opnum, arg.status);
} else {
account->a_type = arg.rid_types.rid_type[0];
account->a_rid = arg.rids.rid[0];
status = NT_STATUS_SUCCESS;
}
ndr_rpc_release(domain_handle);
return (status);
}
DWORD
samr_query_user_info(mlsvc_handle_t *user_handle, WORD switch_value,
union samr_user_info *user_info)
{
struct samr_QueryUserInfo arg;
int opnum;
if (ndr_is_null_handle(user_handle) || user_info == 0)
return (NT_STATUS_INTERNAL_ERROR);
opnum = SAMR_OPNUM_QueryUserInfo;
bzero(&arg, sizeof (struct samr_QueryUserInfo));
(void) memcpy(&arg.user_handle, &user_handle->handle,
sizeof (samr_handle_t));
arg.switch_value = switch_value;
if (ndr_rpc_call(user_handle, opnum, &arg) != 0)
arg.status = RPC_NT_CALL_FAILED;
if (arg.status == 0)
(void) samr_setup_user_info(switch_value, &arg, user_info);
return (arg.status);
}
static int
samr_setup_user_info(WORD switch_value,
struct samr_QueryUserInfo *arg, union samr_user_info *user_info)
{
struct samr_QueryUserInfo1 *info1;
struct samr_QueryUserInfo6 *info6;
switch (switch_value) {
case 1:
info1 = &arg->ru.info1;
user_info->info1.username = strdup(
(char const *)info1->username.str);
user_info->info1.fullname = strdup(
(char const *)info1->fullname.str);
user_info->info1.description = strdup(
(char const *)info1->description.str);
user_info->info1.unknown = 0;
user_info->info1.group_rid = info1->group_rid;
return (0);
case 6:
info6 = &arg->ru.info6;
user_info->info6.username = strdup(
(char const *)info6->username.str);
user_info->info6.fullname = strdup(
(char const *)info6->fullname.str);
return (0);
case 7:
user_info->info7.username = strdup(
(char const *)arg->ru.info7.username.str);
return (0);
case 8:
user_info->info8.fullname = strdup(
(char const *)arg->ru.info8.fullname.str);
return (0);
case 9:
user_info->info9.group_rid = arg->ru.info9.group_rid;
return (0);
case 16:
user_info->info16.acct_ctrl =
arg->ru.info16.UserAccountControl;
return (0);
default:
break;
};
return (-1);
}
int
samr_query_user_groups(mlsvc_handle_t *user_handle, int *n_groups,
struct samr_UserGroups **groups)
{
struct samr_QueryUserGroups arg;
int opnum;
int rc;
int nbytes;
if (ndr_is_null_handle(user_handle))
return (-1);
opnum = SAMR_OPNUM_QueryUserGroups;
bzero(&arg, sizeof (struct samr_QueryUserGroups));
(void) memcpy(&arg.user_handle, &user_handle->handle,
sizeof (samr_handle_t));
rc = ndr_rpc_call(user_handle, opnum, &arg);
if (rc == 0) {
if (arg.info == 0) {
rc = -1;
} else {
nbytes = arg.info->n_entry *
sizeof (struct samr_UserGroups);
if ((*groups = malloc(nbytes)) == NULL) {
*n_groups = 0;
rc = -1;
} else {
*n_groups = arg.info->n_entry;
bcopy(arg.info->groups, *groups, nbytes);
}
}
}
ndr_rpc_release(user_handle);
return (rc);
}
DWORD
samr_get_user_pwinfo(mlsvc_handle_t *user_handle)
{
struct samr_GetUserPwInfo arg;
int opnum;
DWORD status;
if (ndr_is_null_handle(user_handle))
return (NT_STATUS_INVALID_PARAMETER);
opnum = SAMR_OPNUM_GetUserPwInfo;
bzero(&arg, sizeof (struct samr_GetUserPwInfo));
(void) memcpy(&arg.user_handle, &user_handle->handle,
sizeof (samr_handle_t));
if (ndr_rpc_call(user_handle, opnum, &arg) != 0) {
status = NT_STATUS_INVALID_PARAMETER;
} else if (arg.status != 0) {
ndr_rpc_status(user_handle, opnum, arg.status);
status = NT_SC_VALUE(arg.status);
} else {
status = 0;
}
ndr_rpc_release(user_handle);
return (status);
}
DECL_FIXUP_STRUCT(samr_SetUserInfo_u);
DECL_FIXUP_STRUCT(samr_SetUserInfo_s);
DECL_FIXUP_STRUCT(samr_SetUserInfo);
DWORD
samr_set_user_info(
mlsvc_handle_t *user_handle,
int info_level,
void *info_buf)
{
struct samr_SetUserInfo arg;
uint16_t usize, tsize;
int opnum;
if (ndr_is_null_handle(user_handle))
return (NT_STATUS_INTERNAL_ERROR);
switch (info_level) {
case 16:
usize = sizeof (struct samr_SetUserInfo16);
break;
case 21:
usize = sizeof (struct samr_SetUserInfo21);
break;
case 23:
usize = sizeof (struct samr_SetUserInfo23);
break;
case 24:
usize = sizeof (struct samr_SetUserInfo24);
break;
default:
return (NT_STATUS_INVALID_LEVEL);
}
FIXUP_PDU_SIZE(samr_SetUserInfo_u, usize);
tsize = usize + (2 * sizeof (WORD));
FIXUP_PDU_SIZE(samr_SetUserInfo_s, tsize);
tsize += sizeof (ndr_request_hdr_t) + sizeof (DWORD);
FIXUP_PDU_SIZE(samr_SetUserInfo, tsize);
opnum = SAMR_OPNUM_SetUserInfo;
bzero(&arg, sizeof (arg));
(void) memcpy(&arg.user_handle, &user_handle->handle,
sizeof (samr_handle_t));
arg.info.info_level = info_level;
arg.info.switch_value = info_level;
(void) memcpy(&arg.info.ru, info_buf, usize);
if (ndr_rpc_call(user_handle, opnum, &arg) != 0)
arg.status = RPC_NT_CALL_FAILED;
else if (arg.status != 0)
ndr_rpc_status(user_handle, opnum, arg.status);
ndr_rpc_release(user_handle);
return (arg.status);
}
DWORD
samr_change_password(
mlsvc_handle_t *handle,
char *server,
char *account,
struct samr_encr_passwd *newpw,
struct samr_encr_hash *oldpw)
{
static struct samr_encr_passwd zero_newpw;
static struct samr_encr_hash zero_oldpw;
struct samr_ChangePasswordUser2 arg;
int opnum = SAMR_OPNUM_ChangePasswordUser2;
char *slashserver;
int len;
(void) memset(&arg, 0, sizeof (arg));
len = 2 + strlen(server) + 1;
slashserver = ndr_rpc_malloc(handle, len);
if (slashserver == NULL)
return (NT_STATUS_NO_MEMORY);
(void) snprintf(slashserver, len, "\\\\%s", server);
arg.servername = ndr_rpc_malloc(handle, sizeof (samr_string_t));
if (arg.servername == NULL)
return (NT_STATUS_NO_MEMORY);
len = smb_wcequiv_strlen(slashserver);
if (len < 1)
return (NT_STATUS_INVALID_PARAMETER);
len += 2;
arg.servername->length = len;
arg.servername->allosize = len;
arg.servername->str = (uint8_t *)slashserver;
arg.username = ndr_rpc_malloc(handle, sizeof (samr_string_t));
if (arg.username == NULL)
return (NT_STATUS_NO_MEMORY);
len = smb_wcequiv_strlen(account);
if (len < 1)
return (NT_STATUS_INVALID_PARAMETER);
len += 2;
arg.username->length = len;
arg.username->allosize = len;
arg.username->str = (uint8_t *)account;
arg.nt_newpw = newpw;
arg.nt_oldpw = oldpw;
arg.lm_newpw = &zero_newpw;
arg.lm_oldpw = &zero_oldpw;
if (ndr_rpc_call(handle, opnum, &arg) != 0)
arg.status = RPC_NT_CALL_FAILED;
else if (arg.status != 0)
ndr_rpc_status(handle, opnum, arg.status);
ndr_rpc_release(handle);
return (arg.status);
}