#include <strings.h>
#include <unistd.h>
#include <smbsrv/libsmb.h>
#include <smbsrv/libmlsvc.h>
#include <smbsrv/smbinfo.h>
#include <smbsrv/smb_token.h>
#include <lsalib.h>
static uint32_t lsa_lookup_name_int(char *, uint16_t, smb_account_t *,
boolean_t);
static uint32_t lsa_lookup_sid_int(smb_sid_t *, smb_account_t *, boolean_t);
static uint32_t lsa_lookup_name_builtin(char *, char *, smb_account_t *);
static uint32_t lsa_lookup_name_domain(char *, smb_account_t *);
static uint32_t lsa_lookup_sid_builtin(smb_sid_t *, smb_account_t *);
static uint32_t lsa_lookup_sid_domain(smb_sid_t *, smb_account_t *);
static uint32_t lsa_list_accounts(mlsvc_handle_t *);
static uint32_t lsa_map_status(uint32_t);
uint32_t
lsa_lookup_name(char *account, uint16_t type, smb_account_t *info)
{
return (lsa_lookup_name_int(account, type, info, B_TRUE));
}
uint32_t
lsa_lookup_lname(char *account, uint16_t type, smb_account_t *info)
{
return (lsa_lookup_name_int(account, type, info, B_FALSE));
}
uint32_t
lsa_lookup_name_int(char *account, uint16_t type, smb_account_t *info,
boolean_t try_ad)
{
char nambuf[SMB_USERNAME_MAXLEN];
char dombuf[SMB_PI_MAX_DOMAIN];
char *name, *domain;
uint32_t status;
char *slash;
if (account == NULL)
return (NT_STATUS_NONE_MAPPED);
(void) strsubst(account, '/', '\\');
(void) strcanon(account, "\\");
account += strspn(account, "\\");
if ((slash = strchr(account, '\\')) != NULL) {
*slash = '\0';
(void) strlcpy(dombuf, account, sizeof (dombuf));
(void) strlcpy(nambuf, slash + 1, sizeof (nambuf));
*slash = '\\';
name = nambuf;
domain = dombuf;
} else {
name = account;
domain = NULL;
}
status = lsa_lookup_name_builtin(domain, name, info);
if (status == NT_STATUS_NOT_FOUND) {
status = smb_sam_lookup_name(domain, name, type, info);
if (status == NT_STATUS_SUCCESS)
return (status);
if (try_ad && ((domain == NULL) ||
(status == NT_STATUS_NOT_FOUND))) {
status = lsa_lookup_name_domain(account, info);
}
}
return ((status == NT_STATUS_SUCCESS) ? status : NT_STATUS_NONE_MAPPED);
}
uint32_t
lsa_lookup_sid(smb_sid_t *sid, smb_account_t *info)
{
return (lsa_lookup_sid_int(sid, info, B_TRUE));
}
uint32_t
lsa_lookup_lsid(smb_sid_t *sid, smb_account_t *info)
{
return (lsa_lookup_sid_int(sid, info, B_FALSE));
}
static uint32_t
lsa_lookup_sid_int(smb_sid_t *sid, smb_account_t *info, boolean_t try_ad)
{
uint32_t status;
if (!smb_sid_isvalid(sid))
return (NT_STATUS_INVALID_SID);
status = lsa_lookup_sid_builtin(sid, info);
if (status == NT_STATUS_NOT_FOUND) {
status = smb_sam_lookup_sid(sid, info);
if (try_ad && status == NT_STATUS_NOT_FOUND) {
status = lsa_lookup_sid_domain(sid, info);
}
}
return ((status == NT_STATUS_SUCCESS) ? status : NT_STATUS_NONE_MAPPED);
}
DWORD
lsa_query_primary_domain_info(char *server, char *domain,
smb_domain_t *info)
{
mlsvc_handle_t domain_handle;
char user[SMB_USERNAME_MAXLEN];
DWORD status;
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
status = lsar_open(server, domain, user, &domain_handle);
if (status != 0)
return (status);
status = lsar_query_info_policy(&domain_handle,
MSLSA_POLICY_PRIMARY_DOMAIN_INFO, info);
(void) lsar_close(&domain_handle);
return (status);
}
DWORD
lsa_query_account_domain_info(char *server, char *domain,
smb_domain_t *info)
{
mlsvc_handle_t domain_handle;
char user[SMB_USERNAME_MAXLEN];
DWORD status;
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
status = lsar_open(server, domain, user, &domain_handle);
if (status != 0)
return (status);
status = lsar_query_info_policy(&domain_handle,
MSLSA_POLICY_ACCOUNT_DOMAIN_INFO, info);
(void) lsar_close(&domain_handle);
return (status);
}
DWORD
lsa_query_dns_domain_info(char *server, char *domain, smb_domain_t *info)
{
mlsvc_handle_t domain_handle;
char user[SMB_USERNAME_MAXLEN];
DWORD status;
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
status = lsar_open(server, domain, user, &domain_handle);
if (status != 0)
return (status);
status = lsar_query_info_policy(&domain_handle,
MSLSA_POLICY_DNS_DOMAIN_INFO, info);
(void) lsar_close(&domain_handle);
return (status);
}
DWORD
lsa_enum_trusted_domains(char *server, char *domain,
smb_trusted_domains_t *info)
{
mlsvc_handle_t domain_handle;
char user[SMB_USERNAME_MAXLEN];
DWORD enum_context;
DWORD status;
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
status = lsar_open(server, domain, user, &domain_handle);
if (status != 0)
return (status);
enum_context = 0;
status = lsar_enum_trusted_domains(&domain_handle, &enum_context, info);
if (status == NT_STATUS_NO_MORE_ENTRIES) {
status = NT_STATUS_SUCCESS;
}
(void) lsar_close(&domain_handle);
return (status);
}
DWORD
lsa_enum_trusted_domains_ex(char *server, char *domain,
smb_trusted_domains_t *info)
{
mlsvc_handle_t domain_handle;
char user[SMB_USERNAME_MAXLEN];
DWORD enum_context;
DWORD status;
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
status = lsar_open(server, domain, user, &domain_handle);
if (status != 0)
return (status);
enum_context = 0;
status = lsar_enum_trusted_domains_ex(&domain_handle, &enum_context,
info);
if (status == NT_STATUS_NO_MORE_ENTRIES) {
status = NT_STATUS_SUCCESS;
}
(void) lsar_close(&domain_handle);
return (status);
}
static uint32_t
lsa_lookup_name_builtin(char *domain, char *name, smb_account_t *info)
{
smb_wka_t *wka;
char *wkadom;
bzero(info, sizeof (smb_account_t));
if ((wka = smb_wka_lookup_name(name)) == NULL)
return (NT_STATUS_NOT_FOUND);
if ((wkadom = smb_wka_get_domain(wka->wka_domidx)) == NULL)
return (NT_STATUS_INTERNAL_ERROR);
if ((domain != NULL) && (smb_strcasecmp(domain, wkadom, 0) != 0))
return (NT_STATUS_NONE_MAPPED);
info->a_name = strdup(name);
info->a_sid = smb_sid_dup(wka->wka_binsid);
info->a_domain = strdup(wkadom);
info->a_domsid = smb_sid_split(wka->wka_binsid, &info->a_rid);
info->a_type = wka->wka_type;
if (!smb_account_validate(info)) {
smb_account_free(info);
return (NT_STATUS_NO_MEMORY);
}
return (NT_STATUS_SUCCESS);
}
static uint32_t
lsa_lookup_name_domain(char *account_name, smb_account_t *info)
{
mlsvc_handle_t domain_handle;
smb_domainex_t dinfo;
char user[SMB_USERNAME_MAXLEN];
uint32_t status;
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
if (!smb_domain_getinfo(&dinfo))
return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname,
user, &domain_handle);
if (status != 0)
return (lsa_map_status(status));
status = lsar_lookup_names(&domain_handle, account_name, info);
(void) lsar_close(&domain_handle);
return (status);
}
DWORD
lsa_lookup_privs(char *account_name, char *target_name, smb_account_t *ainfo)
{
mlsvc_handle_t domain_handle;
smb_domainex_t dinfo;
char user[SMB_USERNAME_MAXLEN];
DWORD status;
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
if (!smb_domain_getinfo(&dinfo))
return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname,
user, &domain_handle);
if (status != 0)
return (lsa_map_status(status));
status = lsa_list_accounts(&domain_handle);
(void) lsar_close(&domain_handle);
return (status);
}
DWORD
lsa_list_privs(char *server, char *domain)
{
static char name[128];
static struct ms_luid luid;
mlsvc_handle_t domain_handle;
char user[SMB_USERNAME_MAXLEN];
DWORD status;
int rc;
int i;
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
status = lsar_open(server, domain, user, &domain_handle);
if (status != 0)
return (lsa_map_status(status));
for (i = 0; i < 30; ++i) {
luid.low_part = i;
rc = lsar_lookup_priv_name(&domain_handle, &luid, name, 128);
if (rc != 0)
continue;
(void) lsar_lookup_priv_value(&domain_handle, name, &luid);
(void) lsar_lookup_priv_display_name(&domain_handle, name,
name, 128);
}
(void) lsar_close(&domain_handle);
return (NT_STATUS_SUCCESS);
}
static DWORD
lsa_list_accounts(mlsvc_handle_t *domain_handle)
{
mlsvc_handle_t account_handle;
struct mslsa_EnumAccountBuf accounts;
struct mslsa_sid *sid;
smb_account_t ainfo;
DWORD enum_context = 0;
DWORD status;
int i;
bzero(&accounts, sizeof (struct mslsa_EnumAccountBuf));
do {
status = lsar_enum_accounts(domain_handle, &enum_context,
&accounts);
if (status != 0)
return (status);
for (i = 0; i < accounts.entries_read; ++i) {
sid = accounts.info[i].sid;
if (lsar_open_account(domain_handle, sid,
&account_handle) == 0) {
(void) lsar_enum_privs_account(&account_handle,
&ainfo);
(void) lsar_close(&account_handle);
}
free(accounts.info[i].sid);
}
if (accounts.info)
free(accounts.info);
} while (status == 0 && accounts.entries_read != 0);
return (0);
}
static uint32_t
lsa_lookup_sid_builtin(smb_sid_t *sid, smb_account_t *ainfo)
{
smb_wka_t *wka;
char *wkadom;
bzero(ainfo, sizeof (smb_account_t));
if ((wka = smb_wka_lookup_sid(sid)) == NULL)
return (NT_STATUS_NOT_FOUND);
if ((wkadom = smb_wka_get_domain(wka->wka_domidx)) == NULL)
return (NT_STATUS_INTERNAL_ERROR);
ainfo->a_name = strdup(wka->wka_name);
ainfo->a_sid = smb_sid_dup(wka->wka_binsid);
ainfo->a_domain = strdup(wkadom);
ainfo->a_domsid = smb_sid_split(ainfo->a_sid, &ainfo->a_rid);
ainfo->a_type = wka->wka_type;
if (!smb_account_validate(ainfo)) {
smb_account_free(ainfo);
return (NT_STATUS_NO_MEMORY);
}
return (NT_STATUS_SUCCESS);
}
static uint32_t
lsa_lookup_sid_domain(smb_sid_t *sid, smb_account_t *ainfo)
{
mlsvc_handle_t domain_handle;
smb_domainex_t dinfo;
char user[SMB_USERNAME_MAXLEN];
uint32_t status;
smb_ipc_get_user(user, SMB_USERNAME_MAXLEN);
if (!smb_domain_getinfo(&dinfo))
return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO);
status = lsar_open(dinfo.d_dci.dc_name, dinfo.d_primary.di_nbname,
user, &domain_handle);
if (status != 0)
return (lsa_map_status(status));
status = lsar_lookup_sids(&domain_handle, sid, ainfo);
(void) lsar_close(&domain_handle);
return (status);
}
static uint32_t
lsa_map_status(uint32_t status)
{
switch (status) {
case NT_STATUS_SUCCESS:
break;
case NT_STATUS_INVALID_PARAMETER:
break;
case NT_STATUS_NO_MEMORY:
break;
case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND:
case NT_STATUS_BAD_NETWORK_PATH:
case NT_STATUS_NETWORK_ACCESS_DENIED:
case NT_STATUS_BAD_NETWORK_NAME:
case NT_STATUS_ACCESS_DENIED:
status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
break;
default:
status = NT_STATUS_UNSUCCESSFUL;
break;
}
return (status);
}