mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 06:15:26 +00:00
74d50a4da3
- Implement SamConnect5() and let the other connect functions call SamrConnect5(). - Add missing and improve incomplete TRACE messages for all Samr functions.
9766 lines
289 KiB
C
9766 lines
289 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: Security Account Manager (SAM) Server
|
|
* FILE: reactos/dll/win32/samsrv/samrpc.c
|
|
* PURPOSE: RPC interface functions
|
|
*
|
|
* PROGRAMMERS: Eric Kohl
|
|
*/
|
|
|
|
#include "samsrv.h"
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
static SID_IDENTIFIER_AUTHORITY NtSidAuthority = {SECURITY_NT_AUTHORITY};
|
|
|
|
static GENERIC_MAPPING ServerMapping =
|
|
{
|
|
SAM_SERVER_READ,
|
|
SAM_SERVER_WRITE,
|
|
SAM_SERVER_EXECUTE,
|
|
SAM_SERVER_ALL_ACCESS
|
|
};
|
|
|
|
static GENERIC_MAPPING DomainMapping =
|
|
{
|
|
DOMAIN_READ,
|
|
DOMAIN_WRITE,
|
|
DOMAIN_EXECUTE,
|
|
DOMAIN_ALL_ACCESS
|
|
};
|
|
|
|
static GENERIC_MAPPING AliasMapping =
|
|
{
|
|
ALIAS_READ,
|
|
ALIAS_WRITE,
|
|
ALIAS_EXECUTE,
|
|
ALIAS_ALL_ACCESS
|
|
};
|
|
|
|
static GENERIC_MAPPING GroupMapping =
|
|
{
|
|
GROUP_READ,
|
|
GROUP_WRITE,
|
|
GROUP_EXECUTE,
|
|
GROUP_ALL_ACCESS
|
|
};
|
|
|
|
static GENERIC_MAPPING UserMapping =
|
|
{
|
|
USER_READ,
|
|
USER_WRITE,
|
|
USER_EXECUTE,
|
|
USER_ALL_ACCESS
|
|
};
|
|
|
|
PGENERIC_MAPPING pServerMapping = &ServerMapping;
|
|
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
static
|
|
LARGE_INTEGER
|
|
SampAddRelativeTimeToTime(IN LARGE_INTEGER AbsoluteTime,
|
|
IN LARGE_INTEGER RelativeTime)
|
|
{
|
|
LARGE_INTEGER NewTime;
|
|
|
|
NewTime.QuadPart = AbsoluteTime.QuadPart - RelativeTime.QuadPart;
|
|
|
|
if (NewTime.QuadPart < 0)
|
|
NewTime.QuadPart = 0;
|
|
|
|
return NewTime;
|
|
}
|
|
|
|
|
|
VOID
|
|
SampStartRpcServer(VOID)
|
|
{
|
|
RPC_STATUS Status;
|
|
|
|
TRACE("SampStartRpcServer() called\n");
|
|
|
|
Status = RpcServerUseProtseqEpW(L"ncacn_np",
|
|
RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
|
|
L"\\pipe\\samr",
|
|
NULL);
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
WARN("RpcServerUseProtseqEpW() failed (Status %lx)\n", Status);
|
|
return;
|
|
}
|
|
|
|
Status = RpcServerRegisterIf(samr_v1_0_s_ifspec,
|
|
NULL,
|
|
NULL);
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
WARN("RpcServerRegisterIf() failed (Status %lx)\n", Status);
|
|
return;
|
|
}
|
|
|
|
Status = RpcServerListen(1, 20, TRUE);
|
|
if (Status != RPC_S_OK)
|
|
{
|
|
WARN("RpcServerListen() failed (Status %lx)\n", Status);
|
|
return;
|
|
}
|
|
|
|
TRACE("SampStartRpcServer() done\n");
|
|
}
|
|
|
|
|
|
void __RPC_FAR * __RPC_USER midl_user_allocate(SIZE_T len)
|
|
{
|
|
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len);
|
|
}
|
|
|
|
|
|
void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, ptr);
|
|
}
|
|
|
|
|
|
void __RPC_USER SAMPR_HANDLE_rundown(SAMPR_HANDLE hHandle)
|
|
{
|
|
}
|
|
|
|
|
|
/* Function 0 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrConnect(IN PSAMPR_SERVER_NAME ServerName,
|
|
OUT SAMPR_HANDLE *ServerHandle,
|
|
IN ACCESS_MASK DesiredAccess)
|
|
{
|
|
SAMPR_REVISION_INFO InRevisionInfo, OutRevisionInfo;
|
|
ULONG OutVersion;
|
|
|
|
TRACE("SamrConnect(%p %p %lx)\n",
|
|
ServerName, ServerHandle, DesiredAccess);
|
|
|
|
InRevisionInfo.V1.Revision = 0;
|
|
InRevisionInfo.V1.SupportedFeatures = 0;
|
|
|
|
return SamrConnect5(ServerName,
|
|
DesiredAccess,
|
|
1,
|
|
&InRevisionInfo,
|
|
&OutVersion,
|
|
&OutRevisionInfo,
|
|
ServerHandle);
|
|
}
|
|
|
|
|
|
/* Function 1 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrCloseHandle(IN OUT SAMPR_HANDLE *SamHandle)
|
|
{
|
|
PSAM_DB_OBJECT DbObject;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
TRACE("SamrCloseHandle(%p)\n", SamHandle);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
Status = SampValidateDbObject(*SamHandle,
|
|
SamDbIgnoreObject,
|
|
0,
|
|
&DbObject);
|
|
if (Status == STATUS_SUCCESS)
|
|
{
|
|
Status = SampCloseDbObject(DbObject);
|
|
*SamHandle = NULL;
|
|
}
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
TRACE("SamrCloseHandle done (Status 0x%08lx)\n", Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 2 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrSetSecurityObject(IN SAMPR_HANDLE ObjectHandle,
|
|
IN SECURITY_INFORMATION SecurityInformation,
|
|
IN PSAMPR_SR_SECURITY_DESCRIPTOR SecurityDescriptor)
|
|
{
|
|
PSAM_DB_OBJECT DbObject = NULL;
|
|
ACCESS_MASK DesiredAccess = 0;
|
|
PSECURITY_DESCRIPTOR RelativeSd = NULL;
|
|
ULONG RelativeSdSize = 0;
|
|
HANDLE TokenHandle = NULL;
|
|
PGENERIC_MAPPING Mapping;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrSetSecurityObject(%p %lx %p)\n",
|
|
ObjectHandle, SecurityInformation, SecurityDescriptor);
|
|
|
|
if ((SecurityDescriptor == NULL) ||
|
|
(SecurityDescriptor->SecurityDescriptor == NULL) ||
|
|
!RtlValidSecurityDescriptor((PSECURITY_DESCRIPTOR)SecurityDescriptor->SecurityDescriptor))
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
if (SecurityInformation == 0 ||
|
|
SecurityInformation & ~(OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION
|
|
| DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION))
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
if (SecurityInformation & SACL_SECURITY_INFORMATION)
|
|
DesiredAccess |= ACCESS_SYSTEM_SECURITY;
|
|
|
|
if (SecurityInformation & DACL_SECURITY_INFORMATION)
|
|
DesiredAccess |= WRITE_DAC;
|
|
|
|
if (SecurityInformation & (OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION))
|
|
DesiredAccess |= WRITE_OWNER;
|
|
|
|
if ((SecurityInformation & OWNER_SECURITY_INFORMATION) &&
|
|
(((PISECURITY_DESCRIPTOR)SecurityDescriptor)->Owner == NULL))
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
if ((SecurityInformation & GROUP_SECURITY_INFORMATION) &&
|
|
(((PISECURITY_DESCRIPTOR)SecurityDescriptor)->Group == NULL))
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
/* Validate the server handle */
|
|
Status = SampValidateDbObject(ObjectHandle,
|
|
SamDbIgnoreObject,
|
|
DesiredAccess,
|
|
&DbObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
/* Get the mapping for the object type */
|
|
switch (DbObject->ObjectType)
|
|
{
|
|
case SamDbServerObject:
|
|
Mapping = &ServerMapping;
|
|
break;
|
|
|
|
case SamDbDomainObject:
|
|
Mapping = &DomainMapping;
|
|
break;
|
|
|
|
case SamDbAliasObject:
|
|
Mapping = &AliasMapping;
|
|
break;
|
|
|
|
case SamDbGroupObject:
|
|
Mapping = &GroupMapping;
|
|
break;
|
|
|
|
case SamDbUserObject:
|
|
Mapping = &UserMapping;
|
|
break;
|
|
|
|
default:
|
|
return STATUS_INVALID_HANDLE;
|
|
}
|
|
|
|
/* Get the size of the SD */
|
|
Status = SampGetObjectAttribute(DbObject,
|
|
L"SecDesc",
|
|
NULL,
|
|
NULL,
|
|
&RelativeSdSize);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
/* Allocate a buffer for the SD */
|
|
RelativeSd = RtlAllocateHeap(RtlGetProcessHeap(), 0, RelativeSdSize);
|
|
if (RelativeSd == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Get the SD */
|
|
Status = SampGetObjectAttribute(DbObject,
|
|
L"SecDesc",
|
|
NULL,
|
|
RelativeSd,
|
|
&RelativeSdSize);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
/* Build the new security descriptor */
|
|
Status = RtlSetSecurityObject(SecurityInformation,
|
|
(PSECURITY_DESCRIPTOR)SecurityDescriptor->SecurityDescriptor,
|
|
&RelativeSd,
|
|
Mapping,
|
|
TokenHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("RtlSetSecurityObject failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the modified SD */
|
|
Status = SampSetObjectAttribute(DbObject,
|
|
L"SecDesc",
|
|
REG_BINARY,
|
|
RelativeSd,
|
|
RtlLengthSecurityDescriptor(RelativeSd));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("SampSetObjectAttribute failed (Status 0x%08lx)\n", Status);
|
|
}
|
|
|
|
done:
|
|
if (TokenHandle != NULL)
|
|
NtClose(TokenHandle);
|
|
|
|
if (RelativeSd != NULL)
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, RelativeSd);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 3 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrQuerySecurityObject(IN SAMPR_HANDLE ObjectHandle,
|
|
IN SECURITY_INFORMATION SecurityInformation,
|
|
OUT PSAMPR_SR_SECURITY_DESCRIPTOR *SecurityDescriptor)
|
|
{
|
|
PSAM_DB_OBJECT SamObject;
|
|
PSAMPR_SR_SECURITY_DESCRIPTOR SdData = NULL;
|
|
PSECURITY_DESCRIPTOR RelativeSd = NULL;
|
|
PSECURITY_DESCRIPTOR ResultSd = NULL;
|
|
ACCESS_MASK DesiredAccess = 0;
|
|
ULONG RelativeSdSize = 0;
|
|
ULONG ResultSdSize = 0;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrQuerySecurityObject(%p %lx %p)\n",
|
|
ObjectHandle, SecurityInformation, SecurityDescriptor);
|
|
|
|
*SecurityDescriptor = NULL;
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
if (SecurityInformation & (DACL_SECURITY_INFORMATION |
|
|
OWNER_SECURITY_INFORMATION |
|
|
GROUP_SECURITY_INFORMATION))
|
|
DesiredAccess |= READ_CONTROL;
|
|
|
|
if (SecurityInformation & SACL_SECURITY_INFORMATION)
|
|
DesiredAccess |= ACCESS_SYSTEM_SECURITY;
|
|
|
|
/* Validate the server handle */
|
|
Status = SampValidateDbObject(ObjectHandle,
|
|
SamDbIgnoreObject,
|
|
DesiredAccess,
|
|
&SamObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
/* Get the size of the SD */
|
|
Status = SampGetObjectAttribute(SamObject,
|
|
L"SecDesc",
|
|
NULL,
|
|
NULL,
|
|
&RelativeSdSize);
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_BUFFER_OVERFLOW)
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Allocate a buffer for the SD */
|
|
RelativeSd = midl_user_allocate(RelativeSdSize);
|
|
if (RelativeSd == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
/* Get the SD */
|
|
Status = SampGetObjectAttribute(SamObject,
|
|
L"SecDesc",
|
|
NULL,
|
|
RelativeSd,
|
|
&RelativeSdSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Invalidate the SD information that was not requested */
|
|
if (!(SecurityInformation & OWNER_SECURITY_INFORMATION))
|
|
((PISECURITY_DESCRIPTOR)RelativeSd)->Owner = NULL;
|
|
|
|
if (!(SecurityInformation & GROUP_SECURITY_INFORMATION))
|
|
((PISECURITY_DESCRIPTOR)RelativeSd)->Group = NULL;
|
|
|
|
if (!(SecurityInformation & DACL_SECURITY_INFORMATION))
|
|
((PISECURITY_DESCRIPTOR)RelativeSd)->Control &= ~SE_DACL_PRESENT;
|
|
|
|
if (!(SecurityInformation & SACL_SECURITY_INFORMATION))
|
|
((PISECURITY_DESCRIPTOR)RelativeSd)->Control &= ~SE_SACL_PRESENT;
|
|
|
|
/* Calculate the required SD size */
|
|
Status = RtlMakeSelfRelativeSD(RelativeSd,
|
|
NULL,
|
|
&ResultSdSize);
|
|
if (Status != STATUS_BUFFER_TOO_SMALL)
|
|
goto done;
|
|
|
|
/* Allocate a buffer for the new SD */
|
|
ResultSd = MIDL_user_allocate(ResultSdSize);
|
|
if (ResultSd == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
/* Build the new SD */
|
|
Status = RtlMakeSelfRelativeSD(RelativeSd,
|
|
ResultSd,
|
|
&ResultSdSize);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
/* Allocate the SD data buffer */
|
|
SdData = midl_user_allocate(sizeof(SAMPR_SR_SECURITY_DESCRIPTOR));
|
|
if (SdData == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
/* Fill the SD data buffer and return it to the caller */
|
|
SdData->Length = RelativeSdSize;
|
|
SdData->SecurityDescriptor = (PBYTE)ResultSd;
|
|
|
|
*SecurityDescriptor = SdData;
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (ResultSd != NULL)
|
|
MIDL_user_free(ResultSd);
|
|
}
|
|
|
|
if (RelativeSd != NULL)
|
|
MIDL_user_free(RelativeSd);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 4 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrShutdownSamServer(IN SAMPR_HANDLE ServerHandle)
|
|
{
|
|
PSAM_DB_OBJECT ServerObject;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrShutdownSamServer(%p)\n",
|
|
ServerHandle);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the server handle */
|
|
Status = SampValidateDbObject(ServerHandle,
|
|
SamDbServerObject,
|
|
SAM_SERVER_SHUTDOWN,
|
|
&ServerObject);
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
/* Shut the server down */
|
|
RpcMgmtStopServerListening(0);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Function 5 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrLookupDomainInSamServer(IN SAMPR_HANDLE ServerHandle,
|
|
IN PRPC_UNICODE_STRING Name,
|
|
OUT PRPC_SID *DomainId)
|
|
{
|
|
PSAM_DB_OBJECT ServerObject;
|
|
HANDLE DomainsKeyHandle = NULL;
|
|
HANDLE DomainKeyHandle = NULL;
|
|
WCHAR DomainKeyName[64];
|
|
ULONG Index;
|
|
WCHAR DomainNameString[MAX_COMPUTERNAME_LENGTH + 1];
|
|
UNICODE_STRING DomainName;
|
|
ULONG Length;
|
|
BOOL Found = FALSE;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrLookupDomainInSamServer(%p %p %p)\n",
|
|
ServerHandle, Name, DomainId);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the server handle */
|
|
Status = SampValidateDbObject(ServerHandle,
|
|
SamDbServerObject,
|
|
SAM_SERVER_LOOKUP_DOMAIN,
|
|
&ServerObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
*DomainId = NULL;
|
|
|
|
Status = SampRegOpenKey(ServerObject->KeyHandle,
|
|
L"Domains",
|
|
KEY_READ,
|
|
&DomainsKeyHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
Index = 0;
|
|
while (Found == FALSE)
|
|
{
|
|
Status = SampRegEnumerateSubKey(DomainsKeyHandle,
|
|
Index,
|
|
64,
|
|
DomainKeyName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Status == STATUS_NO_MORE_ENTRIES)
|
|
Status = STATUS_NO_SUCH_DOMAIN;
|
|
break;
|
|
}
|
|
|
|
TRACE("Domain key name: %S\n", DomainKeyName);
|
|
|
|
Status = SampRegOpenKey(DomainsKeyHandle,
|
|
DomainKeyName,
|
|
KEY_READ,
|
|
&DomainKeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Length = (MAX_COMPUTERNAME_LENGTH + 1) * sizeof(WCHAR);
|
|
Status = SampRegQueryValue(DomainKeyHandle,
|
|
L"Name",
|
|
NULL,
|
|
(PVOID)&DomainNameString,
|
|
&Length);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Domain name: %S\n", DomainNameString);
|
|
|
|
RtlInitUnicodeString(&DomainName,
|
|
DomainNameString);
|
|
if (RtlEqualUnicodeString(&DomainName, (PUNICODE_STRING)Name, TRUE))
|
|
{
|
|
TRACE("Found it!\n");
|
|
Found = TRUE;
|
|
|
|
Status = SampRegQueryValue(DomainKeyHandle,
|
|
L"SID",
|
|
NULL,
|
|
NULL,
|
|
&Length);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*DomainId = midl_user_allocate(Length);
|
|
|
|
SampRegQueryValue(DomainKeyHandle,
|
|
L"SID",
|
|
NULL,
|
|
(PVOID)*DomainId,
|
|
&Length);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
SampRegCloseKey(&DomainKeyHandle);
|
|
}
|
|
|
|
Index++;
|
|
}
|
|
|
|
done:
|
|
SampRegCloseKey(&DomainKeyHandle);
|
|
SampRegCloseKey(&DomainsKeyHandle);
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 6 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrEnumerateDomainsInSamServer(IN SAMPR_HANDLE ServerHandle,
|
|
IN OUT unsigned long *EnumerationContext,
|
|
OUT PSAMPR_ENUMERATION_BUFFER *Buffer,
|
|
IN ULONG PreferedMaximumLength,
|
|
OUT PULONG CountReturned)
|
|
{
|
|
PSAM_DB_OBJECT ServerObject;
|
|
WCHAR DomainKeyName[64];
|
|
HANDLE DomainsKeyHandle = NULL;
|
|
HANDLE DomainKeyHandle = NULL;
|
|
ULONG EnumIndex;
|
|
ULONG EnumCount;
|
|
ULONG RequiredLength;
|
|
ULONG DataLength;
|
|
ULONG i;
|
|
PSAMPR_ENUMERATION_BUFFER EnumBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrEnumerateDomainsInSamServer(%p %p %p %lu %p)\n",
|
|
ServerHandle, EnumerationContext, Buffer, PreferedMaximumLength,
|
|
CountReturned);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the server handle */
|
|
Status = SampValidateDbObject(ServerHandle,
|
|
SamDbServerObject,
|
|
SAM_SERVER_ENUMERATE_DOMAINS,
|
|
&ServerObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
Status = SampRegOpenKey(ServerObject->KeyHandle,
|
|
L"Domains",
|
|
KEY_READ,
|
|
&DomainsKeyHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
EnumIndex = *EnumerationContext;
|
|
EnumCount = 0;
|
|
RequiredLength = 0;
|
|
|
|
while (TRUE)
|
|
{
|
|
Status = SampRegEnumerateSubKey(DomainsKeyHandle,
|
|
EnumIndex,
|
|
64 * sizeof(WCHAR),
|
|
DomainKeyName);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
TRACE("EnumIndex: %lu\n", EnumIndex);
|
|
TRACE("Domain key name: %S\n", DomainKeyName);
|
|
|
|
Status = SampRegOpenKey(DomainsKeyHandle,
|
|
DomainKeyName,
|
|
KEY_READ,
|
|
&DomainKeyHandle);
|
|
TRACE("SampRegOpenKey returned %08lX\n", Status);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
DataLength = 0;
|
|
Status = SampRegQueryValue(DomainKeyHandle,
|
|
L"Name",
|
|
NULL,
|
|
NULL,
|
|
&DataLength);
|
|
TRACE("SampRegQueryValue returned %08lX\n", Status);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Data length: %lu\n", DataLength);
|
|
|
|
if ((RequiredLength + DataLength + sizeof(UNICODE_STRING)) > PreferedMaximumLength)
|
|
break;
|
|
|
|
RequiredLength += (DataLength + sizeof(UNICODE_STRING));
|
|
EnumCount++;
|
|
}
|
|
|
|
SampRegCloseKey(&DomainKeyHandle);
|
|
}
|
|
|
|
EnumIndex++;
|
|
}
|
|
|
|
TRACE("EnumCount: %lu\n", EnumCount);
|
|
TRACE("RequiredLength: %lu\n", RequiredLength);
|
|
|
|
EnumBuffer = midl_user_allocate(sizeof(SAMPR_ENUMERATION_BUFFER));
|
|
if (EnumBuffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
EnumBuffer->EntriesRead = EnumCount;
|
|
EnumBuffer->Buffer = midl_user_allocate(EnumCount * sizeof(SAMPR_RID_ENUMERATION));
|
|
if (EnumBuffer->Buffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
EnumIndex = *EnumerationContext;
|
|
for (i = 0; i < EnumCount; i++, EnumIndex++)
|
|
{
|
|
Status = SampRegEnumerateSubKey(DomainsKeyHandle,
|
|
EnumIndex,
|
|
64 * sizeof(WCHAR),
|
|
DomainKeyName);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
TRACE("EnumIndex: %lu\n", EnumIndex);
|
|
TRACE("Domain key name: %S\n", DomainKeyName);
|
|
|
|
Status = SampRegOpenKey(DomainsKeyHandle,
|
|
DomainKeyName,
|
|
KEY_READ,
|
|
&DomainKeyHandle);
|
|
TRACE("SampRegOpenKey returned %08lX\n", Status);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
DataLength = 0;
|
|
Status = SampRegQueryValue(DomainKeyHandle,
|
|
L"Name",
|
|
NULL,
|
|
NULL,
|
|
&DataLength);
|
|
TRACE("SampRegQueryValue returned %08lX\n", Status);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
EnumBuffer->Buffer[i].RelativeId = 0;
|
|
EnumBuffer->Buffer[i].Name.Length = (USHORT)DataLength - sizeof(WCHAR);
|
|
EnumBuffer->Buffer[i].Name.MaximumLength = (USHORT)DataLength;
|
|
EnumBuffer->Buffer[i].Name.Buffer = midl_user_allocate(DataLength);
|
|
if (EnumBuffer->Buffer[i].Name.Buffer == NULL)
|
|
{
|
|
SampRegCloseKey(&DomainKeyHandle);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
Status = SampRegQueryValue(DomainKeyHandle,
|
|
L"Name",
|
|
NULL,
|
|
EnumBuffer->Buffer[i].Name.Buffer,
|
|
&DataLength);
|
|
TRACE("SampRegQueryValue returned %08lX\n", Status);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Domain name: %S\n", EnumBuffer->Buffer[i].Name.Buffer);
|
|
}
|
|
}
|
|
|
|
SampRegCloseKey(&DomainKeyHandle);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*EnumerationContext += EnumCount;
|
|
*Buffer = EnumBuffer;
|
|
*CountReturned = EnumCount;
|
|
}
|
|
|
|
done:
|
|
SampRegCloseKey(&DomainKeyHandle);
|
|
SampRegCloseKey(&DomainsKeyHandle);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
*EnumerationContext = 0;
|
|
*Buffer = NULL;
|
|
*CountReturned = 0;
|
|
|
|
if (EnumBuffer != NULL)
|
|
{
|
|
if (EnumBuffer->Buffer != NULL)
|
|
{
|
|
if (EnumBuffer->EntriesRead != 0)
|
|
{
|
|
for (i = 0; i < EnumBuffer->EntriesRead; i++)
|
|
{
|
|
if (EnumBuffer->Buffer[i].Name.Buffer != NULL)
|
|
midl_user_free(EnumBuffer->Buffer[i].Name.Buffer);
|
|
}
|
|
}
|
|
|
|
midl_user_free(EnumBuffer->Buffer);
|
|
}
|
|
|
|
midl_user_free(EnumBuffer);
|
|
}
|
|
}
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 7 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrOpenDomain(IN SAMPR_HANDLE ServerHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN PRPC_SID DomainId,
|
|
OUT SAMPR_HANDLE *DomainHandle)
|
|
{
|
|
PSAM_DB_OBJECT ServerObject;
|
|
PSAM_DB_OBJECT DomainObject;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrOpenDomain(%p %lx %p %p)\n",
|
|
ServerHandle, DesiredAccess, DomainId, DomainHandle);
|
|
|
|
/* Map generic access rights */
|
|
RtlMapGenericMask(&DesiredAccess,
|
|
&DomainMapping);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the server handle */
|
|
Status = SampValidateDbObject(ServerHandle,
|
|
SamDbServerObject,
|
|
SAM_SERVER_LOOKUP_DOMAIN,
|
|
&ServerObject);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
/* Validate the Domain SID */
|
|
if ((DomainId->Revision != SID_REVISION) ||
|
|
(DomainId->SubAuthorityCount > SID_MAX_SUB_AUTHORITIES) ||
|
|
(memcmp(&DomainId->IdentifierAuthority, &NtSidAuthority, sizeof(SID_IDENTIFIER_AUTHORITY)) != 0))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Open the domain object */
|
|
if ((DomainId->SubAuthorityCount == 1) &&
|
|
(DomainId->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID))
|
|
{
|
|
/* Builtin domain object */
|
|
TRACE("Opening the builtin domain object.\n");
|
|
|
|
Status = SampOpenDbObject(ServerObject,
|
|
L"Domains",
|
|
L"Builtin",
|
|
0,
|
|
SamDbDomainObject,
|
|
DesiredAccess,
|
|
&DomainObject);
|
|
}
|
|
else if ((DomainId->SubAuthorityCount == 4) &&
|
|
(DomainId->SubAuthority[0] == SECURITY_NT_NON_UNIQUE))
|
|
{
|
|
/* Account domain object */
|
|
TRACE("Opening the account domain object.\n");
|
|
|
|
/* FIXME: Check the account domain sub authorities!!! */
|
|
|
|
Status = SampOpenDbObject(ServerObject,
|
|
L"Domains",
|
|
L"Account",
|
|
0,
|
|
SamDbDomainObject,
|
|
DesiredAccess,
|
|
&DomainObject);
|
|
}
|
|
else
|
|
{
|
|
/* No valid domain SID */
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
*DomainHandle = (SAMPR_HANDLE)DomainObject;
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
TRACE("SamrOpenDomain done (Status 0x%08lx)\n", Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryDomainPassword(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_DOMAIN_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->Password.MinPasswordLength = FixedData.MinPasswordLength;
|
|
InfoBuffer->Password.PasswordHistoryLength = FixedData.PasswordHistoryLength;
|
|
InfoBuffer->Password.PasswordProperties = FixedData.PasswordProperties;
|
|
InfoBuffer->Password.MaxPasswordAge.LowPart = FixedData.MaxPasswordAge.LowPart;
|
|
InfoBuffer->Password.MaxPasswordAge.HighPart = FixedData.MaxPasswordAge.HighPart;
|
|
InfoBuffer->Password.MinPasswordAge.LowPart = FixedData.MinPasswordAge.LowPart;
|
|
InfoBuffer->Password.MinPasswordAge.HighPart = FixedData.MinPasswordAge.HighPart;
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampGetNumberOfAccounts(PSAM_DB_OBJECT DomainObject,
|
|
LPCWSTR AccountType,
|
|
PULONG Count)
|
|
{
|
|
HANDLE AccountKeyHandle = NULL;
|
|
HANDLE NamesKeyHandle = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Count = 0;
|
|
|
|
Status = SampRegOpenKey(DomainObject->KeyHandle,
|
|
AccountType,
|
|
KEY_READ,
|
|
&AccountKeyHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
Status = SampRegOpenKey(AccountKeyHandle,
|
|
L"Names",
|
|
KEY_READ,
|
|
&NamesKeyHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
Status = SampRegQueryKeyInfo(NamesKeyHandle,
|
|
NULL,
|
|
Count);
|
|
|
|
done:
|
|
SampRegCloseKey(&NamesKeyHandle);
|
|
SampRegCloseKey(&AccountKeyHandle);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryDomainGeneral(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_DOMAIN_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->General.ForceLogoff.LowPart = FixedData.ForceLogoff.LowPart;
|
|
InfoBuffer->General.ForceLogoff.HighPart = FixedData.ForceLogoff.HighPart;
|
|
InfoBuffer->General.DomainModifiedCount.LowPart = FixedData.DomainModifiedCount.LowPart;
|
|
InfoBuffer->General.DomainModifiedCount.HighPart = FixedData.DomainModifiedCount.HighPart;
|
|
InfoBuffer->General.DomainServerState = FixedData.DomainServerState;
|
|
InfoBuffer->General.DomainServerRole = FixedData.DomainServerRole;
|
|
InfoBuffer->General.UasCompatibilityRequired = FixedData.UasCompatibilityRequired;
|
|
|
|
/* Get the OemInformation string */
|
|
Status = SampGetObjectAttributeString(DomainObject,
|
|
L"OemInformation",
|
|
&InfoBuffer->General.OemInformation);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the Name string */
|
|
Status = SampGetObjectAttributeString(DomainObject,
|
|
L"Name",
|
|
&InfoBuffer->General.DomainName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the ReplicaSourceNodeName string */
|
|
Status = SampGetObjectAttributeString(DomainObject,
|
|
L"ReplicaSourceNodeName",
|
|
&InfoBuffer->General.ReplicaSourceNodeName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the number of Users in the Domain */
|
|
Status = SampGetNumberOfAccounts(DomainObject,
|
|
L"Users",
|
|
&InfoBuffer->General.UserCount);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the number of Groups in the Domain */
|
|
Status = SampGetNumberOfAccounts(DomainObject,
|
|
L"Groups",
|
|
&InfoBuffer->General.GroupCount);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the number of Aliases in the Domain */
|
|
Status = SampGetNumberOfAccounts(DomainObject,
|
|
L"Aliases",
|
|
&InfoBuffer->General.AliasCount);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->General.OemInformation.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->General.OemInformation.Buffer);
|
|
|
|
if (InfoBuffer->General.DomainName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->General.DomainName.Buffer);
|
|
|
|
if (InfoBuffer->General.ReplicaSourceNodeName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->General.ReplicaSourceNodeName.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryDomainLogoff(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_DOMAIN_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->Logoff.ForceLogoff.LowPart = FixedData.ForceLogoff.LowPart;
|
|
InfoBuffer->Logoff.ForceLogoff.HighPart = FixedData.ForceLogoff.HighPart;
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryDomainOem(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Get the OemInformation string */
|
|
Status = SampGetObjectAttributeString(DomainObject,
|
|
L"OemInformation",
|
|
&InfoBuffer->Oem.OemInformation);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->Oem.OemInformation.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Oem.OemInformation.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryDomainName(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Get the Name string */
|
|
Status = SampGetObjectAttributeString(DomainObject,
|
|
L"Name",
|
|
&InfoBuffer->Name.DomainName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->Name.DomainName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Name.DomainName.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryDomainReplication(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Get the ReplicaSourceNodeName string */
|
|
Status = SampGetObjectAttributeString(DomainObject,
|
|
L"ReplicaSourceNodeName",
|
|
&InfoBuffer->Replication.ReplicaSourceNodeName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->Replication.ReplicaSourceNodeName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Replication.ReplicaSourceNodeName.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryDomainServerRole(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_DOMAIN_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->Role.DomainServerRole = FixedData.DomainServerRole;
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryDomainModified(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_DOMAIN_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->Modified.DomainModifiedCount.LowPart = FixedData.DomainModifiedCount.LowPart;
|
|
InfoBuffer->Modified.DomainModifiedCount.HighPart = FixedData.DomainModifiedCount.HighPart;
|
|
InfoBuffer->Modified.CreationTime.LowPart = FixedData.CreationTime.LowPart;
|
|
InfoBuffer->Modified.CreationTime.HighPart = FixedData.CreationTime.HighPart;
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryDomainState(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_DOMAIN_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->State.DomainServerState = FixedData.DomainServerState;
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryDomainGeneral2(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_DOMAIN_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->General2.I1.ForceLogoff.LowPart = FixedData.ForceLogoff.LowPart;
|
|
InfoBuffer->General2.I1.ForceLogoff.HighPart = FixedData.ForceLogoff.HighPart;
|
|
InfoBuffer->General2.I1.DomainModifiedCount.LowPart = FixedData.DomainModifiedCount.LowPart;
|
|
InfoBuffer->General2.I1.DomainModifiedCount.HighPart = FixedData.DomainModifiedCount.HighPart;
|
|
InfoBuffer->General2.I1.DomainServerState = FixedData.DomainServerState;
|
|
InfoBuffer->General2.I1.DomainServerRole = FixedData.DomainServerRole;
|
|
InfoBuffer->General2.I1.UasCompatibilityRequired = FixedData.UasCompatibilityRequired;
|
|
|
|
InfoBuffer->General2.LockoutDuration = FixedData.LockoutDuration;
|
|
InfoBuffer->General2.LockoutObservationWindow = FixedData.LockoutObservationWindow;
|
|
InfoBuffer->General2.LockoutThreshold = FixedData.LockoutThreshold;
|
|
|
|
/* Get the OemInformation string */
|
|
Status = SampGetObjectAttributeString(DomainObject,
|
|
L"OemInformation",
|
|
&InfoBuffer->General2.I1.OemInformation);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the Name string */
|
|
Status = SampGetObjectAttributeString(DomainObject,
|
|
L"Name",
|
|
&InfoBuffer->General2.I1.DomainName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the ReplicaSourceNodeName string */
|
|
Status = SampGetObjectAttributeString(DomainObject,
|
|
L"ReplicaSourceNodeName",
|
|
&InfoBuffer->General2.I1.ReplicaSourceNodeName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the number of Users in the Domain */
|
|
Status = SampGetNumberOfAccounts(DomainObject,
|
|
L"Users",
|
|
&InfoBuffer->General2.I1.UserCount);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the number of Groups in the Domain */
|
|
Status = SampGetNumberOfAccounts(DomainObject,
|
|
L"Groups",
|
|
&InfoBuffer->General2.I1.GroupCount);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the number of Aliases in the Domain */
|
|
Status = SampGetNumberOfAccounts(DomainObject,
|
|
L"Aliases",
|
|
&InfoBuffer->General2.I1.AliasCount);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->General2.I1.OemInformation.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->General2.I1.OemInformation.Buffer);
|
|
|
|
if (InfoBuffer->General2.I1.DomainName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->General2.I1.DomainName.Buffer);
|
|
|
|
if (InfoBuffer->General2.I1.ReplicaSourceNodeName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->General2.I1.ReplicaSourceNodeName.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryDomainLockout(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_DOMAIN_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->Lockout.LockoutDuration = FixedData.LockoutDuration;
|
|
InfoBuffer->Lockout.LockoutObservationWindow = FixedData.LockoutObservationWindow;
|
|
InfoBuffer->Lockout.LockoutThreshold = FixedData.LockoutThreshold;
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryDomainModified2(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_DOMAIN_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_DOMAIN_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_DOMAIN_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->Modified2.DomainModifiedCount.LowPart = FixedData.DomainModifiedCount.LowPart;
|
|
InfoBuffer->Modified2.DomainModifiedCount.HighPart = FixedData.DomainModifiedCount.HighPart;
|
|
InfoBuffer->Modified2.CreationTime.LowPart = FixedData.CreationTime.LowPart;
|
|
InfoBuffer->Modified2.CreationTime.HighPart = FixedData.CreationTime.HighPart;
|
|
InfoBuffer->Modified2.ModifiedCountAtLastPromotion.LowPart = FixedData.ModifiedCountAtLastPromotion.LowPart;
|
|
InfoBuffer->Modified2.ModifiedCountAtLastPromotion.HighPart = FixedData.ModifiedCountAtLastPromotion.HighPart;
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 8 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrQueryInformationDomain(IN SAMPR_HANDLE DomainHandle,
|
|
IN DOMAIN_INFORMATION_CLASS DomainInformationClass,
|
|
OUT PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAM_DB_OBJECT DomainObject;
|
|
ACCESS_MASK DesiredAccess;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrQueryInformationDomain(%p %lu %p)\n",
|
|
DomainHandle, DomainInformationClass, Buffer);
|
|
|
|
switch (DomainInformationClass)
|
|
{
|
|
case DomainPasswordInformation:
|
|
case DomainLockoutInformation:
|
|
DesiredAccess = DOMAIN_READ_PASSWORD_PARAMETERS;
|
|
break;
|
|
|
|
case DomainGeneralInformation:
|
|
case DomainLogoffInformation:
|
|
case DomainOemInformation:
|
|
case DomainNameInformation:
|
|
case DomainReplicationInformation:
|
|
case DomainServerRoleInformation:
|
|
case DomainModifiedInformation:
|
|
case DomainStateInformation:
|
|
case DomainModifiedInformation2:
|
|
DesiredAccess = DOMAIN_READ_OTHER_PARAMETERS;
|
|
break;
|
|
|
|
case DomainGeneralInformation2:
|
|
DesiredAccess = DOMAIN_READ_PASSWORD_PARAMETERS |
|
|
DOMAIN_READ_OTHER_PARAMETERS;
|
|
break;
|
|
|
|
default:
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the server handle */
|
|
Status = SampValidateDbObject(DomainHandle,
|
|
SamDbDomainObject,
|
|
DesiredAccess,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
switch (DomainInformationClass)
|
|
{
|
|
case DomainPasswordInformation:
|
|
Status = SampQueryDomainPassword(DomainObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case DomainGeneralInformation:
|
|
Status = SampQueryDomainGeneral(DomainObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case DomainLogoffInformation:
|
|
Status = SampQueryDomainLogoff(DomainObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case DomainOemInformation:
|
|
Status = SampQueryDomainOem(DomainObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case DomainNameInformation:
|
|
Status = SampQueryDomainName(DomainObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case DomainReplicationInformation:
|
|
Status = SampQueryDomainReplication(DomainObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case DomainServerRoleInformation:
|
|
Status = SampQueryDomainServerRole(DomainObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case DomainModifiedInformation:
|
|
Status = SampQueryDomainModified(DomainObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case DomainStateInformation:
|
|
Status = SampQueryDomainState(DomainObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case DomainGeneralInformation2:
|
|
Status = SampQueryDomainGeneral2(DomainObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case DomainLockoutInformation:
|
|
Status = SampQueryDomainLockout(DomainObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case DomainModifiedInformation2:
|
|
Status = SampQueryDomainModified2(DomainObject,
|
|
Buffer);
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetDomainPassword(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER Buffer)
|
|
{
|
|
SAM_DOMAIN_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
FixedData.MinPasswordLength = Buffer->Password.MinPasswordLength;
|
|
FixedData.PasswordHistoryLength = Buffer->Password.PasswordHistoryLength;
|
|
FixedData.PasswordProperties = Buffer->Password.PasswordProperties;
|
|
FixedData.MaxPasswordAge.LowPart = Buffer->Password.MaxPasswordAge.LowPart;
|
|
FixedData.MaxPasswordAge.HighPart = Buffer->Password.MaxPasswordAge.HighPart;
|
|
FixedData.MinPasswordAge.LowPart = Buffer->Password.MinPasswordAge.LowPart;
|
|
FixedData.MinPasswordAge.HighPart = Buffer->Password.MinPasswordAge.HighPart;
|
|
|
|
Status = SampSetObjectAttribute(DomainObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedData,
|
|
Length);
|
|
|
|
done:
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetDomainLogoff(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER Buffer)
|
|
{
|
|
SAM_DOMAIN_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
FixedData.ForceLogoff.LowPart = Buffer->Logoff.ForceLogoff.LowPart;
|
|
FixedData.ForceLogoff.HighPart = Buffer->Logoff.ForceLogoff.HighPart;
|
|
|
|
Status = SampSetObjectAttribute(DomainObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedData,
|
|
Length);
|
|
|
|
done:
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetDomainServerRole(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER Buffer)
|
|
{
|
|
SAM_DOMAIN_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
FixedData.DomainServerRole = Buffer->Role.DomainServerRole;
|
|
|
|
Status = SampSetObjectAttribute(DomainObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedData,
|
|
Length);
|
|
|
|
done:
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetDomainState(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER Buffer)
|
|
{
|
|
SAM_DOMAIN_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
FixedData.DomainServerState = Buffer->State.DomainServerState;
|
|
|
|
Status = SampSetObjectAttribute(DomainObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedData,
|
|
Length);
|
|
|
|
done:
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetDomainLockout(PSAM_DB_OBJECT DomainObject,
|
|
PSAMPR_DOMAIN_INFO_BUFFER Buffer)
|
|
{
|
|
SAM_DOMAIN_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
FixedData.LockoutDuration = Buffer->Lockout.LockoutDuration;
|
|
FixedData.LockoutObservationWindow = Buffer->Lockout.LockoutObservationWindow;
|
|
FixedData.LockoutThreshold = Buffer->Lockout.LockoutThreshold;
|
|
|
|
Status = SampSetObjectAttribute(DomainObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedData,
|
|
Length);
|
|
|
|
done:
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 9 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrSetInformationDomain(IN SAMPR_HANDLE DomainHandle,
|
|
IN DOMAIN_INFORMATION_CLASS DomainInformationClass,
|
|
IN PSAMPR_DOMAIN_INFO_BUFFER DomainInformation)
|
|
{
|
|
PSAM_DB_OBJECT DomainObject;
|
|
ACCESS_MASK DesiredAccess;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrSetInformationDomain(%p %lu %p)\n",
|
|
DomainHandle, DomainInformationClass, DomainInformation);
|
|
|
|
switch (DomainInformationClass)
|
|
{
|
|
case DomainPasswordInformation:
|
|
case DomainLockoutInformation:
|
|
DesiredAccess = DOMAIN_WRITE_PASSWORD_PARAMS;
|
|
break;
|
|
|
|
case DomainLogoffInformation:
|
|
case DomainOemInformation:
|
|
case DomainNameInformation:
|
|
DesiredAccess = DOMAIN_WRITE_OTHER_PARAMETERS;
|
|
break;
|
|
|
|
case DomainReplicationInformation:
|
|
case DomainServerRoleInformation:
|
|
case DomainStateInformation:
|
|
DesiredAccess = DOMAIN_ADMINISTER_SERVER;
|
|
break;
|
|
|
|
default:
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the server handle */
|
|
Status = SampValidateDbObject(DomainHandle,
|
|
SamDbDomainObject,
|
|
DesiredAccess,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
switch (DomainInformationClass)
|
|
{
|
|
case DomainPasswordInformation:
|
|
Status = SampSetDomainPassword(DomainObject,
|
|
DomainInformation);
|
|
break;
|
|
|
|
case DomainLogoffInformation:
|
|
Status = SampSetDomainLogoff(DomainObject,
|
|
DomainInformation);
|
|
break;
|
|
|
|
case DomainOemInformation:
|
|
Status = SampSetObjectAttributeString(DomainObject,
|
|
L"OemInformation",
|
|
&DomainInformation->Oem.OemInformation);
|
|
break;
|
|
|
|
case DomainNameInformation:
|
|
Status = SampSetObjectAttributeString(DomainObject,
|
|
L"Name",
|
|
&DomainInformation->Name.DomainName);
|
|
break;
|
|
|
|
case DomainReplicationInformation:
|
|
Status = SampSetObjectAttributeString(DomainObject,
|
|
L"ReplicaSourceNodeName",
|
|
&DomainInformation->Replication.ReplicaSourceNodeName);
|
|
break;
|
|
|
|
case DomainServerRoleInformation:
|
|
Status = SampSetDomainServerRole(DomainObject,
|
|
DomainInformation);
|
|
break;
|
|
|
|
case DomainStateInformation:
|
|
Status = SampSetDomainState(DomainObject,
|
|
DomainInformation);
|
|
break;
|
|
|
|
case DomainLockoutInformation:
|
|
Status = SampSetDomainLockout(DomainObject,
|
|
DomainInformation);
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 10 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrCreateGroupInDomain(IN SAMPR_HANDLE DomainHandle,
|
|
IN PRPC_UNICODE_STRING Name,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT SAMPR_HANDLE *GroupHandle,
|
|
OUT unsigned long *RelativeId)
|
|
{
|
|
SAM_DOMAIN_FIXED_DATA FixedDomainData;
|
|
SAM_GROUP_FIXED_DATA FixedGroupData;
|
|
PSAM_DB_OBJECT DomainObject;
|
|
PSAM_DB_OBJECT GroupObject;
|
|
PSECURITY_DESCRIPTOR Sd = NULL;
|
|
ULONG SdSize = 0;
|
|
ULONG ulSize;
|
|
ULONG ulRid;
|
|
WCHAR szRid[9];
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrCreateGroupInDomain(%p %p %lx %p %p)\n",
|
|
DomainHandle, Name, DesiredAccess, GroupHandle, RelativeId);
|
|
|
|
/* Map generic access rights */
|
|
RtlMapGenericMask(&DesiredAccess,
|
|
&GroupMapping);
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the domain handle */
|
|
Status = SampValidateDbObject(DomainHandle,
|
|
SamDbDomainObject,
|
|
DOMAIN_CREATE_GROUP,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Check the group account name */
|
|
Status = SampCheckAccountName(Name, 256);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Check if the group name already exists in the domain */
|
|
Status = SampCheckAccountNameInDomain(DomainObject,
|
|
Name->Buffer);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Group name \'%S\' already exists in domain (Status 0x%08lx)\n",
|
|
Name->Buffer, Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Create the security descriptor */
|
|
Status = SampCreateGroupSD(&Sd,
|
|
&SdSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampCreateGroupSD failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the fixed domain attributes */
|
|
ulSize = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedDomainData,
|
|
&ulSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Increment the NextRid attribute */
|
|
ulRid = FixedDomainData.NextRid;
|
|
FixedDomainData.NextRid++;
|
|
|
|
/* Store the fixed domain attributes */
|
|
Status = SampSetObjectAttribute(DomainObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedDomainData,
|
|
ulSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
TRACE("RID: %lx\n", ulRid);
|
|
|
|
/* Convert the RID into a string (hex) */
|
|
swprintf(szRid, L"%08lX", ulRid);
|
|
|
|
/* Create the group object */
|
|
Status = SampCreateDbObject(DomainObject,
|
|
L"Groups",
|
|
szRid,
|
|
ulRid,
|
|
SamDbGroupObject,
|
|
DesiredAccess,
|
|
&GroupObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Add the account name of the user object */
|
|
Status = SampSetAccountNameInDomain(DomainObject,
|
|
L"Groups",
|
|
Name->Buffer,
|
|
ulRid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Initialize fixed user data */
|
|
memset(&FixedGroupData, 0, sizeof(SAM_GROUP_FIXED_DATA));
|
|
FixedGroupData.Version = 1;
|
|
FixedGroupData.GroupId = ulRid;
|
|
|
|
/* Set fixed user data attribute */
|
|
Status = SampSetObjectAttribute(GroupObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
(LPVOID)&FixedGroupData,
|
|
sizeof(SAM_GROUP_FIXED_DATA));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the Name attribute */
|
|
Status = SampSetObjectAttributeString(GroupObject,
|
|
L"Name",
|
|
Name);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the AdminComment attribute */
|
|
Status = SampSetObjectAttributeString(GroupObject,
|
|
L"AdminComment",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the SecDesc attribute*/
|
|
Status = SampSetObjectAttribute(GroupObject,
|
|
L"SecDesc",
|
|
REG_BINARY,
|
|
Sd,
|
|
SdSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*GroupHandle = (SAMPR_HANDLE)GroupObject;
|
|
*RelativeId = ulRid;
|
|
}
|
|
|
|
done:
|
|
if (Sd != NULL)
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, Sd);
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
TRACE("returns with status 0x%08lx\n", Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 11 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrEnumerateGroupsInDomain(IN SAMPR_HANDLE DomainHandle,
|
|
IN OUT unsigned long *EnumerationContext,
|
|
OUT PSAMPR_ENUMERATION_BUFFER *Buffer,
|
|
IN unsigned long PreferedMaximumLength,
|
|
OUT unsigned long *CountReturned)
|
|
{
|
|
PSAMPR_ENUMERATION_BUFFER EnumBuffer = NULL;
|
|
PSAM_DB_OBJECT DomainObject;
|
|
HANDLE GroupsKeyHandle = NULL;
|
|
HANDLE NamesKeyHandle = NULL;
|
|
WCHAR GroupName[64];
|
|
ULONG EnumIndex;
|
|
ULONG EnumCount = 0;
|
|
ULONG RequiredLength = 0;
|
|
ULONG NameLength;
|
|
ULONG DataLength;
|
|
ULONG Rid;
|
|
ULONG i;
|
|
BOOLEAN MoreEntries = FALSE;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrEnumerateUsersInDomain(%p %p %p %lu %p)\n",
|
|
DomainHandle, EnumerationContext, Buffer,
|
|
PreferedMaximumLength, CountReturned);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the domain handle */
|
|
Status = SampValidateDbObject(DomainHandle,
|
|
SamDbDomainObject,
|
|
DOMAIN_LIST_ACCOUNTS,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
Status = SampRegOpenKey(DomainObject->KeyHandle,
|
|
L"Groups",
|
|
KEY_READ,
|
|
&GroupsKeyHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
Status = SampRegOpenKey(GroupsKeyHandle,
|
|
L"Names",
|
|
KEY_READ,
|
|
&NamesKeyHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
TRACE("Part 1\n");
|
|
|
|
EnumIndex = *EnumerationContext;
|
|
|
|
while (TRUE)
|
|
{
|
|
NameLength = 64 * sizeof(WCHAR);
|
|
Status = SampRegEnumerateValue(NamesKeyHandle,
|
|
EnumIndex,
|
|
GroupName,
|
|
&NameLength,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Status == STATUS_NO_MORE_ENTRIES)
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
TRACE("EnumIndex: %lu\n", EnumIndex);
|
|
TRACE("Group name: %S\n", GroupName);
|
|
TRACE("Name length: %lu\n", NameLength);
|
|
|
|
if ((RequiredLength + NameLength + sizeof(UNICODE_NULL) + sizeof(SAMPR_RID_ENUMERATION)) > PreferedMaximumLength)
|
|
{
|
|
MoreEntries = TRUE;
|
|
break;
|
|
}
|
|
|
|
RequiredLength += (NameLength + sizeof(UNICODE_NULL) + sizeof(SAMPR_RID_ENUMERATION));
|
|
EnumCount++;
|
|
|
|
EnumIndex++;
|
|
}
|
|
|
|
TRACE("EnumCount: %lu\n", EnumCount);
|
|
TRACE("RequiredLength: %lu\n", RequiredLength);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
EnumBuffer = midl_user_allocate(sizeof(SAMPR_ENUMERATION_BUFFER));
|
|
if (EnumBuffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
EnumBuffer->EntriesRead = EnumCount;
|
|
if (EnumCount == 0)
|
|
goto done;
|
|
|
|
EnumBuffer->Buffer = midl_user_allocate(EnumCount * sizeof(SAMPR_RID_ENUMERATION));
|
|
if (EnumBuffer->Buffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
TRACE("Part 2\n");
|
|
|
|
EnumIndex = *EnumerationContext;
|
|
for (i = 0; i < EnumCount; i++, EnumIndex++)
|
|
{
|
|
NameLength = 64 * sizeof(WCHAR);
|
|
DataLength = sizeof(ULONG);
|
|
Status = SampRegEnumerateValue(NamesKeyHandle,
|
|
EnumIndex,
|
|
GroupName,
|
|
&NameLength,
|
|
NULL,
|
|
&Rid,
|
|
&DataLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Status == STATUS_NO_MORE_ENTRIES)
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
TRACE("EnumIndex: %lu\n", EnumIndex);
|
|
TRACE("Group name: %S\n", GroupName);
|
|
TRACE("Name length: %lu\n", NameLength);
|
|
TRACE("RID: %lu\n", Rid);
|
|
|
|
EnumBuffer->Buffer[i].RelativeId = Rid;
|
|
|
|
EnumBuffer->Buffer[i].Name.Length = (USHORT)NameLength;
|
|
EnumBuffer->Buffer[i].Name.MaximumLength = (USHORT)(NameLength + sizeof(UNICODE_NULL));
|
|
|
|
/* FIXME: Disabled because of bugs in widl and rpcrt4 */
|
|
#if 0
|
|
EnumBuffer->Buffer[i].Name.Buffer = midl_user_allocate(EnumBuffer->Buffer[i].Name.MaximumLength);
|
|
if (EnumBuffer->Buffer[i].Name.Buffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
memcpy(EnumBuffer->Buffer[i].Name.Buffer,
|
|
GroupName,
|
|
EnumBuffer->Buffer[i].Name.Length);
|
|
#endif
|
|
}
|
|
|
|
done:
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*EnumerationContext += EnumCount;
|
|
*Buffer = EnumBuffer;
|
|
*CountReturned = EnumCount;
|
|
}
|
|
else
|
|
{
|
|
*EnumerationContext = 0;
|
|
*Buffer = NULL;
|
|
*CountReturned = 0;
|
|
|
|
if (EnumBuffer != NULL)
|
|
{
|
|
if (EnumBuffer->Buffer != NULL)
|
|
{
|
|
if (EnumBuffer->EntriesRead != 0)
|
|
{
|
|
for (i = 0; i < EnumBuffer->EntriesRead; i++)
|
|
{
|
|
if (EnumBuffer->Buffer[i].Name.Buffer != NULL)
|
|
midl_user_free(EnumBuffer->Buffer[i].Name.Buffer);
|
|
}
|
|
}
|
|
|
|
midl_user_free(EnumBuffer->Buffer);
|
|
}
|
|
|
|
midl_user_free(EnumBuffer);
|
|
}
|
|
}
|
|
|
|
SampRegCloseKey(&NamesKeyHandle);
|
|
SampRegCloseKey(&GroupsKeyHandle);
|
|
|
|
if ((Status == STATUS_SUCCESS) && (MoreEntries != FALSE))
|
|
Status = STATUS_MORE_ENTRIES;
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 12 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrCreateUserInDomain(IN SAMPR_HANDLE DomainHandle,
|
|
IN PRPC_UNICODE_STRING Name,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT SAMPR_HANDLE *UserHandle,
|
|
OUT unsigned long *RelativeId)
|
|
{
|
|
SAM_DOMAIN_FIXED_DATA FixedDomainData;
|
|
SAM_USER_FIXED_DATA FixedUserData;
|
|
PSAM_DB_OBJECT DomainObject;
|
|
PSAM_DB_OBJECT UserObject;
|
|
GROUP_MEMBERSHIP GroupMembership;
|
|
UCHAR LogonHours[23];
|
|
ULONG ulSize;
|
|
ULONG ulRid;
|
|
WCHAR szRid[9];
|
|
PSECURITY_DESCRIPTOR Sd = NULL;
|
|
ULONG SdSize = 0;
|
|
PSID UserSid = NULL;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrCreateUserInDomain(%p %p %lx %p %p)\n",
|
|
DomainHandle, Name, DesiredAccess, UserHandle, RelativeId);
|
|
|
|
if (Name == NULL ||
|
|
Name->Length == 0 ||
|
|
Name->Buffer == NULL ||
|
|
UserHandle == NULL ||
|
|
RelativeId == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Map generic access rights */
|
|
RtlMapGenericMask(&DesiredAccess,
|
|
&UserMapping);
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the domain handle */
|
|
Status = SampValidateDbObject(DomainHandle,
|
|
SamDbDomainObject,
|
|
DOMAIN_CREATE_USER,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Check the user account name */
|
|
Status = SampCheckAccountName(Name, 20);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Check if the user name already exists in the domain */
|
|
Status = SampCheckAccountNameInDomain(DomainObject,
|
|
Name->Buffer);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("User name \'%S\' already exists in domain (Status 0x%08lx)\n",
|
|
Name->Buffer, Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the fixed domain attributes */
|
|
ulSize = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedDomainData,
|
|
&ulSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Increment the NextRid attribute */
|
|
ulRid = FixedDomainData.NextRid;
|
|
FixedDomainData.NextRid++;
|
|
|
|
TRACE("RID: %lx\n", ulRid);
|
|
|
|
/* Create the user SID */
|
|
Status = SampCreateAccountSid(DomainObject,
|
|
ulRid,
|
|
&UserSid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampCreateAccountSid failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Create the security descriptor */
|
|
Status = SampCreateUserSD(UserSid,
|
|
&Sd,
|
|
&SdSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampCreateUserSD failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Store the fixed domain attributes */
|
|
Status = SampSetObjectAttribute(DomainObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedDomainData,
|
|
ulSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Convert the RID into a string (hex) */
|
|
swprintf(szRid, L"%08lX", ulRid);
|
|
|
|
/* Create the user object */
|
|
Status = SampCreateDbObject(DomainObject,
|
|
L"Users",
|
|
szRid,
|
|
ulRid,
|
|
SamDbUserObject,
|
|
DesiredAccess,
|
|
&UserObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Add the account name for the user object */
|
|
Status = SampSetAccountNameInDomain(DomainObject,
|
|
L"Users",
|
|
Name->Buffer,
|
|
ulRid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Initialize fixed user data */
|
|
memset(&FixedUserData, 0, sizeof(SAM_USER_FIXED_DATA));
|
|
FixedUserData.Version = 1;
|
|
FixedUserData.Reserved = 0;
|
|
FixedUserData.LastLogon.QuadPart = 0;
|
|
FixedUserData.LastLogoff.QuadPart = 0;
|
|
FixedUserData.PasswordLastSet.QuadPart = 0;
|
|
FixedUserData.AccountExpires.QuadPart = MAXLONGLONG;
|
|
FixedUserData.LastBadPasswordTime.QuadPart = 0;
|
|
FixedUserData.UserId = ulRid;
|
|
FixedUserData.PrimaryGroupId = DOMAIN_GROUP_RID_USERS;
|
|
FixedUserData.UserAccountControl = USER_ACCOUNT_DISABLED |
|
|
USER_PASSWORD_NOT_REQUIRED |
|
|
USER_NORMAL_ACCOUNT;
|
|
FixedUserData.CountryCode = 0;
|
|
FixedUserData.CodePage = 0;
|
|
FixedUserData.BadPasswordCount = 0;
|
|
FixedUserData.LogonCount = 0;
|
|
FixedUserData.AdminCount = 0;
|
|
FixedUserData.OperatorCount = 0;
|
|
|
|
/* Set fixed user data attribute */
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
(LPVOID)&FixedUserData,
|
|
sizeof(SAM_USER_FIXED_DATA));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the Name attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"Name",
|
|
Name);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the FullName attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"FullName",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the HomeDirectory attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"HomeDirectory",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the HomeDirectoryDrive attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"HomeDirectoryDrive",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the ScriptPath attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"ScriptPath",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the ProfilePath attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"ProfilePath",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the AdminComment attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"AdminComment",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the UserComment attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"UserComment",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the WorkStations attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"WorkStations",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the Parameters attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"Parameters",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set LogonHours attribute*/
|
|
*((PUSHORT)LogonHours) = 168;
|
|
memset(&(LogonHours[2]), 0xff, 21);
|
|
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"LogonHours",
|
|
REG_BINARY,
|
|
&LogonHours,
|
|
sizeof(LogonHours));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set Groups attribute*/
|
|
GroupMembership.RelativeId = DOMAIN_GROUP_RID_USERS;
|
|
GroupMembership.Attributes = SE_GROUP_MANDATORY |
|
|
SE_GROUP_ENABLED |
|
|
SE_GROUP_ENABLED_BY_DEFAULT;
|
|
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"Groups",
|
|
REG_BINARY,
|
|
&GroupMembership,
|
|
sizeof(GROUP_MEMBERSHIP));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set LMPwd attribute*/
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"LMPwd",
|
|
REG_BINARY,
|
|
&EmptyLmHash,
|
|
sizeof(ENCRYPTED_LM_OWF_PASSWORD));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set NTPwd attribute*/
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"NTPwd",
|
|
REG_BINARY,
|
|
&EmptyNtHash,
|
|
sizeof(ENCRYPTED_NT_OWF_PASSWORD));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set LMPwdHistory attribute*/
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"LMPwdHistory",
|
|
REG_BINARY,
|
|
NULL,
|
|
0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set NTPwdHistory attribute*/
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"NTPwdHistory",
|
|
REG_BINARY,
|
|
NULL,
|
|
0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the PrivateData attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"PrivateData",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the SecDesc attribute*/
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"SecDesc",
|
|
REG_BINARY,
|
|
Sd,
|
|
SdSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*UserHandle = (SAMPR_HANDLE)UserObject;
|
|
*RelativeId = ulRid;
|
|
}
|
|
|
|
done:
|
|
if (Sd != NULL)
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, Sd);
|
|
|
|
if (UserSid != NULL)
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, UserSid);
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
TRACE("returns with status 0x%08lx\n", Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 13 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrEnumerateUsersInDomain(IN SAMPR_HANDLE DomainHandle,
|
|
IN OUT unsigned long *EnumerationContext,
|
|
IN unsigned long UserAccountControl,
|
|
OUT PSAMPR_ENUMERATION_BUFFER *Buffer,
|
|
IN unsigned long PreferedMaximumLength,
|
|
OUT unsigned long *CountReturned)
|
|
{
|
|
PSAMPR_ENUMERATION_BUFFER EnumBuffer = NULL;
|
|
PSAM_DB_OBJECT DomainObject;
|
|
HANDLE UsersKeyHandle = NULL;
|
|
HANDLE NamesKeyHandle = NULL;
|
|
WCHAR UserName[64];
|
|
ULONG EnumIndex;
|
|
ULONG EnumCount = 0;
|
|
ULONG RequiredLength = 0;
|
|
ULONG NameLength;
|
|
ULONG DataLength;
|
|
ULONG Rid;
|
|
ULONG i;
|
|
BOOLEAN MoreEntries = FALSE;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrEnumerateUsersInDomain(%p %p %lx %p %lu %p)\n",
|
|
DomainHandle, EnumerationContext, UserAccountControl, Buffer,
|
|
PreferedMaximumLength, CountReturned);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the domain handle */
|
|
Status = SampValidateDbObject(DomainHandle,
|
|
SamDbDomainObject,
|
|
DOMAIN_LIST_ACCOUNTS,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
Status = SampRegOpenKey(DomainObject->KeyHandle,
|
|
L"Users",
|
|
KEY_READ,
|
|
&UsersKeyHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
Status = SampRegOpenKey(UsersKeyHandle,
|
|
L"Names",
|
|
KEY_READ,
|
|
&NamesKeyHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
TRACE("Part 1\n");
|
|
|
|
EnumIndex = *EnumerationContext;
|
|
|
|
while (TRUE)
|
|
{
|
|
NameLength = 64 * sizeof(WCHAR);
|
|
Status = SampRegEnumerateValue(NamesKeyHandle,
|
|
EnumIndex,
|
|
UserName,
|
|
&NameLength,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Status == STATUS_NO_MORE_ENTRIES)
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
TRACE("EnumIndex: %lu\n", EnumIndex);
|
|
TRACE("User name: %S\n", UserName);
|
|
TRACE("Name length: %lu\n", NameLength);
|
|
|
|
if ((RequiredLength + NameLength + sizeof(UNICODE_NULL) + sizeof(SAMPR_RID_ENUMERATION)) > PreferedMaximumLength)
|
|
{
|
|
MoreEntries = TRUE;
|
|
break;
|
|
}
|
|
|
|
RequiredLength += (NameLength + sizeof(UNICODE_NULL) + sizeof(SAMPR_RID_ENUMERATION));
|
|
EnumCount++;
|
|
|
|
EnumIndex++;
|
|
}
|
|
|
|
TRACE("EnumCount: %lu\n", EnumCount);
|
|
TRACE("RequiredLength: %lu\n", RequiredLength);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
EnumBuffer = midl_user_allocate(sizeof(SAMPR_ENUMERATION_BUFFER));
|
|
if (EnumBuffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
EnumBuffer->EntriesRead = EnumCount;
|
|
if (EnumCount == 0)
|
|
goto done;
|
|
|
|
EnumBuffer->Buffer = midl_user_allocate(EnumCount * sizeof(SAMPR_RID_ENUMERATION));
|
|
if (EnumBuffer->Buffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
TRACE("Part 2\n");
|
|
|
|
EnumIndex = *EnumerationContext;
|
|
for (i = 0; i < EnumCount; i++, EnumIndex++)
|
|
{
|
|
NameLength = 64 * sizeof(WCHAR);
|
|
DataLength = sizeof(ULONG);
|
|
Status = SampRegEnumerateValue(NamesKeyHandle,
|
|
EnumIndex,
|
|
UserName,
|
|
&NameLength,
|
|
NULL,
|
|
&Rid,
|
|
&DataLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Status == STATUS_NO_MORE_ENTRIES)
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
TRACE("EnumIndex: %lu\n", EnumIndex);
|
|
TRACE("User name: %S\n", UserName);
|
|
TRACE("Name length: %lu\n", NameLength);
|
|
TRACE("RID: %lu\n", Rid);
|
|
|
|
EnumBuffer->Buffer[i].RelativeId = Rid;
|
|
|
|
EnumBuffer->Buffer[i].Name.Length = (USHORT)NameLength;
|
|
EnumBuffer->Buffer[i].Name.MaximumLength = (USHORT)(NameLength + sizeof(UNICODE_NULL));
|
|
|
|
/* FIXME: Disabled because of bugs in widl and rpcrt4 */
|
|
#if 0
|
|
EnumBuffer->Buffer[i].Name.Buffer = midl_user_allocate(EnumBuffer->Buffer[i].Name.MaximumLength);
|
|
if (EnumBuffer->Buffer[i].Name.Buffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
memcpy(EnumBuffer->Buffer[i].Name.Buffer,
|
|
UserName,
|
|
EnumBuffer->Buffer[i].Name.Length);
|
|
#endif
|
|
}
|
|
|
|
done:
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*EnumerationContext += EnumCount;
|
|
*Buffer = EnumBuffer;
|
|
*CountReturned = EnumCount;
|
|
}
|
|
else
|
|
{
|
|
*EnumerationContext = 0;
|
|
*Buffer = NULL;
|
|
*CountReturned = 0;
|
|
|
|
if (EnumBuffer != NULL)
|
|
{
|
|
if (EnumBuffer->Buffer != NULL)
|
|
{
|
|
if (EnumBuffer->EntriesRead != 0)
|
|
{
|
|
for (i = 0; i < EnumBuffer->EntriesRead; i++)
|
|
{
|
|
if (EnumBuffer->Buffer[i].Name.Buffer != NULL)
|
|
midl_user_free(EnumBuffer->Buffer[i].Name.Buffer);
|
|
}
|
|
}
|
|
|
|
midl_user_free(EnumBuffer->Buffer);
|
|
}
|
|
|
|
midl_user_free(EnumBuffer);
|
|
}
|
|
}
|
|
|
|
SampRegCloseKey(&NamesKeyHandle);
|
|
SampRegCloseKey(&UsersKeyHandle);
|
|
|
|
if ((Status == STATUS_SUCCESS) && (MoreEntries != FALSE))
|
|
Status = STATUS_MORE_ENTRIES;
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 14 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrCreateAliasInDomain(IN SAMPR_HANDLE DomainHandle,
|
|
IN PRPC_UNICODE_STRING AccountName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT SAMPR_HANDLE *AliasHandle,
|
|
OUT unsigned long *RelativeId)
|
|
{
|
|
SAM_DOMAIN_FIXED_DATA FixedDomainData;
|
|
PSAM_DB_OBJECT DomainObject;
|
|
PSAM_DB_OBJECT AliasObject;
|
|
PSECURITY_DESCRIPTOR Sd = NULL;
|
|
ULONG SdSize = 0;
|
|
ULONG ulSize;
|
|
ULONG ulRid;
|
|
WCHAR szRid[9];
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrCreateAliasInDomain(%p %p %lx %p %p)\n",
|
|
DomainHandle, AccountName, DesiredAccess, AliasHandle, RelativeId);
|
|
|
|
/* Map generic access rights */
|
|
RtlMapGenericMask(&DesiredAccess,
|
|
&AliasMapping);
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the domain handle */
|
|
Status = SampValidateDbObject(DomainHandle,
|
|
SamDbDomainObject,
|
|
DOMAIN_CREATE_ALIAS,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Check the alias account name */
|
|
Status = SampCheckAccountName(AccountName, 256);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Check if the alias name already exists in the domain */
|
|
Status = SampCheckAccountNameInDomain(DomainObject,
|
|
AccountName->Buffer);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Alias name \'%S\' already exists in domain (Status 0x%08lx)\n",
|
|
AccountName->Buffer, Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Create the security descriptor */
|
|
Status = SampCreateAliasSD(&Sd,
|
|
&SdSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampCreateAliasSD failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the fixed domain attributes */
|
|
ulSize = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedDomainData,
|
|
&ulSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Increment the NextRid attribute */
|
|
ulRid = FixedDomainData.NextRid;
|
|
FixedDomainData.NextRid++;
|
|
|
|
/* Store the fixed domain attributes */
|
|
Status = SampSetObjectAttribute(DomainObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedDomainData,
|
|
ulSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
TRACE("RID: %lx\n", ulRid);
|
|
|
|
/* Convert the RID into a string (hex) */
|
|
swprintf(szRid, L"%08lX", ulRid);
|
|
|
|
/* Create the alias object */
|
|
Status = SampCreateDbObject(DomainObject,
|
|
L"Aliases",
|
|
szRid,
|
|
ulRid,
|
|
SamDbAliasObject,
|
|
DesiredAccess,
|
|
&AliasObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Add the account name for the alias object */
|
|
Status = SampSetAccountNameInDomain(DomainObject,
|
|
L"Aliases",
|
|
AccountName->Buffer,
|
|
ulRid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the Name attribute */
|
|
Status = SampSetObjectAttributeString(AliasObject,
|
|
L"Name",
|
|
AccountName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the Description attribute */
|
|
Status = SampSetObjectAttributeString(AliasObject,
|
|
L"Description",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the SecDesc attribute*/
|
|
Status = SampSetObjectAttribute(AliasObject,
|
|
L"SecDesc",
|
|
REG_BINARY,
|
|
Sd,
|
|
SdSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*AliasHandle = (SAMPR_HANDLE)AliasObject;
|
|
*RelativeId = ulRid;
|
|
}
|
|
|
|
done:
|
|
if (Sd != NULL)
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, Sd);
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
TRACE("returns with status 0x%08lx\n", Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 15 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrEnumerateAliasesInDomain(IN SAMPR_HANDLE DomainHandle,
|
|
IN OUT unsigned long *EnumerationContext,
|
|
OUT PSAMPR_ENUMERATION_BUFFER *Buffer,
|
|
IN unsigned long PreferedMaximumLength,
|
|
OUT unsigned long *CountReturned)
|
|
{
|
|
PSAMPR_ENUMERATION_BUFFER EnumBuffer = NULL;
|
|
PSAM_DB_OBJECT DomainObject;
|
|
HANDLE AliasesKeyHandle = NULL;
|
|
HANDLE NamesKeyHandle = NULL;
|
|
WCHAR AliasName[64];
|
|
ULONG EnumIndex;
|
|
ULONG EnumCount = 0;
|
|
ULONG RequiredLength = 0;
|
|
ULONG NameLength;
|
|
ULONG DataLength;
|
|
ULONG Rid;
|
|
ULONG i;
|
|
BOOLEAN MoreEntries = FALSE;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrEnumerateAliasesInDomain(%p %p %p %lu %p)\n",
|
|
DomainHandle, EnumerationContext, Buffer,
|
|
PreferedMaximumLength, CountReturned);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the domain handle */
|
|
Status = SampValidateDbObject(DomainHandle,
|
|
SamDbDomainObject,
|
|
DOMAIN_LIST_ACCOUNTS,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
Status = SampRegOpenKey(DomainObject->KeyHandle,
|
|
L"Aliases",
|
|
KEY_READ,
|
|
&AliasesKeyHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
Status = SampRegOpenKey(AliasesKeyHandle,
|
|
L"Names",
|
|
KEY_READ,
|
|
&NamesKeyHandle);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
TRACE("Part 1\n");
|
|
|
|
EnumIndex = *EnumerationContext;
|
|
|
|
while (TRUE)
|
|
{
|
|
NameLength = 64 * sizeof(WCHAR);
|
|
Status = SampRegEnumerateValue(NamesKeyHandle,
|
|
EnumIndex,
|
|
AliasName,
|
|
&NameLength,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Status == STATUS_NO_MORE_ENTRIES)
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
TRACE("EnumIndex: %lu\n", EnumIndex);
|
|
TRACE("Alias name: %S\n", AliasName);
|
|
TRACE("Name length: %lu\n", NameLength);
|
|
|
|
if ((RequiredLength + NameLength + sizeof(UNICODE_NULL) + sizeof(SAMPR_RID_ENUMERATION)) > PreferedMaximumLength)
|
|
{
|
|
MoreEntries = TRUE;
|
|
break;
|
|
}
|
|
|
|
RequiredLength += (NameLength + sizeof(UNICODE_NULL) + sizeof(SAMPR_RID_ENUMERATION));
|
|
EnumCount++;
|
|
|
|
EnumIndex++;
|
|
}
|
|
|
|
TRACE("EnumCount: %lu\n", EnumCount);
|
|
TRACE("RequiredLength: %lu\n", RequiredLength);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
EnumBuffer = midl_user_allocate(sizeof(SAMPR_ENUMERATION_BUFFER));
|
|
if (EnumBuffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
EnumBuffer->EntriesRead = EnumCount;
|
|
if (EnumCount == 0)
|
|
goto done;
|
|
|
|
EnumBuffer->Buffer = midl_user_allocate(EnumCount * sizeof(SAMPR_RID_ENUMERATION));
|
|
if (EnumBuffer->Buffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
TRACE("Part 2\n");
|
|
|
|
EnumIndex = *EnumerationContext;
|
|
for (i = 0; i < EnumCount; i++, EnumIndex++)
|
|
{
|
|
NameLength = 64 * sizeof(WCHAR);
|
|
DataLength = sizeof(ULONG);
|
|
Status = SampRegEnumerateValue(NamesKeyHandle,
|
|
EnumIndex,
|
|
AliasName,
|
|
&NameLength,
|
|
NULL,
|
|
&Rid,
|
|
&DataLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (Status == STATUS_NO_MORE_ENTRIES)
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
TRACE("EnumIndex: %lu\n", EnumIndex);
|
|
TRACE("Alias name: %S\n", AliasName);
|
|
TRACE("Name length: %lu\n", NameLength);
|
|
TRACE("RID: %lu\n", Rid);
|
|
|
|
EnumBuffer->Buffer[i].RelativeId = Rid;
|
|
|
|
EnumBuffer->Buffer[i].Name.Length = (USHORT)NameLength;
|
|
EnumBuffer->Buffer[i].Name.MaximumLength = (USHORT)(NameLength + sizeof(UNICODE_NULL));
|
|
|
|
/* FIXME: Disabled because of bugs in widl and rpcrt4 */
|
|
#if 0
|
|
EnumBuffer->Buffer[i].Name.Buffer = midl_user_allocate(EnumBuffer->Buffer[i].Name.MaximumLength);
|
|
if (EnumBuffer->Buffer[i].Name.Buffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
memcpy(EnumBuffer->Buffer[i].Name.Buffer,
|
|
AliasName,
|
|
EnumBuffer->Buffer[i].Name.Length);
|
|
#endif
|
|
}
|
|
|
|
done:
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*EnumerationContext += EnumCount;
|
|
*Buffer = EnumBuffer;
|
|
*CountReturned = EnumCount;
|
|
}
|
|
else
|
|
{
|
|
*EnumerationContext = 0;
|
|
*Buffer = NULL;
|
|
*CountReturned = 0;
|
|
|
|
if (EnumBuffer != NULL)
|
|
{
|
|
if (EnumBuffer->Buffer != NULL)
|
|
{
|
|
if (EnumBuffer->EntriesRead != 0)
|
|
{
|
|
for (i = 0; i < EnumBuffer->EntriesRead; i++)
|
|
{
|
|
if (EnumBuffer->Buffer[i].Name.Buffer != NULL)
|
|
midl_user_free(EnumBuffer->Buffer[i].Name.Buffer);
|
|
}
|
|
}
|
|
|
|
midl_user_free(EnumBuffer->Buffer);
|
|
}
|
|
|
|
midl_user_free(EnumBuffer);
|
|
}
|
|
}
|
|
|
|
SampRegCloseKey(&NamesKeyHandle);
|
|
SampRegCloseKey(&AliasesKeyHandle);
|
|
|
|
if ((Status == STATUS_SUCCESS) && (MoreEntries != FALSE))
|
|
Status = STATUS_MORE_ENTRIES;
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 16 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrGetAliasMembership(IN SAMPR_HANDLE DomainHandle,
|
|
IN PSAMPR_PSID_ARRAY SidArray,
|
|
OUT PSAMPR_ULONG_ARRAY Membership)
|
|
{
|
|
PSAM_DB_OBJECT DomainObject;
|
|
HANDLE AliasesKeyHandle = NULL;
|
|
HANDLE MembersKeyHandle = NULL;
|
|
HANDLE MemberKeyHandle = NULL;
|
|
LPWSTR MemberSidString = NULL;
|
|
PULONG RidArray = NULL;
|
|
ULONG MaxSidCount = 0;
|
|
ULONG ValueCount;
|
|
ULONG DataLength;
|
|
ULONG i, j;
|
|
ULONG RidIndex;
|
|
NTSTATUS Status;
|
|
WCHAR NameBuffer[9];
|
|
|
|
TRACE("SamrGetAliasMembership(%p %p %p)\n",
|
|
DomainHandle, SidArray, Membership);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the domain handle */
|
|
Status = SampValidateDbObject(DomainHandle,
|
|
SamDbDomainObject,
|
|
DOMAIN_GET_ALIAS_MEMBERSHIP,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
Status = SampRegOpenKey(DomainObject->KeyHandle,
|
|
L"Aliases",
|
|
KEY_READ,
|
|
&AliasesKeyHandle);
|
|
TRACE("SampRegOpenKey returned %08lX\n", Status);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
Status = SampRegOpenKey(AliasesKeyHandle,
|
|
L"Members",
|
|
KEY_READ,
|
|
&MembersKeyHandle);
|
|
TRACE("SampRegOpenKey returned %08lX\n", Status);
|
|
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
goto done;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
for (i = 0; i < SidArray->Count; i++)
|
|
{
|
|
ConvertSidToStringSid(SidArray->Sids[i].SidPointer, &MemberSidString);
|
|
TRACE("Open %S\n", MemberSidString);
|
|
|
|
Status = SampRegOpenKey(MembersKeyHandle,
|
|
MemberSidString,
|
|
KEY_READ,
|
|
&MemberKeyHandle);
|
|
TRACE("SampRegOpenKey returned %08lX\n", Status);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = SampRegQueryKeyInfo(MemberKeyHandle,
|
|
NULL,
|
|
&ValueCount);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Found %lu values\n", ValueCount);
|
|
MaxSidCount += ValueCount;
|
|
}
|
|
|
|
SampRegCloseKey(&MemberKeyHandle);
|
|
}
|
|
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
Status = STATUS_SUCCESS;
|
|
|
|
LocalFree(MemberSidString);
|
|
}
|
|
|
|
if (MaxSidCount == 0)
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
goto done;
|
|
}
|
|
|
|
TRACE("Maximum sid count: %lu\n", MaxSidCount);
|
|
RidArray = midl_user_allocate(MaxSidCount * sizeof(ULONG));
|
|
if (RidArray == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
RidIndex = 0;
|
|
for (i = 0; i < SidArray->Count; i++)
|
|
{
|
|
ConvertSidToStringSid(SidArray->Sids[i].SidPointer, &MemberSidString);
|
|
TRACE("Open %S\n", MemberSidString);
|
|
|
|
Status = SampRegOpenKey(MembersKeyHandle,
|
|
MemberSidString,
|
|
KEY_READ,
|
|
&MemberKeyHandle);
|
|
TRACE("SampRegOpenKey returned %08lX\n", Status);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = SampRegQueryKeyInfo(MemberKeyHandle,
|
|
NULL,
|
|
&ValueCount);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Found %lu values\n", ValueCount);
|
|
|
|
for (j = 0; j < ValueCount; j++)
|
|
{
|
|
DataLength = 9 * sizeof(WCHAR);
|
|
Status = SampRegEnumerateValue(MemberKeyHandle,
|
|
j,
|
|
NameBuffer,
|
|
&DataLength,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* FIXME: Do not return each RID more than once. */
|
|
RidArray[RidIndex] = wcstoul(NameBuffer, NULL, 16);
|
|
RidIndex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
SampRegCloseKey(&MemberKeyHandle);
|
|
}
|
|
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
Status = STATUS_SUCCESS;
|
|
|
|
LocalFree(MemberSidString);
|
|
}
|
|
|
|
done:
|
|
SampRegCloseKey(&MembersKeyHandle);
|
|
SampRegCloseKey(&AliasesKeyHandle);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Membership->Count = MaxSidCount;
|
|
Membership->Element = RidArray;
|
|
}
|
|
else
|
|
{
|
|
if (RidArray != NULL)
|
|
midl_user_free(RidArray);
|
|
}
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 17 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrLookupNamesInDomain(IN SAMPR_HANDLE DomainHandle,
|
|
IN ULONG Count,
|
|
IN RPC_UNICODE_STRING Names[],
|
|
OUT PSAMPR_ULONG_ARRAY RelativeIds,
|
|
OUT PSAMPR_ULONG_ARRAY Use)
|
|
{
|
|
PSAM_DB_OBJECT DomainObject;
|
|
HANDLE AccountsKeyHandle = NULL;
|
|
HANDLE NamesKeyHandle = NULL;
|
|
ULONG MappedCount = 0;
|
|
ULONG DataLength;
|
|
ULONG i;
|
|
ULONG RelativeId;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrLookupNamesInDomain(%p %lu %p %p %p)\n",
|
|
DomainHandle, Count, Names, RelativeIds, Use);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the domain handle */
|
|
Status = SampValidateDbObject(DomainHandle,
|
|
SamDbDomainObject,
|
|
DOMAIN_LOOKUP,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
RelativeIds->Count = 0;
|
|
Use->Count = 0;
|
|
|
|
if (Count == 0)
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
goto done;
|
|
}
|
|
|
|
/* Allocate the relative IDs array */
|
|
RelativeIds->Element = midl_user_allocate(Count * sizeof(ULONG));
|
|
if (RelativeIds->Element == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
/* Allocate the use array */
|
|
Use->Element = midl_user_allocate(Count * sizeof(ULONG));
|
|
if (Use->Element == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
RelativeIds->Count = Count;
|
|
Use->Count = Count;
|
|
|
|
for (i = 0; i < Count; i++)
|
|
{
|
|
TRACE("Name: %S\n", Names[i].Buffer);
|
|
|
|
RelativeId = 0;
|
|
|
|
/* Lookup aliases */
|
|
Status = SampRegOpenKey(DomainObject->KeyHandle,
|
|
L"Aliases",
|
|
KEY_READ,
|
|
&AccountsKeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = SampRegOpenKey(AccountsKeyHandle,
|
|
L"Names",
|
|
KEY_READ,
|
|
&NamesKeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
DataLength = sizeof(ULONG);
|
|
Status = SampRegQueryValue(NamesKeyHandle,
|
|
Names[i].Buffer,
|
|
NULL,
|
|
&RelativeId,
|
|
&DataLength);
|
|
|
|
SampRegCloseKey(&NamesKeyHandle);
|
|
}
|
|
|
|
SampRegCloseKey(&AccountsKeyHandle);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
|
|
break;
|
|
|
|
/* Return alias account */
|
|
if (NT_SUCCESS(Status) && RelativeId != 0)
|
|
{
|
|
TRACE("Rid: %lu\n", RelativeId);
|
|
RelativeIds->Element[i] = RelativeId;
|
|
Use->Element[i] = SidTypeAlias;
|
|
MappedCount++;
|
|
continue;
|
|
}
|
|
|
|
/* Lookup groups */
|
|
Status = SampRegOpenKey(DomainObject->KeyHandle,
|
|
L"Groups",
|
|
KEY_READ,
|
|
&AccountsKeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = SampRegOpenKey(AccountsKeyHandle,
|
|
L"Names",
|
|
KEY_READ,
|
|
&NamesKeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
DataLength = sizeof(ULONG);
|
|
Status = SampRegQueryValue(NamesKeyHandle,
|
|
Names[i].Buffer,
|
|
NULL,
|
|
&RelativeId,
|
|
&DataLength);
|
|
|
|
SampRegCloseKey(&NamesKeyHandle);
|
|
}
|
|
|
|
SampRegCloseKey(&AccountsKeyHandle);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
|
|
break;
|
|
|
|
/* Return group account */
|
|
if (NT_SUCCESS(Status) && RelativeId != 0)
|
|
{
|
|
TRACE("Rid: %lu\n", RelativeId);
|
|
RelativeIds->Element[i] = RelativeId;
|
|
Use->Element[i] = SidTypeGroup;
|
|
MappedCount++;
|
|
continue;
|
|
}
|
|
|
|
/* Lookup users */
|
|
Status = SampRegOpenKey(DomainObject->KeyHandle,
|
|
L"Users",
|
|
KEY_READ,
|
|
&AccountsKeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = SampRegOpenKey(AccountsKeyHandle,
|
|
L"Names",
|
|
KEY_READ,
|
|
&NamesKeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
DataLength = sizeof(ULONG);
|
|
Status = SampRegQueryValue(NamesKeyHandle,
|
|
Names[i].Buffer,
|
|
NULL,
|
|
&RelativeId,
|
|
&DataLength);
|
|
|
|
SampRegCloseKey(&NamesKeyHandle);
|
|
}
|
|
|
|
SampRegCloseKey(&AccountsKeyHandle);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
|
|
break;
|
|
|
|
/* Return user account */
|
|
if (NT_SUCCESS(Status) && RelativeId != 0)
|
|
{
|
|
TRACE("Rid: %lu\n", RelativeId);
|
|
RelativeIds->Element[i] = RelativeId;
|
|
Use->Element[i] = SidTypeUser;
|
|
MappedCount++;
|
|
continue;
|
|
}
|
|
|
|
/* Return unknown account */
|
|
RelativeIds->Element[i] = 0;
|
|
Use->Element[i] = SidTypeUnknown;
|
|
}
|
|
|
|
done:
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (MappedCount == 0)
|
|
Status = STATUS_NONE_MAPPED;
|
|
else if (MappedCount < Count)
|
|
Status = STATUS_SOME_NOT_MAPPED;
|
|
}
|
|
else
|
|
{
|
|
if (RelativeIds->Element != NULL)
|
|
{
|
|
midl_user_free(RelativeIds->Element);
|
|
RelativeIds->Element = NULL;
|
|
}
|
|
|
|
RelativeIds->Count = 0;
|
|
|
|
if (Use->Element != NULL)
|
|
{
|
|
midl_user_free(Use->Element);
|
|
Use->Element = NULL;
|
|
}
|
|
|
|
Use->Count = 0;
|
|
}
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
TRACE("Returned Status %lx\n", Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 18 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrLookupIdsInDomain(IN SAMPR_HANDLE DomainHandle,
|
|
IN ULONG Count,
|
|
IN ULONG *RelativeIds,
|
|
OUT PSAMPR_RETURNED_USTRING_ARRAY Names,
|
|
OUT PSAMPR_ULONG_ARRAY Use)
|
|
{
|
|
PSAM_DB_OBJECT DomainObject;
|
|
WCHAR RidString[9];
|
|
HANDLE AccountsKeyHandle = NULL;
|
|
HANDLE AccountKeyHandle = NULL;
|
|
ULONG MappedCount = 0;
|
|
ULONG DataLength;
|
|
ULONG i;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrLookupIdsInDomain(%p %lu %p %p %p)\n",
|
|
DomainHandle, Count, RelativeIds, Names, Use);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the domain handle */
|
|
Status = SampValidateDbObject(DomainHandle,
|
|
SamDbDomainObject,
|
|
DOMAIN_LOOKUP,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Names->Count = 0;
|
|
Use->Count = 0;
|
|
|
|
if (Count == 0)
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
goto done;
|
|
}
|
|
|
|
/* Allocate the names array */
|
|
Names->Element = midl_user_allocate(Count * sizeof(*Names->Element));
|
|
if (Names->Element == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
/* Allocate the use array */
|
|
Use->Element = midl_user_allocate(Count * sizeof(*Use->Element));
|
|
if (Use->Element == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
Names->Count = Count;
|
|
Use->Count = Count;
|
|
|
|
for (i = 0; i < Count; i++)
|
|
{
|
|
TRACE("RID: %lu\n", RelativeIds[i]);
|
|
|
|
swprintf(RidString, L"%08lx", RelativeIds[i]);
|
|
|
|
/* Lookup aliases */
|
|
Status = SampRegOpenKey(DomainObject->KeyHandle,
|
|
L"Aliases",
|
|
KEY_READ,
|
|
&AccountsKeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = SampRegOpenKey(AccountsKeyHandle,
|
|
RidString,
|
|
KEY_READ,
|
|
&AccountKeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
DataLength = 0;
|
|
Status = SampRegQueryValue(AccountKeyHandle,
|
|
L"Name",
|
|
NULL,
|
|
NULL,
|
|
&DataLength);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Names->Element[i].Buffer = midl_user_allocate(DataLength);
|
|
if (Names->Element[i].Buffer == NULL)
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Names->Element[i].MaximumLength = (USHORT)DataLength;
|
|
Names->Element[i].Length = (USHORT)(DataLength - sizeof(WCHAR));
|
|
|
|
Status = SampRegQueryValue(AccountKeyHandle,
|
|
L"Name",
|
|
NULL,
|
|
Names->Element[i].Buffer,
|
|
&DataLength);
|
|
}
|
|
}
|
|
|
|
SampRegCloseKey(&AccountKeyHandle);
|
|
}
|
|
|
|
SampRegCloseKey(&AccountsKeyHandle);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
|
|
break;
|
|
|
|
/* Return alias account */
|
|
if (NT_SUCCESS(Status) && Names->Element[i].Buffer != NULL)
|
|
{
|
|
TRACE("Name: %S\n", Names->Element[i].Buffer);
|
|
Use->Element[i] = SidTypeAlias;
|
|
MappedCount++;
|
|
continue;
|
|
}
|
|
|
|
/* Lookup groups */
|
|
Status = SampRegOpenKey(DomainObject->KeyHandle,
|
|
L"Groups",
|
|
KEY_READ,
|
|
&AccountsKeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = SampRegOpenKey(AccountsKeyHandle,
|
|
RidString,
|
|
KEY_READ,
|
|
&AccountKeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
DataLength = 0;
|
|
Status = SampRegQueryValue(AccountKeyHandle,
|
|
L"Name",
|
|
NULL,
|
|
NULL,
|
|
&DataLength);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Names->Element[i].Buffer = midl_user_allocate(DataLength);
|
|
if (Names->Element[i].Buffer == NULL)
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Names->Element[i].MaximumLength = (USHORT)DataLength;
|
|
Names->Element[i].Length = (USHORT)(DataLength - sizeof(WCHAR));
|
|
|
|
Status = SampRegQueryValue(AccountKeyHandle,
|
|
L"Name",
|
|
NULL,
|
|
Names->Element[i].Buffer,
|
|
&DataLength);
|
|
}
|
|
}
|
|
|
|
SampRegCloseKey(&AccountKeyHandle);
|
|
}
|
|
|
|
SampRegCloseKey(&AccountsKeyHandle);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
|
|
break;
|
|
|
|
/* Return group account */
|
|
if (NT_SUCCESS(Status) && Names->Element[i].Buffer != NULL)
|
|
{
|
|
TRACE("Name: %S\n", Names->Element[i].Buffer);
|
|
Use->Element[i] = SidTypeGroup;
|
|
MappedCount++;
|
|
continue;
|
|
}
|
|
|
|
/* Lookup users */
|
|
Status = SampRegOpenKey(DomainObject->KeyHandle,
|
|
L"Users",
|
|
KEY_READ,
|
|
&AccountsKeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = SampRegOpenKey(AccountsKeyHandle,
|
|
RidString,
|
|
KEY_READ,
|
|
&AccountKeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
DataLength = 0;
|
|
Status = SampRegQueryValue(AccountKeyHandle,
|
|
L"Name",
|
|
NULL,
|
|
NULL,
|
|
&DataLength);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
TRACE("DataLength: %lu\n", DataLength);
|
|
|
|
Names->Element[i].Buffer = midl_user_allocate(DataLength);
|
|
if (Names->Element[i].Buffer == NULL)
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Names->Element[i].MaximumLength = (USHORT)DataLength;
|
|
Names->Element[i].Length = (USHORT)(DataLength - sizeof(WCHAR));
|
|
|
|
Status = SampRegQueryValue(AccountKeyHandle,
|
|
L"Name",
|
|
NULL,
|
|
Names->Element[i].Buffer,
|
|
&DataLength);
|
|
}
|
|
}
|
|
|
|
SampRegCloseKey(&AccountKeyHandle);
|
|
}
|
|
|
|
SampRegCloseKey(&AccountsKeyHandle);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
|
|
break;
|
|
|
|
/* Return user account */
|
|
if (NT_SUCCESS(Status) && Names->Element[i].Buffer != NULL)
|
|
{
|
|
TRACE("Name: %S\n", Names->Element[i].Buffer);
|
|
Use->Element[i] = SidTypeUser;
|
|
MappedCount++;
|
|
continue;
|
|
}
|
|
|
|
/* Return unknown account */
|
|
Use->Element[i] = SidTypeUnknown;
|
|
}
|
|
|
|
done:
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (MappedCount == 0)
|
|
Status = STATUS_NONE_MAPPED;
|
|
else if (MappedCount < Count)
|
|
Status = STATUS_SOME_NOT_MAPPED;
|
|
}
|
|
else
|
|
{
|
|
if (Names->Element != NULL)
|
|
{
|
|
for (i = 0; i < Count; i++)
|
|
{
|
|
if (Names->Element[i].Buffer != NULL)
|
|
midl_user_free(Names->Element[i].Buffer);
|
|
}
|
|
|
|
midl_user_free(Names->Element);
|
|
Names->Element = NULL;
|
|
}
|
|
|
|
Names->Count = 0;
|
|
|
|
if (Use->Element != NULL)
|
|
{
|
|
midl_user_free(Use->Element);
|
|
Use->Element = NULL;
|
|
}
|
|
|
|
Use->Count = 0;
|
|
}
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 19 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrOpenGroup(IN SAMPR_HANDLE DomainHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN unsigned long GroupId,
|
|
OUT SAMPR_HANDLE *GroupHandle)
|
|
{
|
|
PSAM_DB_OBJECT DomainObject;
|
|
PSAM_DB_OBJECT GroupObject;
|
|
WCHAR szRid[9];
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrOpenGroup(%p %lx %lx %p)\n",
|
|
DomainHandle, DesiredAccess, GroupId, GroupHandle);
|
|
|
|
/* Map generic access rights */
|
|
RtlMapGenericMask(&DesiredAccess,
|
|
&GroupMapping);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the domain handle */
|
|
Status = SampValidateDbObject(DomainHandle,
|
|
SamDbDomainObject,
|
|
DOMAIN_LOOKUP,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Convert the RID into a string (hex) */
|
|
swprintf(szRid, L"%08lX", GroupId);
|
|
|
|
/* Create the group object */
|
|
Status = SampOpenDbObject(DomainObject,
|
|
L"Groups",
|
|
szRid,
|
|
GroupId,
|
|
SamDbGroupObject,
|
|
DesiredAccess,
|
|
&GroupObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*GroupHandle = (SAMPR_HANDLE)GroupObject;
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryGroupGeneral(PSAM_DB_OBJECT GroupObject,
|
|
PSAMPR_GROUP_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_GROUP_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_GROUP_FIXED_DATA FixedData;
|
|
ULONG MembersLength = 0;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_GROUP_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Status = SampGetObjectAttributeString(GroupObject,
|
|
L"Name",
|
|
&InfoBuffer->General.Name);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Status = SampGetObjectAttributeString(GroupObject,
|
|
L"AdminComment",
|
|
&InfoBuffer->General.AdminComment);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Length = sizeof(SAM_GROUP_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(GroupObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
InfoBuffer->General.Attributes = FixedData.Attributes;
|
|
|
|
Status = SampGetObjectAttribute(GroupObject,
|
|
L"Members",
|
|
NULL,
|
|
NULL,
|
|
&MembersLength);
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
{
|
|
InfoBuffer->General.MemberCount = 0;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
InfoBuffer->General.MemberCount = MembersLength / sizeof(ULONG);
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->General.Name.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->General.Name.Buffer);
|
|
|
|
if (InfoBuffer->General.AdminComment.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->General.AdminComment.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryGroupName(PSAM_DB_OBJECT GroupObject,
|
|
PSAMPR_GROUP_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_GROUP_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_GROUP_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Status = SampGetObjectAttributeString(GroupObject,
|
|
L"Name",
|
|
&InfoBuffer->Name.Name);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->Name.Name.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Name.Name.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryGroupAttribute(PSAM_DB_OBJECT GroupObject,
|
|
PSAMPR_GROUP_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_GROUP_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_GROUP_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_GROUP_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_GROUP_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(GroupObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
InfoBuffer->Attribute.Attributes = FixedData.Attributes;
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryGroupAdminComment(PSAM_DB_OBJECT GroupObject,
|
|
PSAMPR_GROUP_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_GROUP_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_GROUP_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Status = SampGetObjectAttributeString(GroupObject,
|
|
L"AdminComment",
|
|
&InfoBuffer->AdminComment.AdminComment);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->AdminComment.AdminComment.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->AdminComment.AdminComment.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 20 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrQueryInformationGroup(IN SAMPR_HANDLE GroupHandle,
|
|
IN GROUP_INFORMATION_CLASS GroupInformationClass,
|
|
OUT PSAMPR_GROUP_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAM_DB_OBJECT GroupObject;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrQueryInformationGroup(%p %lu %p)\n",
|
|
GroupHandle, GroupInformationClass, Buffer);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the group handle */
|
|
Status = SampValidateDbObject(GroupHandle,
|
|
SamDbGroupObject,
|
|
GROUP_READ_INFORMATION,
|
|
&GroupObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
switch (GroupInformationClass)
|
|
{
|
|
case GroupGeneralInformation:
|
|
Status = SampQueryGroupGeneral(GroupObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case GroupNameInformation:
|
|
Status = SampQueryGroupName(GroupObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case GroupAttributeInformation:
|
|
Status = SampQueryGroupAttribute(GroupObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case GroupAdminCommentInformation:
|
|
Status = SampQueryGroupAdminComment(GroupObject,
|
|
Buffer);
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
break;
|
|
}
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetGroupName(PSAM_DB_OBJECT GroupObject,
|
|
PSAMPR_GROUP_INFO_BUFFER Buffer)
|
|
{
|
|
UNICODE_STRING OldGroupName = {0, 0, NULL};
|
|
UNICODE_STRING NewGroupName;
|
|
NTSTATUS Status;
|
|
|
|
Status = SampGetObjectAttributeString(GroupObject,
|
|
L"Name",
|
|
(PRPC_UNICODE_STRING)&OldGroupName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampGetObjectAttributeString failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Check the new account name */
|
|
Status = SampCheckAccountName(&Buffer->Name.Name, 256);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
NewGroupName.Length = Buffer->Name.Name.Length;
|
|
NewGroupName.MaximumLength = Buffer->Name.Name.MaximumLength;
|
|
NewGroupName.Buffer = Buffer->Name.Name.Buffer;
|
|
|
|
if (!RtlEqualUnicodeString(&OldGroupName, &NewGroupName, TRUE))
|
|
{
|
|
Status = SampCheckAccountNameInDomain(GroupObject->ParentObject,
|
|
NewGroupName.Buffer);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Group name \'%S\' already exists in domain (Status 0x%08lx)\n",
|
|
NewGroupName.Buffer, Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
Status = SampSetAccountNameInDomain(GroupObject->ParentObject,
|
|
L"Groups",
|
|
NewGroupName.Buffer,
|
|
GroupObject->RelativeId);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampSetAccountNameInDomain failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Status = SampRemoveAccountNameFromDomain(GroupObject->ParentObject,
|
|
L"Groups",
|
|
OldGroupName.Buffer);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampRemoveAccountNameFromDomain failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Status = SampSetObjectAttributeString(GroupObject,
|
|
L"Name",
|
|
(PRPC_UNICODE_STRING)&NewGroupName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampSetObjectAttribute failed (Status 0x%08lx)\n", Status);
|
|
}
|
|
|
|
done:
|
|
if (OldGroupName.Buffer != NULL)
|
|
midl_user_free(OldGroupName.Buffer);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetGroupAttribute(PSAM_DB_OBJECT GroupObject,
|
|
PSAMPR_GROUP_INFO_BUFFER Buffer)
|
|
{
|
|
SAM_GROUP_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
Length = sizeof(SAM_GROUP_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(GroupObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
FixedData.Attributes = Buffer->Attribute.Attributes;
|
|
|
|
Status = SampSetObjectAttribute(GroupObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedData,
|
|
Length);
|
|
|
|
done:
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 21 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrSetInformationGroup(IN SAMPR_HANDLE GroupHandle,
|
|
IN GROUP_INFORMATION_CLASS GroupInformationClass,
|
|
IN PSAMPR_GROUP_INFO_BUFFER Buffer)
|
|
{
|
|
PSAM_DB_OBJECT GroupObject;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrSetInformationGroup(%p %lu %p)\n",
|
|
GroupHandle, GroupInformationClass, Buffer);
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the group handle */
|
|
Status = SampValidateDbObject(GroupHandle,
|
|
SamDbGroupObject,
|
|
GROUP_WRITE_ACCOUNT,
|
|
&GroupObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
switch (GroupInformationClass)
|
|
{
|
|
case GroupNameInformation:
|
|
Status = SampSetGroupName(GroupObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case GroupAttributeInformation:
|
|
Status = SampSetGroupAttribute(GroupObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case GroupAdminCommentInformation:
|
|
Status = SampSetObjectAttributeString(GroupObject,
|
|
L"AdminComment",
|
|
&Buffer->AdminComment.AdminComment);
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
break;
|
|
}
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 22 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrAddMemberToGroup(IN SAMPR_HANDLE GroupHandle,
|
|
IN unsigned long MemberId,
|
|
IN unsigned long Attributes)
|
|
{
|
|
PSAM_DB_OBJECT GroupObject;
|
|
PSAM_DB_OBJECT UserObject = NULL;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrAddMemberToGroup(%p %lu %lx)\n",
|
|
GroupHandle, MemberId, Attributes);
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the group handle */
|
|
Status = SampValidateDbObject(GroupHandle,
|
|
SamDbGroupObject,
|
|
GROUP_ADD_MEMBER,
|
|
&GroupObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
/* Open the user object in the same domain */
|
|
Status = SampOpenUserObject(GroupObject->ParentObject,
|
|
MemberId,
|
|
0,
|
|
&UserObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampOpenUserObject() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Add group membership to the user object */
|
|
Status = SampAddGroupMembershipToUser(UserObject,
|
|
GroupObject->RelativeId,
|
|
Attributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampAddGroupMembershipToUser() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Add the member to the group object */
|
|
Status = SampAddMemberToGroup(GroupObject,
|
|
MemberId);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampAddMemberToGroup() failed (Status 0x%08lx)\n", Status);
|
|
}
|
|
|
|
done:
|
|
if (UserObject)
|
|
SampCloseDbObject(UserObject);
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 23 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrDeleteGroup(IN OUT SAMPR_HANDLE *GroupHandle)
|
|
{
|
|
PSAM_DB_OBJECT GroupObject;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrDeleteGroup(%p)\n", GroupHandle);
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the group handle */
|
|
Status = SampValidateDbObject(*GroupHandle,
|
|
SamDbGroupObject,
|
|
DELETE,
|
|
&GroupObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampValidateDbObject() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Fail, if the group is built-in */
|
|
if (GroupObject->RelativeId < 1000)
|
|
{
|
|
TRACE("You can not delete a special account!\n");
|
|
Status = STATUS_SPECIAL_ACCOUNT;
|
|
goto done;
|
|
}
|
|
|
|
/* Get the length of the Members attribute */
|
|
SampGetObjectAttribute(GroupObject,
|
|
L"Members",
|
|
NULL,
|
|
NULL,
|
|
&Length);
|
|
|
|
/* Fail, if the group has members */
|
|
if (Length != 0)
|
|
{
|
|
TRACE("There are still members in the group!\n");
|
|
Status = STATUS_MEMBER_IN_GROUP;
|
|
goto done;
|
|
}
|
|
|
|
/* FIXME: Remove the group from all aliases */
|
|
|
|
/* Delete the group from the database */
|
|
Status = SampDeleteAccountDbObject(GroupObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampDeleteAccountDbObject() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Invalidate the handle */
|
|
*GroupHandle = NULL;
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 24 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrRemoveMemberFromGroup(IN SAMPR_HANDLE GroupHandle,
|
|
IN unsigned long MemberId)
|
|
{
|
|
PSAM_DB_OBJECT GroupObject;
|
|
PSAM_DB_OBJECT UserObject = NULL;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrRemoveMemberFromGroup(%p %lu)\n",
|
|
GroupHandle, MemberId);
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the group handle */
|
|
Status = SampValidateDbObject(GroupHandle,
|
|
SamDbGroupObject,
|
|
GROUP_REMOVE_MEMBER,
|
|
&GroupObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
/* Open the user object in the same domain */
|
|
Status = SampOpenUserObject(GroupObject->ParentObject,
|
|
MemberId,
|
|
0,
|
|
&UserObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("SampOpenUserObject() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Remove group membership from the user object */
|
|
Status = SampRemoveGroupMembershipFromUser(UserObject,
|
|
GroupObject->RelativeId);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("SampAddGroupMembershipToUser() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Remove the member from the group object */
|
|
Status = SampRemoveMemberFromGroup(GroupObject,
|
|
MemberId);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("SampRemoveMemberFromGroup() failed (Status 0x%08lx)\n", Status);
|
|
}
|
|
|
|
done:
|
|
if (UserObject)
|
|
SampCloseDbObject(UserObject);
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 25 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrGetMembersInGroup(IN SAMPR_HANDLE GroupHandle,
|
|
OUT PSAMPR_GET_MEMBERS_BUFFER *Members)
|
|
{
|
|
PSAMPR_GET_MEMBERS_BUFFER MembersBuffer = NULL;
|
|
PSAM_DB_OBJECT GroupObject;
|
|
ULONG Length = 0;
|
|
ULONG i;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrGetMembersInGroup(%p %p)\n",
|
|
GroupHandle, Members);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the group handle */
|
|
Status = SampValidateDbObject(GroupHandle,
|
|
SamDbGroupObject,
|
|
GROUP_LIST_MEMBERS,
|
|
&GroupObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
MembersBuffer = midl_user_allocate(sizeof(SAMPR_GET_MEMBERS_BUFFER));
|
|
if (MembersBuffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
SampGetObjectAttribute(GroupObject,
|
|
L"Members",
|
|
NULL,
|
|
NULL,
|
|
&Length);
|
|
|
|
if (Length == 0)
|
|
{
|
|
MembersBuffer->MemberCount = 0;
|
|
MembersBuffer->Members = NULL;
|
|
MembersBuffer->Attributes = NULL;
|
|
|
|
*Members = MembersBuffer;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto done;
|
|
}
|
|
|
|
MembersBuffer->Members = midl_user_allocate(Length);
|
|
if (MembersBuffer->Members == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
MembersBuffer->Attributes = midl_user_allocate(Length);
|
|
if (MembersBuffer->Attributes == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
Status = SampGetObjectAttribute(GroupObject,
|
|
L"Members",
|
|
NULL,
|
|
MembersBuffer->Members,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampGetObjectAttributes() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
MembersBuffer->MemberCount = Length / sizeof(ULONG);
|
|
|
|
for (i = 0; i < MembersBuffer->MemberCount; i++)
|
|
{
|
|
Status = SampGetUserGroupAttributes(GroupObject->ParentObject,
|
|
MembersBuffer->Members[i],
|
|
GroupObject->RelativeId,
|
|
&(MembersBuffer->Attributes[i]));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampGetUserGroupAttributes() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
*Members = MembersBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (MembersBuffer != NULL)
|
|
{
|
|
if (MembersBuffer->Members != NULL)
|
|
midl_user_free(MembersBuffer->Members);
|
|
|
|
if (MembersBuffer->Attributes != NULL)
|
|
midl_user_free(MembersBuffer->Attributes);
|
|
|
|
midl_user_free(MembersBuffer);
|
|
}
|
|
}
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 26 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrSetMemberAttributesOfGroup(IN SAMPR_HANDLE GroupHandle,
|
|
IN unsigned long MemberId,
|
|
IN unsigned long Attributes)
|
|
{
|
|
PSAM_DB_OBJECT GroupObject;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrSetMemberAttributesOfGroup(%p %lu %lx)\n",
|
|
GroupHandle, MemberId, Attributes);
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the group handle */
|
|
Status = SampValidateDbObject(GroupHandle,
|
|
SamDbGroupObject,
|
|
GROUP_ADD_MEMBER,
|
|
&GroupObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Status = SampSetUserGroupAttributes(GroupObject->ParentObject,
|
|
MemberId,
|
|
GroupObject->RelativeId,
|
|
Attributes);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampSetUserGroupAttributes failed with status 0x%08lx\n", Status);
|
|
}
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 27 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrOpenAlias(IN SAMPR_HANDLE DomainHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN ULONG AliasId,
|
|
OUT SAMPR_HANDLE *AliasHandle)
|
|
{
|
|
PSAM_DB_OBJECT DomainObject;
|
|
PSAM_DB_OBJECT AliasObject;
|
|
WCHAR szRid[9];
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrOpenAlias(%p %lx %lx %p)\n",
|
|
DomainHandle, DesiredAccess, AliasId, AliasHandle);
|
|
|
|
/* Map generic access rights */
|
|
RtlMapGenericMask(&DesiredAccess,
|
|
&AliasMapping);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the domain handle */
|
|
Status = SampValidateDbObject(DomainHandle,
|
|
SamDbDomainObject,
|
|
DOMAIN_LOOKUP,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Convert the RID into a string (hex) */
|
|
swprintf(szRid, L"%08lX", AliasId);
|
|
|
|
/* Create the alias object */
|
|
Status = SampOpenDbObject(DomainObject,
|
|
L"Aliases",
|
|
szRid,
|
|
AliasId,
|
|
SamDbAliasObject,
|
|
DesiredAccess,
|
|
&AliasObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*AliasHandle = (SAMPR_HANDLE)AliasObject;
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryAliasGeneral(PSAM_DB_OBJECT AliasObject,
|
|
PSAMPR_ALIAS_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_ALIAS_INFO_BUFFER InfoBuffer = NULL;
|
|
HANDLE MembersKeyHandle = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_ALIAS_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Status = SampGetObjectAttributeString(AliasObject,
|
|
L"Name",
|
|
&InfoBuffer->General.Name);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Status = SampGetObjectAttributeString(AliasObject,
|
|
L"Description",
|
|
&InfoBuffer->General.AdminComment);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Open the Members subkey */
|
|
Status = SampRegOpenKey(AliasObject->KeyHandle,
|
|
L"Members",
|
|
KEY_READ,
|
|
&MembersKeyHandle);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Retrieve the number of members of the alias */
|
|
Status = SampRegQueryKeyInfo(MembersKeyHandle,
|
|
NULL,
|
|
&InfoBuffer->General.MemberCount);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
}
|
|
else if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
|
{
|
|
InfoBuffer->General.MemberCount = 0;
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
SampRegCloseKey(&MembersKeyHandle);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->General.Name.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->General.Name.Buffer);
|
|
|
|
if (InfoBuffer->General.AdminComment.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->General.AdminComment.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryAliasName(PSAM_DB_OBJECT AliasObject,
|
|
PSAMPR_ALIAS_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_ALIAS_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_ALIAS_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Status = SampGetObjectAttributeString(AliasObject,
|
|
L"Name",
|
|
&InfoBuffer->Name.Name);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->Name.Name.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Name.Name.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryAliasAdminComment(PSAM_DB_OBJECT AliasObject,
|
|
PSAMPR_ALIAS_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_ALIAS_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_ALIAS_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Status = SampGetObjectAttributeString(AliasObject,
|
|
L"Description",
|
|
&InfoBuffer->AdminComment.AdminComment);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->AdminComment.AdminComment.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->AdminComment.AdminComment.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 28 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrQueryInformationAlias(IN SAMPR_HANDLE AliasHandle,
|
|
IN ALIAS_INFORMATION_CLASS AliasInformationClass,
|
|
OUT PSAMPR_ALIAS_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAM_DB_OBJECT AliasObject;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrQueryInformationAlias(%p %lu %p)\n",
|
|
AliasHandle, AliasInformationClass, Buffer);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the alias handle */
|
|
Status = SampValidateDbObject(AliasHandle,
|
|
SamDbAliasObject,
|
|
ALIAS_READ_INFORMATION,
|
|
&AliasObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
switch (AliasInformationClass)
|
|
{
|
|
case AliasGeneralInformation:
|
|
Status = SampQueryAliasGeneral(AliasObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case AliasNameInformation:
|
|
Status = SampQueryAliasName(AliasObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case AliasAdminCommentInformation:
|
|
Status = SampQueryAliasAdminComment(AliasObject,
|
|
Buffer);
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
break;
|
|
}
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetAliasName(PSAM_DB_OBJECT AliasObject,
|
|
PSAMPR_ALIAS_INFO_BUFFER Buffer)
|
|
{
|
|
UNICODE_STRING OldAliasName = {0, 0, NULL};
|
|
UNICODE_STRING NewAliasName;
|
|
NTSTATUS Status;
|
|
|
|
Status = SampGetObjectAttributeString(AliasObject,
|
|
L"Name",
|
|
(PRPC_UNICODE_STRING)&OldAliasName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampGetObjectAttributeString failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Check the new account name */
|
|
Status = SampCheckAccountName(&Buffer->Name.Name, 256);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
NewAliasName.Length = Buffer->Name.Name.Length;
|
|
NewAliasName.MaximumLength = Buffer->Name.Name.MaximumLength;
|
|
NewAliasName.Buffer = Buffer->Name.Name.Buffer;
|
|
|
|
if (!RtlEqualUnicodeString(&OldAliasName, &NewAliasName, TRUE))
|
|
{
|
|
Status = SampCheckAccountNameInDomain(AliasObject->ParentObject,
|
|
NewAliasName.Buffer);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Alias name \'%S\' already exists in domain (Status 0x%08lx)\n",
|
|
NewAliasName.Buffer, Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
Status = SampSetAccountNameInDomain(AliasObject->ParentObject,
|
|
L"Aliases",
|
|
NewAliasName.Buffer,
|
|
AliasObject->RelativeId);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampSetAccountNameInDomain failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Status = SampRemoveAccountNameFromDomain(AliasObject->ParentObject,
|
|
L"Aliases",
|
|
OldAliasName.Buffer);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampRemoveAccountNameFromDomain failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Status = SampSetObjectAttributeString(AliasObject,
|
|
L"Name",
|
|
(PRPC_UNICODE_STRING)&NewAliasName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampSetObjectAttribute failed (Status 0x%08lx)\n", Status);
|
|
}
|
|
|
|
done:
|
|
if (OldAliasName.Buffer != NULL)
|
|
midl_user_free(OldAliasName.Buffer);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 29 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrSetInformationAlias(IN SAMPR_HANDLE AliasHandle,
|
|
IN ALIAS_INFORMATION_CLASS AliasInformationClass,
|
|
IN PSAMPR_ALIAS_INFO_BUFFER Buffer)
|
|
{
|
|
PSAM_DB_OBJECT AliasObject;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrSetInformationAlias(%p %lu %p)\n",
|
|
AliasHandle, AliasInformationClass, Buffer);
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the alias handle */
|
|
Status = SampValidateDbObject(AliasHandle,
|
|
SamDbAliasObject,
|
|
ALIAS_WRITE_ACCOUNT,
|
|
&AliasObject);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
switch (AliasInformationClass)
|
|
{
|
|
case AliasNameInformation:
|
|
Status = SampSetAliasName(AliasObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case AliasAdminCommentInformation:
|
|
Status = SampSetObjectAttributeString(AliasObject,
|
|
L"Description",
|
|
&Buffer->AdminComment.AdminComment);
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
break;
|
|
}
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 30 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrDeleteAlias(IN OUT SAMPR_HANDLE *AliasHandle)
|
|
{
|
|
PSAM_DB_OBJECT AliasObject;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrDeleteAlias(%p)\n", AliasHandle);
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the alias handle */
|
|
Status = SampValidateDbObject(*AliasHandle,
|
|
SamDbAliasObject,
|
|
DELETE,
|
|
&AliasObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampValidateDbObject failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Fail, if the alias is built-in */
|
|
if (AliasObject->RelativeId < 1000)
|
|
{
|
|
TRACE("You can not delete a special account!\n");
|
|
Status = STATUS_SPECIAL_ACCOUNT;
|
|
goto done;
|
|
}
|
|
|
|
/* Remove all members from the alias */
|
|
Status = SampRemoveAllMembersFromAlias(AliasObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampRemoveAllMembersFromAlias() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Delete the alias from the database */
|
|
Status = SampDeleteAccountDbObject(AliasObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampDeleteAccountDbObject() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Invalidate the handle */
|
|
*AliasHandle = NULL;
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 31 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrAddMemberToAlias(IN SAMPR_HANDLE AliasHandle,
|
|
IN PRPC_SID MemberId)
|
|
{
|
|
PSAM_DB_OBJECT AliasObject;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrAddMemberToAlias(%p %p)\n",
|
|
AliasHandle, MemberId);
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the alias handle */
|
|
Status = SampValidateDbObject(AliasHandle,
|
|
SamDbAliasObject,
|
|
ALIAS_ADD_MEMBER,
|
|
&AliasObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Status = SampAddMemberToAlias(AliasObject,
|
|
MemberId);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
}
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 32 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrRemoveMemberFromAlias(IN SAMPR_HANDLE AliasHandle,
|
|
IN PRPC_SID MemberId)
|
|
{
|
|
PSAM_DB_OBJECT AliasObject;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrRemoveMemberFromAlias(%p %p)\n",
|
|
AliasHandle, MemberId);
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the alias handle */
|
|
Status = SampValidateDbObject(AliasHandle,
|
|
SamDbAliasObject,
|
|
ALIAS_REMOVE_MEMBER,
|
|
&AliasObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Status = SampRemoveMemberFromAlias(AliasObject,
|
|
MemberId);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
}
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 33 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrGetMembersInAlias(IN SAMPR_HANDLE AliasHandle,
|
|
OUT PSAMPR_PSID_ARRAY_OUT Members)
|
|
{
|
|
PSAM_DB_OBJECT AliasObject;
|
|
PSAMPR_SID_INFORMATION MemberArray = NULL;
|
|
ULONG MemberCount = 0;
|
|
ULONG Index;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrGetMembersInAlias(%p %p %p)\n",
|
|
AliasHandle, Members);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the alias handle */
|
|
Status = SampValidateDbObject(AliasHandle,
|
|
SamDbAliasObject,
|
|
ALIAS_LIST_MEMBERS,
|
|
&AliasObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Status = SampGetMembersInAlias(AliasObject,
|
|
&MemberCount,
|
|
&MemberArray);
|
|
|
|
/* Return the number of members and the member array */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Members->Count = MemberCount;
|
|
Members->Sids = MemberArray;
|
|
}
|
|
|
|
done:
|
|
/* Clean up the members array and the SID buffers if something failed */
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (MemberArray != NULL)
|
|
{
|
|
for (Index = 0; Index < MemberCount; Index++)
|
|
{
|
|
if (MemberArray[Index].SidPointer != NULL)
|
|
midl_user_free(MemberArray[Index].SidPointer);
|
|
}
|
|
|
|
midl_user_free(MemberArray);
|
|
}
|
|
}
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 34 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrOpenUser(IN SAMPR_HANDLE DomainHandle,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN unsigned long UserId,
|
|
OUT SAMPR_HANDLE *UserHandle)
|
|
{
|
|
PSAM_DB_OBJECT DomainObject;
|
|
PSAM_DB_OBJECT UserObject;
|
|
WCHAR szRid[9];
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrOpenUser(%p %lx %lx %p)\n",
|
|
DomainHandle, DesiredAccess, UserId, UserHandle);
|
|
|
|
/* Map generic access rights */
|
|
RtlMapGenericMask(&DesiredAccess,
|
|
&UserMapping);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the domain handle */
|
|
Status = SampValidateDbObject(DomainHandle,
|
|
SamDbDomainObject,
|
|
DOMAIN_LOOKUP,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Convert the RID into a string (hex) */
|
|
swprintf(szRid, L"%08lX", UserId);
|
|
|
|
/* Create the user object */
|
|
Status = SampOpenDbObject(DomainObject,
|
|
L"Users",
|
|
szRid,
|
|
UserId,
|
|
SamDbUserObject,
|
|
DesiredAccess,
|
|
&UserObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*UserHandle = (SAMPR_HANDLE)UserObject;
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 35 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrDeleteUser(IN OUT SAMPR_HANDLE *UserHandle)
|
|
{
|
|
PSAM_DB_OBJECT UserObject;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrDeleteUser(%p)\n", UserHandle);
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the user handle */
|
|
Status = SampValidateDbObject(*UserHandle,
|
|
SamDbUserObject,
|
|
DELETE,
|
|
&UserObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampValidateDbObject() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Fail, if the user is built-in */
|
|
if (UserObject->RelativeId < 1000)
|
|
{
|
|
TRACE("You can not delete a special account!\n");
|
|
Status = STATUS_SPECIAL_ACCOUNT;
|
|
goto done;
|
|
}
|
|
|
|
/* Remove the user from all groups */
|
|
Status = SampRemoveUserFromAllGroups(UserObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampRemoveUserFromAllGroups() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Remove the user from all aliases */
|
|
Status = SampRemoveUserFromAllAliases(UserObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampRemoveUserFromAllAliases() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Delete the user from the database */
|
|
Status = SampDeleteAccountDbObject(UserObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampDeleteAccountDbObject() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Invalidate the handle */
|
|
*UserHandle = NULL;
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
SampQueryUserGeneral(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->General.PrimaryGroupId = FixedData.PrimaryGroupId;
|
|
|
|
/* Get the Name string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"Name",
|
|
&InfoBuffer->General.UserName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the FullName string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"FullName",
|
|
&InfoBuffer->General.FullName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the AdminComment string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"AdminComment",
|
|
&InfoBuffer->General.AdminComment);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the UserComment string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"UserComment",
|
|
&InfoBuffer->General.UserComment);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->General.UserName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->General.UserName.Buffer);
|
|
|
|
if (InfoBuffer->General.FullName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->General.FullName.Buffer);
|
|
|
|
if (InfoBuffer->General.AdminComment.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->General.AdminComment.Buffer);
|
|
|
|
if (InfoBuffer->General.UserComment.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->General.UserComment.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
SampQueryUserPreferences(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->Preferences.CountryCode = FixedData.CountryCode;
|
|
InfoBuffer->Preferences.CodePage = FixedData.CodePage;
|
|
|
|
/* Get the UserComment string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"UserComment",
|
|
&InfoBuffer->Preferences.UserComment);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->Preferences.UserComment.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Preferences.UserComment.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
SampQueryUserLogon(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_DOMAIN_FIXED_DATA DomainFixedData;
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
LARGE_INTEGER PasswordCanChange;
|
|
LARGE_INTEGER PasswordMustChange;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Get the fixed size domain data */
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject->ParentObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&DomainFixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
/* Get the fixed size user data */
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->Logon.UserId = FixedData.UserId;
|
|
InfoBuffer->Logon.PrimaryGroupId = FixedData.PrimaryGroupId;
|
|
InfoBuffer->Logon.LastLogon.LowPart = FixedData.LastLogon.LowPart;
|
|
InfoBuffer->Logon.LastLogon.HighPart = FixedData.LastLogon.HighPart;
|
|
InfoBuffer->Logon.LastLogoff.LowPart = FixedData.LastLogoff.LowPart;
|
|
InfoBuffer->Logon.LastLogoff.HighPart = FixedData.LastLogoff.HighPart;
|
|
InfoBuffer->Logon.PasswordLastSet.LowPart = FixedData.PasswordLastSet.LowPart;
|
|
InfoBuffer->Logon.PasswordLastSet.HighPart = FixedData.PasswordLastSet.HighPart;
|
|
InfoBuffer->Logon.BadPasswordCount = FixedData.BadPasswordCount;
|
|
InfoBuffer->Logon.LogonCount = FixedData.LogonCount;
|
|
InfoBuffer->Logon.UserAccountControl = FixedData.UserAccountControl;
|
|
|
|
PasswordCanChange = SampAddRelativeTimeToTime(FixedData.PasswordLastSet,
|
|
DomainFixedData.MinPasswordAge);
|
|
InfoBuffer->Logon.PasswordCanChange.LowPart = PasswordCanChange.LowPart;
|
|
InfoBuffer->Logon.PasswordCanChange.HighPart = PasswordCanChange.HighPart;
|
|
|
|
PasswordMustChange = SampAddRelativeTimeToTime(FixedData.PasswordLastSet,
|
|
DomainFixedData.MaxPasswordAge);
|
|
InfoBuffer->Logon.PasswordMustChange.LowPart = PasswordMustChange.LowPart;
|
|
InfoBuffer->Logon.PasswordMustChange.HighPart = PasswordMustChange.HighPart;
|
|
|
|
/* Get the Name string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"Name",
|
|
&InfoBuffer->Logon.UserName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the FullName string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"FullName",
|
|
&InfoBuffer->Logon.FullName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the HomeDirectory string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"HomeDirectory",
|
|
&InfoBuffer->Logon.HomeDirectory);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the HomeDirectoryDrive string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"HomeDirectoryDrive",
|
|
&InfoBuffer->Logon.HomeDirectoryDrive);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the ScriptPath string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"ScriptPath",
|
|
&InfoBuffer->Logon.ScriptPath);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the ProfilePath string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"ProfilePath",
|
|
&InfoBuffer->Logon.ProfilePath);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the WorkStations string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"WorkStations",
|
|
&InfoBuffer->Logon.WorkStations);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the LogonHours attribute */
|
|
Status = SampGetLogonHoursAttribute(UserObject,
|
|
&InfoBuffer->Logon.LogonHours);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->Logon.UserName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Logon.UserName.Buffer);
|
|
|
|
if (InfoBuffer->Logon.FullName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Logon.FullName.Buffer);
|
|
|
|
if (InfoBuffer->Logon.HomeDirectory.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Logon.HomeDirectory.Buffer);
|
|
|
|
if (InfoBuffer->Logon.HomeDirectoryDrive.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Logon.HomeDirectoryDrive.Buffer);
|
|
|
|
if (InfoBuffer->Logon.ScriptPath.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Logon.ScriptPath.Buffer);
|
|
|
|
if (InfoBuffer->Logon.ProfilePath.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Logon.ProfilePath.Buffer);
|
|
|
|
if (InfoBuffer->Logon.WorkStations.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Logon.WorkStations.Buffer);
|
|
|
|
if (InfoBuffer->Logon.LogonHours.LogonHours != NULL)
|
|
midl_user_free(InfoBuffer->Logon.LogonHours.LogonHours);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
SampQueryUserAccount(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->Account.UserId = FixedData.UserId;
|
|
InfoBuffer->Account.PrimaryGroupId = FixedData.PrimaryGroupId;
|
|
InfoBuffer->Account.LastLogon.LowPart = FixedData.LastLogon.LowPart;
|
|
InfoBuffer->Account.LastLogon.HighPart = FixedData.LastLogon.HighPart;
|
|
InfoBuffer->Account.LastLogoff.LowPart = FixedData.LastLogoff.LowPart;
|
|
InfoBuffer->Account.LastLogoff.HighPart = FixedData.LastLogoff.HighPart;
|
|
InfoBuffer->Account.PasswordLastSet.LowPart = FixedData.PasswordLastSet.LowPart;
|
|
InfoBuffer->Account.PasswordLastSet.HighPart = FixedData.PasswordLastSet.HighPart;
|
|
InfoBuffer->Account.AccountExpires.LowPart = FixedData.AccountExpires.LowPart;
|
|
InfoBuffer->Account.AccountExpires.HighPart = FixedData.AccountExpires.HighPart;
|
|
InfoBuffer->Account.BadPasswordCount = FixedData.BadPasswordCount;
|
|
InfoBuffer->Account.LogonCount = FixedData.LogonCount;
|
|
InfoBuffer->Account.UserAccountControl = FixedData.UserAccountControl;
|
|
|
|
/* Get the Name string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"Name",
|
|
&InfoBuffer->Account.UserName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the FullName string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"FullName",
|
|
&InfoBuffer->Account.FullName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the HomeDirectory string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"HomeDirectory",
|
|
&InfoBuffer->Account.HomeDirectory);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the HomeDirectoryDrive string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"HomeDirectoryDrive",
|
|
&InfoBuffer->Account.HomeDirectoryDrive);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the ScriptPath string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"ScriptPath",
|
|
&InfoBuffer->Account.ScriptPath);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the ProfilePath string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"ProfilePath",
|
|
&InfoBuffer->Account.ProfilePath);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the AdminComment string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"AdminComment",
|
|
&InfoBuffer->Account.AdminComment);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the WorkStations string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"WorkStations",
|
|
&InfoBuffer->Account.WorkStations);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the LogonHours attribute */
|
|
Status = SampGetLogonHoursAttribute(UserObject,
|
|
&InfoBuffer->Account.LogonHours);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->Account.UserName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Account.UserName.Buffer);
|
|
|
|
if (InfoBuffer->Account.FullName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Account.FullName.Buffer);
|
|
|
|
if (InfoBuffer->Account.HomeDirectory.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Account.HomeDirectory.Buffer);
|
|
|
|
if (InfoBuffer->Account.HomeDirectoryDrive.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Account.HomeDirectoryDrive.Buffer);
|
|
|
|
if (InfoBuffer->Account.ScriptPath.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Account.ScriptPath.Buffer);
|
|
|
|
if (InfoBuffer->Account.ProfilePath.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Account.ProfilePath.Buffer);
|
|
|
|
if (InfoBuffer->Account.AdminComment.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Account.AdminComment.Buffer);
|
|
|
|
if (InfoBuffer->Account.WorkStations.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Account.WorkStations.Buffer);
|
|
|
|
if (InfoBuffer->Account.LogonHours.LogonHours != NULL)
|
|
midl_user_free(InfoBuffer->Account.LogonHours.LogonHours);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
SampQueryUserLogonHours(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("(%p %p)\n", UserObject, Buffer);
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
{
|
|
TRACE("Failed to allocate InfoBuffer!\n");
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
Status = SampGetLogonHoursAttribute(UserObject,
|
|
&InfoBuffer->LogonHours.LogonHours);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampGetLogonHoursAttribute failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->LogonHours.LogonHours.LogonHours != NULL)
|
|
midl_user_free(InfoBuffer->LogonHours.LogonHours.LogonHours);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
SampQueryUserName(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Get the Name string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"Name",
|
|
&InfoBuffer->Name.UserName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the FullName string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"FullName",
|
|
&InfoBuffer->Name.FullName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->Name.UserName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Name.UserName.Buffer);
|
|
|
|
if (InfoBuffer->Name.FullName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Name.FullName.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryUserAccountName(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Get the Name string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"Name",
|
|
&InfoBuffer->AccountName.UserName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->AccountName.UserName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->AccountName.UserName.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryUserFullName(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Get the FullName string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"FullName",
|
|
&InfoBuffer->FullName.FullName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->FullName.FullName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->FullName.FullName.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
SampQueryUserPrimaryGroup(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->PrimaryGroup.PrimaryGroupId = FixedData.PrimaryGroupId;
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryUserHome(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Get the HomeDirectory string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"HomeDirectory",
|
|
&InfoBuffer->Home.HomeDirectory);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the HomeDirectoryDrive string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"HomeDirectoryDrive",
|
|
&InfoBuffer->Home.HomeDirectoryDrive);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->Home.HomeDirectory.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Home.HomeDirectory.Buffer);
|
|
|
|
if (InfoBuffer->Home.HomeDirectoryDrive.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Home.HomeDirectoryDrive.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryUserScript(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Get the ScriptPath string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"ScriptPath",
|
|
&InfoBuffer->Script.ScriptPath);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->Script.ScriptPath.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Script.ScriptPath.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryUserProfile(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Get the ProfilePath string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"ProfilePath",
|
|
&InfoBuffer->Profile.ProfilePath);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->Profile.ProfilePath.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Profile.ProfilePath.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryUserAdminComment(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Get the AdminComment string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"AdminComment",
|
|
&InfoBuffer->AdminComment.AdminComment);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->AdminComment.AdminComment.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->AdminComment.AdminComment.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryUserWorkStations(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Get the WorkStations string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"WorkStations",
|
|
&InfoBuffer->WorkStations.WorkStations);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->WorkStations.WorkStations.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->WorkStations.WorkStations.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
SampQueryUserControl(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->Control.UserAccountControl = FixedData.UserAccountControl;
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
SampQueryUserExpires(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->Expires.AccountExpires.LowPart = FixedData.AccountExpires.LowPart;
|
|
InfoBuffer->Expires.AccountExpires.HighPart = FixedData.AccountExpires.HighPart;
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
SampQueryUserInternal1(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
/* Fail, if the caller is not a trusted caller */
|
|
if (UserObject->Trusted == FALSE)
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
InfoBuffer->Internal1.LmPasswordPresent = FALSE;
|
|
InfoBuffer->Internal1.NtPasswordPresent = FALSE;
|
|
|
|
/* Get the NT password */
|
|
Length = 0;
|
|
SampGetObjectAttribute(UserObject,
|
|
L"NTPwd",
|
|
NULL,
|
|
NULL,
|
|
&Length);
|
|
|
|
if (Length == sizeof(ENCRYPTED_NT_OWF_PASSWORD))
|
|
{
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"NTPwd",
|
|
NULL,
|
|
(PVOID)&InfoBuffer->Internal1.EncryptedNtOwfPassword,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
if (memcmp(&InfoBuffer->Internal1.EncryptedNtOwfPassword,
|
|
&EmptyNtHash,
|
|
sizeof(ENCRYPTED_NT_OWF_PASSWORD)))
|
|
InfoBuffer->Internal1.NtPasswordPresent = TRUE;
|
|
}
|
|
|
|
|
|
/* Get the LM password */
|
|
Length = 0;
|
|
SampGetObjectAttribute(UserObject,
|
|
L"LMPwd",
|
|
NULL,
|
|
NULL,
|
|
&Length);
|
|
|
|
if (Length == sizeof(ENCRYPTED_LM_OWF_PASSWORD))
|
|
{
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"LMPwd",
|
|
NULL,
|
|
(PVOID)&InfoBuffer->Internal1.EncryptedLmOwfPassword,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
if (memcmp(&InfoBuffer->Internal1.EncryptedLmOwfPassword,
|
|
&EmptyLmHash,
|
|
sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
|
|
InfoBuffer->Internal1.LmPasswordPresent = TRUE;
|
|
}
|
|
|
|
InfoBuffer->Internal1.PasswordExpired = FALSE;
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
SampQueryUserInternal2(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
InfoBuffer->Internal2.Flags = 0;
|
|
InfoBuffer->Internal2.LastLogon.LowPart = FixedData.LastLogon.LowPart;
|
|
InfoBuffer->Internal2.LastLogon.HighPart = FixedData.LastLogon.HighPart;
|
|
InfoBuffer->Internal2.LastLogoff.LowPart = FixedData.LastLogoff.LowPart;
|
|
InfoBuffer->Internal2.LastLogoff.HighPart = FixedData.LastLogoff.HighPart;
|
|
InfoBuffer->Internal2.BadPasswordCount = FixedData.BadPasswordCount;
|
|
InfoBuffer->Internal2.LogonCount = FixedData.LogonCount;
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryUserParameters(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Get the Parameters string */
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"Parameters",
|
|
&InfoBuffer->Parameters.Parameters);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->Parameters.Parameters.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->Parameters.Parameters.Buffer);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampQueryUserAll(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAMPR_USER_INFO_BUFFER InfoBuffer = NULL;
|
|
SAM_DOMAIN_FIXED_DATA DomainFixedData;
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
LARGE_INTEGER PasswordCanChange;
|
|
LARGE_INTEGER PasswordMustChange;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
*Buffer = NULL;
|
|
|
|
InfoBuffer = midl_user_allocate(sizeof(SAMPR_USER_INFO_BUFFER));
|
|
if (InfoBuffer == NULL)
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
/* Get the fixed size domain data */
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject->ParentObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&DomainFixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
/* Get the fixed size user data */
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
/* Set the fields to be returned */
|
|
if (UserObject->Trusted)
|
|
{
|
|
InfoBuffer->All.WhichFields = USER_ALL_READ_GENERAL_MASK |
|
|
USER_ALL_READ_LOGON_MASK |
|
|
USER_ALL_READ_ACCOUNT_MASK |
|
|
USER_ALL_READ_PREFERENCES_MASK |
|
|
USER_ALL_READ_TRUSTED_MASK;
|
|
}
|
|
else
|
|
{
|
|
InfoBuffer->All.WhichFields = 0;
|
|
|
|
if (UserObject->Access & USER_READ_GENERAL)
|
|
InfoBuffer->All.WhichFields |= USER_ALL_READ_GENERAL_MASK;
|
|
|
|
if (UserObject->Access & USER_READ_LOGON)
|
|
InfoBuffer->All.WhichFields |= USER_ALL_READ_LOGON_MASK;
|
|
|
|
if (UserObject->Access & USER_READ_ACCOUNT)
|
|
InfoBuffer->All.WhichFields |= USER_ALL_READ_ACCOUNT_MASK;
|
|
|
|
if (UserObject->Access & USER_READ_PREFERENCES)
|
|
InfoBuffer->All.WhichFields |= USER_ALL_READ_PREFERENCES_MASK;
|
|
}
|
|
|
|
/* Fail, if no fields are to be returned */
|
|
if (InfoBuffer->All.WhichFields == 0)
|
|
{
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto done;
|
|
}
|
|
|
|
/* Get the UserName attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_USERNAME)
|
|
{
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"Name",
|
|
&InfoBuffer->All.UserName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Get the FullName attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_FULLNAME)
|
|
{
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"FullName",
|
|
&InfoBuffer->All.FullName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Get the UserId attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_USERID)
|
|
{
|
|
InfoBuffer->All.UserId = FixedData.UserId;
|
|
}
|
|
|
|
/* Get the PrimaryGroupId attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_PRIMARYGROUPID)
|
|
{
|
|
InfoBuffer->All.PrimaryGroupId = FixedData.PrimaryGroupId;
|
|
}
|
|
|
|
/* Get the AdminComment attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_ADMINCOMMENT)
|
|
{
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"AdminComment",
|
|
&InfoBuffer->All.AdminComment);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Get the UserComment attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_USERCOMMENT)
|
|
{
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"UserComment",
|
|
&InfoBuffer->All.UserComment);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Get the HomeDirectory attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_HOMEDIRECTORY)
|
|
{
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"HomeDirectory",
|
|
&InfoBuffer->All.HomeDirectory);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Get the HomeDirectoryDrive attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_HOMEDIRECTORYDRIVE)
|
|
{
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"HomeDirectoryDrive",
|
|
&InfoBuffer->Home.HomeDirectoryDrive);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Get the ScriptPath attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_SCRIPTPATH)
|
|
{
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"ScriptPath",
|
|
&InfoBuffer->All.ScriptPath);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Get the ProfilePath attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_PROFILEPATH)
|
|
{
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"ProfilePath",
|
|
&InfoBuffer->All.ProfilePath);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Get the WorkStations attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_WORKSTATIONS)
|
|
{
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"WorkStations",
|
|
&InfoBuffer->All.WorkStations);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Get the LastLogon attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_LASTLOGON)
|
|
{
|
|
InfoBuffer->All.LastLogon.LowPart = FixedData.LastLogon.LowPart;
|
|
InfoBuffer->All.LastLogon.HighPart = FixedData.LastLogon.HighPart;
|
|
}
|
|
|
|
/* Get the LastLogoff attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_LASTLOGOFF)
|
|
{
|
|
InfoBuffer->All.LastLogoff.LowPart = FixedData.LastLogoff.LowPart;
|
|
InfoBuffer->All.LastLogoff.HighPart = FixedData.LastLogoff.HighPart;
|
|
}
|
|
|
|
/* Get the LogonHours attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_LOGONHOURS)
|
|
{
|
|
Status = SampGetLogonHoursAttribute(UserObject,
|
|
&InfoBuffer->All.LogonHours);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Get the BadPasswordCount attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_BADPASSWORDCOUNT)
|
|
{
|
|
InfoBuffer->All.BadPasswordCount = FixedData.BadPasswordCount;
|
|
}
|
|
|
|
/* Get the LogonCount attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_LOGONCOUNT)
|
|
{
|
|
InfoBuffer->All.LogonCount = FixedData.LogonCount;
|
|
}
|
|
|
|
/* Get the PasswordCanChange attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_PASSWORDCANCHANGE)
|
|
{
|
|
PasswordCanChange = SampAddRelativeTimeToTime(FixedData.PasswordLastSet,
|
|
DomainFixedData.MinPasswordAge);
|
|
InfoBuffer->All.PasswordCanChange.LowPart = PasswordCanChange.LowPart;
|
|
InfoBuffer->All.PasswordCanChange.HighPart = PasswordCanChange.HighPart;
|
|
}
|
|
|
|
/* Get the PasswordMustChange attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_PASSWORDMUSTCHANGE)
|
|
{
|
|
PasswordMustChange = SampAddRelativeTimeToTime(FixedData.PasswordLastSet,
|
|
DomainFixedData.MaxPasswordAge);
|
|
InfoBuffer->All.PasswordMustChange.LowPart = PasswordMustChange.LowPart;
|
|
InfoBuffer->All.PasswordMustChange.HighPart = PasswordMustChange.HighPart;
|
|
}
|
|
|
|
/* Get the PasswordLastSet attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_PASSWORDLASTSET)
|
|
{
|
|
InfoBuffer->All.PasswordLastSet.LowPart = FixedData.PasswordLastSet.LowPart;
|
|
InfoBuffer->All.PasswordLastSet.HighPart = FixedData.PasswordLastSet.HighPart;
|
|
}
|
|
|
|
/* Get the AccountExpires attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_ACCOUNTEXPIRES)
|
|
{
|
|
InfoBuffer->All.AccountExpires.LowPart = FixedData.AccountExpires.LowPart;
|
|
InfoBuffer->All.AccountExpires.HighPart = FixedData.AccountExpires.HighPart;
|
|
}
|
|
|
|
/* Get the UserAccountControl attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_USERACCOUNTCONTROL)
|
|
{
|
|
InfoBuffer->All.UserAccountControl = FixedData.UserAccountControl;
|
|
}
|
|
|
|
/* Get the Parameters attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_PARAMETERS)
|
|
{
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"Parameters",
|
|
&InfoBuffer->All.Parameters);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Get the CountryCode attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_COUNTRYCODE)
|
|
{
|
|
InfoBuffer->All.CountryCode = FixedData.CountryCode;
|
|
}
|
|
|
|
/* Get the CodePage attribute */
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_CODEPAGE)
|
|
{
|
|
InfoBuffer->All.CodePage = FixedData.CodePage;
|
|
}
|
|
|
|
/* Get the LmPassword and NtPassword attributes */
|
|
if (InfoBuffer->All.WhichFields & (USER_ALL_NTPASSWORDPRESENT | USER_ALL_LMPASSWORDPRESENT))
|
|
{
|
|
InfoBuffer->All.LmPasswordPresent = FALSE;
|
|
InfoBuffer->All.NtPasswordPresent = FALSE;
|
|
|
|
/* Get the NT password */
|
|
Length = 0;
|
|
SampGetObjectAttribute(UserObject,
|
|
L"NTPwd",
|
|
NULL,
|
|
NULL,
|
|
&Length);
|
|
|
|
if (Length == sizeof(ENCRYPTED_NT_OWF_PASSWORD))
|
|
{
|
|
InfoBuffer->All.NtOwfPassword.Buffer = midl_user_allocate(sizeof(ENCRYPTED_NT_OWF_PASSWORD));
|
|
if (InfoBuffer->All.NtOwfPassword.Buffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
InfoBuffer->All.NtOwfPassword.Length = sizeof(ENCRYPTED_NT_OWF_PASSWORD);
|
|
InfoBuffer->All.NtOwfPassword.MaximumLength = sizeof(ENCRYPTED_NT_OWF_PASSWORD);
|
|
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"NTPwd",
|
|
NULL,
|
|
(PVOID)InfoBuffer->All.NtOwfPassword.Buffer,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
if (memcmp(InfoBuffer->All.NtOwfPassword.Buffer,
|
|
&EmptyNtHash,
|
|
sizeof(ENCRYPTED_NT_OWF_PASSWORD)))
|
|
InfoBuffer->All.NtPasswordPresent = TRUE;
|
|
}
|
|
|
|
/* Get the LM password */
|
|
Length = 0;
|
|
SampGetObjectAttribute(UserObject,
|
|
L"LMPwd",
|
|
NULL,
|
|
NULL,
|
|
&Length);
|
|
|
|
if (Length == sizeof(ENCRYPTED_LM_OWF_PASSWORD))
|
|
{
|
|
InfoBuffer->All.LmOwfPassword.Buffer = midl_user_allocate(sizeof(ENCRYPTED_LM_OWF_PASSWORD));
|
|
if (InfoBuffer->All.LmOwfPassword.Buffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
InfoBuffer->All.LmOwfPassword.Length = sizeof(ENCRYPTED_LM_OWF_PASSWORD);
|
|
InfoBuffer->All.LmOwfPassword.MaximumLength = sizeof(ENCRYPTED_LM_OWF_PASSWORD);
|
|
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"LMPwd",
|
|
NULL,
|
|
(PVOID)InfoBuffer->All.LmOwfPassword.Buffer,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
if (memcmp(InfoBuffer->All.LmOwfPassword.Buffer,
|
|
&EmptyLmHash,
|
|
sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
|
|
InfoBuffer->All.LmPasswordPresent = TRUE;
|
|
}
|
|
}
|
|
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_PRIVATEDATA)
|
|
{
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"PrivateData",
|
|
&InfoBuffer->All.PrivateData);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("Status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_PASSWORDEXPIRED)
|
|
{
|
|
/* FIXME */
|
|
}
|
|
|
|
if (InfoBuffer->All.WhichFields & USER_ALL_SECURITYDESCRIPTOR)
|
|
{
|
|
Length = 0;
|
|
SampGetObjectAttribute(UserObject,
|
|
L"SecDesc",
|
|
NULL,
|
|
NULL,
|
|
&Length);
|
|
|
|
if (Length > 0)
|
|
{
|
|
InfoBuffer->All.SecurityDescriptor.SecurityDescriptor = midl_user_allocate(Length);
|
|
if (InfoBuffer->All.SecurityDescriptor.SecurityDescriptor == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
InfoBuffer->All.SecurityDescriptor.Length = Length;
|
|
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"SecDesc",
|
|
NULL,
|
|
(PVOID)InfoBuffer->All.SecurityDescriptor.SecurityDescriptor,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
*Buffer = InfoBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (InfoBuffer != NULL)
|
|
{
|
|
if (InfoBuffer->All.UserName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->All.UserName.Buffer);
|
|
|
|
if (InfoBuffer->All.FullName.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->All.FullName.Buffer);
|
|
|
|
if (InfoBuffer->All.AdminComment.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->All.AdminComment.Buffer);
|
|
|
|
if (InfoBuffer->All.UserComment.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->All.UserComment.Buffer);
|
|
|
|
if (InfoBuffer->All.HomeDirectory.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->All.HomeDirectory.Buffer);
|
|
|
|
if (InfoBuffer->All.HomeDirectoryDrive.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->All.HomeDirectoryDrive.Buffer);
|
|
|
|
if (InfoBuffer->All.ScriptPath.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->All.ScriptPath.Buffer);
|
|
|
|
if (InfoBuffer->All.ProfilePath.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->All.ProfilePath.Buffer);
|
|
|
|
if (InfoBuffer->All.WorkStations.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->All.WorkStations.Buffer);
|
|
|
|
if (InfoBuffer->All.LogonHours.LogonHours != NULL)
|
|
midl_user_free(InfoBuffer->All.LogonHours.LogonHours);
|
|
|
|
if (InfoBuffer->All.Parameters.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->All.Parameters.Buffer);
|
|
|
|
if (InfoBuffer->All.LmOwfPassword.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->All.LmOwfPassword.Buffer);
|
|
|
|
if (InfoBuffer->All.NtOwfPassword.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->All.NtOwfPassword.Buffer);
|
|
|
|
if (InfoBuffer->All.PrivateData.Buffer != NULL)
|
|
midl_user_free(InfoBuffer->All.PrivateData.Buffer);
|
|
|
|
if (InfoBuffer->All.SecurityDescriptor.SecurityDescriptor != NULL)
|
|
midl_user_free(InfoBuffer->All.SecurityDescriptor.SecurityDescriptor);
|
|
|
|
midl_user_free(InfoBuffer);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 36 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrQueryInformationUser(IN SAMPR_HANDLE UserHandle,
|
|
IN USER_INFORMATION_CLASS UserInformationClass,
|
|
OUT PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
PSAM_DB_OBJECT UserObject;
|
|
ACCESS_MASK DesiredAccess;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrQueryInformationUser(%p %lu %p)\n",
|
|
UserHandle, UserInformationClass, Buffer);
|
|
|
|
switch (UserInformationClass)
|
|
{
|
|
case UserGeneralInformation:
|
|
case UserNameInformation:
|
|
case UserAccountNameInformation:
|
|
case UserFullNameInformation:
|
|
case UserPrimaryGroupInformation:
|
|
case UserAdminCommentInformation:
|
|
DesiredAccess = USER_READ_GENERAL;
|
|
break;
|
|
|
|
case UserLogonHoursInformation:
|
|
case UserHomeInformation:
|
|
case UserScriptInformation:
|
|
case UserProfileInformation:
|
|
case UserWorkStationsInformation:
|
|
DesiredAccess = USER_READ_LOGON;
|
|
break;
|
|
|
|
case UserControlInformation:
|
|
case UserExpiresInformation:
|
|
case UserParametersInformation:
|
|
DesiredAccess = USER_READ_ACCOUNT;
|
|
break;
|
|
|
|
case UserPreferencesInformation:
|
|
DesiredAccess = USER_READ_GENERAL |
|
|
USER_READ_PREFERENCES;
|
|
break;
|
|
|
|
case UserLogonInformation:
|
|
case UserAccountInformation:
|
|
DesiredAccess = USER_READ_GENERAL |
|
|
USER_READ_PREFERENCES |
|
|
USER_READ_LOGON |
|
|
USER_READ_ACCOUNT;
|
|
break;
|
|
|
|
case UserInternal1Information:
|
|
case UserInternal2Information:
|
|
case UserAllInformation:
|
|
DesiredAccess = 0;
|
|
break;
|
|
|
|
default:
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the domain handle */
|
|
Status = SampValidateDbObject(UserHandle,
|
|
SamDbUserObject,
|
|
DesiredAccess,
|
|
&UserObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
switch (UserInformationClass)
|
|
{
|
|
case UserGeneralInformation:
|
|
Status = SampQueryUserGeneral(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserPreferencesInformation:
|
|
Status = SampQueryUserPreferences(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserLogonInformation:
|
|
Status = SampQueryUserLogon(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserLogonHoursInformation:
|
|
Status = SampQueryUserLogonHours(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserAccountInformation:
|
|
Status = SampQueryUserAccount(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserNameInformation:
|
|
Status = SampQueryUserName(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserAccountNameInformation:
|
|
Status = SampQueryUserAccountName(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserFullNameInformation:
|
|
Status = SampQueryUserFullName(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserPrimaryGroupInformation:
|
|
Status = SampQueryUserPrimaryGroup(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserHomeInformation:
|
|
Status = SampQueryUserHome(UserObject,
|
|
Buffer);
|
|
|
|
case UserScriptInformation:
|
|
Status = SampQueryUserScript(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserProfileInformation:
|
|
Status = SampQueryUserProfile(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserAdminCommentInformation:
|
|
Status = SampQueryUserAdminComment(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserWorkStationsInformation:
|
|
Status = SampQueryUserWorkStations(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserControlInformation:
|
|
Status = SampQueryUserControl(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserExpiresInformation:
|
|
Status = SampQueryUserExpires(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserInternal1Information:
|
|
Status = SampQueryUserInternal1(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserInternal2Information:
|
|
Status = SampQueryUserInternal2(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserParametersInformation:
|
|
Status = SampQueryUserParameters(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserAllInformation:
|
|
Status = SampQueryUserAll(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
// case UserInternal4Information:
|
|
// case UserInternal5Information:
|
|
// case UserInternal4InformationNew:
|
|
// case UserInternal5InformationNew:
|
|
|
|
default:
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetUserName(PSAM_DB_OBJECT UserObject,
|
|
PRPC_UNICODE_STRING NewUserName)
|
|
{
|
|
UNICODE_STRING OldUserName = {0, 0, NULL};
|
|
NTSTATUS Status;
|
|
|
|
/* Check the account name */
|
|
Status = SampCheckAccountName(NewUserName, 20);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
Status = SampGetObjectAttributeString(UserObject,
|
|
L"Name",
|
|
(PRPC_UNICODE_STRING)&OldUserName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampGetObjectAttributeString failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
if (!RtlEqualUnicodeString(&OldUserName, (PCUNICODE_STRING)NewUserName, TRUE))
|
|
{
|
|
Status = SampCheckAccountNameInDomain(UserObject->ParentObject,
|
|
NewUserName->Buffer);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("User name \'%S\' already exists in domain (Status 0x%08lx)\n",
|
|
NewUserName->Buffer, Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
Status = SampSetAccountNameInDomain(UserObject->ParentObject,
|
|
L"Users",
|
|
NewUserName->Buffer,
|
|
UserObject->RelativeId);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampSetAccountNameInDomain failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Status = SampRemoveAccountNameFromDomain(UserObject->ParentObject,
|
|
L"Users",
|
|
OldUserName.Buffer);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampRemoveAccountNameFromDomain failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"Name",
|
|
NewUserName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampSetObjectAttribute failed (Status 0x%08lx)\n", Status);
|
|
}
|
|
|
|
done:
|
|
if (OldUserName.Buffer != NULL)
|
|
midl_user_free(OldUserName.Buffer);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetUserGeneral(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER Buffer)
|
|
{
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
FixedData.PrimaryGroupId = Buffer->General.PrimaryGroupId;
|
|
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedData,
|
|
Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
Status = SampSetUserName(UserObject,
|
|
&Buffer->General.UserName);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"FullName",
|
|
&Buffer->General.FullName);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"AdminComment",
|
|
&Buffer->General.AdminComment);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"UserComment",
|
|
&Buffer->General.UserComment);
|
|
|
|
done:
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetUserPreferences(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER Buffer)
|
|
{
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
FixedData.CountryCode = Buffer->Preferences.CountryCode;
|
|
FixedData.CodePage = Buffer->Preferences.CodePage;
|
|
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedData,
|
|
Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"UserComment",
|
|
&Buffer->Preferences.UserComment);
|
|
|
|
done:
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetUserPrimaryGroup(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER Buffer)
|
|
{
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
FixedData.PrimaryGroupId = Buffer->PrimaryGroup.PrimaryGroupId;
|
|
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedData,
|
|
Length);
|
|
|
|
done:
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetUserControl(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER Buffer)
|
|
{
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
FixedData.UserAccountControl = Buffer->Control.UserAccountControl;
|
|
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedData,
|
|
Length);
|
|
|
|
done:
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetUserExpires(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER Buffer)
|
|
{
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
FixedData.AccountExpires.LowPart = Buffer->Expires.AccountExpires.LowPart;
|
|
FixedData.AccountExpires.HighPart = Buffer->Expires.AccountExpires.HighPart;
|
|
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedData,
|
|
Length);
|
|
|
|
done:
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetUserInternal1(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER Buffer)
|
|
{
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
/* FIXME: Decrypt NT password */
|
|
/* FIXME: Decrypt LM password */
|
|
|
|
Status = SampSetUserPassword(UserObject,
|
|
&Buffer->Internal1.EncryptedNtOwfPassword,
|
|
Buffer->Internal1.NtPasswordPresent,
|
|
&Buffer->Internal1.EncryptedLmOwfPassword,
|
|
Buffer->Internal1.LmPasswordPresent);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
/* Get the fixed user attributes */
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
if (Buffer->Internal1.PasswordExpired)
|
|
{
|
|
/* The password was last set ages ago */
|
|
FixedData.PasswordLastSet.LowPart = 0;
|
|
FixedData.PasswordLastSet.HighPart = 0;
|
|
}
|
|
else
|
|
{
|
|
/* The password was last set right now */
|
|
Status = NtQuerySystemTime(&FixedData.PasswordLastSet);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
|
|
/* Set the fixed user attributes */
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedData,
|
|
Length);
|
|
|
|
done:
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetUserInternal2(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER Buffer)
|
|
{
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
/* Get the fixed user attributes */
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
if ((Buffer->Internal2.Flags & USER_LOGON_SUCCESS) &&
|
|
((Buffer->Internal2.Flags & ~USER_LOGON_SUCCESS) == 0))
|
|
{
|
|
/* Update the LastLogon time */
|
|
Status = NtQuerySystemTime(&FixedData.LastLogon);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
FixedData.LogonCount++;
|
|
FixedData.BadPasswordCount = 0;
|
|
}
|
|
|
|
if ((Buffer->Internal2.Flags & USER_LOGON_BAD_PASSWORD) &&
|
|
((Buffer->Internal2.Flags & ~USER_LOGON_BAD_PASSWORD) == 0))
|
|
{
|
|
/* Update the LastBadPasswordTime */
|
|
Status = NtQuerySystemTime(&FixedData.LastBadPasswordTime);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
FixedData.BadPasswordCount++;
|
|
}
|
|
|
|
/* Set the fixed user attributes */
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedData,
|
|
Length);
|
|
|
|
done:
|
|
return Status;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
SampSetUserAll(PSAM_DB_OBJECT UserObject,
|
|
PSAMPR_USER_INFO_BUFFER Buffer)
|
|
{
|
|
SAM_USER_FIXED_DATA FixedData;
|
|
ULONG Length = 0;
|
|
ULONG WhichFields;
|
|
PENCRYPTED_NT_OWF_PASSWORD NtPassword = NULL;
|
|
PENCRYPTED_LM_OWF_PASSWORD LmPassword = NULL;
|
|
BOOLEAN NtPasswordPresent = FALSE;
|
|
BOOLEAN LmPasswordPresent = FALSE;
|
|
BOOLEAN WriteFixedData = FALSE;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
WhichFields = Buffer->All.WhichFields;
|
|
|
|
/* Get the fixed size attributes */
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
if (WhichFields & USER_ALL_USERNAME)
|
|
{
|
|
Status = SampSetUserName(UserObject,
|
|
&Buffer->All.UserName);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_FULLNAME)
|
|
{
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"FullName",
|
|
&Buffer->All.FullName);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_ADMINCOMMENT)
|
|
{
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"AdminComment",
|
|
&Buffer->All.AdminComment);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_USERCOMMENT)
|
|
{
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"UserComment",
|
|
&Buffer->All.UserComment);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_HOMEDIRECTORY)
|
|
{
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"HomeDirectory",
|
|
&Buffer->All.HomeDirectory);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_HOMEDIRECTORYDRIVE)
|
|
{
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"HomeDirectoryDrive",
|
|
&Buffer->All.HomeDirectoryDrive);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_SCRIPTPATH)
|
|
{
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"ScriptPath",
|
|
&Buffer->All.ScriptPath);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_PROFILEPATH)
|
|
{
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"ProfilePath",
|
|
&Buffer->All.ProfilePath);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_WORKSTATIONS)
|
|
{
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"WorkStations",
|
|
&Buffer->All.WorkStations);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_PARAMETERS)
|
|
{
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"Parameters",
|
|
&Buffer->All.Parameters);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_LOGONHOURS)
|
|
{
|
|
Status = SampSetLogonHoursAttribute(UserObject,
|
|
&Buffer->All.LogonHours);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_PRIMARYGROUPID)
|
|
{
|
|
FixedData.PrimaryGroupId = Buffer->All.PrimaryGroupId;
|
|
WriteFixedData = TRUE;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_ACCOUNTEXPIRES)
|
|
{
|
|
FixedData.AccountExpires.LowPart = Buffer->All.AccountExpires.LowPart;
|
|
FixedData.AccountExpires.HighPart = Buffer->All.AccountExpires.HighPart;
|
|
WriteFixedData = TRUE;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_USERACCOUNTCONTROL)
|
|
{
|
|
FixedData.UserAccountControl = Buffer->All.UserAccountControl;
|
|
WriteFixedData = TRUE;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_COUNTRYCODE)
|
|
{
|
|
FixedData.CountryCode = Buffer->All.CountryCode;
|
|
WriteFixedData = TRUE;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_CODEPAGE)
|
|
{
|
|
FixedData.CodePage = Buffer->All.CodePage;
|
|
WriteFixedData = TRUE;
|
|
}
|
|
|
|
if (WhichFields & (USER_ALL_NTPASSWORDPRESENT |
|
|
USER_ALL_LMPASSWORDPRESENT))
|
|
{
|
|
if (WhichFields & USER_ALL_NTPASSWORDPRESENT)
|
|
{
|
|
NtPassword = (PENCRYPTED_NT_OWF_PASSWORD)Buffer->All.NtOwfPassword.Buffer;
|
|
NtPasswordPresent = Buffer->All.NtPasswordPresent;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_LMPASSWORDPRESENT)
|
|
{
|
|
LmPassword = (PENCRYPTED_LM_OWF_PASSWORD)Buffer->All.LmOwfPassword.Buffer;
|
|
LmPasswordPresent = Buffer->All.LmPasswordPresent;
|
|
}
|
|
|
|
Status = SampSetUserPassword(UserObject,
|
|
NtPassword,
|
|
NtPasswordPresent,
|
|
LmPassword,
|
|
LmPasswordPresent);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
/* The password has just been set */
|
|
Status = NtQuerySystemTime(&FixedData.PasswordLastSet);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
|
|
WriteFixedData = TRUE;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_PRIVATEDATA)
|
|
{
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"PrivateData",
|
|
&Buffer->All.PrivateData);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_PASSWORDEXPIRED)
|
|
{
|
|
if (Buffer->All.PasswordExpired)
|
|
{
|
|
/* The password was last set ages ago */
|
|
FixedData.PasswordLastSet.LowPart = 0;
|
|
FixedData.PasswordLastSet.HighPart = 0;
|
|
}
|
|
else
|
|
{
|
|
/* The password was last set right now */
|
|
Status = NtQuerySystemTime(&FixedData.PasswordLastSet);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
|
|
WriteFixedData = TRUE;
|
|
}
|
|
|
|
if (WhichFields & USER_ALL_SECURITYDESCRIPTOR)
|
|
{
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"SecDesc",
|
|
REG_BINARY,
|
|
Buffer->All.SecurityDescriptor.SecurityDescriptor,
|
|
Buffer->All.SecurityDescriptor.Length);
|
|
}
|
|
|
|
if (WriteFixedData != FALSE)
|
|
{
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedData,
|
|
Length);
|
|
if (!NT_SUCCESS(Status))
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 37 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrSetInformationUser(IN SAMPR_HANDLE UserHandle,
|
|
IN USER_INFORMATION_CLASS UserInformationClass,
|
|
IN PSAMPR_USER_INFO_BUFFER Buffer)
|
|
{
|
|
PSAM_DB_OBJECT UserObject;
|
|
ACCESS_MASK DesiredAccess;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrSetInformationUser(%p %lu %p)\n",
|
|
UserHandle, UserInformationClass, Buffer);
|
|
|
|
switch (UserInformationClass)
|
|
{
|
|
case UserLogonHoursInformation:
|
|
case UserNameInformation:
|
|
case UserAccountNameInformation:
|
|
case UserFullNameInformation:
|
|
case UserPrimaryGroupInformation:
|
|
case UserHomeInformation:
|
|
case UserScriptInformation:
|
|
case UserProfileInformation:
|
|
case UserAdminCommentInformation:
|
|
case UserWorkStationsInformation:
|
|
case UserControlInformation:
|
|
case UserExpiresInformation:
|
|
case UserParametersInformation:
|
|
DesiredAccess = USER_WRITE_ACCOUNT;
|
|
break;
|
|
|
|
case UserGeneralInformation:
|
|
DesiredAccess = USER_WRITE_ACCOUNT |
|
|
USER_WRITE_PREFERENCES;
|
|
break;
|
|
|
|
case UserPreferencesInformation:
|
|
DesiredAccess = USER_WRITE_PREFERENCES;
|
|
break;
|
|
|
|
case UserSetPasswordInformation:
|
|
case UserInternal1Information:
|
|
DesiredAccess = USER_FORCE_PASSWORD_CHANGE;
|
|
break;
|
|
|
|
case UserAllInformation:
|
|
case UserInternal2Information:
|
|
DesiredAccess = 0; /* FIXME */
|
|
break;
|
|
|
|
default:
|
|
return STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the domain handle */
|
|
Status = SampValidateDbObject(UserHandle,
|
|
SamDbUserObject,
|
|
DesiredAccess,
|
|
&UserObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
switch (UserInformationClass)
|
|
{
|
|
case UserGeneralInformation:
|
|
Status = SampSetUserGeneral(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserPreferencesInformation:
|
|
Status = SampSetUserPreferences(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserLogonHoursInformation:
|
|
Status = SampSetLogonHoursAttribute(UserObject,
|
|
&Buffer->LogonHours.LogonHours);
|
|
break;
|
|
|
|
case UserNameInformation:
|
|
Status = SampSetUserName(UserObject,
|
|
&Buffer->Name.UserName);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"FullName",
|
|
&Buffer->Name.FullName);
|
|
break;
|
|
|
|
case UserAccountNameInformation:
|
|
Status = SampSetUserName(UserObject,
|
|
&Buffer->AccountName.UserName);
|
|
break;
|
|
|
|
case UserFullNameInformation:
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"FullName",
|
|
&Buffer->FullName.FullName);
|
|
break;
|
|
|
|
case UserPrimaryGroupInformation:
|
|
Status = SampSetUserPrimaryGroup(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserHomeInformation:
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"HomeDirectory",
|
|
&Buffer->Home.HomeDirectory);
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"HomeDirectoryDrive",
|
|
&Buffer->Home.HomeDirectoryDrive);
|
|
break;
|
|
|
|
case UserScriptInformation:
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"ScriptPath",
|
|
&Buffer->Script.ScriptPath);
|
|
break;
|
|
|
|
case UserProfileInformation:
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"ProfilePath",
|
|
&Buffer->Profile.ProfilePath);
|
|
break;
|
|
|
|
case UserAdminCommentInformation:
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"AdminComment",
|
|
&Buffer->AdminComment.AdminComment);
|
|
break;
|
|
|
|
case UserWorkStationsInformation:
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"WorkStations",
|
|
&Buffer->WorkStations.WorkStations);
|
|
break;
|
|
|
|
case UserSetPasswordInformation:
|
|
TRACE("Password: %S\n", Buffer->SetPassword.Password.Buffer);
|
|
TRACE("PasswordExpired: %d\n", Buffer->SetPassword.PasswordExpired);
|
|
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"Password",
|
|
&Buffer->SetPassword.Password);
|
|
break;
|
|
|
|
case UserControlInformation:
|
|
Status = SampSetUserControl(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserExpiresInformation:
|
|
Status = SampSetUserExpires(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserInternal1Information:
|
|
Status = SampSetUserInternal1(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserInternal2Information:
|
|
Status = SampSetUserInternal2(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
case UserParametersInformation:
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"Parameters",
|
|
&Buffer->Parameters.Parameters);
|
|
break;
|
|
|
|
case UserAllInformation:
|
|
Status = SampSetUserAll(UserObject,
|
|
Buffer);
|
|
break;
|
|
|
|
// case UserInternal4Information:
|
|
// case UserInternal5Information:
|
|
// case UserInternal4InformationNew:
|
|
// case UserInternal5InformationNew:
|
|
|
|
default:
|
|
Status = STATUS_INVALID_INFO_CLASS;
|
|
}
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 38 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrChangePasswordUser(IN SAMPR_HANDLE UserHandle,
|
|
IN unsigned char LmPresent,
|
|
IN PENCRYPTED_LM_OWF_PASSWORD OldLmEncryptedWithNewLm,
|
|
IN PENCRYPTED_LM_OWF_PASSWORD NewLmEncryptedWithOldLm,
|
|
IN unsigned char NtPresent,
|
|
IN PENCRYPTED_NT_OWF_PASSWORD OldNtEncryptedWithNewNt,
|
|
IN PENCRYPTED_NT_OWF_PASSWORD NewNtEncryptedWithOldNt,
|
|
IN unsigned char NtCrossEncryptionPresent,
|
|
IN PENCRYPTED_NT_OWF_PASSWORD NewNtEncryptedWithNewLm,
|
|
IN unsigned char LmCrossEncryptionPresent,
|
|
IN PENCRYPTED_LM_OWF_PASSWORD NewLmEncryptedWithNewNt)
|
|
{
|
|
ENCRYPTED_LM_OWF_PASSWORD StoredLmPassword;
|
|
ENCRYPTED_NT_OWF_PASSWORD StoredNtPassword;
|
|
LM_OWF_PASSWORD OldLmPassword;
|
|
LM_OWF_PASSWORD NewLmPassword;
|
|
NT_OWF_PASSWORD OldNtPassword;
|
|
NT_OWF_PASSWORD NewNtPassword;
|
|
BOOLEAN StoredLmPresent = FALSE;
|
|
BOOLEAN StoredNtPresent = FALSE;
|
|
BOOLEAN StoredLmEmpty = TRUE;
|
|
BOOLEAN StoredNtEmpty = TRUE;
|
|
PSAM_DB_OBJECT UserObject;
|
|
ULONG Length;
|
|
SAM_USER_FIXED_DATA UserFixedData;
|
|
SAM_DOMAIN_FIXED_DATA DomainFixedData;
|
|
LARGE_INTEGER SystemTime;
|
|
NTSTATUS Status;
|
|
|
|
DBG_UNREFERENCED_LOCAL_VARIABLE(StoredLmPresent);
|
|
DBG_UNREFERENCED_LOCAL_VARIABLE(StoredNtPresent);
|
|
DBG_UNREFERENCED_LOCAL_VARIABLE(StoredLmEmpty);
|
|
|
|
TRACE("SamrChangePasswordUser(%p %u %p %p %u %p %p %u %p %u %p)\n",
|
|
UserHandle, LmPresent, OldLmEncryptedWithNewLm, NewLmEncryptedWithOldLm,
|
|
NtPresent, OldNtEncryptedWithNewNt, NewNtEncryptedWithOldNt, NtCrossEncryptionPresent,
|
|
NewNtEncryptedWithNewLm, LmCrossEncryptionPresent, NewLmEncryptedWithNewNt);
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the user handle */
|
|
Status = SampValidateDbObject(UserHandle,
|
|
SamDbUserObject,
|
|
USER_CHANGE_PASSWORD,
|
|
&UserObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the current time */
|
|
Status = NtQuerySystemTime(&SystemTime);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("NtQuerySystemTime failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Retrieve the LM password */
|
|
Length = sizeof(ENCRYPTED_LM_OWF_PASSWORD);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"LMPwd",
|
|
NULL,
|
|
&StoredLmPassword,
|
|
&Length);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (Length == sizeof(ENCRYPTED_LM_OWF_PASSWORD))
|
|
{
|
|
StoredLmPresent = TRUE;
|
|
if (!RtlEqualMemory(&StoredLmPassword,
|
|
&EmptyLmHash,
|
|
sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
|
|
StoredLmEmpty = FALSE;
|
|
}
|
|
}
|
|
|
|
/* Retrieve the NT password */
|
|
Length = sizeof(ENCRYPTED_NT_OWF_PASSWORD);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"NTPwd",
|
|
NULL,
|
|
&StoredNtPassword,
|
|
&Length);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (Length == sizeof(ENCRYPTED_NT_OWF_PASSWORD))
|
|
{
|
|
StoredNtPresent = TRUE;
|
|
if (!RtlEqualMemory(&StoredNtPassword,
|
|
&EmptyNtHash,
|
|
sizeof(ENCRYPTED_NT_OWF_PASSWORD)))
|
|
StoredNtEmpty = FALSE;
|
|
}
|
|
}
|
|
|
|
/* Retrieve the fixed size user data */
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
&UserFixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampGetObjectAttribute failed to retrieve the fixed user data (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Check if we can change the password at this time */
|
|
if ((StoredLmEmpty == FALSE) || (StoredNtEmpty == FALSE))
|
|
{
|
|
/* Get fixed domain data */
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject->ParentObject,
|
|
L"F",
|
|
NULL,
|
|
&DomainFixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampGetObjectAttribute failed to retrieve the fixed domain data (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
if (DomainFixedData.MinPasswordAge.QuadPart > 0)
|
|
{
|
|
if (SystemTime.QuadPart < (UserFixedData.PasswordLastSet.QuadPart + DomainFixedData.MinPasswordAge.QuadPart))
|
|
{
|
|
Status = STATUS_ACCOUNT_RESTRICTION;
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Decrypt the LM passwords, if present */
|
|
if (LmPresent)
|
|
{
|
|
Status = SystemFunction013((const BYTE *)NewLmEncryptedWithOldLm,
|
|
(const BYTE *)&StoredLmPassword,
|
|
(LPBYTE)&NewLmPassword);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SystemFunction013 failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Status = SystemFunction013((const BYTE *)OldLmEncryptedWithNewLm,
|
|
(const BYTE *)&NewLmPassword,
|
|
(LPBYTE)&OldLmPassword);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SystemFunction013 failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Decrypt the NT passwords, if present */
|
|
if (NtPresent)
|
|
{
|
|
Status = SystemFunction013((const BYTE *)NewNtEncryptedWithOldNt,
|
|
(const BYTE *)&StoredNtPassword,
|
|
(LPBYTE)&NewNtPassword);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SystemFunction013 failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Status = SystemFunction013((const BYTE *)OldNtEncryptedWithNewNt,
|
|
(const BYTE *)&NewNtPassword,
|
|
(LPBYTE)&OldNtPassword);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SystemFunction013 failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/* Check if the old passwords match the stored ones */
|
|
if (NtPresent)
|
|
{
|
|
if (LmPresent)
|
|
{
|
|
if (!RtlEqualMemory(&StoredLmPassword,
|
|
&OldLmPassword,
|
|
sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
|
|
{
|
|
TRACE("Old LM Password does not match!\n");
|
|
Status = STATUS_WRONG_PASSWORD;
|
|
}
|
|
else
|
|
{
|
|
if (!RtlEqualMemory(&StoredNtPassword,
|
|
&OldNtPassword,
|
|
sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
|
|
{
|
|
TRACE("Old NT Password does not match!\n");
|
|
Status = STATUS_WRONG_PASSWORD;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!RtlEqualMemory(&StoredNtPassword,
|
|
&OldNtPassword,
|
|
sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
|
|
{
|
|
TRACE("Old NT Password does not match!\n");
|
|
Status = STATUS_WRONG_PASSWORD;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (LmPresent)
|
|
{
|
|
if (!RtlEqualMemory(&StoredLmPassword,
|
|
&OldLmPassword,
|
|
sizeof(ENCRYPTED_LM_OWF_PASSWORD)))
|
|
{
|
|
TRACE("Old LM Password does not match!\n");
|
|
Status = STATUS_WRONG_PASSWORD;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
/* Store the new password hashes */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = SampSetUserPassword(UserObject,
|
|
&NewNtPassword,
|
|
NtPresent,
|
|
&NewLmPassword,
|
|
LmPresent);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Update PasswordLastSet */
|
|
UserFixedData.PasswordLastSet.QuadPart = SystemTime.QuadPart;
|
|
|
|
/* Set the fixed size user data */
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&UserFixedData,
|
|
Length);
|
|
}
|
|
}
|
|
|
|
if (Status == STATUS_WRONG_PASSWORD)
|
|
{
|
|
/* Update BadPasswordCount and LastBadPasswordTime */
|
|
UserFixedData.BadPasswordCount++;
|
|
UserFixedData.LastBadPasswordTime.QuadPart = SystemTime.QuadPart;
|
|
|
|
/* Set the fixed size user data */
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&UserFixedData,
|
|
Length);
|
|
}
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 39 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrGetGroupsForUser(IN SAMPR_HANDLE UserHandle,
|
|
OUT PSAMPR_GET_GROUPS_BUFFER *Groups)
|
|
{
|
|
PSAMPR_GET_GROUPS_BUFFER GroupsBuffer = NULL;
|
|
PSAM_DB_OBJECT UserObject;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrGetGroupsForUser(%p %p)\n",
|
|
UserHandle, Groups);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the user handle */
|
|
Status = SampValidateDbObject(UserHandle,
|
|
SamDbUserObject,
|
|
USER_LIST_GROUPS,
|
|
&UserObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Allocate the groups buffer */
|
|
GroupsBuffer = midl_user_allocate(sizeof(SAMPR_GET_GROUPS_BUFFER));
|
|
if (GroupsBuffer == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* Get the size of the Groups attribute.
|
|
* Do not check the status code because in case of an error
|
|
* Length will be 0. And that is all we need.
|
|
*/
|
|
SampGetObjectAttribute(UserObject,
|
|
L"Groups",
|
|
NULL,
|
|
NULL,
|
|
&Length);
|
|
|
|
/* If there is no Groups attribute, return a groups buffer without an array */
|
|
if (Length == 0)
|
|
{
|
|
GroupsBuffer->MembershipCount = 0;
|
|
GroupsBuffer->Groups = NULL;
|
|
|
|
*Groups = GroupsBuffer;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto done;
|
|
}
|
|
|
|
/* Allocate a buffer for the Groups attribute */
|
|
GroupsBuffer->Groups = midl_user_allocate(Length);
|
|
if (GroupsBuffer->Groups == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
/* Retrieve the Grous attribute */
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"Groups",
|
|
NULL,
|
|
GroupsBuffer->Groups,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampGetObjectAttribute failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Calculate the membership count */
|
|
GroupsBuffer->MembershipCount = Length / sizeof(GROUP_MEMBERSHIP);
|
|
|
|
/* Return the groups buffer to the caller */
|
|
*Groups = GroupsBuffer;
|
|
|
|
done:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (GroupsBuffer != NULL)
|
|
{
|
|
if (GroupsBuffer->Groups != NULL)
|
|
midl_user_free(GroupsBuffer->Groups);
|
|
|
|
midl_user_free(GroupsBuffer);
|
|
}
|
|
}
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 40 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrQueryDisplayInformation(IN SAMPR_HANDLE DomainHandle,
|
|
IN DOMAIN_DISPLAY_INFORMATION DisplayInformationClass,
|
|
IN unsigned long Index,
|
|
IN unsigned long EntryCount,
|
|
IN unsigned long PreferredMaximumLength,
|
|
OUT unsigned long *TotalAvailable,
|
|
OUT unsigned long *TotalReturned,
|
|
OUT PSAMPR_DISPLAY_INFO_BUFFER Buffer)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* Function 41 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrGetDisplayEnumerationIndex(IN SAMPR_HANDLE DomainHandle,
|
|
IN DOMAIN_DISPLAY_INFORMATION DisplayInformationClass,
|
|
IN PRPC_UNICODE_STRING Prefix,
|
|
OUT unsigned long *Index)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* Function 42 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrTestPrivateFunctionsDomain(IN SAMPR_HANDLE DomainHandle)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* Function 43 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrTestPrivateFunctionsUser(IN SAMPR_HANDLE UserHandle)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
/* Function 44 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrGetUserDomainPasswordInformation(IN SAMPR_HANDLE UserHandle,
|
|
OUT PUSER_DOMAIN_PASSWORD_INFORMATION PasswordInformation)
|
|
{
|
|
SAM_DOMAIN_FIXED_DATA DomainFixedData;
|
|
SAM_USER_FIXED_DATA UserFixedData;
|
|
PSAM_DB_OBJECT DomainObject;
|
|
PSAM_DB_OBJECT UserObject;
|
|
ULONG Length = 0;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrGetUserDomainPasswordInformation(%p %p)\n",
|
|
UserHandle, PasswordInformation);
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the user handle */
|
|
Status = SampValidateDbObject(UserHandle,
|
|
SamDbUserObject,
|
|
0,
|
|
&UserObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Validate the domain object */
|
|
Status = SampValidateDbObject((SAMPR_HANDLE)UserObject->ParentObject,
|
|
SamDbDomainObject,
|
|
DOMAIN_READ_PASSWORD_PARAMETERS,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get fixed user data */
|
|
Length = sizeof(SAM_USER_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(UserObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&UserFixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampGetObjectAttribute failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
if ((UserObject->RelativeId == DOMAIN_USER_RID_KRBTGT) ||
|
|
(UserFixedData.UserAccountControl & (USER_INTERDOMAIN_TRUST_ACCOUNT |
|
|
USER_WORKSTATION_TRUST_ACCOUNT |
|
|
USER_SERVER_TRUST_ACCOUNT)))
|
|
{
|
|
PasswordInformation->MinPasswordLength = 0;
|
|
PasswordInformation->PasswordProperties = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Get fixed domain data */
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&DomainFixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampGetObjectAttribute failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
PasswordInformation->MinPasswordLength = DomainFixedData.MinPasswordLength;
|
|
PasswordInformation->PasswordProperties = DomainFixedData.PasswordProperties;
|
|
}
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* Function 45 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrRemoveMemberFromForeignDomain(IN SAMPR_HANDLE DomainHandle,
|
|
IN PRPC_SID MemberSid)
|
|
{
|
|
PSAM_DB_OBJECT DomainObject;
|
|
ULONG Rid = 0;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrRemoveMemberFromForeignDomain(%p %p)\n",
|
|
DomainHandle, MemberSid);
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the domain object */
|
|
Status = SampValidateDbObject(DomainHandle,
|
|
SamDbDomainObject,
|
|
DOMAIN_LOOKUP,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampValidateDbObject failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Retrieve the RID from the MemberSID */
|
|
Status = SampGetRidFromSid((PSID)MemberSid,
|
|
&Rid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampGetRidFromSid failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Fail, if the RID represents a special account */
|
|
if (Rid < 1000)
|
|
{
|
|
TRACE("Cannot remove a special account (RID: %lu)\n", Rid);
|
|
Status = STATUS_SPECIAL_ACCOUNT;
|
|
goto done;
|
|
}
|
|
|
|
/* Remove the member from all aliases in the domain */
|
|
Status = SampRemoveMemberFromAllAliases(DomainObject,
|
|
MemberSid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampRemoveMemberFromAllAliases failed with status 0x%08lx\n", Status);
|
|
}
|
|
|
|
done:
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 46 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrQueryInformationDomain2(IN SAMPR_HANDLE DomainHandle,
|
|
IN DOMAIN_INFORMATION_CLASS DomainInformationClass,
|
|
OUT PSAMPR_DOMAIN_INFO_BUFFER *Buffer)
|
|
{
|
|
TRACE("SamrQueryInformationDomain2(%p %lu %p)\n",
|
|
DomainHandle, DomainInformationClass, Buffer);
|
|
|
|
return SamrQueryInformationDomain(DomainHandle,
|
|
DomainInformationClass,
|
|
Buffer);
|
|
}
|
|
|
|
|
|
/* Function 47 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrQueryInformationUser2(IN SAMPR_HANDLE UserHandle,
|
|
IN USER_INFORMATION_CLASS UserInformationClass,
|
|
OUT PSAMPR_USER_INFO_BUFFER *Buffer)
|
|
{
|
|
TRACE("SamrQueryInformationUser2(%p %lu %p)\n",
|
|
UserHandle, UserInformationClass, Buffer);
|
|
|
|
return SamrQueryInformationUser(UserHandle,
|
|
UserInformationClass,
|
|
Buffer);
|
|
}
|
|
|
|
|
|
/* Function 48 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrQueryDisplayInformation2(IN SAMPR_HANDLE DomainHandle,
|
|
IN DOMAIN_DISPLAY_INFORMATION DisplayInformationClass,
|
|
IN unsigned long Index,
|
|
IN unsigned long EntryCount,
|
|
IN unsigned long PreferredMaximumLength,
|
|
OUT unsigned long *TotalAvailable,
|
|
OUT unsigned long *TotalReturned,
|
|
OUT PSAMPR_DISPLAY_INFO_BUFFER Buffer)
|
|
{
|
|
TRACE("SamrQueryDisplayInformation2(%p %lu %lu %lu %lu %p %p %p)\n",
|
|
DomainHandle, DisplayInformationClass, Index,
|
|
EntryCount, PreferredMaximumLength, TotalAvailable,
|
|
TotalReturned, Buffer);
|
|
|
|
return SamrQueryDisplayInformation(DomainHandle,
|
|
DisplayInformationClass,
|
|
Index,
|
|
EntryCount,
|
|
PreferredMaximumLength,
|
|
TotalAvailable,
|
|
TotalReturned,
|
|
Buffer);
|
|
}
|
|
|
|
|
|
/* Function 49 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrGetDisplayEnumerationIndex2(IN SAMPR_HANDLE DomainHandle,
|
|
IN DOMAIN_DISPLAY_INFORMATION DisplayInformationClass,
|
|
IN PRPC_UNICODE_STRING Prefix,
|
|
OUT unsigned long *Index)
|
|
{
|
|
TRACE("SamrGetDisplayEnumerationIndex2(%p %lu %p %p)\n",
|
|
DomainHandle, DisplayInformationClass, Prefix, Index);
|
|
|
|
return SamrGetDisplayEnumerationIndex(DomainHandle,
|
|
DisplayInformationClass,
|
|
Prefix,
|
|
Index);
|
|
}
|
|
|
|
|
|
/* Function 50 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrCreateUser2InDomain(IN SAMPR_HANDLE DomainHandle,
|
|
IN PRPC_UNICODE_STRING Name,
|
|
IN unsigned long AccountType,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
OUT SAMPR_HANDLE *UserHandle,
|
|
OUT unsigned long *GrantedAccess,
|
|
OUT unsigned long *RelativeId)
|
|
{
|
|
SAM_DOMAIN_FIXED_DATA FixedDomainData;
|
|
SAM_USER_FIXED_DATA FixedUserData;
|
|
PSAM_DB_OBJECT DomainObject;
|
|
PSAM_DB_OBJECT UserObject;
|
|
GROUP_MEMBERSHIP GroupMembership;
|
|
UCHAR LogonHours[23];
|
|
ULONG ulSize;
|
|
ULONG ulRid;
|
|
WCHAR szRid[9];
|
|
PSECURITY_DESCRIPTOR Sd = NULL;
|
|
ULONG SdSize = 0;
|
|
PSID UserSid = NULL;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrCreateUserInDomain(%p %p %lx %p %p)\n",
|
|
DomainHandle, Name, DesiredAccess, UserHandle, RelativeId);
|
|
|
|
if (Name == NULL ||
|
|
Name->Length == 0 ||
|
|
Name->Buffer == NULL ||
|
|
UserHandle == NULL ||
|
|
RelativeId == NULL)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Check for valid account type */
|
|
if (AccountType != USER_NORMAL_ACCOUNT &&
|
|
AccountType != USER_WORKSTATION_TRUST_ACCOUNT &&
|
|
AccountType != USER_INTERDOMAIN_TRUST_ACCOUNT &&
|
|
AccountType != USER_SERVER_TRUST_ACCOUNT &&
|
|
AccountType != USER_TEMP_DUPLICATE_ACCOUNT)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
/* Map generic access rights */
|
|
RtlMapGenericMask(&DesiredAccess,
|
|
&UserMapping);
|
|
|
|
RtlAcquireResourceExclusive(&SampResource,
|
|
TRUE);
|
|
|
|
/* Validate the domain handle */
|
|
Status = SampValidateDbObject(DomainHandle,
|
|
SamDbDomainObject,
|
|
DOMAIN_CREATE_USER,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Check the user account name */
|
|
Status = SampCheckAccountName(Name, 20);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampCheckAccountName failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Check if the user name already exists in the domain */
|
|
Status = SampCheckAccountNameInDomain(DomainObject,
|
|
Name->Buffer);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("User name \'%S\' already exists in domain (Status 0x%08lx)\n",
|
|
Name->Buffer, Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Get the fixed domain attributes */
|
|
ulSize = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
(PVOID)&FixedDomainData,
|
|
&ulSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Increment the NextRid attribute */
|
|
ulRid = FixedDomainData.NextRid;
|
|
FixedDomainData.NextRid++;
|
|
|
|
TRACE("RID: %lx\n", ulRid);
|
|
|
|
/* Create the user SID */
|
|
Status = SampCreateAccountSid(DomainObject,
|
|
ulRid,
|
|
&UserSid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampCreateAccountSid failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Create the security descriptor */
|
|
Status = SampCreateUserSD(UserSid,
|
|
&Sd,
|
|
&SdSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampCreateUserSD failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Store the fixed domain attributes */
|
|
Status = SampSetObjectAttribute(DomainObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
&FixedDomainData,
|
|
ulSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Convert the RID into a string (hex) */
|
|
swprintf(szRid, L"%08lX", ulRid);
|
|
|
|
/* Create the user object */
|
|
Status = SampCreateDbObject(DomainObject,
|
|
L"Users",
|
|
szRid,
|
|
ulRid,
|
|
SamDbUserObject,
|
|
DesiredAccess,
|
|
&UserObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Add the account name for the user object */
|
|
Status = SampSetAccountNameInDomain(DomainObject,
|
|
L"Users",
|
|
Name->Buffer,
|
|
ulRid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Initialize fixed user data */
|
|
FixedUserData.Version = 1;
|
|
FixedUserData.Reserved = 0;
|
|
FixedUserData.LastLogon.QuadPart = 0;
|
|
FixedUserData.LastLogoff.QuadPart = 0;
|
|
FixedUserData.PasswordLastSet.QuadPart = 0;
|
|
FixedUserData.AccountExpires.LowPart = MAXULONG;
|
|
FixedUserData.AccountExpires.HighPart = MAXLONG;
|
|
FixedUserData.LastBadPasswordTime.QuadPart = 0;
|
|
FixedUserData.UserId = ulRid;
|
|
FixedUserData.PrimaryGroupId = DOMAIN_GROUP_RID_USERS;
|
|
FixedUserData.UserAccountControl = USER_ACCOUNT_DISABLED |
|
|
USER_PASSWORD_NOT_REQUIRED |
|
|
AccountType;
|
|
FixedUserData.CountryCode = 0;
|
|
FixedUserData.CodePage = 0;
|
|
FixedUserData.BadPasswordCount = 0;
|
|
FixedUserData.LogonCount = 0;
|
|
FixedUserData.AdminCount = 0;
|
|
FixedUserData.OperatorCount = 0;
|
|
|
|
/* Set fixed user data attribute */
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"F",
|
|
REG_BINARY,
|
|
(LPVOID)&FixedUserData,
|
|
sizeof(SAM_USER_FIXED_DATA));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the Name attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"Name",
|
|
Name);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the FullName attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"FullName",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the HomeDirectory attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"HomeDirectory",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the HomeDirectoryDrive attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"HomeDirectoryDrive",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the ScriptPath attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"ScriptPath",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the ProfilePath attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"ProfilePath",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the AdminComment attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"AdminComment",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the UserComment attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"UserComment",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the WorkStations attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"WorkStations",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the Parameters attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"Parameters",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set LogonHours attribute*/
|
|
*((PUSHORT)LogonHours) = 168;
|
|
memset(&(LogonHours[2]), 0xff, 21);
|
|
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"LogonHours",
|
|
REG_BINARY,
|
|
&LogonHours,
|
|
sizeof(LogonHours));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set Groups attribute*/
|
|
GroupMembership.RelativeId = DOMAIN_GROUP_RID_USERS;
|
|
GroupMembership.Attributes = SE_GROUP_MANDATORY |
|
|
SE_GROUP_ENABLED |
|
|
SE_GROUP_ENABLED_BY_DEFAULT;
|
|
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"Groups",
|
|
REG_BINARY,
|
|
&GroupMembership,
|
|
sizeof(GROUP_MEMBERSHIP));
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set LMPwd attribute*/
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"LMPwd",
|
|
REG_BINARY,
|
|
NULL,
|
|
0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set NTPwd attribute*/
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"NTPwd",
|
|
REG_BINARY,
|
|
NULL,
|
|
0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set LMPwdHistory attribute*/
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"LMPwdHistory",
|
|
REG_BINARY,
|
|
NULL,
|
|
0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set NTPwdHistory attribute*/
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"NTPwdHistory",
|
|
REG_BINARY,
|
|
NULL,
|
|
0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the PrivateData attribute */
|
|
Status = SampSetObjectAttributeString(UserObject,
|
|
L"PrivateData",
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
/* Set the SecDesc attribute*/
|
|
Status = SampSetObjectAttribute(UserObject,
|
|
L"SecDesc",
|
|
REG_BINARY,
|
|
Sd,
|
|
SdSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("failed with status 0x%08lx\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*UserHandle = (SAMPR_HANDLE)UserObject;
|
|
*RelativeId = ulRid;
|
|
*GrantedAccess = UserObject->Access;
|
|
}
|
|
|
|
done:
|
|
if (Sd != NULL)
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, Sd);
|
|
|
|
if (UserSid != NULL)
|
|
RtlFreeHeap(RtlGetProcessHeap(), 0, UserSid);
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
TRACE("returns with status 0x%08lx\n", Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 51 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrQueryDisplayInformation3(IN SAMPR_HANDLE DomainHandle,
|
|
IN DOMAIN_DISPLAY_INFORMATION DisplayInformationClass,
|
|
IN unsigned long Index,
|
|
IN unsigned long EntryCount,
|
|
IN unsigned long PreferredMaximumLength,
|
|
OUT unsigned long *TotalAvailable,
|
|
OUT unsigned long *TotalReturned,
|
|
OUT PSAMPR_DISPLAY_INFO_BUFFER Buffer)
|
|
{
|
|
TRACE("SamrQueryDisplayInformation3(%p %lu %lu %lu %lu %p %p %p)\n",
|
|
DomainHandle, DisplayInformationClass, Index,
|
|
EntryCount, PreferredMaximumLength, TotalAvailable,
|
|
TotalReturned, Buffer);
|
|
|
|
return SamrQueryDisplayInformation(DomainHandle,
|
|
DisplayInformationClass,
|
|
Index,
|
|
EntryCount,
|
|
PreferredMaximumLength,
|
|
TotalAvailable,
|
|
TotalReturned,
|
|
Buffer);
|
|
}
|
|
|
|
|
|
/* Function 52 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrAddMultipleMembersToAlias(IN SAMPR_HANDLE AliasHandle,
|
|
IN PSAMPR_PSID_ARRAY MembersBuffer)
|
|
{
|
|
ULONG i;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
TRACE("SamrAddMultipleMembersToAlias(%p %p)\n",
|
|
AliasHandle, MembersBuffer);
|
|
|
|
for (i = 0; i < MembersBuffer->Count; i++)
|
|
{
|
|
Status = SamrAddMemberToAlias(AliasHandle,
|
|
((PSID *)MembersBuffer->Sids)[i]);
|
|
|
|
if (Status == STATUS_MEMBER_IN_ALIAS)
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 53 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrRemoveMultipleMembersFromAlias(IN SAMPR_HANDLE AliasHandle,
|
|
IN PSAMPR_PSID_ARRAY MembersBuffer)
|
|
{
|
|
ULONG i;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
TRACE("SamrRemoveMultipleMembersFromAlias(%p %p)\n",
|
|
AliasHandle, MembersBuffer);
|
|
|
|
for (i = 0; i < MembersBuffer->Count; i++)
|
|
{
|
|
Status = SamrRemoveMemberFromAlias(AliasHandle,
|
|
((PSID *)MembersBuffer->Sids)[i]);
|
|
|
|
if (Status == STATUS_MEMBER_IN_ALIAS)
|
|
Status = STATUS_SUCCESS;
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
break;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 54 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrOemChangePasswordUser2(IN handle_t BindingHandle,
|
|
IN PRPC_STRING ServerName,
|
|
IN PRPC_STRING UserName,
|
|
IN PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldLm,
|
|
IN PENCRYPTED_LM_OWF_PASSWORD OldLmOwfPasswordEncryptedWithNewLm)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* Function 55 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrUnicodeChangePasswordUser2(IN handle_t BindingHandle,
|
|
IN PRPC_UNICODE_STRING ServerName,
|
|
IN PRPC_UNICODE_STRING UserName,
|
|
IN PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldNt,
|
|
IN PENCRYPTED_NT_OWF_PASSWORD OldNtOwfPasswordEncryptedWithNewNt,
|
|
IN unsigned char LmPresent,
|
|
IN PSAMPR_ENCRYPTED_USER_PASSWORD NewPasswordEncryptedWithOldLm,
|
|
IN PENCRYPTED_LM_OWF_PASSWORD OldLmOwfPasswordEncryptedWithNewNt)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
/* Function 56 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrGetDomainPasswordInformation(IN handle_t BindingHandle,
|
|
IN PRPC_UNICODE_STRING Unused,
|
|
OUT PUSER_DOMAIN_PASSWORD_INFORMATION PasswordInformation)
|
|
{
|
|
SAMPR_HANDLE ServerHandle = NULL;
|
|
PSAM_DB_OBJECT DomainObject = NULL;
|
|
SAM_DOMAIN_FIXED_DATA FixedData;
|
|
ULONG Length;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrGetDomainPasswordInformation(%p %p %p)\n",
|
|
BindingHandle, Unused, PasswordInformation);
|
|
|
|
Status = SamrConnect(NULL,
|
|
&ServerHandle,
|
|
SAM_SERVER_LOOKUP_DOMAIN);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SamrConnect() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Status = SampOpenDbObject((PSAM_DB_OBJECT)ServerHandle,
|
|
L"Domains",
|
|
L"Account",
|
|
0,
|
|
SamDbDomainObject,
|
|
DOMAIN_READ_PASSWORD_PARAMETERS,
|
|
&DomainObject);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampOpenDbObject() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
Length = sizeof(SAM_DOMAIN_FIXED_DATA);
|
|
Status = SampGetObjectAttribute(DomainObject,
|
|
L"F",
|
|
NULL,
|
|
&FixedData,
|
|
&Length);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
TRACE("SampGetObjectAttribute() failed (Status 0x%08lx)\n", Status);
|
|
goto done;
|
|
}
|
|
|
|
PasswordInformation->MinPasswordLength = FixedData.MinPasswordLength;
|
|
PasswordInformation->PasswordProperties = FixedData.PasswordProperties;
|
|
|
|
done:
|
|
if (DomainObject != NULL)
|
|
SampCloseDbObject(DomainObject);
|
|
|
|
if (ServerHandle != NULL)
|
|
SamrCloseHandle(ServerHandle);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 57 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrConnect2(IN PSAMPR_SERVER_NAME ServerName,
|
|
OUT SAMPR_HANDLE *ServerHandle,
|
|
IN ACCESS_MASK DesiredAccess)
|
|
{
|
|
SAMPR_REVISION_INFO InRevisionInfo, OutRevisionInfo;
|
|
ULONG OutVersion;
|
|
|
|
TRACE("SamrConnect2(%p %p %lx)\n",
|
|
ServerName, ServerHandle, DesiredAccess);
|
|
|
|
InRevisionInfo.V1.Revision = 1;
|
|
InRevisionInfo.V1.SupportedFeatures = 0;
|
|
|
|
return SamrConnect5(ServerName,
|
|
DesiredAccess,
|
|
1,
|
|
&InRevisionInfo,
|
|
&OutVersion,
|
|
&OutRevisionInfo,
|
|
ServerHandle);
|
|
}
|
|
|
|
|
|
/* Function 58 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrSetInformationUser2(IN SAMPR_HANDLE UserHandle,
|
|
IN USER_INFORMATION_CLASS UserInformationClass,
|
|
IN PSAMPR_USER_INFO_BUFFER Buffer)
|
|
{
|
|
TRACE("SamrSetInformationUser2(%p %lu %p)\n",
|
|
UserHandle, UserInformationClass, Buffer);
|
|
|
|
return SamrSetInformationUser(UserHandle,
|
|
UserInformationClass,
|
|
Buffer);
|
|
}
|
|
|
|
|
|
/* Function 59 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrSetBootKeyInformation(IN handle_t BindingHandle) /* FIXME */
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* Function 60 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrGetBootKeyInformation(IN handle_t BindingHandle) /* FIXME */
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* Function 61 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrConnect3(IN handle_t BindingHandle) /* FIXME */
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
/* Function 62 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrConnect4(IN PSAMPR_SERVER_NAME ServerName,
|
|
OUT SAMPR_HANDLE *ServerHandle,
|
|
IN unsigned long ClientRevision,
|
|
IN ACCESS_MASK DesiredAccess)
|
|
{
|
|
SAMPR_REVISION_INFO InRevisionInfo, OutRevisionInfo;
|
|
ULONG OutVersion;
|
|
|
|
TRACE("SamrConnect4(%p %p %lu 0x%lx)\n",
|
|
ServerName, ServerHandle, ClientRevision, DesiredAccess);
|
|
|
|
InRevisionInfo.V1.Revision = 2;
|
|
InRevisionInfo.V1.SupportedFeatures = 0;
|
|
|
|
return SamrConnect5(ServerName,
|
|
DesiredAccess,
|
|
1,
|
|
&InRevisionInfo,
|
|
&OutVersion,
|
|
&OutRevisionInfo,
|
|
ServerHandle);
|
|
}
|
|
|
|
|
|
/* Function 63 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrUnicodeChangePasswordUser3(IN handle_t BindingHandle) /* FIXME */
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
/* Function 64 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrConnect5(IN PSAMPR_SERVER_NAME ServerName,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN unsigned long InVersion,
|
|
IN SAMPR_REVISION_INFO *InRevisionInfo,
|
|
OUT unsigned long *OutVersion,
|
|
OUT SAMPR_REVISION_INFO *OutRevisionInfo,
|
|
OUT SAMPR_HANDLE *ServerHandle)
|
|
{
|
|
PSAM_DB_OBJECT ServerObject;
|
|
NTSTATUS Status;
|
|
|
|
TRACE("SamrConnect5(%p 0x%lx %lu %p %p %p %p)\n",
|
|
ServerName, DesiredAccess, InVersion, InRevisionInfo,
|
|
OutVersion, OutRevisionInfo, ServerHandle);
|
|
|
|
if (InVersion != 1)
|
|
return STATUS_NOT_SUPPORTED;
|
|
|
|
RtlAcquireResourceShared(&SampResource,
|
|
TRUE);
|
|
|
|
/* Map generic access rights */
|
|
RtlMapGenericMask(&DesiredAccess,
|
|
&ServerMapping);
|
|
|
|
/* Open the Server Object */
|
|
Status = SampOpenDbObject(NULL,
|
|
NULL,
|
|
L"SAM",
|
|
0,
|
|
SamDbServerObject,
|
|
DesiredAccess,
|
|
&ServerObject);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
*OutVersion = 1;
|
|
|
|
OutRevisionInfo->V1.Revision = 3;
|
|
OutRevisionInfo->V1.SupportedFeatures = 0;
|
|
|
|
*ServerHandle = (SAMPR_HANDLE)ServerObject;
|
|
}
|
|
|
|
RtlReleaseResource(&SampResource);
|
|
|
|
TRACE("SamrConnect5 done (Status 0x%08lx)\n", Status);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Function 65 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrRidToSid(IN SAMPR_HANDLE ObjectHandle,
|
|
IN unsigned long Rid,
|
|
OUT PRPC_SID *Sid)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* Function 66 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrSetDSRMPassword(IN handle_t BindingHandle,
|
|
IN PRPC_UNICODE_STRING Unused,
|
|
IN unsigned long UserId,
|
|
IN PENCRYPTED_NT_OWF_PASSWORD EncryptedNtOwfPassword)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* Function 67 */
|
|
NTSTATUS
|
|
NTAPI
|
|
SamrValidatePassword(IN handle_t Handle,
|
|
IN PASSWORD_POLICY_VALIDATION_TYPE ValidationType,
|
|
IN PSAM_VALIDATE_INPUT_ARG InputArg,
|
|
OUT PSAM_VALIDATE_OUTPUT_ARG *OutputArg)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
/* EOF */
|