#include <smbclient/smbclient.h>
#include <smbclient/ntstatus.h>
#include <smbclient/smbclient_internal.h>
#include <algorithm>
#include <vector>
#include <cstdlib>
#include <assert.h>
#include <string>
#include "LsarLookup.h"
#include "memory.hpp"
#include "rpc_helpers.hpp"
extern "C" {
#include <dce/dcethread.h>
}
static
CFStringRef CreateFullyQualifiedAccountName(PRPC_UNICODE_STRING AccountName,
PRPC_UNICODE_STRING DomainName)
{
CFStringRef AccountRef = NULL, DomainRef = NULL;
CFMutableStringRef FullyQualifiedRef = NULL;
UInt16 *nullchar;
if (!AccountName || (AccountName->Buffer == NULL)) {
return NULL;
}
if (!DomainName || (DomainName->Buffer == NULL)) {
return NULL;
}
nullchar = (UInt16 *)(void *)&((UInt8 *)(DomainName->Buffer))[DomainName->Length-2];
if (*nullchar == 0) {
DomainName->Length -= 2;
}
nullchar = (UInt16 *)(void *)&((UInt8 *)(AccountName->Buffer))[AccountName->Length-2];
if (*nullchar == 0) {
AccountName->Length -= 2;
}
DomainRef = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault,
(const UInt8 *)DomainName->Buffer,
DomainName->Length,
kCFStringEncodingUTF16LE,
false, kCFAllocatorNull);
if (DomainRef) {
AccountRef = CFStringCreateWithBytesNoCopy(kCFAllocatorDefault,
(const UInt8 *)AccountName->Buffer,
AccountName->Length,
kCFStringEncodingUTF16LE,
false, kCFAllocatorNull);
}
if (!AccountRef) {
goto done;
}
FullyQualifiedRef = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, DomainRef);
if (FullyQualifiedRef == NULL) {
goto done;
}
CFStringAppend(FullyQualifiedRef, CFSTR("\\"));
CFStringAppend(FullyQualifiedRef, AccountRef);
done:
if (AccountRef) {
CFRelease(AccountRef);
}
if (DomainRef) {
CFRelease(DomainRef);
}
return FullyQualifiedRef;
}
static
NTSTATUS GetAccountNameSID(WCHAR * ServerName, PRPC_UNICODE_STRING AccountName,
ntsid_t **ntsid, rpc_binding *binding)
{
LSAPR_OBJECT_ATTRIBUTES ObjectAttributes;
LSAPR_HANDLE PolicyHandle = NULL;
ACCESS_MASK DesiredAccess = 0x00000800;
NTSTATUS nt_status = STATUS_SUCCESS;
error_status_t rpc_status = rpc_s_ok;
LSAPR_TRANSLATED_SIDS TranslatedSids;
idl_ulong_int RequestCount, MappedCount = 0;
PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains = NULL;
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
PRPC_SID DomainSid = NULL;
ntsid_t *sid;
int ii;
RequestCount = 1;
memset(&ObjectAttributes, 0, sizeof(ObjectAttributes));
SecurityQualityOfService.Length = 12;
SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
SecurityQualityOfService.ContextTrackingMode = 1;
SecurityQualityOfService.EffectiveOnly = 0;
ObjectAttributes.Length = 24;
ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
memset(&TranslatedSids, 0, sizeof(TranslatedSids));
DCETHREAD_TRY
nt_status = LsarOpenPolicy2(binding->get(), ServerName, &ObjectAttributes,
DesiredAccess, &PolicyHandle, &rpc_status);
if ((NT_SUCCESS(nt_status)) && (rpc_status == rpc_s_ok)) {
nt_status = LsarLookupNames(binding->get(), PolicyHandle, RequestCount,
AccountName, &ReferencedDomains, &TranslatedSids,
LsapLookupWksta, &MappedCount, &rpc_status);
}
if (PolicyHandle) {
(void)LsarClose(binding->get(), &PolicyHandle, &rpc_status);
}
DCETHREAD_CATCH_ALL(exc)
rpc_status = rpc_exception_status(exc);
DCETHREAD_ENDTRY
if (rpc_status != rpc_s_ok) {
SMBLogInfo("RPC to lsarpc gave rpc status of %#08x", ASL_LEVEL_DEBUG, rpc_status);
return (STATUS_UNSUCCESSFUL);
} else if (!NT_SUCCESS(nt_status)) {
SMBLogInfo("RPC to lsarpc gave nt status of %#08x", ASL_LEVEL_DEBUG, nt_status);
return nt_status;
}
if ((TranslatedSids.Entries == 0) ||
(TranslatedSids.Sids == NULL) ||
(TranslatedSids.Sids->Use != SidTypeUser)) {
SMBLogInfo("No Relative Id (RID)?", ASL_LEVEL_DEBUG);
return STATUS_NO_SUCH_USER;
}
if (ReferencedDomains && ReferencedDomains->Entries && ReferencedDomains->Domains) {
DomainSid = ReferencedDomains->Domains->Sid;
}
if (!DomainSid) {
SMBLogInfo("No domain sid?", ASL_LEVEL_DEBUG);
return (STATUS_NO_SUCH_DOMAIN);
}
if (DomainSid->SubAuthorityCount >= KAUTH_NTSID_MAX_AUTHORITIES) {
SMBLogInfo("Invalid domain sid?", ASL_LEVEL_DEBUG);
return (STATUS_INVALID_SID);
}
sid = (ntsid_t *)malloc(sizeof(ntsid_t));
if (sid == NULL) {
SMBLogInfo("Couldn't allocate ntsid", ASL_LEVEL_DEBUG);
return (STATUS_NO_MEMORY);
}
memset(sid, 0, sizeof(*sid));
sid->sid_kind = DomainSid->Revision;
sid->sid_authcount = DomainSid->SubAuthorityCount;
memcpy(sid->sid_authority, DomainSid->IdentifierAuthority.Value,
sizeof(sid->sid_authority));
for (ii = 0; ii < sid->sid_authcount; ii++)
sid->sid_authorities[ii] = DomainSid->SubAuthority[ii];
sid->sid_authorities[sid->sid_authcount++] = TranslatedSids.Sids->RelativeId;
*ntsid = sid;
return 0;
}
static
NTSTATUS GetAccountName(WCHAR * ServerName, PRPC_UNICODE_STRING *UserName,
PRPC_UNICODE_STRING *DomainName, rpc_binding *binding)
{
#pragma unused(ServerName)
NTSTATUS nt_status = STATUS_SUCCESS;
error_status_t rpc_status = rpc_s_ok;
*UserName = NULL;
*DomainName = NULL;
DCETHREAD_TRY
nt_status = LsarGetUserName(binding->get(), ServerName, UserName,
DomainName, &rpc_status);
DCETHREAD_CATCH_ALL(exc)
rpc_status = rpc_exception_status(exc);
DCETHREAD_ENDTRY
if (rpc_status != rpc_s_ok) {
SMBLogInfo("RPC to lsarpc gave rpc status of %#08x", ASL_LEVEL_DEBUG, rpc_status);
return (STATUS_UNSUCCESSFUL);
} else if (!NT_SUCCESS(nt_status)) {
SMBLogInfo("RPC to lsarpc gave nt status of %#08x", ASL_LEVEL_DEBUG, nt_status);
return nt_status;
}
return 0;
}
NTSTATUS GetNetworkAccountSID(const char *ServerName, char **account, char **domain, ntsid_t **ntsid)
{
CFStringRef FullyQualifiedRef = NULL;
PRPC_UNICODE_STRING AccountName = NULL;
PRPC_UNICODE_STRING DomainName = NULL;
NTSTATUS nt_status = STATUS_SUCCESS;
WCHAR * UTF16ServerName = SMBConvertFromUTF8ToUTF16(ServerName, 1024, 0);
rpc_ss_allocator_t allocator;
rpc_mempool * mempool(rpc_mempool::allocate(0));
rpc_binding binding = make_rpc_binding(ServerName, "lsarpc");
if (binding.get() == NULL) {
SMBLogInfo("make_rpc_binding failed", ASL_LEVEL_DEBUG);
nt_status = STATUS_UNSUCCESSFUL;
errno = EINVAL;
goto done;
}
if (!UTF16ServerName) {
nt_status = STATUS_NO_MEMORY;
errno = ENOMEM;
goto done;
}
memset(&allocator, 0, sizeof(allocator));
allocator.p_allocate = rpc_pool_allocate;
allocator.p_free = rpc_pool_free;
allocator.p_context = (idl_void_p_t)mempool;
rpc_ss_swap_client_alloc_free_ex(&allocator, &allocator);
nt_status = GetAccountName(UTF16ServerName, &AccountName, &DomainName, &binding);
if (!AccountName && (NT_SUCCESS(nt_status))) {
SMBLogInfo("Server return a NULL account name", ASL_LEVEL_DEBUG);
nt_status = STATUS_NO_SUCH_USER;
}
if (!NT_SUCCESS(nt_status)) {
SMBLogInfo("Couldn't get the account name: %d", ASL_LEVEL_DEBUG, nt_status);
errno = ENOENT;
goto done;
}
FullyQualifiedRef = CreateFullyQualifiedAccountName(AccountName, DomainName);
if (FullyQualifiedRef) {
RPC_UNICODE_STRING FullyQualified;
FullyQualified.MaximumLength = CFStringGetLength(FullyQualifiedRef) * sizeof(UniChar);
FullyQualified.Length = FullyQualified.MaximumLength;
FullyQualified.Buffer = (WCHAR *)CFStringGetCharactersPtr(FullyQualifiedRef);
if (FullyQualified.Buffer) {
nt_status = GetAccountNameSID(UTF16ServerName, &FullyQualified, ntsid, &binding);
} else {
nt_status = STATUS_NO_MEMORY;
}
CFRelease(FullyQualifiedRef);
} else {
nt_status = STATUS_UNSUCCESSFUL;
}
if (!NT_SUCCESS(nt_status)) {
SMBLogInfo("Failed to get the sid using the fully qualified account name", ASL_LEVEL_DEBUG);
nt_status = GetAccountNameSID(UTF16ServerName, AccountName, ntsid, &binding);
}
if (!NT_SUCCESS(nt_status)) {
SMBLogInfo("Couldn't get the account sid: %d", ASL_LEVEL_DEBUG, nt_status);
errno = ENOENT;
goto done;
}
if (account) {
*account = NULL;
if (AccountName && AccountName->Length && AccountName->Buffer && ((char *)AccountName->Buffer != (char *)AccountName)) {
*account = SMBConvertFromUTF16ToUTF8((const uint16_t *)AccountName->Buffer, AccountName->Length, 0);
}
}
if (domain) {
*domain = NULL;
if (DomainName && DomainName->Length && DomainName->Buffer && ((char *)DomainName->Buffer != (char *)DomainName)) {
*domain = SMBConvertFromUTF16ToUTF8((const uint16_t *)DomainName->Buffer, DomainName->Length, 0);
}
}
done:
if (UTF16ServerName) {
free(UTF16ServerName);
}
rpc_mempool::destroy(mempool);
return nt_status;
}