You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4703 lines
109 KiB
4703 lines
109 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
context.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the internal worker routines to create and manipulate
|
|
client context.
|
|
|
|
Author:
|
|
|
|
Kedar Dubhashi - March 2000
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
|
|
Revision History:
|
|
|
|
Created - March 2000
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
#pragma hdrstop
|
|
|
|
#include <authzp.h>
|
|
|
|
LUID AuthzTakeOwnershipPrivilege = {SE_TAKE_OWNERSHIP_PRIVILEGE, 0};
|
|
LUID AuthzSecurityPrivilege = {SE_SECURITY_PRIVILEGE, 0};
|
|
|
|
|
|
//
|
|
// Definitions used by AuthzpGetAllGroups.
|
|
//
|
|
|
|
const DWORD c_dwMaxSidCount = LSAI_CONTEXT_SID_LIMIT;
|
|
static DWORD s_dwPageSize = 0;
|
|
|
|
typedef struct _SID_DESC
|
|
{
|
|
DWORD dwAttributes;
|
|
DWORD dwLength;
|
|
BYTE sid[SECURITY_MAX_SID_SIZE];
|
|
}
|
|
SID_DESC, *PSID_DESC;
|
|
|
|
typedef struct _SID_SET
|
|
{
|
|
DWORD dwCount;
|
|
DWORD dwMaxCount;
|
|
PSID_DESC pSidDesc;
|
|
|
|
DWORD dwFlags;
|
|
DWORD dwBaseCount;
|
|
|
|
// user information
|
|
PSID pUserSid;
|
|
PSID pDomainSid;
|
|
PUNICODE_STRING pusUserName;
|
|
PUNICODE_STRING pusDomainName;
|
|
|
|
// user name & domain
|
|
PLSA_TRANSLATED_NAME pNames;
|
|
PLSA_REFERENCED_DOMAIN_LIST pDomains;
|
|
PWSTR pDomainsName;
|
|
PLSA_TRANSLATED_SID2 pSids;
|
|
SID_NAME_USE sidUse;
|
|
|
|
// information about the local machine
|
|
PPOLICY_ACCOUNT_DOMAIN_INFO pAccountInfo;
|
|
PPOLICY_PRIMARY_DOMAIN_INFO pPrimaryInfo;
|
|
PWSTR pPrimaryInfoName;
|
|
BOOL bStandalone;
|
|
BOOL bSkipNonLocal;
|
|
|
|
// user domain dc info
|
|
PDOMAIN_CONTROLLER_INFO pUdDcInfo;
|
|
PDOMAIN_CONTROLLER_INFO pPdDcInfo;
|
|
PDOMAIN_CONTROLLER_INFO pRdDcInfo;
|
|
|
|
// role information for user domain & primary domain
|
|
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pUdBasicInfo;
|
|
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pPdBasicInfo;
|
|
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pRdBasicInfo;
|
|
|
|
// name of the user domain DC
|
|
PWSTR pszUdDcName;
|
|
PWSTR pszRdDcName;
|
|
}
|
|
SID_SET, *PSID_SET;
|
|
|
|
|
|
//
|
|
// Forward declarations of functions called by
|
|
// AuthzpGetAllGroups.
|
|
//
|
|
|
|
DWORD
|
|
AuthzpAddWellKnownSids(
|
|
IN OUT PSID_SET pSidSet
|
|
);
|
|
|
|
DWORD
|
|
AuthzpGetTokenGroupsXp(
|
|
IN OUT PSID_SET pSidSet
|
|
);
|
|
|
|
DWORD
|
|
AuthzpGetTokenGroupsDownlevel(
|
|
IN OUT PSID_SET pSidSet
|
|
);
|
|
|
|
DWORD
|
|
AuthzpGetAccountDomainGroupsDs(
|
|
IN OUT PSID_SET pSidSet
|
|
);
|
|
|
|
DWORD
|
|
AuthzpGetAccountDomainGroupsSam(
|
|
IN OUT PSID_SET pSidSet
|
|
);
|
|
|
|
DWORD
|
|
AuthzpGetResourceDomainGroups(
|
|
IN OUT PSID_SET pSidSet
|
|
);
|
|
|
|
DWORD
|
|
AuthzpGetLocalGroups(
|
|
IN BOOL bAddPrimaryGroup,
|
|
IN OUT PSID_SET pSidSet
|
|
);
|
|
|
|
DWORD
|
|
AuthzpGetSidHistory(
|
|
IN OUT PSID_SET pSidSet
|
|
);
|
|
|
|
DWORD
|
|
AuthzpGetPrimaryGroup(
|
|
IN SAM_HANDLE hSam,
|
|
IN OUT PSID_SET pSidSet
|
|
);
|
|
|
|
DWORD
|
|
AuthzpGetAliasMembership(
|
|
IN SAM_HANDLE hSam,
|
|
IN PSID pDomainSid,
|
|
IN OUT PSID_SET pSidSet
|
|
);
|
|
|
|
DWORD
|
|
AuthzpInitializeSidSetByName(
|
|
IN PUNICODE_STRING pusUserName,
|
|
IN PUNICODE_STRING pusDomainName,
|
|
IN DWORD dwFlags,
|
|
IN PSID_SET pSidSet
|
|
);
|
|
|
|
DWORD
|
|
AuthzpIsDC(
|
|
OUT PBOOL pbIsDC
|
|
);
|
|
|
|
DWORD
|
|
AuthzpInitializeSidSetBySid(
|
|
IN PSID pUserSid,
|
|
IN DWORD dwFlags,
|
|
IN PSID_SET pSidSet
|
|
);
|
|
|
|
DWORD
|
|
AuthzpDeleteSidSet(
|
|
IN PSID_SET pSidSet
|
|
);
|
|
|
|
DWORD
|
|
AuthzpAddSidToSidSet(
|
|
IN PSID_SET pSidSet,
|
|
IN PSID pSid,
|
|
IN DWORD dwSidLength,
|
|
IN DWORD dwAttributes,
|
|
OUT PBOOL pbAdded OPTIONAL,
|
|
OUT PSID* ppSid OPTIONAL
|
|
);
|
|
|
|
DWORD
|
|
AuthzpGetUserDomainSid(
|
|
IN OUT PSID_SET pSidSet
|
|
);
|
|
|
|
DWORD
|
|
AuthzpGetUserDomainName(
|
|
IN OUT PSID_SET pSidSet
|
|
);
|
|
|
|
DWORD
|
|
AuthzpGetLocalInfo(
|
|
IN OUT PSID_SET pSidSet
|
|
);
|
|
|
|
DWORD
|
|
AuthzpGetDcName(
|
|
IN LPCTSTR pszDomain,
|
|
IN OUT PDOMAIN_CONTROLLER_INFO* ppDcInfo
|
|
);
|
|
|
|
VOID
|
|
AuthzpConvertSidToEdn(
|
|
IN PSID pSid,
|
|
OUT PWSTR pszSid
|
|
);
|
|
|
|
|
|
BOOL
|
|
AuthzpCopySidsAndAttributes(
|
|
IN OUT PSID_AND_ATTRIBUTES DestSidAttr,
|
|
IN PSID_AND_ATTRIBUTES SidAttr1,
|
|
IN DWORD Count1,
|
|
IN PSID_AND_ATTRIBUTES SidAttr2,
|
|
IN DWORD Count2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine takes two sets of sid and attribute strucutes and concatenates
|
|
them into a single one. The new structure is constructed into the buffer
|
|
supplied by the caller.
|
|
|
|
Arguments:
|
|
|
|
DestSidAttr - Caller supplied buffer into which the resultant structure
|
|
will be copied. The caller has already computed the size of the buffer
|
|
required to hold the output.
|
|
|
|
SidAttr1 - The first sid and attributes structure.
|
|
|
|
Count1 - The number of elements in SidAttr1 structure.
|
|
|
|
SidAttr2 - The second sid and attributes structure.
|
|
|
|
Count2 - The number of elements in SidAttr2 structure.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PUCHAR pCurrent = ((PUCHAR) DestSidAttr) + (sizeof(SID_AND_ATTRIBUTES) * (Count1 + Count2));
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
DWORD Length = 0;
|
|
DWORD i = 0;
|
|
|
|
//
|
|
// Loop thru the first set and copy the sids and their attribtes.
|
|
//
|
|
|
|
for (i = 0; i < Count1; i++)
|
|
{
|
|
Length = RtlLengthSid(SidAttr1[i].Sid);
|
|
|
|
Status = RtlCopySid(
|
|
Length,
|
|
pCurrent,
|
|
SidAttr1[i].Sid
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
DestSidAttr[i].Sid = (PSID) pCurrent;
|
|
DestSidAttr[i].Attributes = SidAttr1[i].Attributes;
|
|
pCurrent += Length;
|
|
}
|
|
|
|
//
|
|
// Loop thru the second set and copy the sids and their attribtes.
|
|
//
|
|
|
|
for (; i < (Count1 + Count2); i++)
|
|
{
|
|
Length = RtlLengthSid(SidAttr2[i - Count1].Sid);
|
|
|
|
Status = RtlCopySid(
|
|
Length,
|
|
pCurrent,
|
|
SidAttr2[i - Count1].Sid
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
return FALSE;
|
|
}
|
|
|
|
DestSidAttr[i].Sid = (PSID) pCurrent;
|
|
DestSidAttr[i].Attributes = SidAttr2[i - Count1].Attributes;
|
|
pCurrent += Length;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
AuthzpCopyLuidAndAttributes(
|
|
IN OUT PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PLUID_AND_ATTRIBUTES Source,
|
|
IN DWORD Count,
|
|
IN OUT PLUID_AND_ATTRIBUTES Destination
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine takes a luid and attributes array and copies them into a caller
|
|
supplied buffer. It also records presence of SecurityPrivilege and
|
|
SeTakeOwnershipPrivilege into the client context flags.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context structure into which the presence of
|
|
privileges would be recorded.
|
|
|
|
Source - The array of privileges and attributes to be copied into a supplied
|
|
buffer.
|
|
|
|
Count - Number of elements in the array.
|
|
|
|
Destination - Caller allocated buffer into which the input array will be
|
|
copied.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i = 0;
|
|
|
|
for (i = 0; i < Count; i++)
|
|
{
|
|
//
|
|
// Record the presence of SecurityPrivilege or SeTakeOwnershipPrivilege.
|
|
//
|
|
|
|
if ((RtlEqualLuid(&AuthzTakeOwnershipPrivilege, &Source[i].Luid)) &&
|
|
(Source[i].Attributes & SE_PRIVILEGE_ENABLED))
|
|
{
|
|
pCC->Flags |= AUTHZ_TAKE_OWNERSHIP_PRIVILEGE_ENABLED;
|
|
}
|
|
else if ((RtlEqualLuid(&AuthzSecurityPrivilege, &Source[i].Luid)) &&
|
|
(Source[i].Attributes & SE_PRIVILEGE_ENABLED))
|
|
{
|
|
pCC->Flags |= AUTHZ_SECURITY_PRIVILEGE_ENABLED;
|
|
}
|
|
|
|
RtlCopyLuid(&(Destination[i].Luid), &(Source[i].Luid));
|
|
|
|
Destination[i].Attributes = Source[i].Attributes;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpGetAllGroupsByName(
|
|
IN PUNICODE_STRING pusUserName,
|
|
IN PUNICODE_STRING pusDomainName,
|
|
IN DWORD dwFlags,
|
|
OUT PSID_AND_ATTRIBUTES* ppSidAndAttributes,
|
|
OUT PDWORD pdwSidCount,
|
|
OUT PDWORD pdwSidLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine works as AuthzpGetAllGroupsBySid but takes a username
|
|
domain name pair instead of a SID. It also accepts a UPN as the
|
|
username and an empty domain name.
|
|
|
|
|
|
Arguments:
|
|
|
|
pusUserName - Name of the user. Can be a UPN.
|
|
|
|
pusDomainName - domain name of the user account or NULL in case
|
|
the user name is a UPN.
|
|
|
|
Flags -
|
|
AUTHZ_SKIP_TOKEN_GROUPS - Do not compute TokenGroups.
|
|
AUTHZ_SKIP_WORLD_SID - Do not add the WORLD SID to the context.
|
|
|
|
ppSidAttr - Returns SidAndAttribute array. The routine allocates memory
|
|
for this array.
|
|
|
|
pSidCount - Returns the number of sids in the array.
|
|
|
|
pSidLength - Returns the size of memory allocated to hold the array.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError;
|
|
BOOL bStatus = TRUE;
|
|
SID_SET sidSet = {0};
|
|
PSID_DESC pSidDesc;
|
|
PBYTE pSid;
|
|
PSID_AND_ATTRIBUTES pSidAndAttribs;
|
|
DWORD i;
|
|
|
|
|
|
//
|
|
// Initialize output parameters to zero.
|
|
//
|
|
|
|
*ppSidAndAttributes = 0;
|
|
*pdwSidCount = 0;
|
|
*pdwSidLength = 0;
|
|
|
|
|
|
//
|
|
// Initialize the SID set
|
|
//
|
|
|
|
dwError = AuthzpInitializeSidSetByName(
|
|
pusUserName,
|
|
pusDomainName,
|
|
dwFlags,
|
|
&sidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (sidSet.dwFlags & AUTHZ_SKIP_TOKEN_GROUPS)
|
|
{
|
|
//
|
|
// Initialize the user SID.
|
|
//
|
|
|
|
dwError = AuthzpGetUserDomainSid(
|
|
&sidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Stick the user SID, the WORLD SID and others
|
|
// into the set if requested.
|
|
//
|
|
|
|
dwError = AuthzpAddWellKnownSids(
|
|
&sidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwError = AuthzpGetTokenGroupsXp(
|
|
&sidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Detect the downlevel case.
|
|
//
|
|
|
|
// if (dwError != SEC_E_NO_S4U_PROT_SUPPORT)
|
|
// {
|
|
// goto Cleanup;
|
|
// }
|
|
|
|
|
|
//
|
|
// Initialize the user SID.
|
|
//
|
|
|
|
dwError = AuthzpGetUserDomainSid(
|
|
&sidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Stick the user SID, the WORLD SID and others
|
|
// into the set if requested.
|
|
//
|
|
|
|
dwError = AuthzpAddWellKnownSids(
|
|
&sidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// In case AuthzpAddWellKnownSids finds that the SID is the
|
|
// Anonymous SID, it sets the AUTHZ_SKIP_TOKEN_GROUPS flag.
|
|
//
|
|
|
|
if (!(sidSet.dwFlags & AUTHZ_SKIP_TOKEN_GROUPS))
|
|
{
|
|
//
|
|
// Try the downlevel scenario.
|
|
//
|
|
|
|
dwError = AuthzpGetTokenGroupsDownlevel(
|
|
&sidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Allocate memory and copy all SIDs
|
|
// from the SID set into ppSidAndAttributes.
|
|
//
|
|
|
|
*pdwSidCount = sidSet.dwCount;
|
|
*pdwSidLength = sidSet.dwCount * sizeof(SID_AND_ATTRIBUTES);
|
|
|
|
pSidDesc = sidSet.pSidDesc;
|
|
|
|
for (i=0;i < sidSet.dwCount;i++,pSidDesc++)
|
|
{
|
|
*pdwSidLength += pSidDesc->dwLength;
|
|
}
|
|
|
|
*ppSidAndAttributes = (PSID_AND_ATTRIBUTES)AuthzpAlloc(*pdwSidLength);
|
|
|
|
if (*ppSidAndAttributes == 0)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
pSid = ((PBYTE)*ppSidAndAttributes) +
|
|
sidSet.dwCount * sizeof(SID_AND_ATTRIBUTES);
|
|
pSidDesc = sidSet.pSidDesc;
|
|
pSidAndAttribs = *ppSidAndAttributes;
|
|
|
|
for (i=0;i < sidSet.dwCount;i++,pSidDesc++,pSidAndAttribs++)
|
|
{
|
|
pSidAndAttribs->Attributes = pSidDesc->dwAttributes;
|
|
pSidAndAttribs->Sid = pSid;
|
|
|
|
RtlCopyMemory(
|
|
pSid,
|
|
pSidDesc->sid,
|
|
pSidDesc->dwLength
|
|
);
|
|
|
|
pSid += pSidDesc->dwLength;
|
|
}
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(dwError);
|
|
bStatus = FALSE;
|
|
|
|
*pdwSidCount = 0;
|
|
*pdwSidLength = 0;
|
|
|
|
if (*ppSidAndAttributes)
|
|
{
|
|
AuthzpFree(*ppSidAndAttributes);
|
|
*ppSidAndAttributes = 0;
|
|
}
|
|
}
|
|
|
|
AuthzpDeleteSidSet(&sidSet);
|
|
|
|
return bStatus;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpGetAllGroupsBySid(
|
|
IN PSID pUserSid,
|
|
IN DWORD dwFlags,
|
|
OUT PSID_AND_ATTRIBUTES* ppSidAndAttributes,
|
|
OUT PDWORD pdwSidCount,
|
|
OUT PDWORD pdwSidLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine computes the groups a given user is a member of.
|
|
It uses a data structure called a SID_SET for collecting the SIDs.
|
|
|
|
1. Initialize the SID_SET.
|
|
|
|
2. Put the user SID into the set.
|
|
If requested, put the EVERYONE SID into the set.
|
|
Add Well Known SIDs.
|
|
|
|
3. If requested, put the SIDs for non-local groups the user is
|
|
a member of into the set. There are three scenarios for this
|
|
step, depending on the version of the DC we are talking to:
|
|
|
|
xp: Use LsaLogonUser with the Kerberos S4U package and
|
|
extract the groups from the token returned
|
|
(AuthzpGetWXPDomainTokenGroups).
|
|
|
|
W2k: Use ldap and the SAM API to compute memberships in
|
|
NT4: the account and primary domain and to get the SID history
|
|
(AuthzpGetW2kDomainTokenGroups).
|
|
|
|
4. Transmogrify the SID_SET into a SID_AND_ATTRIBUTES array
|
|
and free the SID_SET.
|
|
|
|
Arguments:
|
|
|
|
pUserSid - The user SID for which the groups should be computed.
|
|
|
|
Flags -
|
|
AUTHZ_SKIP_TOKEN_GROUPS - Do not compute TokenGroups.
|
|
AUTHZ_SKIP_WORLD_SID - Do not add the WORLD SID to the context.
|
|
AUTHZ_REQUIRE_S4U_LOGON - Do not use the downlevel codepaths. Force S4U or fail.
|
|
This is to enforce account restrictions.
|
|
|
|
ppSidAttr - Returns SidAndAttribute array. The routine allocates memory
|
|
for this array.
|
|
|
|
pSidCount - Returns the number of sids in the array.
|
|
|
|
pSidLength - Returns the size of memory allocated to hold the array.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError;
|
|
BOOL bStatus = TRUE;
|
|
SID_SET sidSet = {0};
|
|
PSID_DESC pSidDesc;
|
|
PBYTE pSid;
|
|
PSID_AND_ATTRIBUTES pSidAndAttribs;
|
|
DWORD i;
|
|
|
|
|
|
//
|
|
// Initialize output parameters to zero.
|
|
//
|
|
|
|
*ppSidAndAttributes = 0;
|
|
*pdwSidCount = 0;
|
|
*pdwSidLength = 0;
|
|
|
|
|
|
//
|
|
// Initialize the SID set
|
|
//
|
|
|
|
dwError = AuthzpInitializeSidSetBySid(
|
|
pUserSid,
|
|
dwFlags,
|
|
&sidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (sidSet.dwFlags & AUTHZ_SKIP_TOKEN_GROUPS)
|
|
{
|
|
//
|
|
// Stick the user SID, the WORLD SID and others
|
|
// into the set if requested.
|
|
//
|
|
|
|
dwError = AuthzpAddWellKnownSids(
|
|
&sidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Initialize user and domain name members of the sid set.
|
|
//
|
|
|
|
dwError = AuthzpGetUserDomainName(
|
|
&sidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (sidSet.pNames->Use == SidTypeAlias ||
|
|
sidSet.pNames->Use == SidTypeGroup ||
|
|
sidSet.pNames->Use == SidTypeWellKnownGroup)
|
|
{
|
|
//
|
|
// LsaLogonUser cannot log on groups...
|
|
//
|
|
|
|
dwError = ERROR_NOT_SUPPORTED;
|
|
}
|
|
else
|
|
{
|
|
dwError = AuthzpGetTokenGroupsXp(
|
|
&sidSet
|
|
);
|
|
}
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Xp logon code failed. If user prohibits the downlevel path,
|
|
// then exit now.
|
|
//
|
|
|
|
if (dwFlags & AUTHZ_REQUIRE_S4U_LOGON)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Detect the downlevel case.
|
|
//
|
|
|
|
// if (dwError != SEC_E_NO_S4U_PROT_SUPPORT)
|
|
// {
|
|
// goto Cleanup;
|
|
// }
|
|
|
|
//
|
|
//
|
|
// Stick the user SID, the WORLD SID and others
|
|
// into the set if requested.
|
|
//
|
|
|
|
dwError = AuthzpAddWellKnownSids(
|
|
&sidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// In case AuthzpAddWellKnownSids finds that the SID is the
|
|
// Anonymous SID, it sets the AUTHZ_SKIP_TOKEN_GROUPS flag.
|
|
//
|
|
|
|
if (!(sidSet.dwFlags & AUTHZ_SKIP_TOKEN_GROUPS))
|
|
{
|
|
//
|
|
// Try the downlevel scenario.
|
|
//
|
|
|
|
dwError = AuthzpGetTokenGroupsDownlevel(
|
|
&sidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate memory and copy all SIDs
|
|
// from the SID set into ppSidAndAttributes.
|
|
//
|
|
|
|
*pdwSidCount = sidSet.dwCount;
|
|
*pdwSidLength = sidSet.dwCount * sizeof(SID_AND_ATTRIBUTES);
|
|
|
|
pSidDesc = sidSet.pSidDesc;
|
|
|
|
for (i=0;i < sidSet.dwCount;i++,pSidDesc++)
|
|
{
|
|
*pdwSidLength += pSidDesc->dwLength;
|
|
}
|
|
|
|
*ppSidAndAttributes = (PSID_AND_ATTRIBUTES)AuthzpAlloc(*pdwSidLength);
|
|
|
|
if (*ppSidAndAttributes == 0)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
pSid = ((PBYTE)*ppSidAndAttributes) +
|
|
sidSet.dwCount * sizeof(SID_AND_ATTRIBUTES);
|
|
pSidDesc = sidSet.pSidDesc;
|
|
pSidAndAttribs = *ppSidAndAttributes;
|
|
|
|
for (i=0;i < sidSet.dwCount;i++,pSidDesc++,pSidAndAttribs++)
|
|
{
|
|
pSidAndAttribs->Attributes = pSidDesc->dwAttributes;
|
|
pSidAndAttribs->Sid = pSid;
|
|
|
|
RtlCopyMemory(
|
|
pSid,
|
|
pSidDesc->sid,
|
|
pSidDesc->dwLength
|
|
);
|
|
|
|
pSid += pSidDesc->dwLength;
|
|
}
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
SetLastError(dwError);
|
|
bStatus = FALSE;
|
|
|
|
*pdwSidCount = 0;
|
|
*pdwSidLength = 0;
|
|
|
|
if (*ppSidAndAttributes)
|
|
{
|
|
AuthzpFree(*ppSidAndAttributes);
|
|
*ppSidAndAttributes = 0;
|
|
}
|
|
}
|
|
|
|
AuthzpDeleteSidSet(&sidSet);
|
|
|
|
return bStatus;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpAddWellKnownSids(
|
|
IN OUT PSID_SET pSidSet
|
|
)
|
|
{
|
|
DWORD dwError;
|
|
BOOL bStatus;
|
|
BOOL bEqual;
|
|
BOOL bAddEveryone = TRUE;
|
|
BOOL bAddAuthUsers = TRUE;
|
|
BOOL bAddAdministrators = FALSE;
|
|
BYTE sid[SECURITY_MAX_SID_SIZE];
|
|
PSID pSid = (PSID)sid;
|
|
DWORD dwLengthSid;
|
|
|
|
|
|
//
|
|
// Stick the user SID into the set
|
|
//
|
|
|
|
dwError = AuthzpAddSidToSidSet(
|
|
pSidSet,
|
|
pSidSet->pUserSid,
|
|
0,
|
|
SE_GROUP_ENABLED,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
pSidSet->dwBaseCount = 1;
|
|
|
|
//
|
|
// Test for some well known SIDs.
|
|
//
|
|
// If the SID passed in is the Anonymous SID, then check the registry
|
|
// value to determine if the Everyone SID should be included in the
|
|
// resulting client context.
|
|
//
|
|
|
|
if (IsWellKnownSid(
|
|
pSidSet->pUserSid,
|
|
WinAnonymousSid))
|
|
{
|
|
bAddEveryone = FALSE;
|
|
bAddAuthUsers = FALSE;
|
|
|
|
bStatus = AuthzpEveryoneIncludesAnonymous(
|
|
&bAddEveryone
|
|
);
|
|
|
|
if (bStatus == FALSE)
|
|
{
|
|
bAddEveryone = FALSE;
|
|
}
|
|
|
|
pSidSet->dwFlags |= AUTHZ_SKIP_TOKEN_GROUPS;
|
|
}
|
|
else if (IsWellKnownSid(
|
|
pSidSet->pUserSid,
|
|
WinLocalSystemSid))
|
|
{
|
|
bAddEveryone = TRUE;
|
|
bAddAuthUsers = TRUE;
|
|
bAddAdministrators = TRUE;
|
|
|
|
pSidSet->dwFlags |= AUTHZ_SKIP_TOKEN_GROUPS;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is a dummy context. Return now.
|
|
//
|
|
|
|
if (pSidSet->dwFlags & AUTHZ_SKIP_TOKEN_GROUPS)
|
|
{
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
dwLengthSid = SECURITY_MAX_SID_SIZE;
|
|
|
|
bStatus = CreateWellKnownSid(
|
|
WinBuiltinDomainSid,
|
|
0,
|
|
pSid,
|
|
&dwLengthSid
|
|
);
|
|
|
|
if (bStatus == FALSE)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
bStatus = EqualDomainSid(
|
|
pSidSet->pUserSid,
|
|
pSid,
|
|
&bEqual
|
|
);
|
|
//
|
|
// ERROR_NON_DOMAIN_SID is returned for wellknown sids.
|
|
// It is ok to ignore this error and continue.
|
|
//
|
|
|
|
if ((bStatus == FALSE) && (GetLastError() != ERROR_NON_DOMAIN_SID))
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (bEqual)
|
|
{
|
|
pSidSet->bSkipNonLocal = TRUE;
|
|
}
|
|
else
|
|
{
|
|
bAddEveryone = TRUE;
|
|
bAddAuthUsers = TRUE;
|
|
}
|
|
}
|
|
|
|
if (bAddEveryone)
|
|
{
|
|
dwLengthSid = SECURITY_MAX_SID_SIZE;
|
|
|
|
bStatus = CreateWellKnownSid(
|
|
WinWorldSid,
|
|
0,
|
|
pSid,
|
|
&dwLengthSid
|
|
);
|
|
|
|
if (bStatus == FALSE)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = AuthzpAddSidToSidSet(
|
|
pSidSet,
|
|
pSid,
|
|
dwLengthSid,
|
|
SE_GROUP_MANDATORY
|
|
| SE_GROUP_ENABLED_BY_DEFAULT
|
|
| SE_GROUP_ENABLED,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
pSidSet->dwBaseCount++;
|
|
}
|
|
|
|
//
|
|
// Add NT AUTHORITY\Authenticated Users to the set
|
|
// only if the user does not have the Guest RID
|
|
//
|
|
|
|
if (bAddAuthUsers &&
|
|
*RtlSubAuthorityCountSid(pSidSet->pUserSid) > 0 &&
|
|
(*RtlSubAuthoritySid(
|
|
pSidSet->pUserSid,
|
|
(ULONG)(*RtlSubAuthorityCountSid(
|
|
pSidSet->pUserSid)) - 1) != DOMAIN_USER_RID_GUEST) &&
|
|
(*RtlSubAuthoritySid(
|
|
pSidSet->pUserSid,
|
|
(ULONG)(*RtlSubAuthorityCountSid(
|
|
pSidSet->pUserSid)) - 1) != DOMAIN_GROUP_RID_GUESTS))
|
|
{
|
|
dwLengthSid = SECURITY_MAX_SID_SIZE;
|
|
|
|
bStatus = CreateWellKnownSid(
|
|
WinAuthenticatedUserSid,
|
|
0,
|
|
pSid,
|
|
&dwLengthSid
|
|
);
|
|
|
|
if (bStatus == FALSE)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = AuthzpAddSidToSidSet(
|
|
pSidSet,
|
|
pSid,
|
|
dwLengthSid,
|
|
SE_GROUP_MANDATORY
|
|
| SE_GROUP_ENABLED_BY_DEFAULT
|
|
| SE_GROUP_ENABLED,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
pSidSet->dwBaseCount++;
|
|
}
|
|
|
|
if (bAddAdministrators)
|
|
{
|
|
dwLengthSid = SECURITY_MAX_SID_SIZE;
|
|
|
|
bStatus = CreateWellKnownSid(
|
|
WinBuiltinAdministratorsSid,
|
|
0,
|
|
pSid,
|
|
&dwLengthSid
|
|
);
|
|
|
|
if (bStatus == FALSE)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = AuthzpAddSidToSidSet(
|
|
pSidSet,
|
|
pSid,
|
|
dwLengthSid,
|
|
SE_GROUP_MANDATORY
|
|
| SE_GROUP_ENABLED_BY_DEFAULT
|
|
| SE_GROUP_ENABLED,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
pSidSet->dwBaseCount++;
|
|
}
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpGetTokenGroupsXp(
|
|
IN OUT PSID_SET pSidSet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine connects to the domain specified by the SID and
|
|
retrieves the list of groups to which the user belongs.
|
|
This routine assumes we are talking to a WinXP DC.
|
|
We take advantage of the new LsaLogonUser package, KerbS4ULogon.
|
|
|
|
Arguments:
|
|
|
|
pUserSid - user SID for which the lookup should be performed.
|
|
|
|
pSidSet - SID_SET in which we collect the SIDs of the groups
|
|
we found in the token.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code:
|
|
|
|
- ERROR_NOT_SUPPORTED if the DC does not support the call
|
|
(pre ~2475 or client)
|
|
- ERROR_INVALID_PARAMETER if the code is running on a
|
|
pre XP platform
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
BOOL bStatus;
|
|
NTSTATUS status;
|
|
HANDLE hLsa = 0;
|
|
LSA_STRING asProcessName;
|
|
LSA_STRING asPackageName;
|
|
ULONG ulAuthPackage;
|
|
TOKEN_SOURCE sourceContext;
|
|
PVOID pProfileBuffer = 0;
|
|
ULONG ulProfileLength = 0;
|
|
LUID luidLogonId;
|
|
HANDLE hToken = 0;
|
|
QUOTA_LIMITS quota;
|
|
NTSTATUS subStatus;
|
|
DWORD dwLength;
|
|
DWORD i;
|
|
PTOKEN_USER pTokenUser = 0;
|
|
PTOKEN_GROUPS pTokenGroups = 0;
|
|
PSID_AND_ATTRIBUTES pSidAndAttribs;
|
|
ULONG ulPackageSize;
|
|
PKERB_S4U_LOGON pPackage = 0;
|
|
|
|
|
|
//
|
|
// Set up the authentication package.
|
|
//
|
|
|
|
ulPackageSize = sizeof(KERB_S4U_LOGON);
|
|
ulPackageSize += pSidSet->pusUserName->Length;
|
|
|
|
if (pSidSet->pusDomainName)
|
|
{
|
|
ulPackageSize += pSidSet->pusDomainName->Length;
|
|
}
|
|
|
|
pPackage = (PKERB_S4U_LOGON)LocalAlloc(
|
|
LMEM_FIXED,
|
|
ulPackageSize
|
|
);
|
|
|
|
if (pPackage == 0)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
pPackage->MessageType = KerbS4ULogon;
|
|
pPackage->Flags = 0;
|
|
|
|
pPackage->ClientUpn.Length = pSidSet->pusUserName->Length;
|
|
pPackage->ClientUpn.MaximumLength = pSidSet->pusUserName->Length;
|
|
pPackage->ClientUpn.Buffer = (PWSTR)(pPackage + 1);
|
|
|
|
RtlCopyMemory(
|
|
pPackage->ClientUpn.Buffer,
|
|
pSidSet->pusUserName->Buffer,
|
|
pSidSet->pusUserName->Length
|
|
);
|
|
|
|
if (pSidSet->pusDomainName)
|
|
{
|
|
pPackage->ClientRealm.Length = pSidSet->pusDomainName->Length;
|
|
pPackage->ClientRealm.MaximumLength = pSidSet->pusDomainName->Length;
|
|
pPackage->ClientRealm.Buffer = (PWSTR)
|
|
(((PBYTE)(pPackage->ClientUpn.Buffer)) + pPackage->ClientUpn.Length);
|
|
|
|
RtlCopyMemory(
|
|
pPackage->ClientRealm.Buffer,
|
|
pSidSet->pusDomainName->Buffer,
|
|
pSidSet->pusDomainName->Length
|
|
);
|
|
}
|
|
else
|
|
{
|
|
pPackage->ClientRealm.Length = 0;
|
|
pPackage->ClientRealm.MaximumLength = 0;
|
|
pPackage->ClientRealm.Buffer = 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Our name is AuthzApi.
|
|
//
|
|
|
|
RtlInitString(
|
|
&asProcessName,
|
|
"AuthzApi"
|
|
);
|
|
|
|
|
|
//
|
|
// Set up the process name and
|
|
// register with the LSA.
|
|
//
|
|
|
|
status = LsaConnectUntrusted(
|
|
&hLsa
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = LsaNtStatusToWinError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the authentication package.
|
|
//
|
|
|
|
RtlInitString(&asPackageName, MICROSOFT_KERBEROS_NAME_A);
|
|
|
|
status = LsaLookupAuthenticationPackage(
|
|
hLsa,
|
|
&asPackageName,
|
|
&ulAuthPackage
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = LsaNtStatusToWinError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Prepare the source context.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
sourceContext.SourceName,
|
|
"Authz ",
|
|
sizeof(sourceContext.SourceName)
|
|
);
|
|
|
|
status = NtAllocateLocallyUniqueId(
|
|
&sourceContext.SourceIdentifier
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = RtlNtStatusToDosError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Do the logon.
|
|
//
|
|
|
|
status = LsaLogonUser(
|
|
hLsa,
|
|
&asProcessName,
|
|
Network,
|
|
ulAuthPackage,
|
|
pPackage,
|
|
ulPackageSize,
|
|
0, // no LocalGroups
|
|
&sourceContext,
|
|
&pProfileBuffer,
|
|
&ulProfileLength,
|
|
&luidLogonId,
|
|
&hToken,
|
|
"a,
|
|
&subStatus
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = LsaNtStatusToWinError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Figure out how much memory to allocate for the user info.
|
|
//
|
|
|
|
dwLength = 0;
|
|
|
|
bStatus = GetTokenInformation(
|
|
hToken,
|
|
TokenUser,
|
|
0,
|
|
0,
|
|
&dwLength
|
|
);
|
|
|
|
if (bStatus == FALSE)
|
|
{
|
|
dwError = GetLastError();
|
|
|
|
if (dwError != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
pTokenUser = (PTOKEN_USER)LocalAlloc(
|
|
LMEM_FIXED,
|
|
dwLength
|
|
);
|
|
|
|
if (pTokenUser == 0)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Extract the user SID from the token and
|
|
// add it to pSidSet.
|
|
//
|
|
|
|
bStatus = GetTokenInformation(
|
|
hToken,
|
|
TokenUser,
|
|
pTokenUser,
|
|
dwLength,
|
|
&dwLength
|
|
);
|
|
|
|
if (bStatus == FALSE)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Stick the user SID into the set.
|
|
//
|
|
|
|
if (!FLAG_ON(pTokenUser->User.Attributes, SE_GROUP_USE_FOR_DENY_ONLY))
|
|
{
|
|
pTokenUser->User.Attributes |= SE_GROUP_ENABLED;
|
|
}
|
|
|
|
dwError = AuthzpAddSidToSidSet(
|
|
pSidSet,
|
|
pTokenUser->User.Sid,
|
|
0,
|
|
pTokenUser->User.Attributes,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Figure out how much memory to allocate for the token groups.
|
|
//
|
|
|
|
dwLength = 0;
|
|
|
|
bStatus = GetTokenInformation(
|
|
hToken,
|
|
TokenGroups,
|
|
0,
|
|
0,
|
|
&dwLength
|
|
);
|
|
|
|
if (bStatus == FALSE)
|
|
{
|
|
dwError = GetLastError();
|
|
|
|
if (dwError != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
pTokenGroups = (PTOKEN_GROUPS)LocalAlloc(
|
|
LMEM_FIXED,
|
|
dwLength
|
|
);
|
|
|
|
if (pTokenGroups == 0)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Extract the user groups from the token and
|
|
// add them to pSidSet.
|
|
//
|
|
|
|
bStatus = GetTokenInformation(
|
|
hToken,
|
|
TokenGroups,
|
|
pTokenGroups,
|
|
dwLength,
|
|
&dwLength
|
|
);
|
|
|
|
if (bStatus == FALSE)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Stick the group SIDs into the set
|
|
// except for the Network and the LUID SID.
|
|
//
|
|
|
|
pSidAndAttribs = pTokenGroups->Groups;
|
|
|
|
for (i=0;i < pTokenGroups->GroupCount;i++,pSidAndAttribs++)
|
|
{
|
|
if (!IsWellKnownSid(
|
|
pSidAndAttribs->Sid,
|
|
WinNetworkSid) &&
|
|
!IsWellKnownSid(
|
|
pSidAndAttribs->Sid,
|
|
WinLogonIdsSid))
|
|
{
|
|
dwError = AuthzpAddSidToSidSet(
|
|
pSidSet,
|
|
pSidAndAttribs->Sid,
|
|
0,
|
|
pSidAndAttribs->Attributes,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if (pTokenUser)
|
|
{
|
|
AuthzpFree((HLOCAL)pTokenUser);
|
|
}
|
|
|
|
if (pTokenGroups)
|
|
{
|
|
AuthzpFree((HLOCAL)pTokenGroups);
|
|
}
|
|
|
|
if (hToken)
|
|
{
|
|
CloseHandle(hToken);
|
|
}
|
|
|
|
if (pProfileBuffer)
|
|
{
|
|
LsaFreeReturnBuffer(pProfileBuffer);
|
|
}
|
|
|
|
if (hLsa)
|
|
{
|
|
LsaDeregisterLogonProcess(hLsa);
|
|
}
|
|
|
|
if (pPackage)
|
|
{
|
|
AuthzpFree((HLOCAL)pPackage);
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpGetTokenGroupsDownlevel(
|
|
IN OUT PSID_SET pSidSet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine connects to the domain specified by the SID and
|
|
retrieves the list of groups to which the user belongs.
|
|
This routine assumes we are talking to a Win2k DC.
|
|
First get the users domain universal and global groups
|
|
memberships.
|
|
Next check for nested memberships in the primary domain.
|
|
The last step is getting the SID history for each SID collected
|
|
so far.
|
|
|
|
Arguments:
|
|
|
|
pUserSid - user SID for which the lookup should be performed.
|
|
|
|
pSidSet - Returns the number of rids in the alias list
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError;
|
|
BOOL bUdIsNative = FALSE;
|
|
BOOL bRdIsNative = FALSE;
|
|
BOOL bAddPrimaryGroup = FALSE;
|
|
|
|
|
|
//
|
|
// Retrieve information about the machine.
|
|
//
|
|
|
|
dwError = AuthzpGetLocalInfo(pSidSet);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pSidSet->bStandalone ||
|
|
pSidSet->bSkipNonLocal)
|
|
{
|
|
//
|
|
// In the standalone case there is no need to hit the wire.
|
|
// We don't have to do anything here since local group
|
|
// memberships are computed later anyway.
|
|
//
|
|
|
|
bAddPrimaryGroup = TRUE;
|
|
goto LocalGroups;
|
|
}
|
|
|
|
|
|
//
|
|
// Compare the user domain SID to the machine domain SID.
|
|
// If they are equal, we can use the local machine for
|
|
// global group computing since the account is either
|
|
// a local account or we are sitting on a DC.
|
|
// The SID of the local machine is never zero in a non
|
|
// standalone / workgroup case.
|
|
//
|
|
|
|
if (pSidSet->pAccountInfo->DomainSid &&
|
|
RtlEqualSid(
|
|
pSidSet->pDomainSid,
|
|
pSidSet->pAccountInfo->DomainSid))
|
|
{
|
|
BOOL bIsDC = FALSE;
|
|
|
|
pSidSet->pszUdDcName = 0;
|
|
|
|
|
|
//
|
|
// Find out if this is a DC.
|
|
//
|
|
|
|
dwError = AuthzpIsDC(&bIsDC);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// If this is not a DC then give up on global group computing.
|
|
//
|
|
|
|
if (FALSE == bIsDC)
|
|
{
|
|
bAddPrimaryGroup = TRUE;
|
|
goto LocalGroups;
|
|
}
|
|
|
|
|
|
//
|
|
// Local machine is a DC. Since AuthZ is not supported on nt4 we can
|
|
// safely assume it is at least w2k.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Find a DC and get its name.
|
|
//
|
|
|
|
dwError = AuthzpGetDcName(
|
|
pSidSet->pusDomainName->Buffer,
|
|
&pSidSet->pUdDcInfo
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
pSidSet->pszUdDcName = pSidSet->pUdDcInfo->DomainControllerName;
|
|
}
|
|
|
|
|
|
//
|
|
// User domain can only be in native mode if DS is running.
|
|
//
|
|
|
|
if ((pSidSet->pUdDcInfo == 0) ||
|
|
(pSidSet->pUdDcInfo->Flags & DS_DS_FLAG) != 0)
|
|
{
|
|
//
|
|
// Collect information about the domain.
|
|
//
|
|
|
|
dwError = DsRoleGetPrimaryDomainInformation(
|
|
pSidSet->pszUdDcName,
|
|
DsRolePrimaryDomainInfoBasic,
|
|
(PBYTE*)&pSidSet->pUdBasicInfo
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// If the domain is in mixed mode and we are passing in a DNS
|
|
// name, the call fails with RPC_S_SERVER_UNAVAILABLE.
|
|
// We have to get rid of the DC name and get a flat one and
|
|
// then try again.
|
|
//
|
|
|
|
if (dwError == RPC_S_SERVER_UNAVAILABLE &&
|
|
pSidSet->pUdDcInfo &&
|
|
(pSidSet->pUdDcInfo->Flags & DS_INET_ADDRESS))
|
|
{
|
|
NetApiBufferFree(pSidSet->pUdDcInfo);
|
|
pSidSet->pUdDcInfo = 0;
|
|
|
|
dwError = DsGetDcName(
|
|
0,
|
|
pSidSet->pDomainsName,
|
|
0,
|
|
0,
|
|
0,
|
|
&pSidSet->pUdDcInfo
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
pSidSet->pszUdDcName = pSidSet->pUdDcInfo->DomainControllerName;
|
|
|
|
dwError = DsRoleGetPrimaryDomainInformation(
|
|
pSidSet->pszUdDcName,
|
|
DsRolePrimaryDomainInfoBasic,
|
|
(PBYTE*)&pSidSet->pUdBasicInfo
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if ((pSidSet->pUdBasicInfo->Flags & DSROLE_PRIMARY_DS_RUNNING) &&
|
|
(pSidSet->pUdBasicInfo->Flags & DSROLE_PRIMARY_DS_MIXED_MODE) == 0)
|
|
{
|
|
bUdIsNative = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Check whether the account domain is in native or mixed mode
|
|
// and call the appropriate routine to get the groups.
|
|
//
|
|
|
|
if (bUdIsNative)
|
|
{
|
|
//
|
|
// User domain is in native mode.
|
|
//
|
|
|
|
dwError = AuthzpGetAccountDomainGroupsDs(
|
|
pSidSet
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// User domain is in mixed mode.
|
|
//
|
|
|
|
dwError = AuthzpGetAccountDomainGroupsSam(
|
|
pSidSet
|
|
);
|
|
}
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Check whether user domain and resource domain are different.
|
|
//
|
|
|
|
if (pSidSet->pPrimaryInfo->Sid &&
|
|
RtlEqualSid(
|
|
pSidSet->pDomainSid,
|
|
pSidSet->pPrimaryInfo->Sid))
|
|
{
|
|
pSidSet->pszRdDcName = pSidSet->pszUdDcName;
|
|
pSidSet->pRdDcInfo = pSidSet->pUdDcInfo;
|
|
pSidSet->pRdBasicInfo = pSidSet->pUdBasicInfo;
|
|
bRdIsNative = bUdIsNative;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Find a DC and get its name.
|
|
//
|
|
|
|
dwError = AuthzpGetDcName(
|
|
pSidSet->pPrimaryInfoName,
|
|
&pSidSet->pPdDcInfo
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
pSidSet->pszRdDcName = pSidSet->pPdDcInfo->DomainControllerName;
|
|
pSidSet->pRdDcInfo = pSidSet->pPdDcInfo;
|
|
|
|
|
|
//
|
|
// Resource domain can only be in native mode if DS is running.
|
|
//
|
|
|
|
if (pSidSet->pRdDcInfo->Flags & DS_DS_FLAG)
|
|
{
|
|
dwError = DsRoleGetPrimaryDomainInformation(
|
|
pSidSet->pszRdDcName,
|
|
DsRolePrimaryDomainInfoBasic,
|
|
(PBYTE*)&pSidSet->pPdBasicInfo
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// If the domain is in mixed mode and we are passing in a DNS
|
|
// name, the call fails. We have to get rid of the DC name
|
|
// and get a flat one and then try again.
|
|
//
|
|
|
|
if (dwError == RPC_S_SERVER_UNAVAILABLE &&
|
|
pSidSet->pPdDcInfo &&
|
|
(pSidSet->pPdDcInfo->Flags & DS_INET_ADDRESS))
|
|
{
|
|
NetApiBufferFree(pSidSet->pPdDcInfo);
|
|
pSidSet->pPdDcInfo = 0;
|
|
pSidSet->pRdDcInfo = 0;
|
|
|
|
dwError = DsGetDcName(
|
|
0,
|
|
pSidSet->pPrimaryInfoName,
|
|
0,
|
|
0,
|
|
0,
|
|
&pSidSet->pPdDcInfo
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
pSidSet->pRdDcInfo = pSidSet->pPdDcInfo;
|
|
pSidSet->pszRdDcName = pSidSet->pRdDcInfo->DomainControllerName;
|
|
|
|
dwError = DsRoleGetPrimaryDomainInformation(
|
|
pSidSet->pszRdDcName,
|
|
DsRolePrimaryDomainInfoBasic,
|
|
(PBYTE*)&pSidSet->pPdBasicInfo
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
pSidSet->pRdBasicInfo = pSidSet->pPdBasicInfo;
|
|
|
|
if ((pSidSet->pRdBasicInfo->Flags & DSROLE_PRIMARY_DS_RUNNING) &&
|
|
(pSidSet->pRdBasicInfo->Flags & DSROLE_PRIMARY_DS_MIXED_MODE) == 0)
|
|
{
|
|
bRdIsNative = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Get domain local groups.
|
|
//
|
|
|
|
if (bRdIsNative)
|
|
{
|
|
//
|
|
// Primary domain operates in native mode.
|
|
// This means there could be domain local groups in the token.
|
|
//
|
|
|
|
dwError = AuthzpGetResourceDomainGroups(
|
|
pSidSet);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
LocalGroups:
|
|
|
|
|
|
//
|
|
// Collect local groups information.
|
|
// If this is the local user case, we have to add the primary group
|
|
// for the user.
|
|
//
|
|
|
|
dwError = AuthzpGetLocalGroups(
|
|
bAddPrimaryGroup,
|
|
pSidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpGetAccountDomainGroupsDs(
|
|
IN OUT PSID_SET pSidSet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine connects to the user domain and queries AD for
|
|
the list of groups (global and universal) the user belongs to.
|
|
|
|
Arguments:
|
|
|
|
pbNativeDomain - Pointer to a BOOL that will receive TRUE or FALSE depending
|
|
on the domain operation mode (native or mixed, resp).
|
|
|
|
pSidSet - Pointer to set of SIDs. New groups will be added to this set.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError;
|
|
PLDAP pLdap = 0;
|
|
LDAPMessage* pResult = 0;
|
|
LDAPMessage* pEntry = 0;
|
|
PLDAP_BERVAL* ppValue = 0;
|
|
PWCHAR ppszAttributes[] = {L"tokenGroupsGlobalAndUniversal", 0};
|
|
DWORD i;
|
|
DWORD dwSidCount;
|
|
WCHAR szSidEdn[SECURITY_MAX_SID_SIZE * 2 + 8];
|
|
|
|
AuthzpConvertSidToEdn(
|
|
pSidSet->pUserSid,
|
|
szSidEdn
|
|
);
|
|
|
|
|
|
//
|
|
// We now have the user's SID in LDAP readable form. Fetch the
|
|
// tokenGroupsGlobalAndUniversal attribute.
|
|
//
|
|
|
|
pLdap = ldap_init(
|
|
pSidSet->pszUdDcName ? pSidSet->pszUdDcName + 2 : 0,
|
|
LDAP_PORT
|
|
);
|
|
|
|
if (pLdap == 0)
|
|
{
|
|
dwError = LdapMapErrorToWin32(LdapGetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pSidSet->pszUdDcName)
|
|
{
|
|
dwError = ldap_set_option(
|
|
pLdap,
|
|
LDAP_OPT_AREC_EXCLUSIVE,
|
|
LDAP_OPT_ON
|
|
);
|
|
|
|
if (dwError != LDAP_SUCCESS)
|
|
{
|
|
dwError = LdapMapErrorToWin32(dwError);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Set the sign and seal options to true.
|
|
//
|
|
|
|
dwError = ldap_set_option(
|
|
pLdap,
|
|
LDAP_OPT_SIGN,
|
|
LDAP_OPT_ON
|
|
);
|
|
|
|
if (dwError != LDAP_SUCCESS)
|
|
{
|
|
dwError = LdapMapErrorToWin32(dwError);
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = ldap_set_option(
|
|
pLdap,
|
|
LDAP_OPT_ENCRYPT,
|
|
LDAP_OPT_ON
|
|
);
|
|
|
|
if (dwError != LDAP_SUCCESS)
|
|
{
|
|
dwError = LdapMapErrorToWin32(dwError);
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = ldap_bind_s(
|
|
pLdap,
|
|
0,
|
|
0,
|
|
LDAP_AUTH_NEGOTIATE
|
|
);
|
|
|
|
if (dwError != LDAP_SUCCESS)
|
|
{
|
|
dwError = LdapMapErrorToWin32(dwError);
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = ldap_search_s(
|
|
pLdap,
|
|
szSidEdn,
|
|
LDAP_SCOPE_BASE,
|
|
L"objectClass=*",
|
|
ppszAttributes,
|
|
FALSE,
|
|
&pResult
|
|
);
|
|
|
|
if (dwError != LDAP_SUCCESS)
|
|
{
|
|
dwError = LdapMapErrorToWin32(dwError);
|
|
goto Cleanup;
|
|
}
|
|
|
|
pEntry = ldap_first_entry(
|
|
pLdap,
|
|
pResult
|
|
);
|
|
|
|
if (pEntry == 0)
|
|
{
|
|
dwError = ERROR_ACCESS_DENIED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ppValue = ldap_get_values_len(
|
|
pLdap,
|
|
pEntry,
|
|
ppszAttributes[0]
|
|
);
|
|
|
|
if (ppValue == 0)
|
|
{
|
|
switch (pSidSet->sidUse)
|
|
{
|
|
case SidTypeAlias:
|
|
case SidTypeWellKnownGroup:
|
|
case SidTypeInvalid:
|
|
case SidTypeUnknown:
|
|
case SidTypeGroup:
|
|
break;
|
|
case SidTypeComputer:
|
|
case SidTypeDomain:
|
|
case SidTypeDeletedAccount:
|
|
case SidTypeUser:
|
|
default:
|
|
dwError = ERROR_ACCESS_DENIED;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
dwSidCount = ldap_count_values_len(ppValue);
|
|
|
|
|
|
//
|
|
// Merge the groups for our user into the result set.
|
|
//
|
|
|
|
for (i=0;i < dwSidCount;i++)
|
|
{
|
|
dwError = AuthzpAddSidToSidSet(
|
|
pSidSet,
|
|
(*ppValue[i]).bv_val,
|
|
(*ppValue[i]).bv_len,
|
|
SE_GROUP_ENABLED,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if (ppValue)
|
|
{
|
|
ldap_value_free_len(ppValue);
|
|
}
|
|
|
|
if (pResult)
|
|
{
|
|
ldap_msgfree(pResult);
|
|
}
|
|
|
|
if (pLdap)
|
|
{
|
|
ldap_unbind(pLdap);
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpGetAccountDomainGroupsSam(
|
|
IN OUT PSID_SET pSidSet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine connects to the domain specified by the SID and
|
|
retrieves the list of groups to which the user belongs.
|
|
This resembles what the NetUserGetGroups API does. We are
|
|
not using it, because the Net APIs are name based and we
|
|
are working with SIDs.
|
|
|
|
Arguments:
|
|
|
|
pusDcName - DC on which the lookup should be performed.
|
|
|
|
pSidSet - Returns the number of SIDs in the alias list.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
PGROUP_MEMBERSHIP pGroups = 0;
|
|
PGROUP_MEMBERSHIP pGroup;
|
|
DWORD dwGroupCount = 0;
|
|
DWORD dwRelativeId = 0;
|
|
DWORD i;
|
|
PSID pSid = 0;
|
|
SAM_HANDLE hSam = 0;
|
|
SAM_HANDLE hDomain = 0;
|
|
SAM_HANDLE hUser = 0;
|
|
OBJECT_ATTRIBUTES obja = {0};
|
|
UNICODE_STRING usUdDcName = {0};
|
|
|
|
|
|
//
|
|
// If the sid is not a principal,
|
|
// it won't be a member of a SAM group.
|
|
//
|
|
|
|
if (pSidSet->sidUse != SidTypeUser &&
|
|
pSidSet->sidUse != SidTypeComputer)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Connect to the SAM server on the DC.
|
|
// If we are on the DC, connect locally.
|
|
//
|
|
|
|
if (pSidSet->pszUdDcName)
|
|
{
|
|
RtlInitUnicodeString(
|
|
&usUdDcName,
|
|
pSidSet->pszUdDcName);
|
|
|
|
status = SamConnect(
|
|
&usUdDcName,
|
|
&hSam,
|
|
SAM_SERVER_LOOKUP_DOMAIN,
|
|
&obja
|
|
);
|
|
}
|
|
else
|
|
{
|
|
status = SamConnect(
|
|
0,
|
|
&hSam,
|
|
SAM_SERVER_LOOKUP_DOMAIN,
|
|
&obja
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = RtlNtStatusToDosError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Open the domain we are interested in.
|
|
//
|
|
|
|
status = SamOpenDomain(
|
|
hSam,
|
|
DOMAIN_LOOKUP,
|
|
pSidSet->pDomainSid,
|
|
&hDomain
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = RtlNtStatusToDosError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Finally, get a SAM handle to the user.
|
|
//
|
|
|
|
dwRelativeId = *RtlSubAuthoritySid(
|
|
pSidSet->pUserSid,
|
|
*RtlSubAuthorityCountSid(pSidSet->pUserSid) - 1
|
|
);
|
|
|
|
status = SamOpenUser(
|
|
hDomain,
|
|
USER_LIST_GROUPS,
|
|
dwRelativeId,
|
|
&hUser
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = RtlNtStatusToDosError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Request all groups the user is a member of.
|
|
//
|
|
|
|
status = SamGetGroupsForUser(
|
|
hUser,
|
|
&pGroups,
|
|
&dwGroupCount
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = RtlNtStatusToDosError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Stuff the group SIDs into pSidSet.
|
|
//
|
|
|
|
pGroup = pGroups;
|
|
|
|
for (i=0;i < dwGroupCount;i++,pGroup++)
|
|
{
|
|
status = SamRidToSid(
|
|
hDomain,
|
|
pGroup->RelativeId,
|
|
&pSid
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = RtlNtStatusToDosError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = AuthzpAddSidToSidSet(
|
|
pSidSet,
|
|
pSid,
|
|
0,
|
|
pGroup->Attributes,
|
|
0,
|
|
0
|
|
);
|
|
|
|
SamFreeMemory(pSid);
|
|
pSid = 0;
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if (pGroups)
|
|
{
|
|
SamFreeMemory(pGroups);
|
|
}
|
|
|
|
if (hUser)
|
|
{
|
|
SamCloseHandle(hUser);
|
|
}
|
|
|
|
if (hDomain)
|
|
{
|
|
SamCloseHandle(hDomain);
|
|
}
|
|
|
|
if (hSam)
|
|
{
|
|
SamCloseHandle(hSam);
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpGetResourceDomainGroups(
|
|
IN OUT PSID_SET pSidSet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine connects to the primary (resource) domain and
|
|
queries SAM for nested memberships.
|
|
|
|
Arguments:
|
|
|
|
pSidSet - Pointer to set of SIDs. New groups will be added to this set.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
NTSTATUS status;
|
|
OBJECT_ATTRIBUTES obja = {0};
|
|
SAM_HANDLE hSam = 0;
|
|
UNICODE_STRING usRdDcName;
|
|
|
|
|
|
//
|
|
// Open a SAM handle to the resource domain.
|
|
//
|
|
|
|
if (pSidSet->pszRdDcName)
|
|
{
|
|
RtlInitUnicodeString(
|
|
&usRdDcName,
|
|
pSidSet->pszRdDcName);
|
|
|
|
status = SamConnect(
|
|
&usRdDcName,
|
|
&hSam,
|
|
SAM_SERVER_LOOKUP_DOMAIN,
|
|
&obja
|
|
);
|
|
}
|
|
else
|
|
{
|
|
status = SamConnect(
|
|
0,
|
|
&hSam,
|
|
SAM_SERVER_LOOKUP_DOMAIN,
|
|
&obja
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = RtlNtStatusToDosError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Call AuthzpGetAliasMembership to get nested memberships.
|
|
//
|
|
|
|
dwError = AuthzpGetAliasMembership(
|
|
hSam,
|
|
pSidSet->pPrimaryInfo->Sid,
|
|
pSidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Retrieve the SID history.
|
|
//
|
|
|
|
dwError = AuthzpGetSidHistory(
|
|
pSidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if (hSam)
|
|
{
|
|
SamCloseHandle(hSam);
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpGetLocalGroups(
|
|
IN BOOL bAddPrimaryGroup,
|
|
IN OUT PSID_SET pSidSet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine connects to the domain specified by the caller and
|
|
retrieves the list of groups to which the user belongs.
|
|
We are checking the account domain and the builtin domain
|
|
using Sam APIs.
|
|
|
|
Arguments:
|
|
|
|
bAddPrimaryGroup - Boolean that indicates wheter the primary group of the
|
|
user should be computed and added to the sid set.
|
|
|
|
pSidSet - Pointer to the SID of the user for which group membership array
|
|
will be returned.
|
|
|
|
Return Value:
|
|
|
|
Win32 error.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
NTSTATUS status;
|
|
SAM_HANDLE hSam = 0;
|
|
OBJECT_ATTRIBUTES obja = {0};
|
|
BOOL bStatus;
|
|
BYTE sid[SECURITY_MAX_SID_SIZE];
|
|
PSID pBuiltinSid = (PSID)sid;
|
|
DWORD dwLengthSid = SECURITY_MAX_SID_SIZE;
|
|
|
|
|
|
//
|
|
// Open a handle to the SAM on the local computer.
|
|
//
|
|
|
|
status = SamConnect(
|
|
0,
|
|
&hSam,
|
|
SAM_SERVER_LOOKUP_DOMAIN,
|
|
&obja
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = RtlNtStatusToDosError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Add primary group information if requested.
|
|
//
|
|
|
|
if (bAddPrimaryGroup)
|
|
{
|
|
dwError = AuthzpGetPrimaryGroup(
|
|
hSam,
|
|
pSidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Retrieve recursive membership for the account domain.
|
|
//
|
|
|
|
dwError = AuthzpGetAliasMembership(
|
|
hSam,
|
|
pSidSet->pAccountInfo->DomainSid,
|
|
pSidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Retrieve recursive membership for the BUILTIN domain.
|
|
//
|
|
|
|
bStatus = CreateWellKnownSid(
|
|
WinBuiltinDomainSid,
|
|
0,
|
|
pBuiltinSid,
|
|
&dwLengthSid
|
|
);
|
|
|
|
if (bStatus == FALSE)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = AuthzpGetAliasMembership(
|
|
hSam,
|
|
pBuiltinSid,
|
|
pSidSet
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if (hSam)
|
|
{
|
|
SamCloseHandle(hSam);
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpGetSidHistory(
|
|
IN OUT PSID_SET pSidSet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine queries ldap for the sidHistory attribute for every SID
|
|
in the set and adds the history SIDs the the set as well.
|
|
|
|
Arguments:
|
|
|
|
pszDomainName - Name of the domain to connect to.
|
|
|
|
pSidSet - Pointer to set of SIDs. New groups will be added to this set.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
PLDAP pLdap = 0;
|
|
LDAPMessage* pResult = 0;
|
|
LDAPMessage* pEntry = 0;
|
|
PLDAP_BERVAL* ppValue = 0;
|
|
PWCHAR ppszAttributes[] = {L"sidHistory", 0};
|
|
DWORD i, j;
|
|
DWORD dwSidCount;
|
|
DWORD dwValueCount;
|
|
PSID_DESC pSidDesc;
|
|
WCHAR szSidEdn[SECURITY_MAX_SID_SIZE * 2 + 8];
|
|
|
|
|
|
//
|
|
// Open a ldap connection to the primary domain.
|
|
// Get rid of the leading \\ before using the DC name.
|
|
//
|
|
|
|
pLdap = ldap_init(
|
|
pSidSet->pszRdDcName ? pSidSet->pszRdDcName + 2 : 0,
|
|
LDAP_PORT
|
|
);
|
|
|
|
if (pLdap == 0)
|
|
{
|
|
dwError = LdapMapErrorToWin32(LdapGetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pSidSet->pszRdDcName)
|
|
{
|
|
dwError = ldap_set_option(
|
|
pLdap,
|
|
LDAP_OPT_AREC_EXCLUSIVE,
|
|
LDAP_OPT_ON
|
|
);
|
|
|
|
if (dwError != LDAP_SUCCESS)
|
|
{
|
|
dwError = LdapMapErrorToWin32(dwError);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the sign and seal options to true.
|
|
//
|
|
|
|
dwError = ldap_set_option(
|
|
pLdap,
|
|
LDAP_OPT_SIGN,
|
|
LDAP_OPT_ON
|
|
);
|
|
|
|
if (dwError != LDAP_SUCCESS)
|
|
{
|
|
dwError = LdapMapErrorToWin32(dwError);
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = ldap_set_option(
|
|
pLdap,
|
|
LDAP_OPT_ENCRYPT,
|
|
LDAP_OPT_ON
|
|
);
|
|
|
|
if (dwError != LDAP_SUCCESS)
|
|
{
|
|
dwError = LdapMapErrorToWin32(dwError);
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = ldap_bind_s(
|
|
pLdap,
|
|
0,
|
|
0,
|
|
LDAP_AUTH_NEGOTIATE
|
|
);
|
|
|
|
if (dwError != LDAP_SUCCESS)
|
|
{
|
|
dwError = LdapMapErrorToWin32(dwError);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Loop through all SIDs and retrieve the history attribute
|
|
// for each one of them.
|
|
//
|
|
|
|
dwSidCount = pSidSet->dwCount;
|
|
pSidDesc = pSidSet->pSidDesc;
|
|
|
|
for (i=0;i < dwSidCount;i++,pSidDesc++)
|
|
{
|
|
AuthzpConvertSidToEdn(
|
|
pSidDesc->sid,
|
|
szSidEdn
|
|
);
|
|
|
|
dwError = ldap_search_s(
|
|
pLdap,
|
|
szSidEdn,
|
|
LDAP_SCOPE_BASE,
|
|
L"objectClass=*",
|
|
ppszAttributes,
|
|
FALSE,
|
|
&pResult
|
|
);
|
|
|
|
if (dwError != LDAP_SUCCESS)
|
|
{
|
|
if (dwError == LDAP_NO_SUCH_OBJECT)
|
|
{
|
|
//
|
|
// The SID was not found, this is not an error.
|
|
//
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
if (pResult)
|
|
{
|
|
ldap_msgfree(pResult);
|
|
pResult = NULL;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
dwError = LdapMapErrorToWin32(dwError);
|
|
goto Cleanup;
|
|
}
|
|
|
|
pEntry = ldap_first_entry(
|
|
pLdap,
|
|
pResult);
|
|
|
|
if (pEntry == 0)
|
|
{
|
|
dwError = ERROR_ACCESS_DENIED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ppValue = ldap_get_values_len(
|
|
pLdap,
|
|
pEntry,
|
|
ppszAttributes[0]
|
|
);
|
|
|
|
|
|
//
|
|
// Now we have the history attribute for our group.
|
|
// Merge it into the result set.
|
|
//
|
|
|
|
dwValueCount = ldap_count_values_len(ppValue);
|
|
|
|
for (j=0;j < dwValueCount;j++)
|
|
{
|
|
dwError = AuthzpAddSidToSidSet(
|
|
pSidSet,
|
|
(*ppValue[j]).bv_val,
|
|
(*ppValue[j]).bv_len,
|
|
SE_GROUP_MANDATORY
|
|
| SE_GROUP_ENABLED_BY_DEFAULT
|
|
| SE_GROUP_ENABLED,
|
|
0,
|
|
0
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if (ppValue)
|
|
{
|
|
ldap_value_free_len(ppValue);
|
|
ppValue = 0;
|
|
}
|
|
|
|
if (pResult)
|
|
{
|
|
ldap_msgfree(pResult);
|
|
pResult = 0;
|
|
}
|
|
}
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if (ppValue)
|
|
{
|
|
ldap_value_free_len(ppValue);
|
|
}
|
|
|
|
if (pResult)
|
|
{
|
|
ldap_msgfree(pResult);
|
|
}
|
|
|
|
if (pLdap)
|
|
{
|
|
ldap_unbind(pLdap);
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpGetAliasMembership(
|
|
IN SAM_HANDLE hSam,
|
|
IN PSID pDomainSid,
|
|
IN OUT PSID_SET pSidSet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
We try to find nested groups here. This only makes sense on domains
|
|
in native mode.
|
|
This routine calls SamGetAliasMembership iteratively until no
|
|
more nested groups are returned.
|
|
|
|
Arguments:
|
|
|
|
hSam - Handle to the SAM database.
|
|
|
|
pDomainSid - SID of the domain to operate on.
|
|
|
|
ppSidSet - Set of SIDs that are checked for membership. Newly
|
|
found group SIDs are added to the set.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
NTSTATUS status;
|
|
PSID pSid = 0;
|
|
SAM_HANDLE hDomain = 0;
|
|
DWORD dwSidCount;
|
|
DWORD dwSidCountNew;
|
|
DWORD dwSidListSize;
|
|
DWORD i;
|
|
BOOL bAdded;
|
|
PSID* ppSidList = 0;
|
|
PDWORD pRidList = 0;
|
|
PDWORD pRid;
|
|
|
|
|
|
//
|
|
// Get a SAM handle to the domain.
|
|
//
|
|
|
|
status = SamOpenDomain(
|
|
hSam,
|
|
DOMAIN_GET_ALIAS_MEMBERSHIP,
|
|
pDomainSid,
|
|
&hDomain
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = RtlNtStatusToDosError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Retrieve the memberships iteratively.
|
|
//
|
|
|
|
dwSidCount = pSidSet->dwCount;
|
|
dwSidListSize = dwSidCount;
|
|
|
|
ppSidList = (PSID*)AuthzpAlloc(
|
|
dwSidCount * sizeof(PSID)
|
|
);
|
|
|
|
if (ppSidList == 0)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
for (i=0;i < dwSidCount;i++)
|
|
{
|
|
ppSidList[i] = pSidSet->pSidDesc[i].sid;
|
|
}
|
|
|
|
do
|
|
{
|
|
status = SamGetAliasMembership(
|
|
hDomain,
|
|
dwSidCount,
|
|
ppSidList,
|
|
&dwSidCountNew,
|
|
&pRidList
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = RtlNtStatusToDosError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (dwSidCountNew > dwSidListSize)
|
|
{
|
|
AuthzpFree(ppSidList);
|
|
|
|
ppSidList = (PSID*)AuthzpAlloc(
|
|
dwSidCountNew * sizeof(PSID)
|
|
);
|
|
|
|
if (ppSidList == 0)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwSidListSize = dwSidCountNew;
|
|
}
|
|
|
|
dwSidCount = 0;
|
|
pRid = pRidList;
|
|
|
|
for (i=0;i < dwSidCountNew;i++,pRid++)
|
|
{
|
|
status = SamRidToSid(
|
|
hDomain,
|
|
*pRid,
|
|
&pSid
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = RtlNtStatusToDosError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = AuthzpAddSidToSidSet(
|
|
pSidSet,
|
|
pSid,
|
|
0,
|
|
SE_GROUP_MANDATORY
|
|
| SE_GROUP_ENABLED_BY_DEFAULT
|
|
| SE_GROUP_ENABLED,
|
|
&bAdded,
|
|
ppSidList + dwSidCount
|
|
);
|
|
|
|
SamFreeMemory(pSid);
|
|
pSid = 0;
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (bAdded)
|
|
{
|
|
dwSidCount++;
|
|
}
|
|
}
|
|
|
|
if (pRidList)
|
|
{
|
|
SamFreeMemory(pRidList);
|
|
pRidList = 0;
|
|
}
|
|
}
|
|
while (dwSidCount);
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if (pRidList)
|
|
{
|
|
SamFreeMemory(pRidList);
|
|
}
|
|
|
|
if (ppSidList)
|
|
{
|
|
AuthzpFree(ppSidList);
|
|
}
|
|
|
|
if (hDomain)
|
|
{
|
|
SamCloseHandle(hDomain);
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpGetPrimaryGroup(
|
|
IN SAM_HANDLE hSam,
|
|
IN OUT PSID_SET pSidSet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
Add the primary group of the user to the sid set.
|
|
|
|
Arguments:
|
|
|
|
hSam - Handle to the SAM database.
|
|
|
|
pSidSet - Add the sid of the primary group to this set.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
NTSTATUS status;
|
|
SAM_HANDLE hDomain = 0;
|
|
SAM_HANDLE hUser = 0;
|
|
PUSER_PRIMARY_GROUP_INFORMATION
|
|
pInfo = 0;
|
|
PSID pPrimaryGroupSid = NULL;
|
|
DWORD dwRelativeId = 0;
|
|
|
|
|
|
//
|
|
// Open the account domain.
|
|
//
|
|
|
|
status = SamOpenDomain(
|
|
hSam,
|
|
DOMAIN_LOOKUP,
|
|
pSidSet->pDomainSid,
|
|
&hDomain
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = RtlNtStatusToDosError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Extract the rid from the user sid.
|
|
//
|
|
|
|
dwRelativeId = *RtlSubAuthoritySid(
|
|
pSidSet->pUserSid,
|
|
*RtlSubAuthorityCountSid(pSidSet->pUserSid) - 1
|
|
);
|
|
|
|
|
|
//
|
|
// Open the user for read.
|
|
//
|
|
|
|
status = SamOpenUser(
|
|
hDomain,
|
|
USER_READ_GENERAL,
|
|
dwRelativeId,
|
|
&hUser
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = RtlNtStatusToDosError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the primary group information for the user.
|
|
//
|
|
|
|
status = SamQueryInformationUser(
|
|
hUser,
|
|
UserPrimaryGroupInformation,
|
|
&pInfo
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = RtlNtStatusToDosError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Convert the group rid to a sid.
|
|
//
|
|
|
|
status = SamRidToSid(
|
|
hDomain,
|
|
pInfo->PrimaryGroupId,
|
|
&pPrimaryGroupSid
|
|
);
|
|
|
|
SamFreeMemory(pInfo);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = RtlNtStatusToDosError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Add the group sid to the set.
|
|
//
|
|
|
|
dwError = AuthzpAddSidToSidSet(
|
|
pSidSet,
|
|
pPrimaryGroupSid,
|
|
0,
|
|
SE_GROUP_ENABLED,
|
|
0,
|
|
0
|
|
);
|
|
|
|
SamFreeMemory(pPrimaryGroupSid);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if (hUser)
|
|
{
|
|
SamCloseHandle(hUser);
|
|
}
|
|
|
|
if (hDomain)
|
|
{
|
|
SamCloseHandle(hDomain);
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpInitializeSidSetByName(
|
|
IN PUNICODE_STRING pusUserName,
|
|
IN PUNICODE_STRING pusDomainName,
|
|
IN DWORD dwFlags,
|
|
IN PSID_SET pSidSet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
Initializes a sid set and reserves memory for the
|
|
max amount of memory it will ever need.
|
|
The memory is not allocated yet. This only happens as SIDs get
|
|
added to the set. All members are initialized to meaningful values.
|
|
|
|
Arguments:
|
|
|
|
pSidSet - The sid set to operate on.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
SYSTEM_INFO sysInfo;
|
|
|
|
if (s_dwPageSize == 0)
|
|
{
|
|
GetSystemInfo(&sysInfo);
|
|
|
|
s_dwPageSize = sysInfo.dwPageSize;
|
|
}
|
|
|
|
pSidSet->pSidDesc = (PSID_DESC)VirtualAlloc(
|
|
0,
|
|
c_dwMaxSidCount * sizeof(SID_DESC),
|
|
MEM_RESERVE,
|
|
PAGE_NOACCESS
|
|
);
|
|
|
|
if (pSidSet->pSidDesc == 0)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
pSidSet->dwCount = 0;
|
|
pSidSet->dwMaxCount = 0;
|
|
|
|
pSidSet->dwBaseCount = 0;
|
|
pSidSet->dwFlags = dwFlags;
|
|
|
|
pSidSet->pUserSid = 0;
|
|
pSidSet->pDomainSid = 0;
|
|
pSidSet->pusUserName = pusUserName;
|
|
|
|
|
|
//
|
|
// Verify for once we got a valid domain.
|
|
// Otherwise we assume we got a UPN in pusUserName.
|
|
//
|
|
|
|
if (pusDomainName &&
|
|
pusDomainName->Length &&
|
|
pusDomainName->Buffer)
|
|
{
|
|
pSidSet->pusDomainName = pusDomainName;
|
|
}
|
|
else
|
|
{
|
|
pSidSet->pusDomainName = 0;
|
|
}
|
|
|
|
pSidSet->pNames = 0;
|
|
pSidSet->pDomains = 0;
|
|
pSidSet->pDomainsName = 0;
|
|
pSidSet->pSids = 0;
|
|
pSidSet->sidUse = SidTypeUnknown;
|
|
|
|
pSidSet->pAccountInfo = 0;
|
|
pSidSet->pPrimaryInfo = 0;
|
|
pSidSet->pPrimaryInfoName = 0;
|
|
pSidSet->bStandalone = TRUE;
|
|
pSidSet->bSkipNonLocal = FALSE;
|
|
|
|
pSidSet->pUdDcInfo = 0;
|
|
pSidSet->pPdDcInfo = 0;
|
|
pSidSet->pRdDcInfo = 0;
|
|
|
|
pSidSet->pUdBasicInfo = 0;
|
|
pSidSet->pPdBasicInfo = 0;
|
|
pSidSet->pRdBasicInfo = 0;
|
|
|
|
pSidSet->pszUdDcName = 0;
|
|
pSidSet->pszRdDcName = 0;
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpInitializeSidSetBySid(
|
|
IN PSID pUserSid,
|
|
IN DWORD dwFlags,
|
|
IN PSID_SET pSidSet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
Initializes a sid set and reserves memory for the
|
|
max amount of memory it will ever need.
|
|
The memory is not allocated yet. This only happens as SIDs get
|
|
added to the set. All members are initialized to meaningful values.
|
|
|
|
Arguments:
|
|
|
|
pSidSet - The sid set to operate on.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
SYSTEM_INFO sysInfo;
|
|
|
|
if (s_dwPageSize == 0)
|
|
{
|
|
GetSystemInfo(&sysInfo);
|
|
|
|
s_dwPageSize = sysInfo.dwPageSize;
|
|
}
|
|
|
|
if (!RtlValidSid(pUserSid))
|
|
{
|
|
dwError = ERROR_INVALID_SID;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pSidSet->pSidDesc = (PSID_DESC)VirtualAlloc(
|
|
0,
|
|
c_dwMaxSidCount * sizeof(SID_DESC),
|
|
MEM_RESERVE,
|
|
PAGE_NOACCESS
|
|
);
|
|
|
|
if (pSidSet->pSidDesc == 0)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
pSidSet->dwCount = 0;
|
|
pSidSet->dwMaxCount = 0;
|
|
|
|
pSidSet->dwBaseCount = 0;
|
|
pSidSet->dwFlags = dwFlags;
|
|
|
|
pSidSet->pUserSid = pUserSid;
|
|
pSidSet->pDomainSid = 0;
|
|
pSidSet->pusUserName = 0;
|
|
pSidSet->pusDomainName = 0;
|
|
|
|
pSidSet->pNames = 0;
|
|
pSidSet->pDomains = 0;
|
|
pSidSet->pDomainsName = 0;
|
|
pSidSet->pSids = 0;
|
|
pSidSet->sidUse = SidTypeUnknown;
|
|
|
|
pSidSet->pAccountInfo = 0;
|
|
pSidSet->pPrimaryInfo = 0;
|
|
pSidSet->pPrimaryInfoName = 0;
|
|
pSidSet->bStandalone = TRUE;
|
|
pSidSet->bSkipNonLocal = FALSE;
|
|
|
|
pSidSet->pUdDcInfo = 0;
|
|
pSidSet->pPdDcInfo = 0;
|
|
pSidSet->pRdDcInfo = 0;
|
|
|
|
pSidSet->pUdBasicInfo = 0;
|
|
pSidSet->pPdBasicInfo = 0;
|
|
pSidSet->pRdBasicInfo = 0;
|
|
|
|
pSidSet->pszUdDcName = 0;
|
|
pSidSet->pszRdDcName = 0;
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpDeleteSidSet(
|
|
IN PSID_SET pSidSet
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
Deletes all memory allocated to the sid set
|
|
structure and resets all members to zero.
|
|
|
|
Arguments:
|
|
|
|
pSidSet - The sid set to operate on.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (pSidSet->pSidDesc)
|
|
{
|
|
VirtualFree(pSidSet->pSidDesc, 0, MEM_RELEASE);
|
|
}
|
|
|
|
if (pSidSet->pNames)
|
|
{
|
|
LsaFreeMemory(pSidSet->pNames);
|
|
}
|
|
|
|
if (pSidSet->pDomains)
|
|
{
|
|
LsaFreeMemory(pSidSet->pDomains);
|
|
}
|
|
|
|
if (pSidSet->pDomainsName)
|
|
{
|
|
AuthzpFree(pSidSet->pDomainsName);
|
|
}
|
|
|
|
if (pSidSet->pSids)
|
|
{
|
|
LsaFreeMemory(pSidSet->pSids);
|
|
}
|
|
|
|
if (pSidSet->pAccountInfo)
|
|
{
|
|
LsaFreeMemory(pSidSet->pAccountInfo);
|
|
}
|
|
|
|
if (pSidSet->pPrimaryInfo)
|
|
{
|
|
LsaFreeMemory(pSidSet->pPrimaryInfo);
|
|
}
|
|
|
|
if (pSidSet->pPrimaryInfoName)
|
|
{
|
|
AuthzpFree(pSidSet->pPrimaryInfoName);
|
|
}
|
|
|
|
if (pSidSet->pUdDcInfo)
|
|
{
|
|
NetApiBufferFree(pSidSet->pUdDcInfo);
|
|
}
|
|
|
|
if (pSidSet->pPdDcInfo)
|
|
{
|
|
NetApiBufferFree(pSidSet->pPdDcInfo);
|
|
}
|
|
|
|
if (pSidSet->pUdBasicInfo)
|
|
{
|
|
DsRoleFreeMemory(pSidSet->pUdBasicInfo);
|
|
}
|
|
|
|
if (pSidSet->pPdBasicInfo)
|
|
{
|
|
DsRoleFreeMemory(pSidSet->pPdBasicInfo);
|
|
}
|
|
|
|
RtlZeroMemory(
|
|
pSidSet,
|
|
sizeof(SID_SET));
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpAddSidToSidSet(
|
|
IN PSID_SET pSidSet,
|
|
IN PSID pSid,
|
|
IN DWORD dwSidLength OPTIONAL,
|
|
IN DWORD dwAttributes,
|
|
OUT PBOOL pbAdded OPTIONAL,
|
|
OUT PSID* ppSid OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
Check if the given SID already exists in the set. If yes, return.
|
|
Otherwise, add it to the set.
|
|
|
|
Arguments:
|
|
|
|
pSidSet - The sid set to operate on.
|
|
|
|
pSid - The SID to add to the set.
|
|
|
|
dwSidLength - Length of the SID in bytes. If zero is passed in,
|
|
the routine calculates the length itself.
|
|
|
|
dwAttributes - Attributes of the SID like in the
|
|
SID_AND_ATTRIBUTES structure.
|
|
|
|
pbAdded - Optional pointer that receives indication if the SID
|
|
was indeed added or not (because it was a duplicate).
|
|
|
|
ppSid - Optional pointer to where the new sid is stored.
|
|
|
|
Return Value:
|
|
|
|
Win32 error code.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
DWORD i;
|
|
DWORD dwSize;
|
|
BOOL bAdded = FALSE;
|
|
PSID_DESC pSidDesc;
|
|
|
|
if (dwSidLength == 0)
|
|
{
|
|
dwSidLength = RtlLengthSid(pSid);
|
|
}
|
|
|
|
pSidDesc = pSidSet->pSidDesc;
|
|
|
|
for (i=0;i < pSidSet->dwCount;i++,pSidDesc++)
|
|
{
|
|
if (dwSidLength == pSidDesc->dwLength)
|
|
{
|
|
if (RtlEqualSid(
|
|
pSid,
|
|
pSidDesc->sid))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pSidSet->dwCount >= pSidSet->dwMaxCount)
|
|
{
|
|
if (pSidSet->dwCount >= c_dwMaxSidCount)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Commit one more page in the buffer.
|
|
//
|
|
|
|
dwSize = (pSidSet->dwCount + 1) * sizeof(SID_DESC);
|
|
dwSize += s_dwPageSize - 1;
|
|
dwSize &= ~(s_dwPageSize - 1);
|
|
|
|
pSidDesc = (PSID_DESC)VirtualAlloc(
|
|
pSidSet->pSidDesc,
|
|
dwSize,
|
|
MEM_COMMIT,
|
|
PAGE_READWRITE
|
|
);
|
|
|
|
if (pSidDesc != pSidSet->pSidDesc)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
pSidSet->dwMaxCount = dwSize / sizeof(SID_DESC);
|
|
}
|
|
|
|
pSidDesc = pSidSet->pSidDesc + pSidSet->dwCount;
|
|
|
|
pSidDesc->dwAttributes = dwAttributes;
|
|
pSidDesc->dwLength = dwSidLength;
|
|
|
|
RtlCopyMemory(
|
|
pSidDesc->sid,
|
|
pSid,
|
|
dwSidLength
|
|
);
|
|
|
|
bAdded = TRUE;
|
|
|
|
pSidSet->dwCount++;
|
|
|
|
if (ppSid)
|
|
{
|
|
*ppSid = pSidDesc->sid;
|
|
}
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if (pbAdded)
|
|
{
|
|
*pbAdded = bAdded;
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpGetUserDomainSid(
|
|
PSID_SET pSidSet
|
|
)
|
|
{
|
|
DWORD dwError;
|
|
NTSTATUS status;
|
|
LSA_HANDLE hPolicy = 0;
|
|
OBJECT_ATTRIBUTES obja = {0};
|
|
SECURITY_QUALITY_OF_SERVICE sqos;
|
|
WCHAR wc[2] = L"\\";
|
|
UNICODE_STRING usName = {0};
|
|
PUNICODE_STRING pusName = 0;
|
|
|
|
|
|
//
|
|
// Build the string domain - name string that should be
|
|
// translated.
|
|
//
|
|
|
|
if (pSidSet->pusDomainName)
|
|
{
|
|
usName.MaximumLength =
|
|
pSidSet->pusDomainName->Length +
|
|
sizeof(WCHAR) +
|
|
pSidSet->pusUserName->Length +
|
|
sizeof(WCHAR);
|
|
|
|
usName.Buffer = (PWSTR)LocalAlloc(
|
|
LMEM_FIXED,
|
|
usName.MaximumLength
|
|
);
|
|
|
|
if (usName.Buffer == 0)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
usName.Buffer,
|
|
pSidSet->pusDomainName->Buffer,
|
|
pSidSet->pusDomainName->Length
|
|
);
|
|
|
|
usName.Length = (USHORT)(usName.Length + pSidSet->pusDomainName->Length);
|
|
|
|
RtlCopyMemory(
|
|
((PBYTE)usName.Buffer) + usName.Length,
|
|
wc + 0,
|
|
sizeof(WCHAR)
|
|
);
|
|
|
|
usName.Length += sizeof(WCHAR);
|
|
|
|
RtlCopyMemory(
|
|
((PBYTE)usName.Buffer) + usName.Length,
|
|
pSidSet->pusUserName->Buffer,
|
|
pSidSet->pusUserName->Length
|
|
);
|
|
|
|
usName.Length = (USHORT)(usName.Length + pSidSet->pusUserName->Length);
|
|
|
|
RtlCopyMemory(
|
|
((PBYTE)usName.Buffer) + usName.Length,
|
|
wc + 1,
|
|
sizeof(WCHAR)
|
|
);
|
|
|
|
pusName = &usName;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Assume we got a UPN.
|
|
//
|
|
|
|
pusName = pSidSet->pusUserName;
|
|
}
|
|
|
|
//
|
|
// set up the object attributes prior to opening the LSA
|
|
//
|
|
|
|
sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
|
|
sqos.ImpersonationLevel = SecurityImpersonation;
|
|
sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
|
sqos.EffectiveOnly = FALSE;
|
|
|
|
obja.SecurityQualityOfService = &sqos;
|
|
|
|
|
|
//
|
|
// open the LSA policy
|
|
//
|
|
|
|
status = LsaOpenPolicy(
|
|
0,
|
|
&obja,
|
|
POLICY_LOOKUP_NAMES,
|
|
&hPolicy
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = LsaNtStatusToWinError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
status = LsaLookupNames2(
|
|
hPolicy,
|
|
0, // no flags
|
|
1,
|
|
pusName,
|
|
&pSidSet->pDomains,
|
|
&pSidSet->pSids
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = LsaNtStatusToWinError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pSidSet->pSids == 0)
|
|
{
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
switch (pSidSet->pSids->Use)
|
|
{
|
|
case SidTypeDomain:
|
|
case SidTypeInvalid:
|
|
case SidTypeUnknown:
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// The name was successfully translated.
|
|
// There should be exactly one domain and its index should be zero.
|
|
//
|
|
|
|
ASSERT(pSidSet->pDomains->Entries == 1);
|
|
ASSERT(pSidSet->pDomains->Domains != 0);
|
|
ASSERT(pSidSet->pSids->DomainIndex == 0);
|
|
|
|
pSidSet->pUserSid = pSidSet->pSids->Sid;
|
|
pSidSet->pDomainSid = pSidSet->pDomains->Domains->Sid;
|
|
pSidSet->sidUse = pSidSet->pSids->Use;
|
|
|
|
pSidSet->pDomainsName = (PWSTR) AuthzpAlloc(pSidSet->pDomains->Domains->Name.Length + sizeof(WCHAR));
|
|
|
|
if (pSidSet->pDomainsName == NULL)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
wcsncpy(pSidSet->pDomainsName, pSidSet->pDomains->Domains->Name.Buffer, pSidSet->pDomains->Domains->Name.Length/sizeof(WCHAR));
|
|
pSidSet->pDomainsName[pSidSet->pDomains->Domains->Name.Length/sizeof(WCHAR)] = L'\0';
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if (hPolicy)
|
|
{
|
|
LsaClose(hPolicy);
|
|
}
|
|
|
|
if (usName.Buffer)
|
|
{
|
|
AuthzpFree((HLOCAL)usName.Buffer);
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpGetUserDomainName(
|
|
PSID_SET pSidSet
|
|
)
|
|
{
|
|
DWORD dwError;
|
|
NTSTATUS status;
|
|
LSA_HANDLE hPolicy = 0;
|
|
OBJECT_ATTRIBUTES obja = {0};
|
|
SECURITY_QUALITY_OF_SERVICE sqos;
|
|
|
|
|
|
//
|
|
// set up the object attributes prior to opening the LSA
|
|
//
|
|
|
|
sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
|
|
sqos.ImpersonationLevel = SecurityImpersonation;
|
|
sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
|
sqos.EffectiveOnly = FALSE;
|
|
|
|
obja.SecurityQualityOfService = &sqos;
|
|
|
|
|
|
//
|
|
// open the LSA policy
|
|
//
|
|
|
|
status = LsaOpenPolicy(
|
|
0,
|
|
&obja,
|
|
POLICY_LOOKUP_NAMES,
|
|
&hPolicy
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = LsaNtStatusToWinError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
status = LsaLookupSids(
|
|
hPolicy,
|
|
1,
|
|
&pSidSet->pUserSid,
|
|
&pSidSet->pDomains,
|
|
&pSidSet->pNames
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = LsaNtStatusToWinError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pSidSet->pNames == 0)
|
|
{
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
switch (pSidSet->pNames->Use)
|
|
{
|
|
case SidTypeDomain:
|
|
case SidTypeUnknown:
|
|
case SidTypeInvalid:
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// The SID was successfully translated.
|
|
// There should be exactly one domain and its index should be zero.
|
|
//
|
|
|
|
ASSERT(pSidSet->pDomains->Entries == 1);
|
|
ASSERT(pSidSet->pDomains->Domains != 0);
|
|
ASSERT(pSidSet->pNames->DomainIndex == 0);
|
|
|
|
pSidSet->pDomainSid = pSidSet->pDomains->Domains->Sid;
|
|
pSidSet->pusUserName = &pSidSet->pNames->Name;
|
|
pSidSet->pusDomainName = &pSidSet->pDomains->Domains->Name;
|
|
pSidSet->sidUse = pSidSet->pNames->Use;
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if (hPolicy)
|
|
{
|
|
LsaClose(hPolicy);
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpGetLocalInfo(
|
|
IN PSID_SET pSidSet
|
|
)
|
|
{
|
|
DWORD dwError;
|
|
NTSTATUS status;
|
|
LSA_HANDLE hPolicy = 0;
|
|
OBJECT_ATTRIBUTES obja = {0};
|
|
SECURITY_QUALITY_OF_SERVICE sqos;
|
|
NT_PRODUCT_TYPE ProductType;
|
|
PPOLICY_LSA_SERVER_ROLE_INFO pRole = 0;
|
|
|
|
|
|
//
|
|
// Set up the object attributes prior to opening the LSA.
|
|
//
|
|
|
|
sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
|
|
sqos.ImpersonationLevel = SecurityImpersonation;
|
|
sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
|
|
sqos.EffectiveOnly = FALSE;
|
|
|
|
obja.SecurityQualityOfService = &sqos;
|
|
|
|
|
|
//
|
|
// open LSA policy
|
|
//
|
|
|
|
status = LsaOpenPolicy(
|
|
0,
|
|
&obja,
|
|
POLICY_VIEW_LOCAL_INFORMATION,
|
|
&hPolicy
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = LsaNtStatusToWinError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
status = LsaQueryInformationPolicy(
|
|
hPolicy,
|
|
PolicyAccountDomainInformation,
|
|
(PVOID*)&pSidSet->pAccountInfo
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = LsaNtStatusToWinError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
status = LsaQueryInformationPolicy(
|
|
hPolicy,
|
|
PolicyPrimaryDomainInformation,
|
|
(PVOID*)&pSidSet->pPrimaryInfo
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = LsaNtStatusToWinError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
pSidSet->pPrimaryInfoName = (PWSTR) AuthzpAlloc(pSidSet->pPrimaryInfo->Name.Length + sizeof(WCHAR));
|
|
|
|
if (pSidSet->pPrimaryInfoName == NULL)
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
wcsncpy(pSidSet->pPrimaryInfoName, pSidSet->pPrimaryInfo->Name.Buffer, pSidSet->pPrimaryInfo->Name.Length/sizeof(WCHAR));
|
|
pSidSet->pPrimaryInfoName[pSidSet->pPrimaryInfo->Name.Length/sizeof(WCHAR)] = L'\0';
|
|
|
|
//
|
|
// Determine the role of the machine.
|
|
//
|
|
|
|
if (RtlGetNtProductType(&ProductType) == FALSE)
|
|
{
|
|
dwError = ERROR_GEN_FAILURE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
switch (ProductType)
|
|
{
|
|
case NtProductWinNt:
|
|
case NtProductServer:
|
|
pSidSet->bStandalone = pSidSet->pPrimaryInfo->Sid == 0 ? TRUE : FALSE;
|
|
break;
|
|
|
|
case NtProductLanManNt:
|
|
status = LsaQueryInformationPolicy(
|
|
hPolicy,
|
|
PolicyLsaServerRoleInformation,
|
|
(PVOID*)&pRole
|
|
);
|
|
|
|
if (!NT_SUCCESS(status))
|
|
{
|
|
dwError = LsaNtStatusToWinError(status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
pSidSet->bStandalone = FALSE;
|
|
|
|
if (pRole->LsaServerRole == PolicyServerRolePrimary)
|
|
{
|
|
//
|
|
// If we think we're a primary domain controller, we'll need to
|
|
// guard against the case where we're actually standalone
|
|
// during setup
|
|
//
|
|
|
|
if (pSidSet->pPrimaryInfo->Sid == 0 ||
|
|
pSidSet->pAccountInfo->DomainSid == 0 ||
|
|
!RtlEqualSid(
|
|
pSidSet->pPrimaryInfo->Sid,
|
|
pSidSet->pAccountInfo->DomainSid))
|
|
{
|
|
pSidSet->bStandalone = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
dwError = ERROR_GEN_FAILURE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if (pRole)
|
|
{
|
|
LsaFreeMemory(pRole);
|
|
}
|
|
|
|
if (hPolicy)
|
|
{
|
|
LsaClose(hPolicy);
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
AuthzpGetDcName(
|
|
IN LPCTSTR pszDomain,
|
|
IN OUT PDOMAIN_CONTROLLER_INFO* ppDcInfo
|
|
)
|
|
{
|
|
DWORD dwError;
|
|
|
|
|
|
//
|
|
// First try to get a DC with DS running.
|
|
//
|
|
|
|
dwError = DsGetDcName(
|
|
0,
|
|
pszDomain,
|
|
0,
|
|
0,
|
|
DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_DNS_NAME,
|
|
ppDcInfo
|
|
);
|
|
|
|
if (dwError == ERROR_NO_SUCH_DOMAIN)
|
|
{
|
|
//
|
|
// Try again with no flags set, because this is the only way
|
|
// an NT4 domain will reveal its secrets.
|
|
//
|
|
|
|
dwError = DsGetDcName(
|
|
0,
|
|
pszDomain,
|
|
0,
|
|
0,
|
|
0,
|
|
ppDcInfo
|
|
);
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
VOID
|
|
AuthzpConvertSidToEdn(
|
|
IN PSID pSid,
|
|
OUT PWSTR pszSidEdn
|
|
)
|
|
|
|
/*++
|
|
|
|
Print pSid into pszSidEdn as an Extended Distinguished Name.
|
|
|
|
pszSidEdn should provide room for at least
|
|
SECURITY_MAX_SID_SIZE * 2 + 8 WCHARs.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwLength = RtlLengthSid(pSid);
|
|
DWORD i;
|
|
PBYTE pbSid = (PBYTE)pSid;
|
|
PWCHAR pChar = pszSidEdn;
|
|
static WCHAR szHex[] = L"0123456789ABCDEF";
|
|
|
|
*pChar++ = L'<';
|
|
*pChar++ = L'S';
|
|
*pChar++ = L'I';
|
|
*pChar++ = L'D';
|
|
*pChar++ = L'=';
|
|
|
|
for (i=0;i < dwLength;i++,pbSid++)
|
|
{
|
|
*pChar++ = szHex[*pbSid >> 4];
|
|
*pChar++ = szHex[*pbSid & 0x0F];
|
|
}
|
|
|
|
*pChar++ = L'>';
|
|
*pChar = L'\0';
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpAllocateAndInitializeClientContext(
|
|
OUT PAUTHZI_CLIENT_CONTEXT *ppCC,
|
|
IN PAUTHZI_CLIENT_CONTEXT Server,
|
|
IN DWORD Revision,
|
|
IN LUID Identifier,
|
|
IN LARGE_INTEGER ExpirationTime,
|
|
IN DWORD Flags,
|
|
IN DWORD SidCount,
|
|
IN DWORD SidLength,
|
|
IN PSID_AND_ATTRIBUTES Sids,
|
|
IN DWORD RestrictedSidCount,
|
|
IN DWORD RestrictedSidLength,
|
|
IN PSID_AND_ATTRIBUTES RestrictedSids,
|
|
IN DWORD PrivilegeCount,
|
|
IN DWORD PrivilegeLength,
|
|
IN PLUID_AND_ATTRIBUTES Privileges,
|
|
IN LUID AuthenticationId,
|
|
IN PAUTHZI_HANDLE AuthzHandleHead,
|
|
IN PAUTHZI_RESOURCE_MANAGER pRM
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine initializes fields in a client context. It is called by all the
|
|
AuthzInitializClientContextFrom* routines.
|
|
|
|
Arguments:
|
|
|
|
ppCC - Returns the newly allocated and initialized client context structure.
|
|
|
|
Rest of the parameters are copied into the client context. For explanation
|
|
of these, see the definition of AUTHZI_CLIENT_CONTEXT.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PAUTHZI_CLIENT_CONTEXT pCC = (PAUTHZI_CLIENT_CONTEXT) AuthzpAlloc(sizeof(AUTHZI_CLIENT_CONTEXT));
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pCC))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
*ppCC = pCC;
|
|
|
|
RtlZeroMemory(
|
|
pCC,
|
|
sizeof(AUTHZ_CLIENT_CONTEXT_HANDLE)
|
|
);
|
|
|
|
pCC->AuthenticationId = AuthenticationId;
|
|
pCC->AuthzHandleHead = AuthzHandleHead;
|
|
pCC->ExpirationTime = ExpirationTime;
|
|
pCC->Flags = Flags;
|
|
pCC->Identifier = Identifier;
|
|
pCC->pResourceManager = pRM;
|
|
pCC->PrivilegeCount = PrivilegeCount;
|
|
pCC->PrivilegeLength = PrivilegeLength;
|
|
pCC->Privileges = Privileges;
|
|
pCC->RestrictedSidCount = RestrictedSidCount;
|
|
pCC->RestrictedSidLength = RestrictedSidLength;
|
|
pCC->RestrictedSids = RestrictedSids;
|
|
pCC->Revision = Revision;
|
|
pCC->Server = Server;
|
|
pCC->SidCount = SidCount;
|
|
pCC->SidLength = SidLength;
|
|
pCC->Sids = Sids;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpAddDynamicSidsToToken(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PAUTHZI_RESOURCE_MANAGER pRM,
|
|
IN PVOID DynamicGroupArgs,
|
|
IN PSID_AND_ATTRIBUTES Sids,
|
|
IN DWORD SidLength,
|
|
IN DWORD SidCount,
|
|
IN PSID_AND_ATTRIBUTES RestrictedSids,
|
|
IN DWORD RestrictedSidLength,
|
|
IN DWORD RestrictedSidCount,
|
|
IN PLUID_AND_ATTRIBUTES Privileges,
|
|
IN DWORD PrivilegeLength,
|
|
IN DWORD PrivilegeCount,
|
|
IN BOOL bAllocated
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine computes resource manager specific groups and add them to the
|
|
client context. This is a worker routine for all AuthzInitializeFrom*
|
|
routines.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context structure for which the three fields
|
|
will be set - sids, restricted sids, privileges.
|
|
|
|
pRM - Pointer to the resource manager structure, supplies the callback
|
|
function to be used.
|
|
|
|
DynamicGroupArgs - Caller supplied argument pointer to be passed as an input
|
|
to the callback function that'd compute dynamic groups
|
|
|
|
Sids - The sid and atttribute array for the normal part of the client
|
|
context.
|
|
|
|
SidLength - Size of the buffer required to hold this array.
|
|
|
|
SidCount - Number of sids in the array.
|
|
|
|
RestrictedSids - The sid and atttribute array for the normal part of the
|
|
client context.
|
|
|
|
RestrictedSidLength - Size of the buffer required to hold this array.
|
|
|
|
RestrictedSidCount - Number of restricted sids in the array.
|
|
|
|
Privileges - The privilege and attribute array.
|
|
|
|
PrivilegeLength - Size required to hold this array.
|
|
|
|
PrivilegeCount - The number of privileges in the array.
|
|
|
|
bAllocated - To specify whether the Sids and RestrictedSids pointers in
|
|
client context have been allocated separately.
|
|
|
|
When the client context has been created thru a token, the two pointers
|
|
point somewhere into a buffer and a new buffer has to be allocated to store
|
|
these.
|
|
|
|
When the client context has been created thru a sid, the buffer is a valid
|
|
allocated one. If no dynamic groups need to be added then we do not have to
|
|
do anything int this case.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b = TRUE;
|
|
PSID_AND_ATTRIBUTES pRMSids = NULL;
|
|
PSID_AND_ATTRIBUTES pRMRestrictedSids = NULL;
|
|
PSID_AND_ATTRIBUTES pLocalSids = NULL;
|
|
PSID_AND_ATTRIBUTES pLocalRestrictedSids = NULL;
|
|
PLUID_AND_ATTRIBUTES pLocalPrivileges = NULL;
|
|
DWORD RMSidCount = 0;
|
|
DWORD RMRestrictedSidCount = 0;
|
|
DWORD LocalSidLength = 0;
|
|
DWORD LocalRestrictedSidLength = 0;
|
|
DWORD i = 0;
|
|
|
|
//
|
|
// Compute dynamic groups.
|
|
//
|
|
|
|
if (AUTHZ_NON_NULL_PTR(pRM->pfnComputeDynamicGroups))
|
|
{
|
|
b = pRM->pfnComputeDynamicGroups(
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
DynamicGroupArgs,
|
|
&pRMSids,
|
|
&RMSidCount,
|
|
&pRMRestrictedSids,
|
|
&RMRestrictedSidCount
|
|
);
|
|
|
|
if (!b) goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Copy the existing sids as well as the dynamic ones into a new buffer if
|
|
// needed.
|
|
//
|
|
|
|
if ((0 != RMSidCount) || !bAllocated)
|
|
{
|
|
LocalSidLength = SidLength + RMSidCount * sizeof(SID_AND_ATTRIBUTES);
|
|
|
|
for (i = 0; i < RMSidCount; i++)
|
|
{
|
|
LocalSidLength += RtlLengthSid(pRMSids[i].Sid);
|
|
}
|
|
|
|
pLocalSids = (PSID_AND_ATTRIBUTES) AuthzpAlloc(LocalSidLength);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pLocalSids))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pCC->SidCount = RMSidCount + SidCount;
|
|
pCC->Sids = pLocalSids;
|
|
|
|
b = AuthzpCopySidsAndAttributes(
|
|
pLocalSids,
|
|
Sids,
|
|
SidCount,
|
|
pRMSids,
|
|
RMSidCount
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!FLAG_ON(pCC->Sids[0].Attributes, SE_GROUP_USE_FOR_DENY_ONLY))
|
|
{
|
|
pCC->Sids[0].Attributes |= SE_GROUP_ENABLED;
|
|
}
|
|
|
|
pCC->SidLength = LocalSidLength;
|
|
}
|
|
|
|
if ((0 != RMRestrictedSidCount) || !bAllocated)
|
|
{
|
|
LocalRestrictedSidLength = RestrictedSidLength + RMRestrictedSidCount * sizeof(SID_AND_ATTRIBUTES);
|
|
|
|
for (i = 0; i < RMRestrictedSidCount; i++)
|
|
{
|
|
LocalRestrictedSidLength += RtlLengthSid(pRMRestrictedSids[i].Sid);
|
|
}
|
|
|
|
if (LocalRestrictedSidLength > 0)
|
|
{
|
|
pLocalRestrictedSids = (PSID_AND_ATTRIBUTES) AuthzpAlloc(LocalRestrictedSidLength);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pLocalRestrictedSids))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
pCC->RestrictedSidCount = RMRestrictedSidCount + RestrictedSidCount;
|
|
pCC->RestrictedSids = pLocalRestrictedSids;
|
|
|
|
b = AuthzpCopySidsAndAttributes(
|
|
pLocalRestrictedSids,
|
|
RestrictedSids,
|
|
RestrictedSidCount,
|
|
pRMRestrictedSids,
|
|
RMRestrictedSidCount
|
|
);
|
|
|
|
if (!b) goto Cleanup;
|
|
|
|
pCC->RestrictedSidLength = LocalRestrictedSidLength;
|
|
}
|
|
|
|
//
|
|
// Privileges need to copied only in the case of initilize from token.
|
|
//
|
|
|
|
if (PrivilegeLength > 0)
|
|
{
|
|
pLocalPrivileges = (PLUID_AND_ATTRIBUTES) AuthzpAlloc(PrivilegeLength);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pLocalPrivileges))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pCC->PrivilegeCount = PrivilegeCount;
|
|
pCC->Privileges = pLocalPrivileges;
|
|
|
|
AuthzpCopyLuidAndAttributes(
|
|
pCC,
|
|
Privileges,
|
|
PrivilegeCount,
|
|
pLocalPrivileges
|
|
);
|
|
}
|
|
else
|
|
{
|
|
pCC->Privileges = NULL;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (!b)
|
|
{
|
|
AuthzpFreeNonNull(pLocalSids);
|
|
AuthzpFreeNonNull(pLocalRestrictedSids);
|
|
AuthzpFreeNonNull(pLocalPrivileges);
|
|
}
|
|
|
|
if (AUTHZ_NON_NULL_PTR(pRMSids))
|
|
{
|
|
pRM->pfnFreeDynamicGroups(pRMSids);
|
|
}
|
|
|
|
if (AUTHZ_NON_NULL_PTR(pRMRestrictedSids))
|
|
{
|
|
pRM->pfnFreeDynamicGroups(pRMSids);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
AuthzpComputeSkipFlagsForWellKnownSid(
|
|
IN PSID UserSid,
|
|
OUT PDWORD Flags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine computes skip flags if the User sid is a wellknown sid.
|
|
|
|
Arguments:
|
|
|
|
UserSid - User sid for which SKIP flag will be computed.
|
|
|
|
Flags - to return the SKIP flags.
|
|
|
|
A value of AUTHZ_SKIP_TOKEN_GROUPS is returned for well-known and builtin
|
|
sid. A value of 0 is returned in failure cases as well as other cases.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
BOOL b = TRUE;
|
|
LPWSTR Name = NULL;
|
|
LPWSTR RefDomainName = NULL;
|
|
DWORD NameSize = 0;
|
|
DWORD RefDomainNameSize = 0;
|
|
SID_NAME_USE SidNameUse = SidTypeUnknown;
|
|
|
|
*Flags = 0;
|
|
|
|
//
|
|
// Dummy call to get the size of the buffer.
|
|
//
|
|
|
|
b = LookupAccountSidW(
|
|
NULL,
|
|
UserSid,
|
|
NULL,
|
|
&NameSize,
|
|
NULL,
|
|
&RefDomainNameSize,
|
|
&SidNameUse
|
|
);
|
|
|
|
if (FALSE == b)
|
|
{
|
|
dwErr = GetLastError();
|
|
|
|
//
|
|
// Return if we failed because of any error other than insufficient
|
|
// buffer.
|
|
//
|
|
|
|
if(dwErr != ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// LookupAccountSid returns the size in TCHARS.
|
|
//
|
|
|
|
NameSize *= sizeof(WCHAR);
|
|
RefDomainNameSize *= sizeof(WCHAR);
|
|
|
|
//
|
|
// Allocate memory required to hold the names.
|
|
//
|
|
|
|
Name = (LPWSTR) AuthzpAlloc(NameSize);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(Name))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
RefDomainName = (LPWSTR) AuthzpAlloc(RefDomainNameSize);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(RefDomainName))
|
|
{
|
|
AuthzpFree(Name);
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Do the real lookup.
|
|
//
|
|
|
|
b = LookupAccountSidW(
|
|
NULL,
|
|
UserSid,
|
|
Name,
|
|
&NameSize,
|
|
RefDomainName,
|
|
&RefDomainNameSize,
|
|
&SidNameUse
|
|
);
|
|
|
|
AuthzpFree(Name);
|
|
AuthzpFree(RefDomainName);
|
|
|
|
if (FALSE == b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Add the SKIP flag for the cases required.
|
|
//
|
|
|
|
switch (SidNameUse)
|
|
{
|
|
case SidTypeAlias:
|
|
case SidTypeWellKnownGroup:
|
|
case SidTypeInvalid:
|
|
case SidTypeUnknown:
|
|
case SidTypeGroup:
|
|
*Flags = AUTHZ_SKIP_TOKEN_GROUPS;
|
|
return TRUE;
|
|
case SidTypeComputer:
|
|
case SidTypeDomain:
|
|
case SidTypeDeletedAccount:
|
|
case SidTypeUser:
|
|
default:
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Should not get here.
|
|
//
|
|
|
|
ASSERT(FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD
|
|
AuthzpIsDC(
|
|
OUT PBOOL pbIsDC
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine decides whether a trip to the AD is required to fetch
|
|
TokenGroups attribute.
|
|
|
|
Arguments:
|
|
|
|
pbIsDC - Returns whether or not this is a DC.
|
|
|
|
Return Value:
|
|
|
|
Returns ERROR_SUCCESS on success, appropriate failure value otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
static BOOL bFirstTime = TRUE;
|
|
static BOOL bIsDC = FALSE;
|
|
|
|
DWORD dwErr = ERROR_SUCCESS;
|
|
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC BasicInfo = NULL;
|
|
|
|
if (bFirstTime)
|
|
{
|
|
//
|
|
// Get the information about the local machine.
|
|
//
|
|
|
|
dwErr = DsRoleGetPrimaryDomainInformation(
|
|
NULL,
|
|
DsRolePrimaryDomainInfoBasic,
|
|
& (PBYTE) BasicInfo
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwErr)
|
|
{
|
|
return dwErr;
|
|
}
|
|
|
|
//
|
|
// If the local machine is a DC then TokenGroups should be computed anyway.
|
|
//
|
|
|
|
switch(BasicInfo->MachineRole)
|
|
{
|
|
case DsRole_RolePrimaryDomainController:
|
|
case DsRole_RoleBackupDomainController:
|
|
|
|
bIsDC = TRUE;
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
DsRoleFreeMemory(BasicInfo);
|
|
bFirstTime = FALSE;
|
|
}
|
|
|
|
*pbIsDC = bIsDC;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|