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.
6936 lines
183 KiB
6936 lines
183 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
authz.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the user mode authorization APIs exported to the
|
|
external world.
|
|
|
|
Author:
|
|
|
|
Kedar Dubhashi - March 2000
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
|
|
Revision History:
|
|
|
|
Created - March 2000
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
#pragma hdrstop
|
|
|
|
#include <authzp.h>
|
|
#include <authzi.h>
|
|
#include <sddl.h>
|
|
#include <overflow.h>
|
|
|
|
GUID AuthzpNullGuid = { 0 };
|
|
|
|
DWORD
|
|
DeleteKeyRecursivelyW(
|
|
IN HKEY hkey,
|
|
IN LPCWSTR pwszSubKey
|
|
);
|
|
|
|
BOOL
|
|
AuthzAccessCheck(
|
|
IN DWORD Flags,
|
|
IN AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClientContext,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN AUTHZ_AUDIT_EVENT_HANDLE hAuditEvent OPTIONAL,
|
|
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
IN PSECURITY_DESCRIPTOR *OptionalSecurityDescriptorArray OPTIONAL,
|
|
IN DWORD OptionalSecurityDescriptorCount,
|
|
IN OUT PAUTHZ_ACCESS_REPLY pReply,
|
|
OUT PAUTHZ_ACCESS_CHECK_RESULTS_HANDLE phAccessCheckResults OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API decides what access bits may be granted to a client for a given set
|
|
of security security descriptors. The pReply structure is used to return an
|
|
array of granted access masks and error statuses. There is an option to
|
|
cache the access masks that will always be granted. A handle to cached
|
|
values is returned if the caller asks for caching.
|
|
|
|
Arguments:
|
|
|
|
Flags - AUTHZ_ACCESS_CHECK_NO_DEEP_COPY_SD - do not deep copy the SD information into the caching
|
|
handle. Default behaviour is to perform a deep copy.
|
|
|
|
hAuthzClientContext - Authz context representing the client.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
hAuditEvent - Object specific audit event will be passed in this handle.
|
|
Non-null parameter is an automatic request for audit.
|
|
|
|
pSecurityDescriptor - Primary security descriptor to be used for access
|
|
checks. The owner sid for the object is picked from this one. A NULL
|
|
DACL in this security descriptor represents a NULL DACL for the entire
|
|
object. A NULL SACL in this security descriptor is treated the same way
|
|
as an EMPTY SACL.
|
|
|
|
OptionalSecurityDescriptorArray - The caller may optionally specify a list
|
|
of security descriptors. NULL ACLs in these security descriptors are
|
|
treated as EMPTY ACLS and the ACL for the entire object is the logical
|
|
concatenation of all the ACLs.
|
|
|
|
OptionalSecurityDescriptorCount - Number of optional security descriptors
|
|
This does not include the Primay security descriptor.
|
|
|
|
pReply - Supplies a pointer to a reply structure used to return the results
|
|
of access check as an array of (GrantedAccessMask, ErrorValue) pairs.
|
|
The number of results to be returned in supplied by the caller in
|
|
pResult->ResultListLength.
|
|
|
|
Expected error values are:
|
|
|
|
ERROR_SUCCESS - If all the access bits (not including MAXIMUM_ALLOWED)
|
|
are granted and GrantedAccessMask is not zero.
|
|
|
|
ERROR_PRIVILEGE_NOT_HELD - if the DesiredAccess includes
|
|
ACCESS_SYSTEM_SECURITY and the client does not have SeSecurityPrivilege.
|
|
|
|
ERROR_ACCESS_DENIED in each of the following cases -
|
|
1. any of the bits asked for is not granted.
|
|
2. MaximumAllowed bit it on and granted access is zero.
|
|
3. DesiredAccess is 0.
|
|
|
|
phAccessCheckResults - Supplies a pointer to return a handle to the cached results
|
|
of access check. Non-null phAccessCheckResults is an implicit request to cache
|
|
results of this access check call and will result in a MAXIMUM_ALLOWED
|
|
check.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the API is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b = TRUE;
|
|
DWORD LocalTypeListLength = 0;
|
|
PIOBJECT_TYPE_LIST LocalTypeList = NULL;
|
|
PIOBJECT_TYPE_LIST LocalCachingTypeList = NULL;
|
|
PAUTHZI_CLIENT_CONTEXT pCC = (PAUTHZI_CLIENT_CONTEXT) hAuthzClientContext;
|
|
PAUTHZI_AUDIT_EVENT pAuditEvent = (PAUTHZI_AUDIT_EVENT) hAuditEvent;
|
|
IOBJECT_TYPE_LIST FixedTypeList = {0};
|
|
IOBJECT_TYPE_LIST FixedCachingTypeList = {0};
|
|
|
|
UNREFERENCED_PARAMETER(Flags);
|
|
|
|
#ifdef AUTHZ_PARAM_CHECK
|
|
//
|
|
// Verify that the arguments passed are valid.
|
|
// Also, initialize the output parameters to default.
|
|
//
|
|
|
|
b = AuthzpVerifyAccessCheckArguments(
|
|
pCC,
|
|
pRequest,
|
|
pSecurityDescriptor,
|
|
OptionalSecurityDescriptorArray,
|
|
OptionalSecurityDescriptorCount,
|
|
pReply,
|
|
phAccessCheckResults
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// No client should be able to open an object by asking for zero access.
|
|
// If desired access is 0 then return an error.
|
|
//
|
|
// Note: No audit is generated in this case.
|
|
//
|
|
|
|
if (0 == pRequest->DesiredAccess)
|
|
{
|
|
AuthzpFillReplyStructure(
|
|
pReply,
|
|
ERROR_ACCESS_DENIED,
|
|
0
|
|
);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Generic bits should be mapped to specific ones by the resource manager.
|
|
//
|
|
|
|
if (FLAG_ON(pRequest->DesiredAccess, (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL)))
|
|
{
|
|
SetLastError(ERROR_GENERIC_NOT_MAPPED);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// In the simple case, there is no object type list. Fake one of length = 1
|
|
// to represent the entire object.
|
|
//
|
|
|
|
if (0 == pRequest->ObjectTypeListLength)
|
|
{
|
|
LocalTypeList = &FixedTypeList;
|
|
FixedTypeList.ParentIndex = -1;
|
|
LocalTypeListLength = 1;
|
|
|
|
//
|
|
// If the caller has asked for caching, fake an object type list that'd
|
|
// be used for computing static "always granted" access.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(phAccessCheckResults))
|
|
{
|
|
RtlCopyMemory(
|
|
&FixedCachingTypeList,
|
|
&FixedTypeList,
|
|
sizeof(IOBJECT_TYPE_LIST)
|
|
);
|
|
|
|
LocalCachingTypeList = &FixedCachingTypeList;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DWORD Size = sizeof(IOBJECT_TYPE_LIST) * pRequest->ObjectTypeListLength;
|
|
|
|
//
|
|
// Allocate size for capturing object type list into local structure.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(phAccessCheckResults))
|
|
{
|
|
//
|
|
// We need twice the size in case of caching.
|
|
//
|
|
|
|
SafeAllocaAllocate(LocalTypeList, (2 * Size));
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(LocalTypeList))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
LocalCachingTypeList = (PIOBJECT_TYPE_LIST) (((PUCHAR) LocalTypeList) + Size);
|
|
}
|
|
else
|
|
{
|
|
SafeAllocaAllocate(LocalTypeList, Size);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(LocalTypeList))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Capture the object type list into an internal structure.
|
|
//
|
|
|
|
b = AuthzpCaptureObjectTypeList(
|
|
pRequest->ObjectTypeList,
|
|
pRequest->ObjectTypeListLength,
|
|
LocalTypeList,
|
|
LocalCachingTypeList
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
LocalTypeListLength = pRequest->ObjectTypeListLength;
|
|
}
|
|
|
|
//
|
|
// There are three cases when we have to perform a MaximumAllowed access
|
|
// check and traverse the whole acl:
|
|
// 1. RM has requested for caching.
|
|
// 2. DesiredAccessMask has MAXIMUM_ALLOWED turned on.
|
|
// 3. ObjectTypeList is present and pReply->ResultList has a length > 1
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(phAccessCheckResults) ||
|
|
FLAG_ON(pRequest->DesiredAccess, MAXIMUM_ALLOWED) ||
|
|
(pReply->ResultListLength > 1))
|
|
{
|
|
b = AuthzpAccessCheckWithCaching(
|
|
Flags,
|
|
pCC,
|
|
pRequest,
|
|
pSecurityDescriptor,
|
|
OptionalSecurityDescriptorArray,
|
|
OptionalSecurityDescriptorCount,
|
|
pReply,
|
|
phAccessCheckResults,
|
|
LocalTypeList,
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Perform a normal access check in the default case. Acl traversal may
|
|
// be abandoned if any of the desired access bits are denied before they
|
|
// are granted.
|
|
//
|
|
|
|
b = AuthzpNormalAccessCheckWithoutCaching(
|
|
pCC,
|
|
pRequest,
|
|
pSecurityDescriptor,
|
|
OptionalSecurityDescriptorArray,
|
|
OptionalSecurityDescriptorCount,
|
|
pReply,
|
|
LocalTypeList,
|
|
LocalTypeListLength
|
|
);
|
|
}
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Check if an audit needs to be generated if the RM has requested audit
|
|
// generation by passing a non-null AuditEvent structure.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(pAuditEvent))
|
|
{
|
|
b = AuthzpGenerateAudit(
|
|
pCC,
|
|
pRequest,
|
|
pAuditEvent,
|
|
pSecurityDescriptor,
|
|
OptionalSecurityDescriptorArray,
|
|
OptionalSecurityDescriptorCount,
|
|
pReply,
|
|
LocalTypeList
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// Clean up allocated memory.
|
|
//
|
|
|
|
if ((&FixedTypeList != LocalTypeList) && (AUTHZ_NON_NULL_PTR(LocalTypeList)))
|
|
{
|
|
SafeAllocaFree(LocalTypeList);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzCachedAccessCheck(
|
|
IN DWORD Flags,
|
|
IN AUTHZ_ACCESS_CHECK_RESULTS_HANDLE hAccessCheckResults,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN AUTHZ_AUDIT_EVENT_HANDLE hAuditEvent OPTIONAL,
|
|
IN OUT PAUTHZ_ACCESS_REPLY pReply
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API performs a fast access check based on a cached handle which holds
|
|
the static granted bits evaluated at the time of a previously made
|
|
AuthzAccessCheck call. The pReply structure is used to return an array of
|
|
granted access masks and error statuses.
|
|
|
|
Assumptions:
|
|
The client context pointer is stored in the hAccessCheckResults. The structure of
|
|
the client context must be exactly the same as it was at the time the
|
|
hAccessCheckResults was created. This restriction is for the following fields:
|
|
Sids, RestrictedSids, Privileges.
|
|
Pointers to the primary security descriptor and the optional security
|
|
descriptor array are stored in the hAccessCheckResults at the time of handle
|
|
creation. These must still be valid.
|
|
|
|
Arguments:
|
|
|
|
Flags - TBD.
|
|
|
|
hAccessCheckResults - Handle to the cached access check results.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
AuditEvent - Object specific audit info will be passed in this structure.
|
|
Non-null parameter is an automatic request for audit.
|
|
|
|
pReply - Supplies a pointer to a reply structure used to return the results
|
|
of access check as an array of (GrantedAccessMask, ErrorValue) pairs.
|
|
The number of results to be returned in supplied by the caller in
|
|
pResult->ResultListLength.
|
|
|
|
Expected error values are:
|
|
|
|
ERROR_SUCCESS - If all the access bits (not including MAXIMUM_ALLOWED)
|
|
are granted and GrantedAccessMask is not zero.
|
|
|
|
ERROR_PRIVILEGE_NOT_HELD - if the DesiredAccess includes
|
|
ACCESS_SYSTEM_SECURITY and the client does not have SeSecurityPrivilege.
|
|
|
|
ERROR_ACCESS_DENIED in each of the following cases -
|
|
1. any of the bits asked for is not granted.
|
|
2. MaximumAllowed bit it on and granted access is zero.
|
|
3. DesiredAccess is 0.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the API is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i = 0;
|
|
DWORD LocalTypeListLength = 0;
|
|
PIOBJECT_TYPE_LIST LocalTypeList = NULL;
|
|
PACL pAcl = NULL;
|
|
PAUTHZI_HANDLE pAH = (PAUTHZI_HANDLE) hAccessCheckResults;
|
|
BOOL b = TRUE;
|
|
PAUTHZI_AUDIT_EVENT pAuditEvent = (PAUTHZI_AUDIT_EVENT) hAuditEvent;
|
|
IOBJECT_TYPE_LIST FixedTypeList = {0};
|
|
|
|
UNREFERENCED_PARAMETER(Flags);
|
|
|
|
#ifdef AUTHZ_PARAM_CHECK
|
|
b = AuthzpVerifyCachedAccessCheckArguments(
|
|
pAH,
|
|
pRequest,
|
|
pReply
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// No client should be able to open an object by asking for zero access.
|
|
// If desired access is 0 then return an error.
|
|
//
|
|
// Note: No audit is generated in this case.
|
|
//
|
|
|
|
if (0 == pRequest->DesiredAccess)
|
|
{
|
|
AuthzpFillReplyStructure(
|
|
pReply,
|
|
ERROR_ACCESS_DENIED,
|
|
0
|
|
);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Generic bits should be mapped to specific ones by the resource manager.
|
|
//
|
|
|
|
if (FLAG_ON(pRequest->DesiredAccess, (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL)))
|
|
{
|
|
SetLastError(ERROR_GENERIC_NOT_MAPPED);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Capture the object type list if one has been passed in or fake one with
|
|
// just one element.
|
|
//
|
|
|
|
if (0 == pRequest->ObjectTypeListLength)
|
|
{
|
|
LocalTypeList = &FixedTypeList;
|
|
LocalTypeListLength = 1;
|
|
FixedTypeList.ParentIndex = -1;
|
|
}
|
|
else
|
|
{
|
|
DWORD Size = sizeof(IOBJECT_TYPE_LIST) * pRequest->ObjectTypeListLength;
|
|
|
|
SafeAllocaAllocate(LocalTypeList, Size);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(LocalTypeList))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
b = AuthzpCaptureObjectTypeList(
|
|
pRequest->ObjectTypeList,
|
|
pRequest->ObjectTypeListLength,
|
|
LocalTypeList,
|
|
NULL
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
LocalTypeListLength = pRequest->ObjectTypeListLength;
|
|
}
|
|
|
|
//
|
|
// If all the bits have already been granted then just copy the results and
|
|
// skip access check.
|
|
//
|
|
|
|
if (!FLAG_ON(pRequest->DesiredAccess, ~pAH->GrantedAccessMask[i]))
|
|
{
|
|
AuthzpFillReplyStructure(
|
|
pReply,
|
|
ERROR_SUCCESS,
|
|
pRequest->DesiredAccess
|
|
);
|
|
|
|
goto GenerateAudit;
|
|
}
|
|
|
|
//
|
|
// The assumption is privileges can not be changed. Thus, if the client did
|
|
// not have SecurityPrivilege previously then he does not have it now.
|
|
//
|
|
|
|
if (FLAG_ON(pRequest->DesiredAccess, ACCESS_SYSTEM_SECURITY))
|
|
{
|
|
AuthzpFillReplyStructure(
|
|
pReply,
|
|
ERROR_PRIVILEGE_NOT_HELD,
|
|
0
|
|
);
|
|
|
|
goto GenerateAudit;
|
|
}
|
|
|
|
//
|
|
// If all aces are simple aces then there's nothing to do. All access bits
|
|
// are static.
|
|
//
|
|
|
|
if ((!FLAG_ON(pAH->Flags, AUTHZ_DYNAMIC_EVALUATION_PRESENT)) &&
|
|
(!FLAG_ON(pRequest->DesiredAccess, MAXIMUM_ALLOWED)))
|
|
{
|
|
AuthzpFillReplyStructureFromCachedGrantedAccessMask(
|
|
pReply,
|
|
pRequest->DesiredAccess,
|
|
pAH->GrantedAccessMask
|
|
);
|
|
|
|
goto GenerateAudit;
|
|
}
|
|
|
|
//
|
|
// Get the access bits from the last static access check.
|
|
//
|
|
|
|
for (i = 0; i < LocalTypeListLength; i++)
|
|
{
|
|
LocalTypeList[i].CurrentGranted = pAH->GrantedAccessMask[i];
|
|
LocalTypeList[i].Remaining = pRequest->DesiredAccess & ~pAH->GrantedAccessMask[i];
|
|
}
|
|
|
|
|
|
//
|
|
// NULL Dacl is synonymous with Full Control.
|
|
//
|
|
|
|
pAcl = RtlpDaclAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) pAH->pSecurityDescriptor);
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(pAcl))
|
|
{
|
|
for (i = 0; i < LocalTypeListLength; i++)
|
|
{
|
|
LocalTypeList[i].CurrentGranted |= (STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If there are no deny aces, then perform a quick access check evaluating
|
|
// only the allow aces that are dynamic or have principal self sid in them.
|
|
//
|
|
|
|
if (!FLAG_ON(pAH->Flags, (AUTHZ_DENY_ACE_PRESENT | AUTHZ_DYNAMIC_DENY_ACE_PRESENT)))
|
|
{
|
|
if (FLAG_ON(pRequest->DesiredAccess, MAXIMUM_ALLOWED) ||
|
|
(pReply->ResultListLength > 1))
|
|
{
|
|
b = AuthzpQuickMaximumAllowedAccessCheck(
|
|
pAH->pAuthzClientContext,
|
|
pAH,
|
|
pRequest,
|
|
pReply,
|
|
LocalTypeList,
|
|
LocalTypeListLength
|
|
);
|
|
}
|
|
else
|
|
{
|
|
b = AuthzpQuickNormalAccessCheck(
|
|
pAH->pAuthzClientContext,
|
|
pAH,
|
|
pRequest,
|
|
pReply,
|
|
LocalTypeList,
|
|
LocalTypeListLength
|
|
);
|
|
}
|
|
}
|
|
else if ((0 != pRequest->ObjectTypeListLength) || (FLAG_ON(pRequest->DesiredAccess, MAXIMUM_ALLOWED)))
|
|
{
|
|
//
|
|
// Now we have to evaluate the entire acl since there are deny aces
|
|
// and the caller has asked for a result list.
|
|
//
|
|
|
|
b = AuthzpAccessCheckWithCaching(
|
|
Flags,
|
|
pAH->pAuthzClientContext,
|
|
pRequest,
|
|
pAH->pSecurityDescriptor,
|
|
pAH->OptionalSecurityDescriptorArray,
|
|
pAH->OptionalSecurityDescriptorCount,
|
|
pReply,
|
|
NULL,
|
|
LocalTypeList,
|
|
NULL,
|
|
LocalTypeListLength
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// There are deny aces in the acl but the caller has not asked for
|
|
// entire resultlist. Preform a normal access check.
|
|
//
|
|
|
|
b = AuthzpNormalAccessCheckWithoutCaching(
|
|
pAH->pAuthzClientContext,
|
|
pRequest,
|
|
pAH->pSecurityDescriptor,
|
|
pAH->OptionalSecurityDescriptorArray,
|
|
pAH->OptionalSecurityDescriptorCount,
|
|
pReply,
|
|
LocalTypeList,
|
|
LocalTypeListLength
|
|
);
|
|
}
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
AuthzpFillReplyFromParameters(
|
|
pRequest,
|
|
pReply,
|
|
LocalTypeList
|
|
);
|
|
|
|
GenerateAudit:
|
|
|
|
if (ARGUMENT_PRESENT(pAuditEvent))
|
|
{
|
|
b = AuthzpGenerateAudit(
|
|
pAH->pAuthzClientContext,
|
|
pRequest,
|
|
pAuditEvent,
|
|
pAH->pSecurityDescriptor,
|
|
pAH->OptionalSecurityDescriptorArray,
|
|
pAH->OptionalSecurityDescriptorCount,
|
|
pReply,
|
|
LocalTypeList
|
|
);
|
|
|
|
if (!b) goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if ((&FixedTypeList != LocalTypeList) && (AUTHZ_NON_NULL_PTR(LocalTypeList)))
|
|
{
|
|
SafeAllocaFree(LocalTypeList);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzOpenObjectAudit(
|
|
IN DWORD Flags,
|
|
IN AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClientContext,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN AUTHZ_AUDIT_EVENT_HANDLE hAuditEvent,
|
|
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
IN PSECURITY_DESCRIPTOR *OptionalSecurityDescriptorArray OPTIONAL,
|
|
IN DWORD OptionalSecurityDescriptorCount,
|
|
IN PAUTHZ_ACCESS_REPLY pReply
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This API examines the SACL in the passed security descriptor(s) and generates
|
|
any appropriate audits.
|
|
|
|
Arguments
|
|
|
|
Flags - TBD.
|
|
|
|
hAuthzClientContext - Client context to perform the SACL evaluation against.
|
|
|
|
pRequest - pointer to request structure.
|
|
|
|
hAuditEvent - Handle to the audit that may be generated.
|
|
|
|
pSecurityDescriptor - Pointer to a security descriptor.
|
|
|
|
OptionalSecurityDescriptorArray - Optional array of security descriptors.
|
|
|
|
OptionalSecurityDescriptorCount - Size of optional security descriptor array.
|
|
|
|
pReply - Pointer to the reply structure.
|
|
|
|
Return Value
|
|
|
|
Boolean: TRUE on success; FALSE on failure. Extended information available with GetLastError().
|
|
--*/
|
|
|
|
{
|
|
BOOL b = TRUE;
|
|
DWORD LocalTypeListLength = 0;
|
|
PIOBJECT_TYPE_LIST LocalTypeList = NULL;
|
|
PAUTHZI_CLIENT_CONTEXT pCC = (PAUTHZI_CLIENT_CONTEXT) hAuthzClientContext;
|
|
PAUTHZI_AUDIT_EVENT pAuditEvent = (PAUTHZI_AUDIT_EVENT) hAuditEvent;
|
|
IOBJECT_TYPE_LIST FixedTypeList = {0};
|
|
|
|
UNREFERENCED_PARAMETER(Flags);
|
|
|
|
//
|
|
// Verify that the arguments passed are valid.
|
|
//
|
|
|
|
b = AuthzpVerifyOpenObjectArguments(
|
|
pCC,
|
|
pSecurityDescriptor,
|
|
OptionalSecurityDescriptorArray,
|
|
OptionalSecurityDescriptorCount,
|
|
pAuditEvent
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// In the simple case, there is no object type list. Fake one of length = 1
|
|
// to represent the entire object.
|
|
//
|
|
|
|
if (0 == pRequest->ObjectTypeListLength)
|
|
{
|
|
LocalTypeList = &FixedTypeList;
|
|
FixedTypeList.ParentIndex = -1;
|
|
LocalTypeListLength = 1;
|
|
}
|
|
else
|
|
{
|
|
DWORD Size = sizeof(IOBJECT_TYPE_LIST) * pRequest->ObjectTypeListLength;
|
|
|
|
SafeAllocaAllocate(LocalTypeList, Size);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(LocalTypeList))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Capture the object type list into an internal structure.
|
|
//
|
|
|
|
b = AuthzpCaptureObjectTypeList(
|
|
pRequest->ObjectTypeList,
|
|
pRequest->ObjectTypeListLength,
|
|
LocalTypeList,
|
|
NULL
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
LocalTypeListLength = pRequest->ObjectTypeListLength;
|
|
}
|
|
|
|
b = AuthzpGenerateAudit(
|
|
pCC,
|
|
pRequest,
|
|
pAuditEvent,
|
|
pSecurityDescriptor,
|
|
OptionalSecurityDescriptorArray,
|
|
OptionalSecurityDescriptorCount,
|
|
pReply,
|
|
LocalTypeList
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// Clean up allocated memory.
|
|
//
|
|
|
|
if (&FixedTypeList != LocalTypeList)
|
|
{
|
|
SafeAllocaFree(LocalTypeList);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzFreeHandle(
|
|
IN OUT AUTHZ_ACCESS_CHECK_RESULTS_HANDLE hAccessCheckResults
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API finds and deletes the input handle from the handle list.
|
|
|
|
Arguments:
|
|
|
|
hAcc - Handle to be freed.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the API is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PAUTHZI_HANDLE pAH = (PAUTHZI_HANDLE) hAccessCheckResults;
|
|
PAUTHZI_HANDLE pCurrent = NULL;
|
|
PAUTHZI_HANDLE pPrev = NULL;
|
|
BOOL b = TRUE;
|
|
|
|
//
|
|
// Validate parameters.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(pAH) ||
|
|
!AUTHZ_NON_NULL_PTR(pAH->pAuthzClientContext) ||
|
|
!AUTHZ_NON_NULL_PTR(pAH->pAuthzClientContext->AuthzHandleHead))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
AuthzpAcquireClientCacheWriteLock(pAH->pAuthzClientContext);
|
|
|
|
pCurrent = pAH->pAuthzClientContext->AuthzHandleHead;
|
|
|
|
//
|
|
// Check if the handle is at the beginning of the list.
|
|
//
|
|
|
|
if (pCurrent == pAH)
|
|
{
|
|
pAH->pAuthzClientContext->AuthzHandleHead = pAH->pAuthzClientContext->AuthzHandleHead->next;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The handle is not the head of the list. Loop thru the list to find
|
|
// it.
|
|
//
|
|
|
|
pPrev = pCurrent;
|
|
pCurrent = pCurrent->next;
|
|
|
|
for (; AUTHZ_NON_NULL_PTR(pCurrent); pPrev = pCurrent, pCurrent = pCurrent->next)
|
|
{
|
|
if (pCurrent == pAH)
|
|
{
|
|
pPrev->next = pCurrent->next;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The caller has sent us an invalid handle.
|
|
//
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(pCurrent))
|
|
{
|
|
b = FALSE;
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
}
|
|
}
|
|
|
|
AuthzpReleaseClientCacheLock(pCC);
|
|
|
|
//
|
|
// Free the handle node.
|
|
//
|
|
|
|
if (b)
|
|
{
|
|
AuthzpFree(pAH);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzInitializeResourceManager(
|
|
IN DWORD Flags,
|
|
IN PFN_AUTHZ_DYNAMIC_ACCESS_CHECK pfnDynamicAccessCheck OPTIONAL,
|
|
IN PFN_AUTHZ_COMPUTE_DYNAMIC_GROUPS pfnComputeDynamicGroups OPTIONAL,
|
|
IN PFN_AUTHZ_FREE_DYNAMIC_GROUPS pfnFreeDynamicGroups OPTIONAL,
|
|
IN PCWSTR szResourceManagerName,
|
|
OUT PAUTHZ_RESOURCE_MANAGER_HANDLE phAuthzResourceManager
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API allocates and initializes a resource manager structure.
|
|
|
|
Arguments:
|
|
|
|
Flags - AUTHZ_RM_FLAG_NO_AUDIT - use if the RM will never generate an audit to
|
|
save some cycles.
|
|
|
|
- AUTHZ_RM_FLAG_INITIALIZE_UNDER_IMPERSONATION - if the current thread is
|
|
impersonating then use the impersonation token as the identity of the
|
|
resource manager.
|
|
|
|
pfnAccessCheck - Pointer to the RM supplied access check function to be
|
|
called when a callback ace is encountered by the access check algorithm.
|
|
|
|
pfnComputeDynamicGroups - Pointer to the RM supplied function to compute
|
|
groups to be added to the client context at the time of its creation.
|
|
|
|
pfnFreeDynamicGroups - Pointer to the function to free the memory allocated
|
|
by the pfnComputeDynamicGroups function.
|
|
|
|
szResourceManagerName - the name of the resource manager.
|
|
|
|
pAuthzResourceManager - To return the resource manager handle. The returned
|
|
handle must be freed using AuthzFreeResourceManager.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the API is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PAUTHZI_RESOURCE_MANAGER pRM = NULL;
|
|
BOOL b = TRUE;
|
|
ULONG len = 0;
|
|
|
|
if (!ARGUMENT_PRESENT(phAuthzResourceManager) ||
|
|
(Flags & ~AUTHZ_VALID_RM_INIT_FLAGS))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*phAuthzResourceManager = NULL;
|
|
|
|
if (AUTHZ_NON_NULL_PTR(szResourceManagerName))
|
|
{
|
|
len = (ULONG) wcslen(szResourceManagerName) + 1;
|
|
}
|
|
|
|
pRM = (PAUTHZI_RESOURCE_MANAGER)
|
|
AuthzpAlloc(sizeof(AUTHZI_RESOURCE_MANAGER) + sizeof(WCHAR) * len);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pRM))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Use the default pessimistic function if none has been specified.
|
|
//
|
|
|
|
if (AUTHZ_NON_NULL_PTR(pfnDynamicAccessCheck))
|
|
{
|
|
pRM->pfnDynamicAccessCheck = pfnDynamicAccessCheck;
|
|
}
|
|
else
|
|
{
|
|
pRM->pfnDynamicAccessCheck = &AuthzpDefaultAccessCheck;
|
|
}
|
|
|
|
if (!FLAG_ON(Flags, AUTHZ_RM_FLAG_NO_AUDIT))
|
|
{
|
|
|
|
//
|
|
// Initialize the generic audit queue and generic audit events.
|
|
//
|
|
|
|
b = AuthziInitializeAuditQueue(
|
|
AUTHZ_MONITOR_AUDIT_QUEUE_SIZE,
|
|
1000,
|
|
100,
|
|
NULL,
|
|
&pRM->hAuditQueue
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Initialize the generic audit event.
|
|
//
|
|
|
|
b = AuthziInitializeAuditEventType(
|
|
AUTHZP_DEFAULT_RM_EVENTS | AUTHZP_INIT_GENERIC_AUDIT_EVENT,
|
|
0,
|
|
0,
|
|
0,
|
|
&pRM->hAET
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
b = AuthziInitializeAuditEventType(
|
|
AUTHZP_DEFAULT_RM_EVENTS,
|
|
SE_CATEGID_DS_ACCESS,
|
|
SE_AUDITID_OBJECT_OPERATION,
|
|
AUTHZP_NUM_PARAMS_FOR_SE_AUDITID_OBJECT_OPERATION,
|
|
&pRM->hAETDS
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
pRM->pfnComputeDynamicGroups = pfnComputeDynamicGroups;
|
|
pRM->pfnFreeDynamicGroups = pfnFreeDynamicGroups;
|
|
pRM->Flags = Flags;
|
|
pRM->pUserSID = NULL;
|
|
pRM->szResourceManagerName = (PWSTR)((PUCHAR)pRM + sizeof(AUTHZI_RESOURCE_MANAGER));
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_RM_FLAG_INITIALIZE_UNDER_IMPERSONATION))
|
|
{
|
|
b = AuthzpGetThreadTokenInfo(
|
|
&pRM->pUserSID,
|
|
&pRM->AuthID
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
b = AuthzpGetProcessTokenInfo(
|
|
&pRM->pUserSID,
|
|
&pRM->AuthID
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if (0 != len)
|
|
{
|
|
RtlCopyMemory(
|
|
pRM->szResourceManagerName,
|
|
szResourceManagerName,
|
|
sizeof(WCHAR) * len
|
|
);
|
|
}
|
|
else
|
|
{
|
|
pRM->szResourceManagerName = NULL;
|
|
}
|
|
|
|
*phAuthzResourceManager = (AUTHZ_RESOURCE_MANAGER_HANDLE) pRM;
|
|
|
|
Cleanup:
|
|
|
|
if (!b)
|
|
{
|
|
//
|
|
// Copy LastError value, since the calls to AuthziFreeAuditEventType can succeed and
|
|
// overwrite it with 0x103 (STATUS_PENDING).
|
|
//
|
|
|
|
DWORD dwError = GetLastError();
|
|
|
|
if (NULL != pRM)
|
|
{
|
|
if (!FLAG_ON(Flags, AUTHZ_RM_FLAG_NO_AUDIT))
|
|
{
|
|
AuthziFreeAuditQueue(pRM->hAuditQueue);
|
|
AuthziFreeAuditEventType(pRM->hAET);
|
|
AuthziFreeAuditEventType(pRM->hAETDS);
|
|
}
|
|
AuthzpFreeNonNull(pRM->pUserSID);
|
|
AuthzpFree(pRM);
|
|
}
|
|
|
|
SetLastError(dwError);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzFreeResourceManager(
|
|
IN OUT AUTHZ_RESOURCE_MANAGER_HANDLE hAuthzResourceManager
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API frees up a resource manager. If the default queue is in use, this call will wait for that
|
|
queue to empty.
|
|
|
|
Arguments:
|
|
|
|
hAuthzResourceManager - Handle to the resource manager object to be freed.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the API is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PAUTHZI_RESOURCE_MANAGER pRM = (PAUTHZI_RESOURCE_MANAGER) hAuthzResourceManager;
|
|
|
|
if (!ARGUMENT_PRESENT(pRM))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!FLAG_ON(pRM->Flags, AUTHZ_RM_FLAG_NO_AUDIT))
|
|
{
|
|
(VOID) AuthziFreeAuditQueue(pRM->hAuditQueue);
|
|
(VOID) AuthziFreeAuditEventType(pRM->hAET);
|
|
(VOID) AuthziFreeAuditEventType(pRM->hAETDS);
|
|
}
|
|
|
|
AuthzpFreeNonNull(pRM->pUserSID);
|
|
AuthzpFree(pRM);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzInitializeContextFromToken(
|
|
IN DWORD Flags,
|
|
IN HANDLE TokenHandle,
|
|
IN AUTHZ_RESOURCE_MANAGER_HANDLE hAuthzResourceManager,
|
|
IN PLARGE_INTEGER pExpirationTime OPTIONAL,
|
|
IN LUID Identifier,
|
|
IN PVOID DynamicGroupArgs,
|
|
OUT PAUTHZ_CLIENT_CONTEXT_HANDLE phAuthzClientContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the authz context from the handle to the kernel token. The token
|
|
must have been opened for TOKEN_QUERY.
|
|
|
|
Arguments:
|
|
|
|
Flags - None
|
|
|
|
TokenHandle - Handle to the client token from which the authz context will
|
|
be initialized. The token must have been opened with TOKEN_QUERY access.
|
|
|
|
AuthzResourceManager - The resource manager handle creating this client
|
|
context. This will be stored in the client context structure.
|
|
|
|
pExpirationTime - To set for how long the returned context structure is
|
|
valid. If no value is passed then the token never expires.
|
|
Expiration time is not currently enforced in the system.
|
|
|
|
Identifier - Resource manager manager specific identifier. This is never
|
|
interpreted by Authz.
|
|
|
|
DynamicGroupArgs - To be passed to the callback function that computes
|
|
dynamic groups
|
|
|
|
pAuthzClientContext - To return a handle to the AuthzClientContext
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the API is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
UCHAR Buffer[AUTHZ_MAX_STACK_BUFFER_SIZE];
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PUCHAR pBuffer = (PVOID) Buffer;
|
|
BOOL b = TRUE;
|
|
BOOL bAllocatedSids = FALSE;
|
|
BOOL bLockHeld = FALSE;
|
|
PTOKEN_GROUPS_AND_PRIVILEGES pTokenInfo = NULL;
|
|
PAUTHZI_RESOURCE_MANAGER pRM = NULL;
|
|
PAUTHZI_CLIENT_CONTEXT pCC = NULL;
|
|
DWORD Length = 0;
|
|
LARGE_INTEGER ExpirationTime = {0, 0};
|
|
|
|
UNREFERENCED_PARAMETER(Flags);
|
|
|
|
if (!ARGUMENT_PRESENT(TokenHandle) ||
|
|
!ARGUMENT_PRESENT(hAuthzResourceManager) ||
|
|
!ARGUMENT_PRESENT(phAuthzClientContext))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*phAuthzClientContext = NULL;
|
|
|
|
//
|
|
// Query the token information into user mode buffer. A local stack buffer
|
|
// is used in the first call hoping that it would be sufficient to hold
|
|
// the return values.
|
|
//
|
|
|
|
Status = NtQueryInformationToken(
|
|
TokenHandle,
|
|
TokenGroupsAndPrivileges,
|
|
pBuffer,
|
|
AUTHZ_MAX_STACK_BUFFER_SIZE,
|
|
&Length
|
|
);
|
|
|
|
if (STATUS_BUFFER_TOO_SMALL == Status)
|
|
{
|
|
pBuffer = (PVOID) AuthzpAlloc(Length);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pBuffer))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
Status = NtQueryInformationToken(
|
|
TokenHandle,
|
|
TokenGroupsAndPrivileges,
|
|
pBuffer,
|
|
Length,
|
|
&Length
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
|
|
#ifdef AUTHZ_DEBUG
|
|
wprintf(L"\nNtQueryInformationToken failed with %d\n", Status);
|
|
#endif
|
|
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pTokenInfo = (PTOKEN_GROUPS_AND_PRIVILEGES) pBuffer;
|
|
|
|
pRM = (PAUTHZI_RESOURCE_MANAGER) hAuthzResourceManager;
|
|
|
|
if (ARGUMENT_PRESENT(pExpirationTime))
|
|
{
|
|
ExpirationTime = *pExpirationTime;
|
|
}
|
|
|
|
//
|
|
// Initialize the client context. The callee allocates memory for the client
|
|
// context structure.
|
|
//
|
|
|
|
b = AuthzpAllocateAndInitializeClientContext(
|
|
&pCC,
|
|
NULL,
|
|
AUTHZ_CURRENT_CONTEXT_REVISION,
|
|
Identifier,
|
|
ExpirationTime,
|
|
0,
|
|
pTokenInfo->SidCount,
|
|
pTokenInfo->SidLength,
|
|
pTokenInfo->Sids,
|
|
pTokenInfo->RestrictedSidCount,
|
|
pTokenInfo->RestrictedSidLength,
|
|
pTokenInfo->RestrictedSids,
|
|
pTokenInfo->PrivilegeCount,
|
|
pTokenInfo->PrivilegeLength,
|
|
pTokenInfo->Privileges,
|
|
pTokenInfo->AuthenticationId,
|
|
NULL,
|
|
pRM
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
AuthzpAcquireClientContextReadLock(pCC);
|
|
|
|
bLockHeld = TRUE;
|
|
|
|
//
|
|
// Add dynamic sids to the token.
|
|
//
|
|
|
|
b = AuthzpAddDynamicSidsToToken(
|
|
pCC,
|
|
pRM,
|
|
DynamicGroupArgs,
|
|
pTokenInfo->Sids,
|
|
pTokenInfo->SidLength,
|
|
pTokenInfo->SidCount,
|
|
pTokenInfo->RestrictedSids,
|
|
pTokenInfo->RestrictedSidLength,
|
|
pTokenInfo->RestrictedSidCount,
|
|
pTokenInfo->Privileges,
|
|
pTokenInfo->PrivilegeLength,
|
|
pTokenInfo->PrivilegeCount,
|
|
FALSE
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
bAllocatedSids = TRUE;
|
|
*phAuthzClientContext = (AUTHZ_CLIENT_CONTEXT_HANDLE) pCC;
|
|
|
|
AuthzPrintContext(pCC);
|
|
|
|
//
|
|
// initialize the sid hash for regular sids
|
|
//
|
|
|
|
AuthzpInitSidHash(
|
|
pCC->Sids,
|
|
pCC->SidCount,
|
|
pCC->SidHash
|
|
);
|
|
|
|
//
|
|
// initialize the sid hash for restricted sids
|
|
//
|
|
|
|
AuthzpInitSidHash(
|
|
pCC->RestrictedSids,
|
|
pCC->RestrictedSidCount,
|
|
pCC->RestrictedSidHash
|
|
);
|
|
|
|
Cleanup:
|
|
|
|
if ((PVOID) Buffer != pBuffer)
|
|
{
|
|
AuthzpFreeNonNull(pBuffer);
|
|
}
|
|
|
|
if (!b)
|
|
{
|
|
DWORD dwSavedError = GetLastError();
|
|
|
|
if (AUTHZ_NON_NULL_PTR(pCC))
|
|
{
|
|
if (bAllocatedSids)
|
|
{
|
|
AuthzFreeContext((AUTHZ_CLIENT_CONTEXT_HANDLE)pCC);
|
|
SetLastError(dwSavedError);
|
|
}
|
|
else
|
|
{
|
|
AuthzpFree(pCC);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bLockHeld)
|
|
{
|
|
AuthzpReleaseClientContextLock(pCC);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpInitializeContextFromSid(
|
|
IN DWORD Flags,
|
|
IN PSID UserSid,
|
|
IN AUTHZ_RESOURCE_MANAGER_HANDLE hAuthzResourceManager,
|
|
IN PLARGE_INTEGER pExpirationTime OPTIONAL,
|
|
IN LUID Identifier,
|
|
IN PVOID DynamicGroupArgs,
|
|
OUT PAUTHZ_CLIENT_CONTEXT_HANDLE phAuthzClientContext,
|
|
IN BOOL bIsInternalRoutine
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API takes a user sid and creates a user mode client context from it.
|
|
It fetches the TokenGroups attributes from the AD in case of domain sids.
|
|
The machine local groups are computed on the ServerName specified. The
|
|
resource manager may dynamic groups using callback mechanism.
|
|
|
|
Arguments:
|
|
|
|
Flags -
|
|
AUTHZ_SKIP_TOKEN_GROUPS - Do not token groups if this is on.
|
|
|
|
UserSid - The sid of the user for whom a client context will be created.
|
|
|
|
ServerName - The machine on which local groups should be computed. A NULL
|
|
server name defaults to the local machine.
|
|
|
|
AuthzResourceManager - The resource manager handle creating this client
|
|
context. This will be stored in the client context structure.
|
|
|
|
pExpirationTime - To set for how long the returned context structure is
|
|
valid. If no value is passed then the token never expires.
|
|
Expiration time is not currently enforced in the system.
|
|
|
|
Identifier - Resource manager manager specific identifier. This is never
|
|
interpreted by Authz.
|
|
|
|
DynamicGroupArgs - To be passed to the callback function that computes
|
|
dynamic groups
|
|
|
|
pAuthzClientContext - To return a handle to the AuthzClientContext
|
|
structure. The returned handle must be freed using AuthzFreeContext.
|
|
|
|
bIsInternalRoutine - When this is on, Group context is built recursively.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the API is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PSID_AND_ATTRIBUTES pSidAttr = NULL;
|
|
PAUTHZI_CLIENT_CONTEXT pCC = NULL;
|
|
BOOL b = FALSE;
|
|
DWORD SidCount = 0;
|
|
DWORD SidLength = 0;
|
|
LARGE_INTEGER ExpirationTime = {0, 0};
|
|
LUID NullLuid = {0, 0};
|
|
PAUTHZI_RESOURCE_MANAGER pRM = (PAUTHZI_RESOURCE_MANAGER) hAuthzResourceManager;
|
|
|
|
if (!ARGUMENT_PRESENT(UserSid) ||
|
|
!ARGUMENT_PRESENT(hAuthzResourceManager) ||
|
|
!ARGUMENT_PRESENT(phAuthzClientContext))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*phAuthzClientContext = NULL;
|
|
|
|
if ((0 == (Flags & AUTHZ_SKIP_TOKEN_GROUPS)) && (FALSE == bIsInternalRoutine))
|
|
{
|
|
DWORD LocalFlags = 0;
|
|
|
|
//
|
|
// If the caller did not supply AUTHZ_SKIP_TOKEN_GROUPS, check whether
|
|
// we should add it ourselves. This should be done for WellKnown and
|
|
// Builtins.
|
|
//
|
|
|
|
b = AuthzpComputeSkipFlagsForWellKnownSid(UserSid, &LocalFlags);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
Flags |= LocalFlags;
|
|
}
|
|
|
|
//
|
|
// Compute the token groups and the machine local groups. These will be
|
|
// returned in memory allocated by the callee.
|
|
//
|
|
|
|
b = AuthzpGetAllGroupsBySid(
|
|
UserSid,
|
|
Flags,
|
|
&pSidAttr,
|
|
&SidCount,
|
|
&SidLength
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(pExpirationTime))
|
|
{
|
|
ExpirationTime = *pExpirationTime;
|
|
}
|
|
|
|
//
|
|
// Initialize the client context. The callee allocates memory for the client
|
|
// context structure.
|
|
//
|
|
|
|
b = AuthzpAllocateAndInitializeClientContext(
|
|
&pCC,
|
|
NULL,
|
|
AUTHZ_CURRENT_CONTEXT_REVISION,
|
|
Identifier,
|
|
ExpirationTime,
|
|
0,
|
|
SidCount,
|
|
SidLength,
|
|
pSidAttr,
|
|
0,
|
|
0,
|
|
NULL,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NullLuid,
|
|
NULL,
|
|
pRM
|
|
);
|
|
|
|
if (!b) goto Cleanup;
|
|
|
|
//
|
|
// Add dynamic sids to the token.
|
|
//
|
|
|
|
b = AuthzpAddDynamicSidsToToken(
|
|
pCC,
|
|
pRM,
|
|
DynamicGroupArgs,
|
|
pSidAttr,
|
|
SidLength,
|
|
SidCount,
|
|
NULL,
|
|
0,
|
|
0,
|
|
NULL,
|
|
0,
|
|
0,
|
|
TRUE
|
|
);
|
|
|
|
if (!b) goto Cleanup;
|
|
|
|
*phAuthzClientContext = (AUTHZ_CLIENT_CONTEXT_HANDLE) pCC;
|
|
|
|
AuthzPrintContext(pCC);
|
|
|
|
//
|
|
// initialize the sid hash for regular sids
|
|
//
|
|
|
|
AuthzpInitSidHash(
|
|
pCC->Sids,
|
|
pCC->SidCount,
|
|
pCC->SidHash
|
|
);
|
|
|
|
//
|
|
// initialize the sid hash for restricted sids
|
|
//
|
|
|
|
AuthzpInitSidHash(
|
|
pCC->RestrictedSids,
|
|
pCC->RestrictedSidCount,
|
|
pCC->RestrictedSidHash
|
|
);
|
|
|
|
Cleanup:
|
|
|
|
if (!b)
|
|
{
|
|
AuthzpFreeNonNull(pSidAttr);
|
|
if (AUTHZ_NON_NULL_PTR(pCC))
|
|
{
|
|
if (pSidAttr != pCC->Sids)
|
|
{
|
|
AuthzpFreeNonNull(pCC->Sids);
|
|
}
|
|
|
|
AuthzpFreeNonNull(pCC->RestrictedSids);
|
|
AuthzpFree(pCC);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pSidAttr != pCC->Sids)
|
|
{
|
|
AuthzpFree(pSidAttr);
|
|
}
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
AuthzInitializeContextFromSid(
|
|
IN DWORD Flags,
|
|
IN PSID UserSid,
|
|
IN AUTHZ_RESOURCE_MANAGER_HANDLE hAuthzResourceManager,
|
|
IN PLARGE_INTEGER pExpirationTime OPTIONAL,
|
|
IN LUID Identifier,
|
|
IN PVOID DynamicGroupArgs,
|
|
OUT PAUTHZ_CLIENT_CONTEXT_HANDLE phAuthzClientContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API takes a user sid and creates a user mode client context from it.
|
|
It fetches the TokenGroups attributes from the AD in case of domain sids.
|
|
The machine local groups are computed on the ServerName specified. The
|
|
resource manager may dynamic groups using callback mechanism.
|
|
|
|
Arguments:
|
|
|
|
Flags -
|
|
AUTHZ_SKIP_TOKEN_GROUPS - Do not evaluate token groups if this is on.
|
|
|
|
UserSid - The sid of the user for whom a client context will be created.
|
|
|
|
ServerName - The machine on which local groups should be computed. A NULL
|
|
server name defaults to the local machine.
|
|
|
|
AuthzResourceManager - The resource manager handle creating this client
|
|
context. This will be stored in the client context structure.
|
|
|
|
pExpirationTime - To set for how long the returned context structure is
|
|
valid. If no value is passed then the token never expires.
|
|
Expiration time is not currently enforced in the system.
|
|
|
|
Identifier - Resource manager manager specific identifier. This is never
|
|
interpreted by Authz.
|
|
|
|
DynamicGroupArgs - To be passed to the callback function that computes
|
|
dynamic groups
|
|
|
|
pAuthzClientContext - To return a handle to the AuthzClientContext
|
|
structure. The returned handle must be freed using AuthzFreeContext.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the API is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
return AuthzpInitializeContextFromSid(
|
|
Flags,
|
|
UserSid,
|
|
hAuthzResourceManager,
|
|
pExpirationTime,
|
|
Identifier,
|
|
DynamicGroupArgs,
|
|
phAuthzClientContext,
|
|
FALSE // This is not the internal routine.
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthziInitializeContextFromSid(
|
|
IN DWORD Flags,
|
|
IN PSID UserSid,
|
|
IN AUTHZ_RESOURCE_MANAGER_HANDLE hAuthzResourceManager,
|
|
IN PLARGE_INTEGER pExpirationTime OPTIONAL,
|
|
IN LUID Identifier,
|
|
IN PVOID DynamicGroupArgs,
|
|
OUT PAUTHZ_CLIENT_CONTEXT_HANDLE phAuthzClientContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API takes a user sid and creates a user mode client context from it.
|
|
It fetches the TokenGroups attributes from the AD in case of domain sids.
|
|
The machine local groups are computed on the ServerName specified. The
|
|
resource manager may dynamic groups using callback mechanism.
|
|
|
|
Arguments:
|
|
|
|
Flags -
|
|
AUTHZ_SKIP_TOKEN_GROUPS - Do not evaluate token groups if this is on.
|
|
|
|
UserSid - The sid of the user for whom a client context will be created.
|
|
|
|
ServerName - The machine on which local groups should be computed. A NULL
|
|
server name defaults to the local machine.
|
|
|
|
AuthzResourceManager - The resource manager handle creating this client
|
|
context. This will be stored in the client context structure.
|
|
|
|
pExpirationTime - To set for how long the returned context structure is
|
|
valid. If no value is passed then the token never expires.
|
|
Expiration time is not currently enforced in the system.
|
|
|
|
Identifier - Resource manager manager specific identifier. This is never
|
|
interpreted by Authz.
|
|
|
|
DynamicGroupArgs - To be passed to the callback function that computes
|
|
dynamic groups
|
|
|
|
pAuthzClientContext - To return a handle to the AuthzClientContext
|
|
structure. The returned handle must be freed using AuthzFreeContext.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the API is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
return AuthzpInitializeContextFromSid(
|
|
Flags,
|
|
UserSid,
|
|
hAuthzResourceManager,
|
|
pExpirationTime,
|
|
Identifier,
|
|
DynamicGroupArgs,
|
|
phAuthzClientContext,
|
|
TRUE // This is the internal routine.
|
|
);
|
|
}
|
|
|
|
BOOL
|
|
AuthzInitializeContextFromAuthzContext(
|
|
IN DWORD Flags,
|
|
IN AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClientContext,
|
|
IN PLARGE_INTEGER pExpirationTime OPTIONAL,
|
|
IN LUID Identifier,
|
|
IN PVOID DynamicGroupArgs,
|
|
OUT PAUTHZ_CLIENT_CONTEXT_HANDLE phNewAuthzClientContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API creates an AUTHZ_CLIENT_CONTEXT based on another AUTHZ_CLIENT_CONTEXT.
|
|
|
|
Arguments:
|
|
|
|
Flags - TBD
|
|
|
|
hAuthzClientContext - Client context to duplicate
|
|
|
|
pExpirationTime - To set for how long the returned context structure is
|
|
valid. If no value is passed then the token never expires.
|
|
Expiration time is not currently enforced in the system.
|
|
|
|
Identifier - Resource manager manager specific identifier.
|
|
|
|
DynamicGroupArgs - To be passed to the callback function that computes
|
|
dynamic groups. If NULL then callback not called.
|
|
|
|
phNewAuthzClientContext - Duplicate of context. Must be freed using AuthzFreeContext.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the API 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) hAuthzClientContext;
|
|
PAUTHZI_CLIENT_CONTEXT pNewCC = NULL;
|
|
PAUTHZI_CLIENT_CONTEXT pServer = NULL;
|
|
BOOL b = FALSE;
|
|
BOOL bAllocatedSids = FALSE;
|
|
LARGE_INTEGER ExpirationTime = {0, 0};
|
|
|
|
|
|
if (!ARGUMENT_PRESENT(phNewAuthzClientContext) ||
|
|
!ARGUMENT_PRESENT(hAuthzClientContext))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*phNewAuthzClientContext = NULL;
|
|
|
|
//
|
|
// Determine the ExpirationTime of the new context.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(pExpirationTime))
|
|
{
|
|
ExpirationTime = *pExpirationTime;
|
|
}
|
|
|
|
AuthzpAcquireClientContextReadLock(pCC);
|
|
|
|
if (AUTHZ_NON_NULL_PTR(pCC->Server))
|
|
{
|
|
b = AuthzInitializeContextFromAuthzContext(
|
|
0,
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC->Server,
|
|
NULL,
|
|
pCC->Server->Identifier,
|
|
NULL,
|
|
(PAUTHZ_CLIENT_CONTEXT_HANDLE) &pServer
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now initialize the new context.
|
|
//
|
|
|
|
b = AuthzpAllocateAndInitializeClientContext(
|
|
&pNewCC,
|
|
pServer,
|
|
pCC->Revision,
|
|
Identifier,
|
|
ExpirationTime,
|
|
Flags,
|
|
pCC->SidCount,
|
|
pCC->SidLength,
|
|
pCC->Sids,
|
|
pCC->RestrictedSidCount,
|
|
pCC->RestrictedSidLength,
|
|
pCC->RestrictedSids,
|
|
pCC->PrivilegeCount,
|
|
pCC->PrivilegeLength,
|
|
pCC->Privileges,
|
|
pCC->AuthenticationId,
|
|
NULL,
|
|
pCC->pResourceManager
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
b = AuthzpAddDynamicSidsToToken(
|
|
pNewCC,
|
|
pNewCC->pResourceManager,
|
|
DynamicGroupArgs,
|
|
pNewCC->Sids,
|
|
pNewCC->SidLength,
|
|
pNewCC->SidCount,
|
|
pNewCC->RestrictedSids,
|
|
pNewCC->RestrictedSidLength,
|
|
pNewCC->RestrictedSidCount,
|
|
pNewCC->Privileges,
|
|
pNewCC->PrivilegeLength,
|
|
pNewCC->PrivilegeCount,
|
|
FALSE
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
bAllocatedSids = TRUE;
|
|
*phNewAuthzClientContext = (AUTHZ_CLIENT_CONTEXT_HANDLE) pNewCC;
|
|
|
|
#ifdef AUTHZ_DEBUG
|
|
wprintf(L"ContextFromAuthzContext: Original Context:\n");
|
|
AuthzPrintContext(pCC);
|
|
wprintf(L"ContextFromAuthzContext: New Context:\n");
|
|
AuthzPrintContext(pNewCC);
|
|
#endif
|
|
|
|
//
|
|
// initialize the sid hash for regular sids
|
|
//
|
|
|
|
AuthzpInitSidHash(
|
|
pNewCC->Sids,
|
|
pNewCC->SidCount,
|
|
pNewCC->SidHash
|
|
);
|
|
|
|
//
|
|
// initialize the sid hash for restricted sids
|
|
//
|
|
|
|
AuthzpInitSidHash(
|
|
pNewCC->RestrictedSids,
|
|
pNewCC->RestrictedSidCount,
|
|
pNewCC->RestrictedSidHash
|
|
);
|
|
|
|
Cleanup:
|
|
|
|
if (!b)
|
|
{
|
|
DWORD dwSavedError = GetLastError();
|
|
|
|
if (AUTHZ_NON_NULL_PTR(pNewCC))
|
|
{
|
|
if (bAllocatedSids)
|
|
{
|
|
AuthzFreeContext((AUTHZ_CLIENT_CONTEXT_HANDLE)pNewCC);
|
|
}
|
|
else
|
|
{
|
|
AuthzpFree(pNewCC);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (AUTHZ_NON_NULL_PTR(pServer))
|
|
{
|
|
AuthzFreeContext((AUTHZ_CLIENT_CONTEXT_HANDLE)pServer);
|
|
}
|
|
}
|
|
SetLastError(dwSavedError);
|
|
}
|
|
|
|
AuthzpReleaseClientContextLock(pCC);
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzAddSidsToContext(
|
|
IN AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClientContext,
|
|
IN PSID_AND_ATTRIBUTES Sids OPTIONAL,
|
|
IN DWORD SidCount,
|
|
IN PSID_AND_ATTRIBUTES RestrictedSids OPTIONAL,
|
|
IN DWORD RestrictedSidCount,
|
|
OUT PAUTHZ_CLIENT_CONTEXT_HANDLE phNewAuthzClientContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API creates a new context given a set of sids as well as restricted sids
|
|
and an already existing context. The original is unchanged.
|
|
|
|
Arguments:
|
|
|
|
hAuthzClientContext - Client context to which the given sids will be added
|
|
|
|
Sids - Sids and attributes to be added to the normal part of the client
|
|
context
|
|
|
|
SidCount - Number of sids to be added
|
|
|
|
RestrictedSids - Sids and attributes to be added to the restricted part of
|
|
the client context
|
|
|
|
RestrictedSidCount - Number of restricted sids to be added
|
|
|
|
phNewAuthzClientContext - The new context with the additional sids.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the API is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i = 0;
|
|
DWORD SidLength = 0;
|
|
DWORD RestrictedSidLength = 0;
|
|
PSID_AND_ATTRIBUTES pSidAttr = NULL;
|
|
PSID_AND_ATTRIBUTES pRestrictedSidAttr = NULL;
|
|
BOOL b = TRUE;
|
|
PAUTHZI_CLIENT_CONTEXT pCC = (PAUTHZI_CLIENT_CONTEXT) hAuthzClientContext;
|
|
PAUTHZI_CLIENT_CONTEXT pNewCC = NULL;
|
|
PAUTHZI_CLIENT_CONTEXT pServer = NULL;
|
|
PLUID_AND_ATTRIBUTES pPrivileges = NULL;
|
|
|
|
if ((!ARGUMENT_PRESENT(phNewAuthzClientContext)) ||
|
|
(!ARGUMENT_PRESENT(hAuthzClientContext)) ||
|
|
(0 != SidCount && !ARGUMENT_PRESENT(Sids)) ||
|
|
(0 != RestrictedSidCount && !ARGUMENT_PRESENT(RestrictedSids)))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*phNewAuthzClientContext = NULL;
|
|
|
|
AuthzpAcquireClientContextReadLock(pCC);
|
|
|
|
//
|
|
// Recursively duplicate the server
|
|
//
|
|
|
|
if (AUTHZ_NON_NULL_PTR(pCC->Server))
|
|
{
|
|
|
|
b = AuthzInitializeContextFromAuthzContext(
|
|
0,
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC->Server,
|
|
NULL,
|
|
pCC->Server->Identifier,
|
|
NULL,
|
|
(PAUTHZ_CLIENT_CONTEXT_HANDLE) &pServer
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Duplicate the context, and do all further work on the duplicate.
|
|
//
|
|
|
|
b = AuthzpAllocateAndInitializeClientContext(
|
|
&pNewCC,
|
|
pServer,
|
|
pCC->Revision,
|
|
pCC->Identifier,
|
|
pCC->ExpirationTime,
|
|
pCC->Flags,
|
|
0,
|
|
0,
|
|
NULL,
|
|
0,
|
|
0,
|
|
NULL,
|
|
0,
|
|
0,
|
|
NULL,
|
|
pCC->AuthenticationId,
|
|
NULL,
|
|
pCC->pResourceManager
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
SidLength = sizeof(SID_AND_ATTRIBUTES) * SidCount;
|
|
|
|
//
|
|
// Compute the length required to hold the new sids.
|
|
//
|
|
|
|
for (i = 0; i < SidCount; i++)
|
|
{
|
|
#ifdef AUTHZ_PARAM_CHECK
|
|
if (FLAG_ON(Sids[i].Attributes, ~AUTHZ_VALID_SID_ATTRIBUTES) ||
|
|
!FLAG_ON(Sids[i].Attributes, AUTHZ_VALID_SID_ATTRIBUTES))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!RtlValidSid(Sids[i].Sid))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
#endif
|
|
SidLength += RtlLengthSid(Sids[i].Sid);
|
|
}
|
|
|
|
RestrictedSidLength = sizeof(SID_AND_ATTRIBUTES) * RestrictedSidCount;
|
|
|
|
//
|
|
// Compute the length required to hold the new restricted sids.
|
|
//
|
|
|
|
for (i = 0; i < RestrictedSidCount; i++)
|
|
{
|
|
#ifdef AUTHZ_PARAM_CHECK
|
|
if (FLAG_ON(RestrictedSids[i].Attributes, ~AUTHZ_VALID_SID_ATTRIBUTES) ||
|
|
!FLAG_ON(RestrictedSids[i].Attributes, AUTHZ_VALID_SID_ATTRIBUTES))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!RtlValidSid(RestrictedSids[i].Sid))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
#endif
|
|
RestrictedSidLength += RtlLengthSid(RestrictedSids[i].Sid);
|
|
}
|
|
|
|
//
|
|
// Copy the existing sids and the new ones into the allocated memory.
|
|
//
|
|
|
|
SidLength += pCC->SidLength;
|
|
|
|
if (0 != SidLength)
|
|
{
|
|
|
|
pSidAttr = (PSID_AND_ATTRIBUTES) AuthzpAlloc(SidLength);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pSidAttr))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
b = AuthzpCopySidsAndAttributes(
|
|
pSidAttr,
|
|
pCC->Sids,
|
|
pCC->SidCount,
|
|
Sids,
|
|
SidCount
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Copy the existing restricted sids and the new ones into the allocated
|
|
// memory.
|
|
//
|
|
|
|
RestrictedSidLength += pCC->RestrictedSidLength;
|
|
|
|
if (0 != RestrictedSidLength)
|
|
{
|
|
|
|
pRestrictedSidAttr = (PSID_AND_ATTRIBUTES) AuthzpAlloc(RestrictedSidLength);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pRestrictedSidAttr))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
b = AuthzpCopySidsAndAttributes(
|
|
pRestrictedSidAttr,
|
|
pCC->RestrictedSids,
|
|
pCC->RestrictedSidCount,
|
|
RestrictedSids,
|
|
RestrictedSidCount
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the existing privileges into the allocated memory.
|
|
//
|
|
|
|
pPrivileges = (PLUID_AND_ATTRIBUTES) AuthzpAlloc(pCC->PrivilegeLength);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pPrivileges))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
AuthzpCopyLuidAndAttributes(
|
|
pNewCC,
|
|
pCC->Privileges,
|
|
pCC->PrivilegeCount,
|
|
pPrivileges
|
|
);
|
|
|
|
//
|
|
// Update fields in the client context.
|
|
//
|
|
|
|
pNewCC->Sids = pSidAttr;
|
|
pNewCC->SidLength = SidLength;
|
|
pNewCC->SidCount = SidCount + pCC->SidCount;
|
|
pSidAttr = NULL;
|
|
|
|
pNewCC->RestrictedSids = pRestrictedSidAttr;
|
|
pNewCC->RestrictedSidLength = RestrictedSidLength;
|
|
pNewCC->RestrictedSidCount = RestrictedSidCount + pCC->RestrictedSidCount;
|
|
pRestrictedSidAttr = NULL;
|
|
|
|
pNewCC->Privileges = pPrivileges;
|
|
pNewCC->PrivilegeCount = pCC->PrivilegeCount;
|
|
pNewCC->PrivilegeLength = pCC->PrivilegeLength;
|
|
pPrivileges = NULL;
|
|
|
|
*phNewAuthzClientContext = (AUTHZ_CLIENT_CONTEXT_HANDLE) pNewCC;
|
|
|
|
#ifdef AUTHZ_DEBUG
|
|
wprintf(L"AddSids: Original Context:\n");
|
|
AuthzPrintContext(pCC);
|
|
wprintf(L"AddSids: New Context:\n");
|
|
AuthzPrintContext(pNewCC);
|
|
#endif
|
|
|
|
//
|
|
// initialize the sid hash for regular sids
|
|
//
|
|
|
|
AuthzpInitSidHash(
|
|
pNewCC->Sids,
|
|
pNewCC->SidCount,
|
|
pNewCC->SidHash
|
|
);
|
|
|
|
//
|
|
// initialize the sid hash for restricted sids
|
|
//
|
|
|
|
AuthzpInitSidHash(
|
|
pNewCC->RestrictedSids,
|
|
pNewCC->RestrictedSidCount,
|
|
pNewCC->RestrictedSidHash
|
|
);
|
|
|
|
Cleanup:
|
|
|
|
AuthzpReleaseClientContextLock(pCC);
|
|
|
|
//
|
|
// These statements are relevant in the failure case.
|
|
// In the success case, the pointers are set to NULL.
|
|
//
|
|
|
|
if (!b)
|
|
{
|
|
DWORD dwSavedError = GetLastError();
|
|
|
|
AuthzpFreeNonNull(pSidAttr);
|
|
AuthzpFreeNonNull(pRestrictedSidAttr);
|
|
AuthzpFreeNonNull(pPrivileges);
|
|
if (AUTHZ_NON_NULL_PTR(pNewCC))
|
|
{
|
|
AuthzFreeContext((AUTHZ_CLIENT_CONTEXT_HANDLE)pNewCC);
|
|
}
|
|
SetLastError(dwSavedError);
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzGetInformationFromContext(
|
|
IN AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClientContext,
|
|
IN AUTHZ_CONTEXT_INFORMATION_CLASS InfoClass,
|
|
IN DWORD BufferSize,
|
|
OUT PDWORD pSizeRequired,
|
|
OUT PVOID Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API returns information about the client context in a buffer supplied
|
|
by the caller. It also returns the size of the buffer required to hold the
|
|
requested information.
|
|
|
|
Arguments:
|
|
|
|
AuthzClientContext - Authz client context from which requested information
|
|
will be read.
|
|
|
|
InfoClass - Type of information to be returned. The caller can ask for
|
|
a. privileges
|
|
TOKEN_PRIVILEGES
|
|
b. sids and their attributes
|
|
TOKEN_GROUPS
|
|
c. restricted sids and their attributes
|
|
TOKEN_GROUPS
|
|
d. authz context persistent structure which can be saved to and
|
|
read from the disk.
|
|
PVOID
|
|
e. User sid
|
|
TOKEN_USER
|
|
f. Server Context one level higher
|
|
PAUTHZ_CLIENT_CONTEXT
|
|
g. Expiration time
|
|
LARGE_INTEGER
|
|
h. Identifier
|
|
LUID
|
|
|
|
BufferSize - Size of the supplied buffer.
|
|
|
|
pSizeRequired - To return the size of the structure needed to hold the results.
|
|
|
|
Buffer - To hold the information requested. The structure returned will
|
|
depend on the information class requested.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the API is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD LocalSize = 0;
|
|
PAUTHZI_CLIENT_CONTEXT pCC = (PAUTHZI_CLIENT_CONTEXT) hAuthzClientContext;
|
|
|
|
if (!ARGUMENT_PRESENT(hAuthzClientContext) ||
|
|
(!ARGUMENT_PRESENT(Buffer) && BufferSize != 0) ||
|
|
!ARGUMENT_PRESENT(pSizeRequired))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*pSizeRequired = 0;
|
|
|
|
switch(InfoClass)
|
|
{
|
|
case AuthzContextInfoUserSid:
|
|
|
|
LocalSize = RtlLengthSid(pCC->Sids[0].Sid) + sizeof(TOKEN_USER);
|
|
|
|
*pSizeRequired = LocalSize;
|
|
|
|
if (LocalSize > BufferSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// xor SE_GROUP_ENABLED from the User attributes. Authz sets this because it simplifies
|
|
// access check logic.
|
|
//
|
|
|
|
((PTOKEN_USER)Buffer)->User.Attributes = pCC->Sids[0].Attributes ^ SE_GROUP_ENABLED;
|
|
((PTOKEN_USER)Buffer)->User.Sid = ((PUCHAR) Buffer) + sizeof(TOKEN_USER);
|
|
|
|
RtlCopyMemory(
|
|
((PTOKEN_USER)Buffer)->User.Sid,
|
|
pCC->Sids[0].Sid,
|
|
RtlLengthSid(pCC->Sids[0].Sid)
|
|
);
|
|
|
|
return TRUE;
|
|
|
|
case AuthzContextInfoGroupsSids:
|
|
|
|
LocalSize = pCC->SidLength +
|
|
sizeof(TOKEN_GROUPS) -
|
|
RtlLengthSid(pCC->Sids[0].Sid) -
|
|
2 * sizeof(SID_AND_ATTRIBUTES);
|
|
|
|
*pSizeRequired = LocalSize;
|
|
|
|
if (LocalSize > BufferSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
((PTOKEN_GROUPS) Buffer)->GroupCount = pCC->SidCount - 1;
|
|
|
|
return AuthzpCopySidsAndAttributes(
|
|
((PTOKEN_GROUPS) Buffer)->Groups,
|
|
pCC->Sids + 1,
|
|
pCC->SidCount - 1,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
case AuthzContextInfoRestrictedSids:
|
|
|
|
LocalSize = pCC->RestrictedSidLength +
|
|
sizeof(TOKEN_GROUPS) -
|
|
sizeof(SID_AND_ATTRIBUTES);
|
|
|
|
*pSizeRequired = LocalSize;
|
|
|
|
if (LocalSize > BufferSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
((PTOKEN_GROUPS) Buffer)->GroupCount = pCC->RestrictedSidCount;
|
|
|
|
return AuthzpCopySidsAndAttributes(
|
|
((PTOKEN_GROUPS) Buffer)->Groups,
|
|
pCC->RestrictedSids,
|
|
pCC->RestrictedSidCount,
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
case AuthzContextInfoPrivileges:
|
|
|
|
LocalSize = pCC->PrivilegeLength +
|
|
sizeof(TOKEN_PRIVILEGES) -
|
|
sizeof(LUID_AND_ATTRIBUTES);
|
|
|
|
*pSizeRequired = LocalSize;
|
|
|
|
if (LocalSize > BufferSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
((PTOKEN_PRIVILEGES) Buffer)->PrivilegeCount = pCC->PrivilegeCount;
|
|
|
|
memcpy(
|
|
((PTOKEN_PRIVILEGES) Buffer)->Privileges,
|
|
pCC->Privileges,
|
|
pCC->PrivilegeLength
|
|
);
|
|
|
|
return TRUE;
|
|
|
|
case AuthzContextInfoExpirationTime:
|
|
|
|
LocalSize = sizeof(LARGE_INTEGER);
|
|
|
|
*pSizeRequired = LocalSize;
|
|
|
|
if (LocalSize > BufferSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
*((PLARGE_INTEGER) Buffer) = pCC->ExpirationTime;
|
|
|
|
return TRUE;
|
|
|
|
case AuthzContextInfoIdentifier:
|
|
|
|
LocalSize = sizeof(LUID);
|
|
|
|
*pSizeRequired = LocalSize;
|
|
|
|
if (LocalSize > BufferSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
*((PLUID) Buffer) = pCC->Identifier;
|
|
|
|
return TRUE;
|
|
|
|
case AuthzContextInfoAuthenticationId:
|
|
|
|
LocalSize = sizeof(LUID);
|
|
|
|
*pSizeRequired = LocalSize;
|
|
|
|
if (LocalSize > BufferSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
*((PLUID) Buffer) = pCC->AuthenticationId;
|
|
|
|
return TRUE;
|
|
|
|
case AuthzContextInfoServerContext:
|
|
|
|
LocalSize = sizeof(AUTHZ_CLIENT_CONTEXT_HANDLE);
|
|
|
|
*pSizeRequired = LocalSize;
|
|
|
|
if (LocalSize > BufferSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
*((PAUTHZ_CLIENT_CONTEXT_HANDLE) Buffer) = (AUTHZ_CLIENT_CONTEXT_HANDLE) pCC->Server;
|
|
|
|
return TRUE;
|
|
|
|
default:
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzFreeContext(
|
|
IN AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClientContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API frees up all the structures/memory accociated with the client
|
|
context. Note that the list of handles for this client will be freed in
|
|
this call.
|
|
|
|
Arguments:
|
|
|
|
AuthzClientContext - Context to be freed.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the API 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) hAuthzClientContext;
|
|
BOOL b = TRUE;
|
|
PAUTHZI_HANDLE pCurrent = NULL;
|
|
PAUTHZI_HANDLE pPrev = NULL;
|
|
|
|
if (!ARGUMENT_PRESENT(pCC))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
AuthzpAcquireClientContextWriteLock(pCC);
|
|
|
|
AuthzpFreeNonNull(pCC->Privileges);
|
|
AuthzpFreeNonNull(pCC->Sids);
|
|
AuthzpFreeNonNull(pCC->RestrictedSids);
|
|
|
|
pCurrent = pCC->AuthzHandleHead;
|
|
|
|
//
|
|
// Loop thru all the handles and free them up.
|
|
//
|
|
|
|
while (AUTHZ_NON_NULL_PTR(pCurrent))
|
|
{
|
|
pPrev = pCurrent;
|
|
pCurrent = pCurrent->next;
|
|
AuthzpFree(pPrev);
|
|
}
|
|
|
|
//
|
|
// Free up the server context. The context is a recursive structure.
|
|
//
|
|
|
|
if (AUTHZ_NON_NULL_PTR(pCC->Server))
|
|
{
|
|
b = AuthzFreeContext((AUTHZ_CLIENT_CONTEXT_HANDLE) pCC->Server);
|
|
}
|
|
|
|
AuthzpFree(pCC);
|
|
|
|
return b;
|
|
}
|
|
|
|
AUTHZAPI
|
|
BOOL
|
|
WINAPI
|
|
AuthzInitializeObjectAccessAuditEvent(
|
|
IN DWORD Flags,
|
|
IN AUTHZ_AUDIT_EVENT_TYPE_HANDLE hAuditEventType,
|
|
IN PWSTR szOperationType,
|
|
IN PWSTR szObjectType,
|
|
IN PWSTR szObjectName,
|
|
IN PWSTR szAdditionalInfo,
|
|
OUT PAUTHZ_AUDIT_EVENT_HANDLE phAuditEvent,
|
|
IN DWORD dwAdditionalParameterCount,
|
|
...
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates and initializes an AUTHZ_AUDIT_EVENT_HANDLE for use with AuthzAccessCheck.
|
|
The handle is used for storing information for audit generation.
|
|
|
|
Arguments:
|
|
|
|
Flags - Audit flags. Currently defined bits are:
|
|
AUTHZ_NO_SUCCESS_AUDIT - disables generation of success audits
|
|
AUTHZ_NO_FAILURE_AUDIT - disables generation of failure audits
|
|
AUTHZ_NO_ALLOC_STRINGS - storage space is not allocated for the 4 wide character strings. Rather,
|
|
the handle will only hold pointers to resource manager memory.
|
|
|
|
hAuditEventType - for future use. Should be NULL.
|
|
|
|
szOperationType - Resource manager defined string that indicates the operation being
|
|
performed that is to be audited.
|
|
|
|
szObjectType - Resource manager defined string that indicates the type of object being
|
|
accessed.
|
|
|
|
szObjectName - the name of the specific object being accessed.
|
|
|
|
szAdditionalInfo - Resource Manager defined string for additional audit information.
|
|
|
|
phAuditEvent - pointer to AUTHZ_AUDIT_EVENT_HANDLE. Space for this is allocated in the function.
|
|
|
|
dwAdditionalParameterCount - Must be zero.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if successful, FALSE if unsuccessful.
|
|
Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER(dwAdditionalParameterCount);
|
|
|
|
return AuthzInitializeObjectAccessAuditEvent2(
|
|
Flags,
|
|
hAuditEventType,
|
|
szOperationType,
|
|
szObjectType,
|
|
szObjectName,
|
|
szAdditionalInfo,
|
|
L"\0",
|
|
phAuditEvent,
|
|
0
|
|
);
|
|
}
|
|
|
|
AUTHZAPI
|
|
BOOL
|
|
WINAPI
|
|
AuthzInitializeObjectAccessAuditEvent2(
|
|
IN DWORD Flags,
|
|
IN AUTHZ_AUDIT_EVENT_TYPE_HANDLE hAuditEventType,
|
|
IN PWSTR szOperationType,
|
|
IN PWSTR szObjectType,
|
|
IN PWSTR szObjectName,
|
|
IN PWSTR szAdditionalInfo,
|
|
IN PWSTR szAdditionalInfo2,
|
|
OUT PAUTHZ_AUDIT_EVENT_HANDLE phAuditEvent,
|
|
IN DWORD dwAdditionalParameterCount,
|
|
...
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates and initializes an AUTHZ_AUDIT_EVENT_HANDLE for use with AuthzAccessCheck.
|
|
The handle is used for storing information for audit generation.
|
|
|
|
Arguments:
|
|
|
|
Flags - Audit flags. Currently defined bits are:
|
|
AUTHZ_NO_SUCCESS_AUDIT - disables generation of success audits
|
|
AUTHZ_NO_FAILURE_AUDIT - disables generation of failure audits
|
|
AUTHZ_NO_ALLOC_STRINGS - storage space is not allocated for the 4 wide character strings. Rather,
|
|
the handle will only hold pointers to resource manager memory.
|
|
|
|
hAuditEventType - for future use. Should be NULL.
|
|
|
|
szOperationType - Resource manager defined string that indicates the operation being
|
|
performed that is to be audited.
|
|
|
|
szObjectType - Resource manager defined string that indicates the type of object being
|
|
accessed.
|
|
|
|
szObjectName - the name of the specific object being accessed.
|
|
|
|
szAdditionalInfo - Resource Manager defined string for additional audit information.
|
|
|
|
szAdditionalInfo2 - Resource Manager defined string for additional audit information.
|
|
|
|
phAuditEvent - pointer to AUTHZ_AUDIT_EVENT_HANDLE. Space for this is allocated in the function.
|
|
|
|
dwAdditionalParameterCount - Must be zero.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if successful, FALSE if unsuccessful.
|
|
Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PAUTHZI_AUDIT_EVENT pAuditEvent = NULL;
|
|
BOOL b = TRUE;
|
|
DWORD dwStringSize = 0;
|
|
DWORD dwObjectTypeLength = 0;
|
|
DWORD dwObjectNameLength = 0;
|
|
DWORD dwOperationTypeLength = 0;
|
|
DWORD dwAdditionalInfoLength = 0;
|
|
DWORD dwAdditionalInfo2Length = 0;
|
|
|
|
if ((!ARGUMENT_PRESENT(phAuditEvent)) ||
|
|
(NULL != hAuditEventType) ||
|
|
(0 != dwAdditionalParameterCount) ||
|
|
(!ARGUMENT_PRESENT(szOperationType)) ||
|
|
(!ARGUMENT_PRESENT(szObjectType)) ||
|
|
(!ARGUMENT_PRESENT(szObjectName)) ||
|
|
(!ARGUMENT_PRESENT(szAdditionalInfo)) ||
|
|
(!ARGUMENT_PRESENT(szAdditionalInfo2)) ||
|
|
(Flags & (~(AUTHZ_VALID_OBJECT_ACCESS_AUDIT_FLAGS | AUTHZ_DS_CATEGORY_FLAG))))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*phAuditEvent = NULL;
|
|
|
|
//
|
|
// Allocate and initialize a new AUTHZI_AUDIT_EVENT. Include for the string in the contiguous memory, if
|
|
// needed.
|
|
//
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_NO_ALLOC_STRINGS))
|
|
{
|
|
dwStringSize = 0;
|
|
}
|
|
else
|
|
{
|
|
dwOperationTypeLength = (DWORD) wcslen(szOperationType) + 1;
|
|
dwObjectTypeLength = (DWORD) wcslen(szObjectType) + 1;
|
|
dwObjectNameLength = (DWORD) wcslen(szObjectName) + 1;
|
|
dwAdditionalInfoLength = (DWORD) wcslen(szAdditionalInfo) + 1;
|
|
dwAdditionalInfo2Length = (DWORD) wcslen(szAdditionalInfo2) + 1;
|
|
|
|
dwStringSize = sizeof(WCHAR) * (dwOperationTypeLength + dwObjectTypeLength + dwObjectNameLength + dwAdditionalInfoLength + dwAdditionalInfo2Length);
|
|
}
|
|
|
|
pAuditEvent = (PAUTHZI_AUDIT_EVENT) AuthzpAlloc(sizeof(AUTHZI_AUDIT_EVENT) + dwStringSize);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pAuditEvent))
|
|
{
|
|
b = FALSE;
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_NO_ALLOC_STRINGS))
|
|
{
|
|
pAuditEvent->szOperationType = szOperationType;
|
|
pAuditEvent->szObjectType = szObjectType;
|
|
pAuditEvent->szObjectName = szObjectName;
|
|
pAuditEvent->szAdditionalInfo = szAdditionalInfo;
|
|
pAuditEvent->szAdditionalInfo2 = szAdditionalInfo2;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set the string pointers into the contiguous memory.
|
|
//
|
|
|
|
pAuditEvent->szOperationType = (PWSTR)((PUCHAR)pAuditEvent + sizeof(AUTHZI_AUDIT_EVENT));
|
|
|
|
RtlCopyMemory(
|
|
pAuditEvent->szOperationType,
|
|
szOperationType,
|
|
sizeof(WCHAR) * dwOperationTypeLength
|
|
);
|
|
|
|
|
|
pAuditEvent->szObjectType = (PWSTR)((PUCHAR)pAuditEvent->szOperationType + (sizeof(WCHAR) * dwOperationTypeLength));
|
|
|
|
RtlCopyMemory(
|
|
pAuditEvent->szObjectType,
|
|
szObjectType,
|
|
sizeof(WCHAR) * dwObjectTypeLength
|
|
);
|
|
|
|
pAuditEvent->szObjectName = (PWSTR)((PUCHAR)pAuditEvent->szObjectType + (sizeof(WCHAR) * dwObjectTypeLength));
|
|
|
|
RtlCopyMemory(
|
|
pAuditEvent->szObjectName,
|
|
szObjectName,
|
|
sizeof(WCHAR) * dwObjectNameLength
|
|
);
|
|
|
|
pAuditEvent->szAdditionalInfo = (PWSTR)((PUCHAR)pAuditEvent->szObjectName + (sizeof(WCHAR) * dwObjectNameLength));
|
|
|
|
RtlCopyMemory(
|
|
pAuditEvent->szAdditionalInfo,
|
|
szAdditionalInfo,
|
|
sizeof(WCHAR) * dwAdditionalInfoLength
|
|
);
|
|
|
|
pAuditEvent->szAdditionalInfo2 = (PWSTR)((PUCHAR)pAuditEvent->szAdditionalInfo + (sizeof(WCHAR) * dwAdditionalInfoLength));
|
|
|
|
RtlCopyMemory(
|
|
pAuditEvent->szAdditionalInfo2,
|
|
szAdditionalInfo2,
|
|
sizeof(WCHAR) * dwAdditionalInfo2Length
|
|
);
|
|
}
|
|
|
|
//
|
|
// AEI and Queue will be filled in from RM in AuthzpCreateAndLogAudit
|
|
//
|
|
|
|
pAuditEvent->hAET = NULL;
|
|
pAuditEvent->hAuditQueue = NULL;
|
|
pAuditEvent->pAuditParams = NULL;
|
|
pAuditEvent->Flags = Flags;
|
|
pAuditEvent->dwTimeOut = INFINITE;
|
|
pAuditEvent->dwSize = sizeof(AUTHZI_AUDIT_EVENT) + dwStringSize;
|
|
|
|
Cleanup:
|
|
|
|
if (!b)
|
|
{
|
|
AuthzpFreeNonNull(pAuditEvent);
|
|
}
|
|
else
|
|
{
|
|
*phAuditEvent = (AUTHZ_AUDIT_EVENT_HANDLE) pAuditEvent;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzGetInformationFromAuditEvent(
|
|
IN AUTHZ_AUDIT_EVENT_HANDLE hAuditEvent,
|
|
IN AUTHZ_AUDIT_EVENT_INFORMATION_CLASS InfoClass,
|
|
IN DWORD BufferSize,
|
|
OUT PDWORD pSizeRequired,
|
|
OUT PVOID Buffer
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Queries information in the AUTHZ_AUDIT_EVENT_HANDLE.
|
|
|
|
Arguments
|
|
|
|
hAuditEvent - the AUTHZ_AUDIT_EVENT_HANDLE to query.
|
|
|
|
InfoClass - The class of information to query. Valid values are:
|
|
|
|
a. AuthzAuditEventInfoFlags - returns the flags set for the handle. Type is DWORD.
|
|
e. AuthzAuditEventInfoOperationType - returns the operation type. Type is PCWSTR.
|
|
e. AuthzAuditEventInfoObjectType - returns the object type. Type is PCWSTR.
|
|
f. AuthzAuditEventInfoObjectName - returns the object name. Type is PCWSTR.
|
|
g. AuthzAuditEventInfoAdditionalInfo - returns the additional info field. Type is PCWSTR.
|
|
|
|
BufferSize - Size of the supplied buffer.
|
|
|
|
pSizeRequired - To return the size of the structure needed to hold the results.
|
|
|
|
Buffer - To hold the information requested. The structure returned will
|
|
depend on the information class requested.
|
|
|
|
Return Value
|
|
|
|
Boolean: TRUE on success; FALSE on failure. Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
DWORD LocalSize = 0;
|
|
PAUTHZI_AUDIT_EVENT pAuditEvent = (PAUTHZI_AUDIT_EVENT) hAuditEvent;
|
|
|
|
if ((!ARGUMENT_PRESENT(hAuditEvent)) ||
|
|
(!ARGUMENT_PRESENT(pSizeRequired)) ||
|
|
(!ARGUMENT_PRESENT(Buffer) && BufferSize > 0))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*pSizeRequired = 0;
|
|
|
|
switch(InfoClass)
|
|
{
|
|
case AuthzAuditEventInfoFlags:
|
|
|
|
LocalSize = sizeof(DWORD);
|
|
|
|
*pSizeRequired = LocalSize;
|
|
|
|
if (LocalSize > BufferSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
*((PDWORD)Buffer) = pAuditEvent->Flags;
|
|
|
|
return TRUE;
|
|
|
|
case AuthzAuditEventInfoOperationType:
|
|
|
|
LocalSize = (DWORD)(sizeof(WCHAR) * (wcslen(pAuditEvent->szOperationType) + 1));
|
|
|
|
*pSizeRequired = LocalSize;
|
|
|
|
if (LocalSize > BufferSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
Buffer,
|
|
pAuditEvent->szOperationType,
|
|
LocalSize
|
|
);
|
|
|
|
return TRUE;
|
|
|
|
case AuthzAuditEventInfoObjectType:
|
|
|
|
LocalSize = (DWORD)(sizeof(WCHAR) * (wcslen(pAuditEvent->szObjectType) + 1));
|
|
|
|
*pSizeRequired = LocalSize;
|
|
|
|
if (LocalSize > BufferSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
Buffer,
|
|
pAuditEvent->szObjectType,
|
|
LocalSize
|
|
);
|
|
|
|
return TRUE;
|
|
|
|
case AuthzAuditEventInfoObjectName:
|
|
|
|
LocalSize = (DWORD)(sizeof(WCHAR) * (wcslen(pAuditEvent->szObjectName) + 1));
|
|
|
|
*pSizeRequired = LocalSize;
|
|
|
|
if (LocalSize > BufferSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
Buffer,
|
|
pAuditEvent->szObjectName,
|
|
LocalSize
|
|
);
|
|
|
|
return TRUE;
|
|
|
|
case AuthzAuditEventInfoAdditionalInfo:
|
|
|
|
if (NULL == pAuditEvent->szAdditionalInfo)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
LocalSize = (DWORD)(sizeof(WCHAR) * (wcslen(pAuditEvent->szAdditionalInfo) + 1));
|
|
|
|
*pSizeRequired = LocalSize;
|
|
|
|
if (LocalSize > BufferSize)
|
|
{
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
Buffer,
|
|
pAuditEvent->szAdditionalInfo,
|
|
LocalSize
|
|
);
|
|
|
|
return TRUE;
|
|
|
|
default:
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzFreeAuditEvent(
|
|
IN AUTHZ_AUDIT_EVENT_HANDLE hAuditEvent
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees hAuditEvent and notifies the appropriate queue to unregister the audit context in LSA.
|
|
|
|
Arguments:
|
|
|
|
hAuditEvent - AUTHZ_AUDIT_EVENT_HANDLE. Must have initially been created
|
|
with AuthzRMInitializeObjectAccessAuditEvent or AuthzInitializeAuditEvent().
|
|
|
|
Return Value:
|
|
|
|
Boolean: TRUE if successful; FALSE if failure.
|
|
Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PAUTHZI_AUDIT_EVENT pAuditEvent = (PAUTHZI_AUDIT_EVENT) hAuditEvent;
|
|
|
|
if (!ARGUMENT_PRESENT(hAuditEvent))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If the RM specified the AuditEvent, then we should deref the context. If the AuditEvent
|
|
// has not been used, or was a default event type, then this field will be NULL.
|
|
//
|
|
|
|
if (AUTHZ_NON_NULL_PTR(pAuditEvent->hAET))
|
|
{
|
|
AuthzpDereferenceAuditEventType(pAuditEvent->hAET);
|
|
}
|
|
|
|
AuthzpFree(pAuditEvent);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// Routines for internal callers.
|
|
//
|
|
|
|
|
|
BOOL
|
|
AuthziInitializeAuditEventType(
|
|
IN DWORD Flags,
|
|
IN USHORT CategoryID,
|
|
IN USHORT AuditID,
|
|
IN USHORT ParameterCount,
|
|
OUT PAUTHZ_AUDIT_EVENT_TYPE_HANDLE phAuditEventType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Initializes an AUTHZ_AUDIT_EVENT_TYPE_HANDLE for use in AuthzInitializeAuditEvent().
|
|
|
|
Arguments
|
|
|
|
phAuditEventType - pointer to pointer to receive memory allocated for AUTHZ_AUDIT_EVENT_TYPE_HANDLE.
|
|
|
|
dwFlags - Flags that control behavior of function.
|
|
AUTHZ_INIT_GENERIC_AUDIT_EVENT - initialize the AUTHZ_AUDIT_EVENT_TYPE for generic object
|
|
access audits. When this flag is specified, none of the optional parameters need to
|
|
be passed. This is equivalent to calling:
|
|
|
|
AuthzInitializeAuditEvent(
|
|
&hAEI,
|
|
0,
|
|
SE_CATEGID_OBJECT_ACCESS,
|
|
SE_AUDITID_OBJECT_OPERATION,
|
|
AUTHZP_NUM_PARAMS_FOR_SE_AUDITID_OBJECT_OPERATION
|
|
);
|
|
|
|
CategoryID - The category id of the audit.
|
|
|
|
AuditID - The ID of the audit in msaudite.
|
|
|
|
ParameterCount - The number of fields in the audit.
|
|
|
|
Return Value
|
|
|
|
Boolean: TRUE on success; FALSE on failure. Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PAUTHZ_AUDIT_EVENT_TYPE_OLD pAET = NULL;
|
|
BOOL b = TRUE;
|
|
AUDIT_HANDLE hAudit = NULL;
|
|
|
|
if (!ARGUMENT_PRESENT(phAuditEventType))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*phAuditEventType = NULL;
|
|
|
|
pAET = AuthzpAlloc(sizeof(AUTHZ_AUDIT_EVENT_TYPE_OLD));
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pAET))
|
|
{
|
|
b = FALSE;
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (FLAG_ON(Flags, AUTHZP_INIT_GENERIC_AUDIT_EVENT))
|
|
{
|
|
pAET->Version = AUDIT_TYPE_LEGACY;
|
|
pAET->u.Legacy.CategoryId = SE_CATEGID_OBJECT_ACCESS;
|
|
pAET->u.Legacy.AuditId = SE_AUDITID_OBJECT_OPERATION;
|
|
pAET->u.Legacy.ParameterCount = AUTHZP_NUM_PARAMS_FOR_SE_AUDITID_OBJECT_OPERATION + AUTHZP_NUM_FIXED_HEADER_PARAMS;
|
|
}
|
|
else
|
|
{
|
|
pAET->Version = AUDIT_TYPE_LEGACY;
|
|
pAET->u.Legacy.CategoryId = CategoryID;
|
|
pAET->u.Legacy.AuditId = AuditID;
|
|
|
|
//
|
|
// ParameterCount gets increased by 2 because the LSA expects the first two
|
|
// parameters to be the user sid and subsystem name.
|
|
//
|
|
|
|
pAET->u.Legacy.ParameterCount = ParameterCount + AUTHZP_NUM_FIXED_HEADER_PARAMS;
|
|
}
|
|
|
|
b = AuthzpRegisterAuditEvent(
|
|
pAET,
|
|
&hAudit
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
pAET->hAudit = (ULONG_PTR) hAudit;
|
|
pAET->dwFlags = Flags & ~AUTHZP_INIT_GENERIC_AUDIT_EVENT;
|
|
|
|
Cleanup:
|
|
|
|
if (!b)
|
|
{
|
|
AuthzpFreeNonNull(pAET);
|
|
}
|
|
else
|
|
{
|
|
AuthzpReferenceAuditEventType((AUTHZ_AUDIT_EVENT_TYPE_HANDLE)pAET);
|
|
*phAuditEventType = (AUTHZ_AUDIT_EVENT_TYPE_HANDLE)pAET;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthziModifyAuditEventType(
|
|
IN DWORD Flags,
|
|
IN USHORT CategoryID,
|
|
IN USHORT AuditID,
|
|
IN USHORT ParameterCount,
|
|
IN OUT AUTHZ_AUDIT_EVENT_TYPE_HANDLE hAuditEventType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Modifies an existing AuditEventType.
|
|
|
|
Arguments
|
|
|
|
Flags - AUTHZ_AUDIT_EVENT_TYPE_AUDITID
|
|
|
|
Return Value
|
|
|
|
--*/
|
|
{
|
|
PAUTHZ_AUDIT_EVENT_TYPE_OLD pAAETO = (PAUTHZ_AUDIT_EVENT_TYPE_OLD) hAuditEventType;
|
|
BOOL b = TRUE;
|
|
|
|
if (!ARGUMENT_PRESENT(hAuditEventType))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
UNREFERENCED_PARAMETER(CategoryID);
|
|
UNREFERENCED_PARAMETER(ParameterCount);
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_AUDIT_EVENT_TYPE_AUDITID))
|
|
{
|
|
pAAETO->u.Legacy.AuditId = AuditID;
|
|
}
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_AUDIT_EVENT_TYPE_CATEGID))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_AUDIT_EVENT_TYPE_PARAM))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthziFreeAuditEventType(
|
|
AUTHZ_AUDIT_EVENT_TYPE_HANDLE hAuditEventType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Frees the PAUDIT_EVENT_TYPE allocated by AuthzInitializeAuditEventType().
|
|
|
|
Arguments
|
|
|
|
pAuditEventType - pointer to memory to free.
|
|
|
|
Return Value
|
|
Boolean: TRUE on success; FALSE on failure. Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b = TRUE;
|
|
|
|
if (!ARGUMENT_PRESENT(hAuditEventType))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
b = AuthzpDereferenceAuditEventType(
|
|
hAuditEventType
|
|
);
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
AUTHZAPI
|
|
BOOL
|
|
WINAPI
|
|
AuthziInitializeAuditQueue(
|
|
IN DWORD Flags,
|
|
IN DWORD dwAuditQueueHigh,
|
|
IN DWORD dwAuditQueueLow,
|
|
IN PVOID Reserved,
|
|
OUT PAUTHZ_AUDIT_QUEUE_HANDLE phAuditQueue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Creates an audit queue.
|
|
|
|
Arguments
|
|
|
|
phAuditQueue - pointer to handle for the audit queue.
|
|
|
|
Flags -
|
|
|
|
AUTHZ_MONITOR_AUDIT_QUEUE_SIZE - notifies Authz that it should not let the size of the
|
|
audit queue grow unchecked.
|
|
|
|
dwAuditQueueHigh - high water mark for the audit queue.
|
|
|
|
dwAuditQueueLow - low water mark for the audit queue.
|
|
|
|
Reserved - for future expansion.
|
|
|
|
Return Value
|
|
|
|
Boolean: TRUE on success; FALSE on failure. Extended information available with GetLastError().
|
|
--*/
|
|
|
|
{
|
|
PAUTHZI_AUDIT_QUEUE pQueue = NULL;
|
|
BOOL b = TRUE;
|
|
BOOL bCrit = FALSE;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
UNREFERENCED_PARAMETER(Reserved);
|
|
|
|
if (!ARGUMENT_PRESENT(phAuditQueue))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*phAuditQueue = NULL;
|
|
|
|
pQueue = AuthzpAlloc(sizeof(AUTHZI_AUDIT_QUEUE));
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pQueue))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pQueue->dwAuditQueueHigh = dwAuditQueueHigh;
|
|
pQueue->dwAuditQueueLow = dwAuditQueueLow;
|
|
pQueue->bWorker = TRUE;
|
|
pQueue->Flags = Flags;
|
|
|
|
|
|
//
|
|
// This event is set whenever an audit is queued with AuthziLogAuditEvent(). It
|
|
// notifies the dequeueing thread that there is work to do.
|
|
//
|
|
|
|
pQueue->hAuthzAuditAddedEvent = CreateEvent(
|
|
NULL,
|
|
TRUE,
|
|
FALSE, // Initially not signaled, since no audit has been added yet.
|
|
NULL
|
|
);
|
|
|
|
if (NULL == pQueue->hAuthzAuditAddedEvent)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// This event is set when the audit queue is empty.
|
|
//
|
|
|
|
pQueue->hAuthzAuditQueueEmptyEvent = CreateEvent(
|
|
NULL,
|
|
TRUE,
|
|
TRUE, // Initially signaled.
|
|
NULL
|
|
);
|
|
|
|
if (NULL == pQueue->hAuthzAuditQueueEmptyEvent)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// This event is set when the audit queue is below the low water mark.
|
|
//
|
|
|
|
pQueue->hAuthzAuditQueueLowEvent = CreateEvent(
|
|
NULL,
|
|
FALSE,// The system only schedules one thread waiting on this event (auto reset event).
|
|
TRUE, // Initially set.
|
|
NULL
|
|
);
|
|
|
|
if (NULL == pQueue->hAuthzAuditQueueLowEvent)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// This boolean is true only when the high water mark has been reached
|
|
//
|
|
|
|
pQueue->bAuthzAuditQueueHighEvent = FALSE;
|
|
|
|
//
|
|
// This lock is taken whenever audits are being added or removed from the queue, or events / boolean being set.
|
|
//
|
|
|
|
Status = RtlInitializeCriticalSection(&pQueue->AuthzAuditQueueLock);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
bCrit = TRUE;
|
|
|
|
//
|
|
// Initialize the list
|
|
//
|
|
|
|
InitializeListHead(&pQueue->AuthzAuditQueue);
|
|
|
|
//
|
|
// Create the worker thread that sends audits to LSA.
|
|
//
|
|
|
|
pQueue->hAuthzAuditThread = CreateThread(
|
|
NULL,
|
|
0,
|
|
AuthzpDeQueueThreadWorker,
|
|
pQueue,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (NULL == pQueue->hAuthzAuditThread)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (!b)
|
|
{
|
|
if (AUTHZ_NON_NULL_PTR(pQueue))
|
|
{
|
|
if (bCrit)
|
|
{
|
|
RtlDeleteCriticalSection(&pQueue->AuthzAuditQueueLock);
|
|
}
|
|
AuthzpCloseHandleNonNull(pQueue->hAuthzAuditQueueLowEvent);
|
|
AuthzpCloseHandleNonNull(pQueue->hAuthzAuditAddedEvent);
|
|
AuthzpCloseHandleNonNull(pQueue->hAuthzAuditQueueEmptyEvent);
|
|
AuthzpCloseHandleNonNull(pQueue->hAuthzAuditThread);
|
|
AuthzpFree(pQueue);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*phAuditQueue = (AUTHZ_AUDIT_QUEUE_HANDLE)pQueue;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
AUTHZAPI
|
|
BOOL
|
|
WINAPI
|
|
AuthziModifyAuditQueue(
|
|
IN OUT AUTHZ_AUDIT_QUEUE_HANDLE hQueue OPTIONAL,
|
|
IN DWORD Flags,
|
|
IN DWORD dwQueueFlags OPTIONAL,
|
|
IN DWORD dwAuditQueueSizeHigh OPTIONAL,
|
|
IN DWORD dwAuditQueueSizeLow OPTIONAL,
|
|
IN DWORD dwThreadPriority OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Allows the Resource Manager to modify audit queue information.
|
|
|
|
Arguments
|
|
|
|
Flags - Flags specifying which fields are to be reinitialized. Valid flags are:
|
|
|
|
AUTHZ_AUDIT_QUEUE_HIGH
|
|
AUTHZ_AUDIT_QUEUE_LOW
|
|
AUTHZ_AUDIT_QUEUE_THREAD_PRIORITY
|
|
AUTHZ_AUDIT_QUEUE_FLAGS
|
|
|
|
Specifying one of the above flags in the Flags field causes the appropriate field of
|
|
the resource manager to be modified to the correct value below:
|
|
|
|
dwQueueFlags - set the flags for the audit queue.
|
|
|
|
dwAuditQueueSizeHigh - High water mark for the audit queue.
|
|
|
|
dwAuditQueueSizeLow - Low water mark for the audit queue.
|
|
|
|
dwThreadPriority - Changes the priority of the audit dequeue thread. Valid values are described
|
|
in the SetThreadPriority API. A RM may wish to change the priority of the thread if, for example,
|
|
the high water mark is being reached too frequently and the RM does not want to allow the queue to
|
|
grow beyond its current size.
|
|
|
|
Return Value
|
|
|
|
Boolean: TRUE on success; FALSE on failure.
|
|
Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b = TRUE;
|
|
PAUTHZI_AUDIT_QUEUE pQueue = NULL;
|
|
|
|
if (!ARGUMENT_PRESENT(hQueue))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
pQueue = (PAUTHZI_AUDIT_QUEUE)hQueue;
|
|
|
|
//
|
|
// Set the fields that the caller has asked us to initialize.
|
|
//
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_AUDIT_QUEUE_FLAGS))
|
|
{
|
|
pQueue->Flags = dwQueueFlags;
|
|
}
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_AUDIT_QUEUE_HIGH))
|
|
{
|
|
pQueue->dwAuditQueueHigh = dwAuditQueueSizeHigh;
|
|
}
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_AUDIT_QUEUE_LOW))
|
|
{
|
|
pQueue->dwAuditQueueLow = dwAuditQueueSizeLow;
|
|
}
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_AUDIT_QUEUE_THREAD_PRIORITY))
|
|
{
|
|
b = SetThreadPriority(pQueue->hAuthzAuditThread, dwThreadPriority);
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
AUTHZAPI
|
|
BOOL
|
|
WINAPI
|
|
AuthziFreeAuditQueue(
|
|
IN AUTHZ_AUDIT_QUEUE_HANDLE hQueue OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This API flushes and frees a queue. The actual freeing of queue memory occurs in the dequeueing thread,
|
|
after all audits have been flushed.
|
|
|
|
Arguments
|
|
|
|
hQueue - handle to the queue object to free.
|
|
|
|
Return Value
|
|
|
|
Boolean: TRUE on success; FALSE on failure. Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PAUTHZI_AUDIT_QUEUE pQueue = (PAUTHZI_AUDIT_QUEUE) hQueue;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
BOOL b = TRUE;
|
|
|
|
if (!ARGUMENT_PRESENT(hQueue))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
dwError = WaitForSingleObject(
|
|
pQueue->hAuthzAuditQueueEmptyEvent,
|
|
INFINITE
|
|
);
|
|
|
|
if (WAIT_OBJECT_0 != dwError)
|
|
{
|
|
ASSERT(L"WaitForSingleObject on hAuthzAuditQueueEmptyEvent failed." && FALSE);
|
|
SetLastError(dwError);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set this BOOL to FALSE so that the dequeueing thread knows it can terminate. Set the
|
|
// AddedEvent so that the thread can be scheduled.
|
|
//
|
|
|
|
pQueue->bWorker = FALSE;
|
|
|
|
b = SetEvent(
|
|
pQueue->hAuthzAuditAddedEvent
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Wait for the thread to terminate.
|
|
//
|
|
|
|
dwError = WaitForSingleObject(
|
|
pQueue->hAuthzAuditThread,
|
|
INFINITE
|
|
);
|
|
|
|
//
|
|
// The wait should succeed since we have told the thread to finish working.
|
|
//
|
|
|
|
if (WAIT_OBJECT_0 != dwError)
|
|
{
|
|
ASSERT(L"WaitForSingleObject on hAuthzAuditThread failed." && FALSE);
|
|
SetLastError(dwError);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlDeleteCriticalSection(&pQueue->AuthzAuditQueueLock);
|
|
AuthzpCloseHandle(pQueue->hAuthzAuditAddedEvent);
|
|
AuthzpCloseHandle(pQueue->hAuthzAuditQueueLowEvent);
|
|
AuthzpCloseHandle(pQueue->hAuthzAuditQueueEmptyEvent);
|
|
AuthzpCloseHandle(pQueue->hAuthzAuditThread);
|
|
AuthzpFree(pQueue);
|
|
|
|
Cleanup:
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthziLogAuditEvent(
|
|
IN DWORD Flags,
|
|
IN AUTHZ_AUDIT_EVENT_HANDLE hAuditEvent,
|
|
IN PVOID pReserved
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This API manages the logging of Audit Records. The function constructs an
|
|
Audit Record from the information provided and appends it to the Audit
|
|
Record Queue, a doubly-linked list of Audit Records awaiting output to the
|
|
Audit Log. A dedicated thread reads this queue, sending the Audit Records
|
|
to the LSA and removing them from the Audit Queue.
|
|
|
|
This call is not guaranteed to return without latency. If the queue is at
|
|
or above the high water mark for size, then the calling thread will be
|
|
suspended until such time that the queue reaches the low water mark. Be
|
|
aware of this latency when fashioning your calls to AuthziLogAuditEvent.
|
|
If such latency is not allowable for the audit that is being generated,
|
|
then specify the correct flag when initializing the
|
|
AUTHZ_AUDIT_EVENT_HANDLE (in AuthzInitAuditEventHandle()). Flags are listed
|
|
in that routines description.
|
|
|
|
Arguments:
|
|
|
|
hAuditEvent - handle previously obtained by calling AuthzInitAuditEventHandle
|
|
|
|
Flags - TBD
|
|
|
|
pReserved - reserved for future enhancements
|
|
|
|
Return Value:
|
|
|
|
Boolean: TRUE on success, FALSE on failure.
|
|
Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
BOOL b = TRUE;
|
|
BOOL bRef = FALSE;
|
|
PAUTHZI_AUDIT_QUEUE pQueue = NULL;
|
|
PAUDIT_PARAMS pMarshalledAuditParams = NULL;
|
|
PAUTHZ_AUDIT_QUEUE_ENTRY pAuthzAuditEntry = NULL;
|
|
PAUTHZI_AUDIT_EVENT pAuditEvent = (PAUTHZI_AUDIT_EVENT)hAuditEvent;
|
|
|
|
//
|
|
// Verify what the caller has passed in.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(hAuditEvent))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Make a self relative copy of the pAuditEvent->pAuditParams.
|
|
//
|
|
|
|
b = AuthzpMarshallAuditParams(
|
|
&pMarshalledAuditParams,
|
|
pAuditEvent->pAuditParams
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
pQueue = (PAUTHZI_AUDIT_QUEUE)pAuditEvent->hAuditQueue;
|
|
|
|
if (NULL == pQueue)
|
|
{
|
|
|
|
b = AuthzpSendAuditToLsa(
|
|
(AUDIT_HANDLE)((PAUTHZ_AUDIT_EVENT_TYPE_OLD)pAuditEvent->hAET)->hAudit,
|
|
0,
|
|
pMarshalledAuditParams,
|
|
NULL
|
|
);
|
|
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// Create the audit queue entry.
|
|
//
|
|
|
|
pAuthzAuditEntry = AuthzpAlloc(sizeof(AUTHZ_AUDIT_QUEUE_ENTRY));
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pAuthzAuditEntry))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pAuthzAuditEntry->pAAETO = (PAUTHZ_AUDIT_EVENT_TYPE_OLD)pAuditEvent->hAET;
|
|
pAuthzAuditEntry->Flags = Flags;
|
|
pAuthzAuditEntry->pReserved = pReserved;
|
|
pAuthzAuditEntry->pAuditParams = pMarshalledAuditParams;
|
|
|
|
AuthzpReferenceAuditEventType(pAuditEvent->hAET);
|
|
bRef = TRUE;
|
|
|
|
if (FLAG_ON(pQueue->Flags, AUTHZ_MONITOR_AUDIT_QUEUE_SIZE))
|
|
{
|
|
|
|
//
|
|
// Monitor queue size if specified by the Resource Manager.
|
|
//
|
|
|
|
//
|
|
// If we are closing in on the high water mark then wait for the queue
|
|
// to be below the low water mark.
|
|
//
|
|
|
|
#define AUTHZ_QUEUE_WAIT_HEURISTIC .75
|
|
|
|
if (pQueue->AuthzAuditQueueLength > pQueue->dwAuditQueueHigh * AUTHZ_QUEUE_WAIT_HEURISTIC)
|
|
{
|
|
dwError = WaitForSingleObject(
|
|
pQueue->hAuthzAuditQueueLowEvent,
|
|
pAuditEvent->dwTimeOut
|
|
);
|
|
|
|
if (WAIT_FAILED == dwError)
|
|
{
|
|
ASSERT(L"WaitForSingleObject on hAuthzAuditQueueLowEvent failed." && FALSE);
|
|
}
|
|
|
|
if (WAIT_OBJECT_0 != dwError)
|
|
{
|
|
b = FALSE;
|
|
|
|
//
|
|
// Don't set last error if WAIT_FAILED, because it is already set.
|
|
//
|
|
|
|
if (WAIT_FAILED != dwError)
|
|
{
|
|
SetLastError(dwError);
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Queue the event and modify appropriate events.
|
|
//
|
|
|
|
b = AuthzpEnQueueAuditEventMonitor(
|
|
pQueue,
|
|
pAuthzAuditEntry
|
|
);
|
|
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// If we are not to monitor the audit queue then simply queue the entry.
|
|
//
|
|
|
|
b = AuthzpEnQueueAuditEvent(
|
|
pQueue,
|
|
pAuthzAuditEntry
|
|
);
|
|
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (pQueue)
|
|
{
|
|
if (FALSE == b)
|
|
{
|
|
if (bRef)
|
|
{
|
|
AuthzpDereferenceAuditEventType(pAuditEvent->hAET);
|
|
}
|
|
AuthzpFreeNonNull(pAuthzAuditEntry);
|
|
AuthzpFreeNonNull(pMarshalledAuditParams);
|
|
}
|
|
|
|
//
|
|
// hAuthzAuditQueueLowEvent is an auto reset event. Only one waiting thread is released when it is signalled, and then
|
|
// event is automatically switched to a nonsignalled state. This is appropriate here because it keeps many threads from
|
|
// running and overflowing the high water mark. However, I must always resignal the event myself if the conditions
|
|
// for signaling are true.
|
|
//
|
|
|
|
RtlEnterCriticalSection(&pQueue->AuthzAuditQueueLock);
|
|
if (!pQueue->bAuthzAuditQueueHighEvent)
|
|
{
|
|
if (pQueue->AuthzAuditQueueLength <= pQueue->dwAuditQueueHigh)
|
|
{
|
|
BOOL bSet;
|
|
bSet = SetEvent(pQueue->hAuthzAuditQueueLowEvent);
|
|
if (!bSet)
|
|
{
|
|
ASSERT(L"SetEvent on hAuthzAuditQueueLowEvent failed" && FALSE);
|
|
}
|
|
|
|
}
|
|
}
|
|
RtlLeaveCriticalSection(&pQueue->AuthzAuditQueueLock);
|
|
}
|
|
else
|
|
{
|
|
AuthzpFreeNonNull(pMarshalledAuditParams);
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
AuthziAllocateAuditParams(
|
|
OUT PAUDIT_PARAMS * ppParams,
|
|
IN USHORT NumParams
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate the AUDIT_PARAMS structure for the correct number of parameters.
|
|
|
|
Arguments:
|
|
|
|
ppParams - pointer to PAUDIT_PARAMS structure to be initialized.
|
|
|
|
NumParams - number of parameters passed in the var-arg section.
|
|
This must be SE_MAX_AUDIT_PARAMETERS or less.
|
|
Return Value:
|
|
|
|
Boolean: TRUE on success, FALSE on failure. Extended information available with GetLastError().
|
|
|
|
--*/
|
|
{
|
|
BOOL b = TRUE;
|
|
PAUDIT_PARAMS pAuditParams = NULL;
|
|
|
|
if (!ARGUMENT_PRESENT(ppParams))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*ppParams = NULL;
|
|
|
|
//
|
|
// the first two params are always fixed. thus the total number
|
|
// is 2 + the passed number.
|
|
//
|
|
|
|
if ((NumParams + 2) > SE_MAX_AUDIT_PARAMETERS)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pAuditParams = AuthzpAlloc(sizeof(AUDIT_PARAMS) + (sizeof(AUDIT_PARAM) * (NumParams + 2)));
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pAuditParams))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pAuditParams->Parameters = (PAUDIT_PARAM)((PUCHAR)pAuditParams + sizeof(AUDIT_PARAMS));
|
|
|
|
Cleanup:
|
|
|
|
if (!b)
|
|
{
|
|
AuthzpFreeNonNull(pAuditParams);
|
|
}
|
|
else
|
|
{
|
|
*ppParams = pAuditParams;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthziInitializeAuditParamsWithRM(
|
|
IN DWORD Flags,
|
|
IN AUTHZ_RESOURCE_MANAGER_HANDLE hResourceManager,
|
|
IN USHORT NumParams,
|
|
OUT PAUDIT_PARAMS pParams,
|
|
...
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the AUDIT_PARAMS structure based on the passed
|
|
data. This is the recommended way to init AUDIT_PARAMS. It is
|
|
faster than AuthzInitializeAuditParams and works with an
|
|
impersonating caller.
|
|
|
|
The caller passes in type ~ value pairs in the vararg section.
|
|
This function will initialize the corresponding array elements
|
|
based on each such pair. In case of certain types, there may
|
|
be more than one value argument required. This requirement
|
|
is documented next to each param-type.
|
|
|
|
Arguments:
|
|
|
|
pParams - pointer to AUDIT_PARAMS structure to be initialized
|
|
the size of pParams->Parameters member must be big enough
|
|
to hold NumParams elements. The caller must allocate
|
|
memory for this structure and its members.
|
|
|
|
hResourceManager - Handle to the Resource manager that is creating the audit.
|
|
|
|
Flags - control flags. one or more of the following:
|
|
APF_AuditSuccess
|
|
|
|
NumParams - number of parameters passed in the var-arg section.
|
|
This must be SE_MAX_AUDIT_PARAMETERS or less.
|
|
|
|
... - The format of the variable arg portion is as follows:
|
|
|
|
<one of APT_ * flags> <zero or more values>
|
|
|
|
APT_String <pointer to null terminated string>
|
|
|
|
Flags:
|
|
AP_Filespec : treats the string as a filename
|
|
|
|
APT_Ulong <ulong value> [<object-type-index>]
|
|
|
|
Flags:
|
|
AP_FormatHex : number appears in hex
|
|
in eventlog
|
|
AP_AccessMask: number is treated as
|
|
an access-mask
|
|
Index to object type must follow
|
|
|
|
APT_Pointer <pointer/handle>
|
|
32 bit on 32 bit systems and
|
|
64 bit on 64 bit systems
|
|
|
|
APT_Sid <pointer to sid>
|
|
|
|
APT_Luid <Luid>
|
|
|
|
APT_Guid <pointer to guid>
|
|
|
|
APT_LogonId [<logon-id>]
|
|
|
|
Flags:
|
|
AP_PrimaryLogonId : logon-id is captured
|
|
from the process token.
|
|
No need to specify one.
|
|
|
|
AP_ClientLogonId : logon-id is captured
|
|
from the thread token.
|
|
No need to specify one.
|
|
|
|
no flags : need to supply a logon-id
|
|
|
|
APT_ObjectTypeList <ptr to obj type list> <obj-type-index>
|
|
Index to object type must be specified
|
|
|
|
APT_Time <filetime>
|
|
|
|
APT_Int64 <ULONGLONG or LARGE_INTEGER>
|
|
|
|
Return Value:
|
|
TRUE on success
|
|
FALSE otherwise
|
|
|
|
call GetLastError() to retrieve the errorcode,
|
|
which will be one of the following:
|
|
|
|
ERROR_INVALID_PARAMETER if one of the params is incorrect
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
PAUTHZI_RESOURCE_MANAGER pRM = (PAUTHZI_RESOURCE_MANAGER) hResourceManager;
|
|
PSID pUserSid = NULL;
|
|
DWORD dwError = NO_ERROR;
|
|
BOOL b = TRUE;
|
|
LUID AuthIdThread = {0};
|
|
|
|
va_list(arglist);
|
|
|
|
if (!ARGUMENT_PRESENT(hResourceManager) ||
|
|
!ARGUMENT_PRESENT(pParams) ||
|
|
(NumParams + AUTHZP_NUM_FIXED_HEADER_PARAMS) > SE_MAX_AUDIT_PARAMETERS)
|
|
{
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
va_start(arglist, pParams);
|
|
|
|
|
|
//
|
|
// If we are not impersonating we want to use the sid stored
|
|
// in the RM as the user sid of the audit.
|
|
//
|
|
|
|
b = AuthzpGetThreadTokenInfo(
|
|
&pUserSid,
|
|
&AuthIdThread
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
dwError = GetLastError();
|
|
|
|
if (dwError == ERROR_NO_TOKEN)
|
|
{
|
|
//
|
|
// We are not impersonating ...
|
|
//
|
|
|
|
pUserSid = pRM->pUserSID;
|
|
dwError = NO_ERROR;
|
|
b = TRUE;
|
|
}
|
|
else
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// This is only a temporary solution and should be replaced after .net ships.
|
|
//
|
|
|
|
if (wcsncmp(pRM->szResourceManagerName, L"DS", 2) == 0)
|
|
{
|
|
Flags |= AUTHZP_INIT_PARAMS_SOURCE_DS;
|
|
}
|
|
|
|
b = AuthzpInitializeAuditParamsV(
|
|
Flags,
|
|
pParams,
|
|
&pUserSid,
|
|
NULL,
|
|
0,
|
|
NumParams,
|
|
arglist
|
|
);
|
|
|
|
Cleanup:
|
|
|
|
if ( dwError != NO_ERROR )
|
|
{
|
|
SetLastError( dwError );
|
|
b = FALSE;
|
|
}
|
|
|
|
if ( !b )
|
|
{
|
|
if (pUserSid != pRM->pUserSID)
|
|
{
|
|
AuthzpFreeNonNull( pUserSid );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// ugly hack to mark the dynamically allocated sid so we
|
|
// know we'll have to free it in AuthziFreeAuditParam.
|
|
//
|
|
|
|
if (pUserSid != pRM->pUserSID)
|
|
{
|
|
pParams->Parameters->Flags |= AUTHZP_PARAM_FREE_SID;
|
|
}
|
|
else
|
|
{
|
|
pParams->Parameters->Flags &= ~AUTHZP_PARAM_FREE_SID;
|
|
}
|
|
}
|
|
|
|
va_end (arglist);
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthziInitializeAuditParamsFromArray(
|
|
IN DWORD Flags,
|
|
IN AUTHZ_RESOURCE_MANAGER_HANDLE hResourceManager,
|
|
IN USHORT NumParams,
|
|
IN PAUDIT_PARAM pParamArray,
|
|
OUT PAUDIT_PARAMS pParams
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the AUDIT_PARAMS structure based on the passed
|
|
data.
|
|
|
|
Arguments:
|
|
|
|
pParams - pointer to AUDIT_PARAMS structure to be initialized
|
|
the size of pParams->Parameters member must be big enough
|
|
to hold NumParams elements. The caller must allocate
|
|
memory for this structure and its members.
|
|
|
|
hResourceManager - Handle to the Resource manager that is creating the audit.
|
|
|
|
Flags - control flags. one or more of the following:
|
|
APF_AuditSuccess
|
|
|
|
pParamArray - an array of type AUDIT_PARAM
|
|
|
|
Return Value:
|
|
TRUE on success
|
|
FALSE otherwise
|
|
|
|
call GetLastError() to retrieve the errorcode,
|
|
which will be one of the following:
|
|
|
|
ERROR_INVALID_PARAMETER if one of the params is incorrect
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
PAUTHZI_RESOURCE_MANAGER pRM = (PAUTHZI_RESOURCE_MANAGER) hResourceManager;
|
|
DWORD dwError = NO_ERROR;
|
|
BOOL b = TRUE;
|
|
BOOL bImpersonating = TRUE;
|
|
PAUDIT_PARAM pParam = NULL;
|
|
LUID AuthIdThread;
|
|
PSID pThreadSID = NULL;
|
|
DWORD i;
|
|
|
|
//
|
|
// the first two params are always fixed. thus the total number
|
|
// is 2 + the passed number.
|
|
//
|
|
|
|
if (!ARGUMENT_PRESENT(hResourceManager) ||
|
|
!ARGUMENT_PRESENT(pParams) ||
|
|
!ARGUMENT_PRESENT(pParamArray) ||
|
|
(NumParams + AUTHZP_NUM_FIXED_HEADER_PARAMS) > SE_MAX_AUDIT_PARAMETERS)
|
|
{
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
b = AuthzpGetThreadTokenInfo(
|
|
&pThreadSID,
|
|
&AuthIdThread
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
dwError = GetLastError();
|
|
|
|
if (dwError == ERROR_NO_TOKEN)
|
|
{
|
|
bImpersonating = FALSE;
|
|
dwError = NO_ERROR;
|
|
b = TRUE;
|
|
}
|
|
else
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
pParams->Length = 0;
|
|
pParams->Flags = Flags;
|
|
pParams->Count = NumParams+AUTHZP_NUM_FIXED_HEADER_PARAMS;
|
|
|
|
pParam = pParams->Parameters;
|
|
|
|
//
|
|
// the first param is always the sid of the user in thread token
|
|
// if thread is not impersonating, sid in the primary token is used.
|
|
//
|
|
|
|
pParam->Type = APT_Sid;
|
|
if (bImpersonating)
|
|
{
|
|
pParam->Data0 = (ULONG_PTR) pThreadSID;
|
|
|
|
//
|
|
// ugly hack to mark the dynamically allocated sid so we
|
|
// know we'll have to free it in AuthziFreeAuditParam.
|
|
//
|
|
|
|
pParam->Flags |= AUTHZP_PARAM_FREE_SID;
|
|
|
|
}
|
|
else
|
|
{
|
|
pParam->Data0 = (ULONG_PTR) pRM->pUserSID;
|
|
pParam->Flags &= ~AUTHZP_PARAM_FREE_SID;
|
|
}
|
|
|
|
pParam++;
|
|
|
|
//
|
|
// the second param is always the sub-system name
|
|
//
|
|
|
|
pParam->Type = APT_String;
|
|
pParam->Data0 = (ULONG_PTR) pRM->szResourceManagerName;
|
|
|
|
pParam++;
|
|
|
|
//
|
|
// now initialize the rest using the array.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
pParam,
|
|
pParamArray,
|
|
sizeof(AUDIT_PARAM) * NumParams
|
|
);
|
|
|
|
//
|
|
// Walk through the parameters and increment by 2 the Data1 member of
|
|
// any instance of AccessMask or ObjectTypeList. This is done to correct
|
|
// for the usersid / subsystem elements that are inserted in the param
|
|
// array. The data1 member of these 2 types should point to the ObjectTypeIndex,
|
|
// which is otherwise off by 2.
|
|
//
|
|
|
|
for (i = 0; i < pParams->Count; i++)
|
|
{
|
|
ULONG TypeFlags = pParams->Parameters[i].Type;
|
|
if ((ApExtractType(TypeFlags) == APT_ObjectTypeList) ||
|
|
(ApExtractType(TypeFlags) == APT_Ulong && FLAG_ON(ApExtractFlags(TypeFlags), AP_AccessMask)))
|
|
{
|
|
pParams->Parameters[i].Data1 += 2;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
if ( dwError != NO_ERROR )
|
|
{
|
|
SetLastError( dwError );
|
|
b = FALSE;
|
|
AuthzpFreeNonNull( pThreadSID );
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthziInitializeAuditParams(
|
|
IN DWORD dwFlags,
|
|
OUT PAUDIT_PARAMS pParams,
|
|
OUT PSID* ppUserSid,
|
|
IN PCWSTR SubsystemName,
|
|
IN USHORT NumParams,
|
|
...
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the AUDIT_PARAMS structure based on the passed
|
|
data.
|
|
|
|
The caller passes in type ~ value pairs in the vararg section.
|
|
This function will initialize the corresponding array elements
|
|
based on each such pair. In case of certain types, there may
|
|
be more than one value argument required. This requirement
|
|
is documented next to each param-type.
|
|
|
|
Arguments:
|
|
|
|
pParams - pointer to AUDIT_PARAMS structure to be initialized
|
|
the size of pParams->Parameters member must be big enough
|
|
to hold NumParams elements. The caller must allocate
|
|
memory for this structure and its members.
|
|
|
|
ppUserSid - pointer to user sid allocated. This sid is referenced
|
|
by the first parameter (index 0) in AUDIT_PARAMS structure.
|
|
The caller should free this by calling LocalFree
|
|
*after* freeing the AUDIT_PARAMS structure.
|
|
|
|
SubsystemName - name of Subsystem that is generating audit
|
|
|
|
dwFlags - control flags. one or more of the following:
|
|
APF_AuditSuccess
|
|
|
|
NumParams - number of parameters passed in the var-arg section.
|
|
This must be SE_MAX_AUDIT_PARAMETERS or less.
|
|
|
|
... - The format of the variable arg portion is as follows:
|
|
|
|
<one of APT_ * flags> <zero or more values>
|
|
|
|
APT_String <pointer to null terminated string>
|
|
|
|
Flags:
|
|
AP_Filespec : treats the string as a filename
|
|
|
|
APT_Ulong <ulong value> [<object-type-index>]
|
|
|
|
Flags:
|
|
AP_FormatHex : number appears in hex
|
|
in eventlog
|
|
AP_AccessMask: number is treated as
|
|
an access-mask
|
|
Index to object type must follow
|
|
|
|
APT_Pointer <pointer/handle>
|
|
32 bit on 32 bit systems and
|
|
64 bit on 64 bit systems
|
|
|
|
APT_Sid <pointer to sid>
|
|
|
|
APT_Luid <Luid>
|
|
|
|
APT_Guid <pointer to guid>
|
|
|
|
APT_LogonId [<logon-id>]
|
|
|
|
Flags:
|
|
AP_PrimaryLogonId : logon-id is captured
|
|
from the process token.
|
|
No need to specify one.
|
|
|
|
AP_ClientLogonId : logon-id is captured
|
|
from the thread token.
|
|
No need to specify one.
|
|
|
|
no flags : need to supply a logon-id
|
|
|
|
APT_ObjectTypeList <ptr to obj type list> <obj-type-index>
|
|
Index to object type must be specified
|
|
|
|
APT_Time <filetime>
|
|
|
|
APT_Int64 <ULONGLONG or LARGE_INTEGER>
|
|
|
|
Return Value:
|
|
TRUE on success
|
|
FALSE otherwise
|
|
|
|
call GetLastError() to retrieve the errorcode,
|
|
which will be one of the following:
|
|
|
|
ERROR_INVALID_PARAMETER if one of the params is incorrect
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL fResult = TRUE;
|
|
|
|
va_list(arglist);
|
|
|
|
*ppUserSid = NULL;
|
|
|
|
va_start (arglist, NumParams);
|
|
|
|
//
|
|
// Turn off any flags that don't belong. Only success/failure flag is permitted.
|
|
//
|
|
|
|
dwFlags = dwFlags & APF_ValidFlags;
|
|
|
|
UNREFERENCED_PARAMETER(SubsystemName);
|
|
|
|
fResult = AuthzpInitializeAuditParamsV(
|
|
dwFlags,
|
|
pParams,
|
|
ppUserSid,
|
|
NULL, // subsystemname will be forced to "Security"
|
|
0,
|
|
NumParams,
|
|
arglist
|
|
);
|
|
|
|
if (!fResult)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (!fResult)
|
|
{
|
|
if (AUTHZ_NON_NULL_PTR(*ppUserSid))
|
|
{
|
|
AuthzpFree(*ppUserSid);
|
|
*ppUserSid = NULL;
|
|
}
|
|
}
|
|
|
|
va_end (arglist);
|
|
return fResult;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthziFreeAuditParams(
|
|
PAUDIT_PARAMS pParams
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Frees the AUDIT_PARAMS created by AuthzAllocateInitializeAuditParamsWithRM.
|
|
|
|
Arguments
|
|
|
|
pParams - pointer to AUDIT_PARAMS.
|
|
|
|
Return Value
|
|
|
|
Boolean: TRUE on success, FALSE on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
if (!ARGUMENT_PRESENT(pParams))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (pParams->Parameters)
|
|
{
|
|
if (pParams->Parameters->Data0 &&
|
|
pParams->Parameters->Type == APT_Sid &&
|
|
(pParams->Parameters->Flags & AUTHZP_PARAM_FREE_SID))
|
|
{
|
|
AuthzpFree((PVOID)(pParams->Parameters->Data0));
|
|
}
|
|
}
|
|
|
|
AuthzpFree(pParams);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
AuthziInitializeAuditEvent(
|
|
IN DWORD Flags,
|
|
IN AUTHZ_RESOURCE_MANAGER_HANDLE hRM,
|
|
IN AUTHZ_AUDIT_EVENT_TYPE_HANDLE hAET OPTIONAL,
|
|
IN PAUDIT_PARAMS pAuditParams OPTIONAL,
|
|
IN AUTHZ_AUDIT_QUEUE_HANDLE hAuditQueue OPTIONAL,
|
|
IN DWORD dwTimeOut,
|
|
IN PWSTR szOperationType,
|
|
IN PWSTR szObjectType,
|
|
IN PWSTR szObjectName,
|
|
IN PWSTR szAdditionalInfo OPTIONAL,
|
|
OUT PAUTHZ_AUDIT_EVENT_HANDLE phAuditEvent
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates and initializes an AUTHZ_AUDIT_EVENT_HANDLE. The handle is used for storing information
|
|
for audit generation per AuthzAccessCheck().
|
|
|
|
Arguments:
|
|
|
|
phAuditEvent - pointer to AUTHZ_AUDIT_EVENT_HANDLE. Space for this is allocated in the function.
|
|
|
|
Flags - Audit flags. Currently defined bits are:
|
|
AUTHZ_NO_SUCCESS_AUDIT - disables generation of success audits
|
|
AUTHZ_NO_FAILURE_AUDIT - disables generation of failure audits
|
|
AUTHZ_DS_CATEGORY_FLAG - swithces the default audit category from OBJECT_ACCESS to DS_ACCESS.
|
|
AUTHZ_NO_ALLOC_STRINGS - storage space is not allocated for the 4 wide character strings. Rather,
|
|
the handle will only hold pointers to resource manager memory.
|
|
|
|
hRM - handle to a Resource Manager.
|
|
|
|
hAET - pointer to an AUTHZ_AUDIT_EVENT_TYPE structure. This is needed if the resource manager
|
|
is creating its own audit types. This is not needed for generic object operation audits.
|
|
|
|
pAuditParams - If this is specified, then pAuditParams will be used to
|
|
create the audit. If NULL is passed, then a generic AUDIT_PARAMS will
|
|
be constructed that is suitable for the generic object access audit.
|
|
|
|
hAuditQueue - queue object created with AuthzInitializeAuditQueue. If none is specified, the
|
|
default RM queue will be used.
|
|
|
|
dwTimeOut - milliseconds that a thread attempting to generate an audit with this
|
|
AUTHZ_AUDIT_EVENT_HANDLE should wait for access to the queue. Use INFINITE to
|
|
specify an unlimited timeout.
|
|
|
|
szOperationType - Resource manager defined string that indicates the operation being
|
|
performed that is to be audited.
|
|
|
|
szObjectType - Resource manager defined string that indicates the type of object being
|
|
accessed.
|
|
|
|
szObjectName - the name of the specific object being accessed.
|
|
|
|
szAdditionalInfo - Resource Manager defined string for additional audit information.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if successful, FALSE if unsuccessful.
|
|
Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PAUTHZI_AUDIT_EVENT pAuditEvent = NULL;
|
|
BOOL b = TRUE;
|
|
BOOL bRef = FALSE;
|
|
DWORD dwStringSize = 0;
|
|
DWORD dwObjectTypeLength = 0;
|
|
DWORD dwObjectNameLength = 0;
|
|
DWORD dwOperationTypeLength = 0;
|
|
DWORD dwAdditionalInfoLength = 0;
|
|
PAUTHZI_RESOURCE_MANAGER pRM = (PAUTHZI_RESOURCE_MANAGER) hRM;
|
|
|
|
if (!ARGUMENT_PRESENT(phAuditEvent) ||
|
|
(!ARGUMENT_PRESENT(hAET) && !ARGUMENT_PRESENT(hRM)) ||
|
|
!ARGUMENT_PRESENT(szOperationType) ||
|
|
!ARGUMENT_PRESENT(szObjectType) ||
|
|
!ARGUMENT_PRESENT(szObjectName) ||
|
|
!ARGUMENT_PRESENT(szAdditionalInfo))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*phAuditEvent = NULL;
|
|
|
|
//
|
|
// Allocate and initialize a new AUTHZI_AUDIT_EVENT.
|
|
//
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_NO_ALLOC_STRINGS))
|
|
{
|
|
dwStringSize = 0;
|
|
}
|
|
else
|
|
{
|
|
dwOperationTypeLength = (DWORD) wcslen(szOperationType) + 1;
|
|
dwObjectTypeLength = (DWORD) wcslen(szObjectType) + 1;
|
|
dwObjectNameLength = (DWORD) wcslen(szObjectName) + 1;
|
|
dwAdditionalInfoLength = (DWORD) wcslen(szAdditionalInfo) + 1;
|
|
|
|
dwStringSize = sizeof(WCHAR) * (dwOperationTypeLength + dwObjectTypeLength + dwObjectNameLength + dwAdditionalInfoLength);
|
|
}
|
|
|
|
pAuditEvent = (PAUTHZI_AUDIT_EVENT) AuthzpAlloc(sizeof(AUTHZI_AUDIT_EVENT) + dwStringSize);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pAuditEvent))
|
|
{
|
|
b = FALSE;
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlZeroMemory(
|
|
pAuditEvent,
|
|
sizeof(AUTHZI_AUDIT_EVENT) + dwStringSize
|
|
);
|
|
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_NO_ALLOC_STRINGS))
|
|
{
|
|
pAuditEvent->szOperationType = szOperationType;
|
|
pAuditEvent->szObjectType = szObjectType;
|
|
pAuditEvent->szObjectName = szObjectName;
|
|
pAuditEvent->szAdditionalInfo = szAdditionalInfo;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Set the string pointers into the contiguous memory.
|
|
//
|
|
|
|
pAuditEvent->szOperationType = (PWSTR)((PUCHAR)pAuditEvent + sizeof(AUTHZI_AUDIT_EVENT));
|
|
|
|
pAuditEvent->szObjectType = (PWSTR)((PUCHAR)pAuditEvent + sizeof(AUTHZI_AUDIT_EVENT)
|
|
+ (sizeof(WCHAR) * dwOperationTypeLength));
|
|
|
|
pAuditEvent->szObjectName = (PWSTR)((PUCHAR)pAuditEvent + sizeof(AUTHZI_AUDIT_EVENT)
|
|
+ (sizeof(WCHAR) * (dwOperationTypeLength + dwObjectTypeLength)));
|
|
|
|
pAuditEvent->szAdditionalInfo = (PWSTR)((PUCHAR)pAuditEvent + sizeof(AUTHZI_AUDIT_EVENT)
|
|
+ (sizeof(WCHAR) * (dwOperationTypeLength + dwObjectTypeLength + dwObjectNameLength)));
|
|
|
|
RtlCopyMemory(
|
|
pAuditEvent->szOperationType,
|
|
szOperationType,
|
|
sizeof(WCHAR) * dwOperationTypeLength
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
pAuditEvent->szObjectType,
|
|
szObjectType,
|
|
sizeof(WCHAR) * dwObjectTypeLength
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
pAuditEvent->szObjectName,
|
|
szObjectName,
|
|
sizeof(WCHAR) * dwObjectNameLength
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
pAuditEvent->szAdditionalInfo,
|
|
szAdditionalInfo,
|
|
sizeof(WCHAR) * dwAdditionalInfoLength
|
|
);
|
|
}
|
|
|
|
//
|
|
// Use the passed audit event type if present, otherwise use the RM's generic Audit Event.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(hAET))
|
|
{
|
|
pAuditEvent->hAET = hAET;
|
|
}
|
|
else
|
|
{
|
|
if (FLAG_ON(Flags, AUTHZ_DS_CATEGORY_FLAG))
|
|
{
|
|
pAuditEvent->hAET = pRM->hAETDS;
|
|
}
|
|
else
|
|
{
|
|
pAuditEvent->hAET = pRM->hAET;
|
|
}
|
|
}
|
|
|
|
AuthzpReferenceAuditEventType(pAuditEvent->hAET);
|
|
bRef = TRUE;
|
|
|
|
//
|
|
// Use the specified queue, if it exists. Else use the RM queue.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(hAuditQueue))
|
|
{
|
|
pAuditEvent->hAuditQueue = hAuditQueue;
|
|
}
|
|
else if (ARGUMENT_PRESENT(hRM))
|
|
{
|
|
pAuditEvent->hAuditQueue = pRM->hAuditQueue;
|
|
}
|
|
|
|
pAuditEvent->pAuditParams = pAuditParams;
|
|
pAuditEvent->Flags = Flags;
|
|
pAuditEvent->dwTimeOut = dwTimeOut;
|
|
pAuditEvent->dwSize = sizeof(AUTHZI_AUDIT_EVENT) + dwStringSize;
|
|
|
|
Cleanup:
|
|
|
|
if (!b)
|
|
{
|
|
if (bRef)
|
|
{
|
|
AuthzpDereferenceAuditEventType(pAuditEvent->hAET);
|
|
}
|
|
AuthzpFreeNonNull(pAuditEvent);
|
|
}
|
|
else
|
|
{
|
|
*phAuditEvent = (AUTHZ_AUDIT_EVENT_HANDLE) pAuditEvent;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthziModifyAuditEvent(
|
|
IN DWORD Flags,
|
|
IN AUTHZ_AUDIT_EVENT_HANDLE hAuditEvent,
|
|
IN DWORD NewFlags,
|
|
IN PWSTR szOperationType,
|
|
IN PWSTR szObjectType,
|
|
IN PWSTR szObjectName,
|
|
IN PWSTR szAdditionalInfo
|
|
)
|
|
|
|
{
|
|
return AuthziModifyAuditEvent2(
|
|
Flags,
|
|
hAuditEvent,
|
|
NewFlags,
|
|
szOperationType,
|
|
szObjectType,
|
|
szObjectName,
|
|
szAdditionalInfo,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthziModifyAuditEvent2(
|
|
IN DWORD Flags,
|
|
IN AUTHZ_AUDIT_EVENT_HANDLE hAuditEvent,
|
|
IN DWORD NewFlags,
|
|
IN PWSTR szOperationType,
|
|
IN PWSTR szObjectType,
|
|
IN PWSTR szObjectName,
|
|
IN PWSTR szAdditionalInfo,
|
|
IN PWSTR szAdditionalInfo2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Arguments
|
|
|
|
Flags - flags to specify which field of the hAuditEvent to modify. Valid flags are:
|
|
|
|
AUTHZ_AUDIT_EVENT_FLAGS
|
|
AUTHZ_AUDIT_EVENT_OPERATION_TYPE
|
|
AUTHZ_AUDIT_EVENT_OBJECT_TYPE
|
|
AUTHZ_AUDIT_EVENT_OBJECT_NAME
|
|
AUTHZ_AUDIT_EVENT_ADDITIONAL_INFO
|
|
AUTHZ_AUDIT_EVENT_ADDITIONAL_INFO2
|
|
|
|
hAuditEvent - handle to modify. Must be created with AUTHZ_NO_ALLOC_STRINGS flag.
|
|
|
|
NewFlags - replacement flags for hAuditEvent.
|
|
|
|
szOperationType - replacement string for hAuditEvent.
|
|
|
|
szObjectType - replacement string for hAuditEvent.
|
|
|
|
szObjectName - replacement string for hAuditEvent.
|
|
|
|
szAdditionalInfo - replacement string for hAuditEvent.
|
|
|
|
Return Value
|
|
|
|
Boolean: TRUE on success; FALSE on failure. Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PAUTHZI_AUDIT_EVENT pAuditEvent = (PAUTHZI_AUDIT_EVENT) hAuditEvent;
|
|
|
|
if ((!ARGUMENT_PRESENT(hAuditEvent)) ||
|
|
(Flags & ~AUTHZ_VALID_MODIFY_AUDIT_EVENT_FLAGS) ||
|
|
(!FLAG_ON(pAuditEvent->Flags, AUTHZ_NO_ALLOC_STRINGS)))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_AUDIT_EVENT_FLAGS))
|
|
{
|
|
pAuditEvent->Flags = NewFlags;
|
|
}
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_AUDIT_EVENT_OPERATION_TYPE))
|
|
{
|
|
pAuditEvent->szOperationType = szOperationType;
|
|
}
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_AUDIT_EVENT_OBJECT_TYPE))
|
|
{
|
|
pAuditEvent->szObjectType = szObjectType;
|
|
}
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_AUDIT_EVENT_OBJECT_NAME))
|
|
{
|
|
pAuditEvent->szObjectName = szObjectName;
|
|
}
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_AUDIT_EVENT_ADDITIONAL_INFO))
|
|
{
|
|
pAuditEvent->szAdditionalInfo = szAdditionalInfo;
|
|
}
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_AUDIT_EVENT_ADDITIONAL_INFO2))
|
|
{
|
|
pAuditEvent->szAdditionalInfo2 = szAdditionalInfo2;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
AuthzInitialize(
|
|
IN PVOID hmod,
|
|
IN ULONG Reason,
|
|
IN PCONTEXT Context
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This is the dll initialization rotuine.
|
|
|
|
Arguments
|
|
Standard arguments.
|
|
|
|
Return Value
|
|
|
|
Boolean: TRUE on success; FALSE on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(hmod);
|
|
UNREFERENCED_PARAMETER(Context);
|
|
|
|
switch (Reason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
SafeAllocaInitialize(
|
|
SAFEALLOCA_USE_DEFAULT,
|
|
SAFEALLOCA_USE_DEFAULT,
|
|
NULL,
|
|
NULL);
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#define PER_USER_POLICY_KEY_NAME L"SYSTEM\\CurrentControlSet\\Control\\Lsa\\Audit\\PerUserAuditing"
|
|
#define PER_USER_POLICY_SYSTEM_KEY_NAME L"SYSTEM\\CurrentControlSet\\Control\\Lsa\\Audit\\PerUserAuditing\\System"
|
|
#define SYSTEM_RM_NAME L"System"
|
|
#define POLICY_BUFFER_BYTE_SIZE 8
|
|
|
|
|
|
BOOL
|
|
AuthziSetAuditPolicy(
|
|
IN DWORD dwFlags,
|
|
IN AUTHZ_CLIENT_CONTEXT_HANDLE hContext,
|
|
IN PCWSTR szResourceManager OPTIONAL,
|
|
IN PTOKEN_AUDIT_POLICY pPolicy
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets a per user policy for a user.
|
|
|
|
Arguments:
|
|
|
|
dwFlags - currently unused.
|
|
|
|
hContext - handle to a client context for which to set the policy.
|
|
|
|
szResourceManager - should be NULL. For future use.
|
|
|
|
pPolicy - the policy that is to be set for the user.
|
|
|
|
Return Value:
|
|
|
|
Boolean: TRUE on success; FALSE on failure. Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PAUTHZI_CLIENT_CONTEXT pContext = (PAUTHZI_CLIENT_CONTEXT) hContext;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
HKEY hRMRoot = NULL;
|
|
LPWSTR szSid = NULL;
|
|
BOOL b;
|
|
DWORD Disposition;
|
|
UCHAR RegPolicy[POLICY_BUFFER_BYTE_SIZE];
|
|
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
|
|
RtlZeroMemory(RegPolicy, sizeof(RegPolicy));
|
|
|
|
if (NULL == hContext || NULL == pPolicy)
|
|
{
|
|
b = FALSE;
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Only allow System RM to be specified. If no RM is given then default to system.
|
|
//
|
|
|
|
if (NULL == szResourceManager || 0 == wcsncmp(
|
|
SYSTEM_RM_NAME,
|
|
szResourceManager,
|
|
sizeof(SYSTEM_RM_NAME) / sizeof(WCHAR)
|
|
))
|
|
{
|
|
dwError = RegCreateKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
PER_USER_POLICY_SYSTEM_KEY_NAME,
|
|
0,
|
|
NULL,
|
|
0,
|
|
KEY_SET_VALUE,
|
|
NULL,
|
|
&hRMRoot,
|
|
&Disposition
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
b = FALSE;
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
b = ConvertSidToStringSid(
|
|
pContext->Sids[0].Sid,
|
|
&szSid
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
b = AuthzpConstructRegistryPolicyPerUserAuditing(
|
|
pPolicy,
|
|
(PULONGLONG)RegPolicy
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = RegSetValueEx(
|
|
hRMRoot,
|
|
szSid,
|
|
0,
|
|
REG_BINARY,
|
|
RegPolicy,
|
|
sizeof(RegPolicy)
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (!b)
|
|
{
|
|
SetLastError(dwError);
|
|
}
|
|
|
|
if (hRMRoot)
|
|
{
|
|
dwError = RegCloseKey(hRMRoot);
|
|
ASSERT(ERROR_SUCCESS == dwError);
|
|
}
|
|
|
|
if (szSid)
|
|
{
|
|
AuthzpFree(szSid);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthziQueryAuditPolicy(
|
|
IN DWORD dwFlags,
|
|
IN AUTHZ_CLIENT_CONTEXT_HANDLE hContext,
|
|
IN PCWSTR szResourceManager OPTIONAL,
|
|
IN DWORD dwEventID,
|
|
OUT PTOKEN_AUDIT_POLICY pPolicy,
|
|
IN OUT PDWORD pPolicySize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function retrieves the audit policy for a specific context handle.
|
|
|
|
Arguments:
|
|
|
|
dwFlags - TBD.
|
|
|
|
hContext - context handle for the target user.
|
|
|
|
szResourceManager - Must be NULL. For future use.
|
|
|
|
dwEventID - Should be zero. For future use.
|
|
|
|
pPolicy - pointer to a structure storing the policy.
|
|
|
|
pPolicySize - pointer to DWORD containing size of pPolicy buffer.
|
|
|
|
Return Value:
|
|
|
|
Boolean: TRUE on success; FALSE on failure. Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PAUTHZI_CLIENT_CONTEXT pContext = (PAUTHZI_CLIENT_CONTEXT) hContext;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
HKEY hRMRoot = NULL;
|
|
DWORD Type;
|
|
BOOL b;
|
|
NTSTATUS Status;
|
|
UCHAR ValueBuffer[POLICY_BUFFER_BYTE_SIZE];
|
|
DWORD BufferSize = sizeof(ValueBuffer);
|
|
UNICODE_STRING szSid = {0};
|
|
WCHAR StringBuffer[256];
|
|
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
UNREFERENCED_PARAMETER(dwEventID);
|
|
|
|
RtlZeroMemory(ValueBuffer, sizeof(ValueBuffer));
|
|
|
|
if (NULL == pContext || NULL == pPolicySize || NULL == pPolicy)
|
|
{
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (NULL == szResourceManager || 0 == wcsncmp(
|
|
SYSTEM_RM_NAME,
|
|
szResourceManager,
|
|
sizeof(SYSTEM_RM_NAME) / sizeof(WCHAR)
|
|
))
|
|
{
|
|
//
|
|
// Default to the system RM.
|
|
//
|
|
|
|
dwError = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
PER_USER_POLICY_SYSTEM_KEY_NAME,
|
|
0,
|
|
KEY_QUERY_VALUE,
|
|
&hRMRoot
|
|
);
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// For now no RM can be specified.
|
|
//
|
|
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlZeroMemory(
|
|
StringBuffer,
|
|
sizeof(StringBuffer)
|
|
);
|
|
|
|
szSid.Buffer = (PWSTR)StringBuffer;
|
|
szSid.Length = 0;
|
|
szSid.MaximumLength = sizeof(StringBuffer);
|
|
|
|
Status = RtlConvertSidToUnicodeString(
|
|
&szSid,
|
|
pContext->Sids[0].Sid,
|
|
FALSE
|
|
);
|
|
|
|
if (ERROR_BUFFER_OVERFLOW == Status)
|
|
{
|
|
Status = RtlConvertSidToUnicodeString(
|
|
&szSid,
|
|
pContext->Sids[0].Sid,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
dwError = RtlNtStatusToDosError(Status);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the policy value for the SID under the given RM.
|
|
//
|
|
|
|
dwError = RegQueryValueEx(
|
|
hRMRoot,
|
|
szSid.Buffer,
|
|
NULL,
|
|
&Type,
|
|
ValueBuffer,
|
|
&BufferSize
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Sanity check the buffer type.
|
|
//
|
|
|
|
if (REG_BINARY != Type)
|
|
{
|
|
b = FALSE;
|
|
dwError = ERROR_INVALID_DATA;
|
|
goto Cleanup;
|
|
}
|
|
|
|
b = AuthzpConstructPolicyPerUserAuditing(
|
|
*((PULONGLONG) ValueBuffer),
|
|
(PTOKEN_AUDIT_POLICY) pPolicy,
|
|
pPolicySize
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (!b)
|
|
{
|
|
SetLastError(dwError);
|
|
}
|
|
|
|
if (szSid.Buffer != StringBuffer)
|
|
{
|
|
RtlFreeUnicodeString(&szSid);
|
|
}
|
|
|
|
if (hRMRoot)
|
|
{
|
|
RegCloseKey(hRMRoot);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthziSourceAudit(
|
|
IN DWORD dwFlags,
|
|
IN USHORT CategoryId,
|
|
IN USHORT AuditId,
|
|
IN PWSTR szSource,
|
|
IN PSID pUserSid OPTIONAL,
|
|
IN USHORT Count,
|
|
...
|
|
)
|
|
|
|
/**
|
|
|
|
Routine Description:
|
|
|
|
This is used to generate an audit with any source. An audit of type
|
|
SE_AUDITID_GENERIC_AUDIT_EVENT is sent to the LSA, and the event viewer
|
|
interprets this audit in a special manner. The first 2 parameters will be
|
|
treated as the actual source and id to be displayed in the security log. The
|
|
source listed must be a valid source listed under the EventLog\Security key.
|
|
|
|
Arguments:
|
|
|
|
dwFlags - APF_AuditSuccess
|
|
CategoryId - The category of the audit.
|
|
AuditId - The audit id to be generated.
|
|
szSource - the Source that should be listed in the security log.
|
|
pUserSid - optional pointer to Sid for audit to be created as.
|
|
Count - number of parameters in the VA section.
|
|
... - VA list of parameters, the semantics of which are described
|
|
in the comment for AuthziInitializeAuditParams.
|
|
|
|
Return Value:
|
|
|
|
Boolean: TRUE on success; FALSE on failure. Extended information available with GetLastError().
|
|
|
|
**/
|
|
|
|
{
|
|
BOOL b;
|
|
AUTHZ_AUDIT_EVENT_TYPE_HANDLE hAET = NULL;
|
|
AUTHZ_AUDIT_EVENT_HANDLE hAE = NULL;
|
|
PAUDIT_PARAMS pParams = NULL;
|
|
PSID pDummySid = NULL;
|
|
va_list arglist;
|
|
|
|
va_start(
|
|
arglist,
|
|
Count
|
|
);
|
|
|
|
//
|
|
// add in 2 because we store the actual audit id and the source
|
|
// string in the audit params.
|
|
//
|
|
|
|
b = AuthziAllocateAuditParams(
|
|
&pParams,
|
|
Count + 2
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
b = AuthziInitializeAuditEventType(
|
|
0,
|
|
CategoryId,
|
|
SE_AUDITID_GENERIC_AUDIT_EVENT,
|
|
Count + 2,
|
|
&hAET
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
b = AuthzpInitializeAuditParamsV(
|
|
dwFlags | AUTHZP_INIT_PARAMS_SOURCE_INFO,
|
|
pParams,
|
|
&pDummySid,
|
|
szSource,
|
|
AuditId,
|
|
Count,
|
|
arglist
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pUserSid)
|
|
{
|
|
//
|
|
// this is ugly, but currently there is no other way
|
|
//
|
|
|
|
pParams->Parameters[0].Data0 = (ULONG_PTR) pUserSid;
|
|
}
|
|
|
|
b = AuthziInitializeAuditEvent(
|
|
0,
|
|
NULL,
|
|
hAET,
|
|
pParams,
|
|
NULL,
|
|
INFINITE,
|
|
L"",
|
|
L"",
|
|
L"",
|
|
L"",
|
|
&hAE
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
b = AuthziLogAuditEvent(
|
|
0,
|
|
hAE,
|
|
NULL
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
va_end(arglist);
|
|
|
|
if (hAET)
|
|
{
|
|
AuthziFreeAuditEventType(
|
|
hAET
|
|
);
|
|
}
|
|
|
|
if (hAE)
|
|
{
|
|
AuthzFreeAuditEvent(
|
|
hAE
|
|
);
|
|
}
|
|
|
|
if (pParams)
|
|
{
|
|
AuthziFreeAuditParams(
|
|
pParams
|
|
);
|
|
}
|
|
|
|
if (pDummySid)
|
|
{
|
|
AuthzpFree(
|
|
pDummySid
|
|
);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzInstallSecurityEventSource(
|
|
IN DWORD dwFlags,
|
|
IN PAUTHZ_SOURCE_SCHEMA_REGISTRATION pRegistration
|
|
)
|
|
|
|
/**
|
|
|
|
Routine Description:
|
|
|
|
This installs a 3rd party as a security event source.
|
|
|
|
- add the name to the security sources key
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
**/
|
|
|
|
{
|
|
HKEY hkSecurity = NULL;
|
|
DWORD dwError;
|
|
BOOL b = TRUE;
|
|
PWCHAR pBuffer = NULL;
|
|
DWORD dwDisp;
|
|
HKEY hkSource = NULL;
|
|
HKEY hkObjectNames = NULL;
|
|
DWORD i;
|
|
|
|
#if 0
|
|
DWORD dwType;
|
|
DWORD dwLength = 0;
|
|
DWORD dwBuffer;
|
|
#endif
|
|
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
|
|
if (NULL == pRegistration)
|
|
{
|
|
b = FALSE;
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
#define SECURITY_KEY_NAME L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Security"
|
|
|
|
//
|
|
// Open the Security key.
|
|
//
|
|
|
|
dwError = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
SECURITY_KEY_NAME,
|
|
0,
|
|
KEY_READ | KEY_WRITE,
|
|
&hkSecurity
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// First make sure that there is not already an installed EventSource with the specified name.
|
|
// If createkey returns the wrong disposition then we know this source is already installed.
|
|
//
|
|
|
|
dwError = RegCreateKeyEx(
|
|
hkSecurity,
|
|
pRegistration->szEventSourceName,
|
|
0,
|
|
NULL,
|
|
0,
|
|
KEY_WRITE,
|
|
NULL,
|
|
&hkSource,
|
|
&dwDisp
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (REG_OPENED_EXISTING_KEY == dwDisp)
|
|
{
|
|
b = FALSE;
|
|
dwError = ERROR_OBJECT_ALREADY_EXISTS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ASSERT(dwDisp == REG_CREATED_NEW_KEY);
|
|
|
|
dwError = RegSetValueEx(
|
|
hkSource,
|
|
L"EventSourceFlags",
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&pRegistration->dwFlags,
|
|
sizeof(DWORD)
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// We created the new key for the Source. We are the only instance of
|
|
// this EventSource being installed. Continue and add the message file
|
|
// and access bit file information under the newly formed Source key.
|
|
//
|
|
|
|
if (pRegistration->szEventMessageFile)
|
|
{
|
|
dwError = RegSetValueEx(
|
|
hkSource,
|
|
L"EventMessageFile",
|
|
0,
|
|
REG_SZ,
|
|
(LPBYTE)pRegistration->szEventMessageFile,
|
|
(DWORD)(sizeof(WCHAR) * (wcslen(pRegistration->szEventMessageFile) + 1))
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if (pRegistration->szEventAccessStringsFile)
|
|
{
|
|
dwError = RegSetValueEx(
|
|
hkSource,
|
|
L"ParameterMessageFile",
|
|
0,
|
|
REG_SZ,
|
|
(LPBYTE)pRegistration->szEventAccessStringsFile,
|
|
(DWORD)(sizeof(WCHAR) * (wcslen(pRegistration->szEventAccessStringsFile) + 1))
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if (pRegistration->szExecutableImagePath)
|
|
{
|
|
dwError = RegSetValueEx(
|
|
hkSource,
|
|
L"ExecutableImagePath",
|
|
0,
|
|
REG_MULTI_SZ,
|
|
(LPBYTE)pRegistration->szExecutableImagePath,
|
|
(DWORD)(sizeof(WCHAR) * (wcslen(pRegistration->szExecutableImagePath) + 1))
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if (pRegistration->szEventSourceXmlSchemaFile)
|
|
{
|
|
dwError = RegSetValueEx(
|
|
hkSource,
|
|
L"XmlSchemaFile",
|
|
0,
|
|
REG_SZ,
|
|
(LPBYTE)pRegistration->szEventSourceXmlSchemaFile,
|
|
(DWORD)(sizeof(WCHAR) * (wcslen(pRegistration->szEventSourceXmlSchemaFile) + 1))
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
// if (pRegistration->szCategoryMessageFile)
|
|
// {
|
|
// dwError = RegSetValueEx(
|
|
// hkSource,
|
|
// L"CategoryMessageFile",
|
|
// 0,
|
|
// REG_SZ,
|
|
// (LPBYTE)pRegistration->szCategoryMessageFile,
|
|
// (DWORD)(sizeof(WCHAR) * (wcslen(pRegistration->szEventMessageFile) + 1))
|
|
// );
|
|
//
|
|
// if (ERROR_SUCCESS != dwError)
|
|
// {
|
|
// b = FALSE;
|
|
// goto Cleanup;
|
|
// }
|
|
// }
|
|
|
|
if (pRegistration->dwObjectTypeNameCount)
|
|
{
|
|
//
|
|
// There are object names to be registered. Create an ObjectNames subkey under
|
|
// hkSource and populate it.
|
|
//
|
|
|
|
dwError = RegCreateKeyEx(
|
|
hkSource,
|
|
L"ObjectNames",
|
|
0,
|
|
NULL,
|
|
0,
|
|
KEY_WRITE,
|
|
NULL,
|
|
&hkObjectNames,
|
|
&dwDisp
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// It would be strange for this to not be a brand new key, given that we just
|
|
// created the parent of hkObjectNames a few lines above...
|
|
//
|
|
|
|
ASSERT(dwDisp == REG_CREATED_NEW_KEY);
|
|
|
|
for (i = 0; i < pRegistration->dwObjectTypeNameCount; i++)
|
|
{
|
|
dwError = RegSetValueEx(
|
|
hkObjectNames,
|
|
pRegistration->ObjectTypeNames[i].szObjectTypeName,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)&(pRegistration->ObjectTypeNames[i].dwOffset),
|
|
sizeof(DWORD)
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
|
|
//
|
|
// The subkey for the source is now complete. All that remains is to add
|
|
// the source name to the REG_MULTI_SZ Sources value. First determine the
|
|
// size of the value.
|
|
//
|
|
|
|
dwError = RegQueryValueEx(
|
|
hkSecurity,
|
|
L"Sources",
|
|
NULL,
|
|
&dwType,
|
|
NULL,
|
|
&dwLength
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ASSERT(dwType == REG_MULTI_SZ);
|
|
|
|
//
|
|
// Allocate space for the new value of Sources. We need space for
|
|
// the current value as well as the new event source to be added. +2
|
|
// because of the need for double terminators.
|
|
//
|
|
|
|
dwBuffer = dwLength + (wcslen(pRegistration->szEventSourceName) + 2) * sizeof(WCHAR);
|
|
|
|
pBuffer = AuthzpAlloc(dwBuffer);
|
|
|
|
if (NULL == pBuffer)
|
|
{
|
|
b = FALSE;
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlZeroMemory(
|
|
pBuffer,
|
|
dwBuffer
|
|
);
|
|
|
|
//
|
|
// Read in the Sources value.
|
|
//
|
|
|
|
dwError = RegQueryValueEx(
|
|
hkSecurity,
|
|
L"Sources",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)pBuffer,
|
|
&dwLength
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ASSERT(dwType == REG_MULTI_SZ);
|
|
|
|
//
|
|
// Now place the new event source into pBuffer at dwLength - 1
|
|
// position (to remove one of the double NULL terminators. We don't
|
|
// explicitly terminate the pBuffer because it has already been zeroed
|
|
// out.
|
|
//
|
|
|
|
dwLength -= sizeof(WCHAR);
|
|
|
|
RtlCopyMemory(
|
|
&((PUCHAR)pBuffer)[dwLength],
|
|
pRegistration->szEventSourceName,
|
|
wcslen(pRegistration->szEventSourceName) * sizeof(WCHAR)
|
|
);
|
|
|
|
dwLength += wcslen(pRegistration->szEventSourceName) * sizeof(WCHAR) + 2 * sizeof(WCHAR); // to add back in the double NULL
|
|
|
|
dwError = RegSetValueEx(
|
|
hkSecurity,
|
|
L"Sources",
|
|
0,
|
|
REG_MULTI_SZ,
|
|
(LPBYTE)pBuffer,
|
|
dwLength
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
#endif
|
|
|
|
Cleanup:
|
|
|
|
if (!b)
|
|
{
|
|
SetLastError(dwError);
|
|
}
|
|
|
|
if (pBuffer)
|
|
{
|
|
LocalFree(pBuffer);
|
|
}
|
|
|
|
if (hkSecurity)
|
|
{
|
|
RegCloseKey(hkSecurity);
|
|
}
|
|
|
|
if (hkSource)
|
|
{
|
|
RegCloseKey(hkSource);
|
|
}
|
|
|
|
if (hkObjectNames)
|
|
{
|
|
RegCloseKey(hkObjectNames);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzUninstallSecurityEventSource(
|
|
IN DWORD dwFlags,
|
|
IN PCWSTR szEventSourceName
|
|
)
|
|
|
|
/**
|
|
|
|
Routine Description:
|
|
|
|
This will remove the source from the security key and remove the source string
|
|
from the list of valid sources.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
**/
|
|
|
|
{
|
|
HKEY hkSecurity = NULL;
|
|
DWORD dwError;
|
|
BOOL b = TRUE;
|
|
PUCHAR pBuffer = NULL;
|
|
#if 0
|
|
DWORD dwLength = 0;
|
|
DWORD dwType;
|
|
PUCHAR pCurrentString = NULL;
|
|
PUCHAR pNextString = NULL;
|
|
BOOL bFound = FALSE;
|
|
DWORD dwSourceStringByteLength;
|
|
#endif
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
|
|
if (NULL == szEventSourceName)
|
|
{
|
|
b = FALSE;
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Open the Security key.
|
|
//
|
|
|
|
dwError = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
SECURITY_KEY_NAME,
|
|
0,
|
|
KEY_READ | KEY_WRITE,
|
|
&hkSecurity
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
#if 0
|
|
|
|
//
|
|
// Remove the source name from the Sources value.
|
|
//
|
|
|
|
dwError = RegQueryValueEx(
|
|
hkSecurity,
|
|
L"Sources",
|
|
NULL,
|
|
&dwType,
|
|
NULL,
|
|
&dwLength
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ASSERT(dwType == REG_MULTI_SZ);
|
|
|
|
//
|
|
// Allocate space for the current value of Sources.
|
|
//
|
|
|
|
pBuffer = AuthzpAlloc(dwLength);
|
|
|
|
if (NULL == pBuffer)
|
|
{
|
|
b = FALSE;
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Read in the Sources value.
|
|
//
|
|
|
|
dwError = RegQueryValueEx(
|
|
hkSecurity,
|
|
L"Sources",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)pBuffer,
|
|
&dwLength
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
ASSERT(dwType == REG_MULTI_SZ);
|
|
|
|
//
|
|
// Now find the substring in the pBuffer that matches the sourcename
|
|
// we wish to delete.
|
|
//
|
|
|
|
pCurrentString = pBuffer;
|
|
dwSourceStringByteLength = (DWORD)(sizeof(WCHAR) * (wcslen(szEventSourceName) + 1));
|
|
bFound = FALSE;
|
|
while (pCurrentString < (pBuffer + dwLength))
|
|
{
|
|
if (dwSourceStringByteLength == RtlCompareMemory(
|
|
szEventSourceName,
|
|
pCurrentString,
|
|
dwSourceStringByteLength
|
|
))
|
|
{
|
|
//
|
|
// We have found the substring of Sources that matches the event source
|
|
// name.
|
|
//
|
|
|
|
bFound = TRUE;
|
|
break;
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Move the pointer to the next string location.
|
|
//
|
|
|
|
pCurrentString += (sizeof(WCHAR) * (1 + wcslen((PWCHAR)pCurrentString)));
|
|
}
|
|
}
|
|
|
|
if (bFound)
|
|
{
|
|
//
|
|
// pCurrentString points at the source name in the pBuffer.
|
|
// Remove this string from the value by copying over it.
|
|
//
|
|
|
|
pNextString = pCurrentString + dwSourceStringByteLength;
|
|
|
|
ASSERT(pNextString <= (pBuffer + dwLength));
|
|
|
|
RtlCopyMemory(
|
|
pCurrentString,
|
|
pNextString,
|
|
pBuffer + dwLength - pNextString
|
|
);
|
|
}
|
|
else
|
|
{
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwError = RegSetValueEx(
|
|
hkSecurity,
|
|
L"Sources",
|
|
0,
|
|
REG_MULTI_SZ,
|
|
(LPBYTE)pBuffer,
|
|
dwLength - dwSourceStringByteLength
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// Delete the key for this source, and the ObjectNames subkey.
|
|
//
|
|
|
|
dwError = DeleteKeyRecursivelyW(
|
|
hkSecurity,
|
|
szEventSourceName
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (pBuffer)
|
|
{
|
|
AuthzpFree(pBuffer);
|
|
}
|
|
|
|
if (hkSecurity)
|
|
{
|
|
RegCloseKey(hkSecurity);
|
|
}
|
|
|
|
if (!b)
|
|
{
|
|
SetLastError(dwError);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzEnumerateSecurityEventSources(
|
|
IN DWORD dwFlags,
|
|
OUT PAUTHZ_SOURCE_SCHEMA_REGISTRATION pBuffer,
|
|
OUT PDWORD pdwCount,
|
|
IN OUT PDWORD pdwLength
|
|
)
|
|
|
|
/**
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
**/
|
|
|
|
{
|
|
HKEY hkSecurity = NULL;
|
|
HKEY hkSource = NULL;
|
|
DWORD dwSourceCount = 0;
|
|
DWORD dwLength = 0;
|
|
PWSTR pName = NULL;
|
|
WCHAR Buffer[128];
|
|
DWORD dwTotalLengthNeeded = 0;
|
|
BOOL b = TRUE;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
FILETIME Time;
|
|
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
|
|
//
|
|
// Open the Security key.
|
|
//
|
|
|
|
dwError = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
SECURITY_KEY_NAME,
|
|
0,
|
|
KEY_READ,
|
|
&hkSecurity
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
do
|
|
{
|
|
pName = Buffer;
|
|
dwLength = sizeof(Buffer) / sizeof(WCHAR);
|
|
|
|
dwError = RegEnumKeyEx(
|
|
hkSecurity,
|
|
dwSourceCount,
|
|
pName,
|
|
&dwLength,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&Time
|
|
);
|
|
|
|
dwLength *= sizeof(WCHAR);
|
|
|
|
if (dwError == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
pName = (PWSTR)AuthzpAlloc(dwLength);
|
|
|
|
if (NULL == pName)
|
|
{
|
|
b = FALSE;
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
dwLength /= sizeof(WCHAR);
|
|
|
|
dwError = RegEnumKeyEx(
|
|
hkSecurity,
|
|
dwSourceCount,
|
|
pName,
|
|
&dwLength,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&Time
|
|
);
|
|
|
|
dwLength *= sizeof(WCHAR);
|
|
}
|
|
}
|
|
|
|
if (dwError == ERROR_NO_MORE_ITEMS)
|
|
{
|
|
//
|
|
// Do nothing. This will fall us through to the end of the
|
|
// while loop and still hit the correct cleanup code.
|
|
//
|
|
}
|
|
else if (dwError != ERROR_SUCCESS)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
else if (dwError == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Space for the Structure
|
|
//
|
|
|
|
dwTotalLengthNeeded += sizeof(AUTHZ_SOURCE_SCHEMA_REGISTRATION);
|
|
|
|
//
|
|
// Space for the Source name + NULL terminator
|
|
//
|
|
|
|
dwTotalLengthNeeded += PtrAlignSize(dwLength + sizeof(WCHAR));
|
|
|
|
//
|
|
// Open the subkey identified by pName and determine the
|
|
// sizes of the values listed therein.
|
|
//
|
|
|
|
dwError = RegOpenKeyEx(
|
|
hkSecurity,
|
|
pName,
|
|
0,
|
|
KEY_READ,
|
|
&hkSource
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwLength = 0;
|
|
|
|
dwError = RegQueryValueEx(
|
|
hkSource,
|
|
L"EventMessageFile",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&dwLength
|
|
);
|
|
|
|
if (ERROR_FILE_NOT_FOUND == dwError)
|
|
{
|
|
dwError = ERROR_SUCCESS;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Space for the value + NULL terminator
|
|
//
|
|
|
|
dwTotalLengthNeeded += PtrAlignSize(dwLength + sizeof(WCHAR));
|
|
}
|
|
|
|
dwLength = 0;
|
|
|
|
dwError = RegQueryValueEx(
|
|
hkSource,
|
|
L"ParameterMessageFile",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&dwLength
|
|
);
|
|
|
|
if (ERROR_FILE_NOT_FOUND == dwError)
|
|
{
|
|
dwError = ERROR_SUCCESS;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Space for the value + NULL terminator
|
|
//
|
|
|
|
dwTotalLengthNeeded += PtrAlignSize(dwLength + sizeof(WCHAR));
|
|
}
|
|
|
|
dwLength = 0;
|
|
dwError = RegQueryValueEx(
|
|
hkSource,
|
|
L"XmlSchemaFile",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&dwLength
|
|
);
|
|
|
|
if (ERROR_FILE_NOT_FOUND == dwError)
|
|
{
|
|
dwError = ERROR_SUCCESS;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Space for the value + NULL terminator
|
|
//
|
|
|
|
dwTotalLengthNeeded += PtrAlignSize(dwLength + sizeof(WCHAR));
|
|
}
|
|
|
|
RegCloseKey(hkSource);
|
|
hkSource = NULL;
|
|
dwSourceCount++;
|
|
}
|
|
|
|
if ((pName != NULL) && (pName != Buffer))
|
|
{
|
|
//
|
|
// free the temporary storage used for the key name.
|
|
//
|
|
|
|
AuthzpFree(pName);
|
|
}
|
|
}
|
|
while (dwError == ERROR_SUCCESS);
|
|
|
|
if (dwTotalLengthNeeded > *pdwLength)
|
|
{
|
|
b = FALSE;
|
|
dwError = ERROR_INSUFFICIENT_BUFFER;
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The passed buffer is big enough. Set it up.
|
|
// Each structure contains string pointers, which point to memory in the
|
|
// blob after the structures.
|
|
//
|
|
|
|
PUCHAR pData;
|
|
DWORD i;
|
|
DWORD dwSpaceUsed = 0;
|
|
|
|
RtlZeroMemory(
|
|
pBuffer,
|
|
*pdwLength
|
|
);
|
|
|
|
//
|
|
// The data for the Schema structures begins at pData.
|
|
//
|
|
|
|
pData = (PUCHAR)((PUCHAR)pBuffer + PtrAlignSize(dwSourceCount * sizeof(AUTHZ_SOURCE_SCHEMA_REGISTRATION)));
|
|
|
|
dwSpaceUsed = PtrAlignSize(dwSourceCount * (sizeof(AUTHZ_SOURCE_SCHEMA_REGISTRATION)));
|
|
|
|
for (i = 0; i < dwSourceCount; i++)
|
|
{
|
|
pBuffer[i].szEventSourceName = (PWSTR)pData;
|
|
dwLength = (*pdwLength - dwSpaceUsed) / sizeof(WCHAR);
|
|
|
|
dwError = RegEnumKeyEx(
|
|
hkSecurity,
|
|
i,
|
|
pBuffer[i].szEventSourceName,
|
|
&dwLength,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&Time
|
|
);
|
|
|
|
dwLength *= sizeof(WCHAR);
|
|
|
|
if (ERROR_NO_MORE_ITEMS == dwError)
|
|
{
|
|
b = TRUE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwSpaceUsed += PtrAlignSize(dwLength + sizeof(WCHAR));
|
|
|
|
//
|
|
// Open the subkey identified by szEventSourceName and
|
|
// copy the values listed therein.
|
|
//
|
|
|
|
dwError = RegOpenKeyEx(
|
|
hkSecurity,
|
|
pBuffer[i].szEventSourceName,
|
|
0,
|
|
KEY_READ,
|
|
&hkSource
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pData += PtrAlignSize(dwLength + sizeof(WCHAR));
|
|
pBuffer[i].szEventMessageFile = (PWSTR)pData;
|
|
dwLength = *pdwLength - dwSpaceUsed;
|
|
|
|
dwError = RegQueryValueEx(
|
|
hkSource,
|
|
L"EventMessageFile",
|
|
NULL,
|
|
NULL,
|
|
(PBYTE)pBuffer[i].szEventMessageFile,
|
|
&dwLength
|
|
);
|
|
|
|
if (ERROR_FILE_NOT_FOUND == dwError)
|
|
{
|
|
dwError = ERROR_SUCCESS;
|
|
dwLength = 0;
|
|
pBuffer[i].szEventMessageFile = NULL;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwSpaceUsed += PtrAlignSize(dwLength + sizeof(WCHAR));
|
|
pData += PtrAlignSize(dwLength + sizeof(WCHAR));
|
|
pBuffer[i].szEventAccessStringsFile = (PWSTR)pData;
|
|
dwLength = *pdwLength - dwSpaceUsed;
|
|
|
|
dwError = RegQueryValueEx(
|
|
hkSource,
|
|
L"ParameterMessageFile",
|
|
NULL,
|
|
NULL,
|
|
(PBYTE)pBuffer[i].szEventAccessStringsFile,
|
|
&dwLength
|
|
);
|
|
|
|
if (ERROR_FILE_NOT_FOUND == dwError)
|
|
{
|
|
dwError = ERROR_SUCCESS;
|
|
dwLength = 0;
|
|
pBuffer[i].szEventAccessStringsFile = NULL;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
dwSpaceUsed += PtrAlignSize(dwLength + sizeof(WCHAR));
|
|
pData += PtrAlignSize(dwLength + sizeof(WCHAR));
|
|
pBuffer[i].szEventSourceXmlSchemaFile = (PWSTR)pData;
|
|
dwLength = *pdwLength - dwSpaceUsed;
|
|
|
|
dwError = RegQueryValueEx(
|
|
hkSource,
|
|
L"XmlSchemaFile",
|
|
NULL,
|
|
NULL,
|
|
(PBYTE)pBuffer[i].szEventSourceXmlSchemaFile,
|
|
&dwLength
|
|
);
|
|
|
|
if (ERROR_FILE_NOT_FOUND == dwError)
|
|
{
|
|
dwError = ERROR_SUCCESS;
|
|
dwLength = 0;
|
|
pBuffer[i].szEventSourceXmlSchemaFile = NULL;
|
|
}
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RegCloseKey(hkSource);
|
|
hkSource = NULL;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (hkSecurity)
|
|
{
|
|
RegCloseKey(hkSecurity);
|
|
}
|
|
if (hkSource)
|
|
{
|
|
RegCloseKey(hkSource);
|
|
}
|
|
if ((pName != NULL) && (pName != (PWSTR)Buffer))
|
|
{
|
|
AuthzpFree(pName);
|
|
}
|
|
if (!b)
|
|
{
|
|
SetLastError(dwError);
|
|
}
|
|
|
|
*pdwLength = dwTotalLengthNeeded;
|
|
*pdwCount = dwSourceCount;
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzRegisterSecurityEventSource(
|
|
IN DWORD dwFlags,
|
|
IN PCWSTR szEventSourceName,
|
|
OUT PAUTHZ_SECURITY_EVENT_PROVIDER_HANDLE phEventProvider
|
|
)
|
|
|
|
/**
|
|
|
|
Routine Description:
|
|
|
|
This allows the client to register a provider with the LSA.
|
|
|
|
Arguments:
|
|
|
|
dwFlags - TBD
|
|
|
|
szEventSourceName - the provider name
|
|
|
|
phEventProvider - pointer to provider handle to initialize.
|
|
|
|
Return Value:
|
|
|
|
Boolean: TRUE on success; FALSE on failure. Extended information available with GetLastError().
|
|
|
|
**/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
BOOL b = TRUE;
|
|
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
|
|
if (NULL == szEventSourceName || wcslen(szEventSourceName) == 0 || NULL == phEventProvider)
|
|
{
|
|
b = FALSE;
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto Cleanup;
|
|
}
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarAdtRegisterSecurityEventSource(
|
|
0,
|
|
szEventSourceName,
|
|
phEventProvider
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = RpcExceptionCode();
|
|
|
|
} RpcEndExcept;
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
b = FALSE;
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
return b;
|
|
}
|
|
|
|
BOOL
|
|
AuthzUnregisterSecurityEventSource(
|
|
IN DWORD dwFlags,
|
|
IN OUT PAUTHZ_SECURITY_EVENT_PROVIDER_HANDLE phEventProvider
|
|
)
|
|
|
|
/**
|
|
|
|
Routine Description:
|
|
|
|
Unregisters a provider with the LSA.
|
|
|
|
Arguments:
|
|
|
|
dwFlags - TBD
|
|
|
|
hEventProvider - the handle returned by AuthzRegisterSecurityEventSource
|
|
|
|
Return Value:
|
|
|
|
Boolean: TRUE on success; FALSE on failure. Extended information available with GetLastError().
|
|
|
|
**/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
BOOL b = TRUE;
|
|
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
|
|
if (NULL == phEventProvider)
|
|
{
|
|
b = FALSE;
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto Cleanup;
|
|
}
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarAdtUnregisterSecurityEventSource(
|
|
0,
|
|
phEventProvider
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = RpcExceptionCode();
|
|
|
|
} RpcEndExcept;
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
b = FALSE;
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
return b;
|
|
}
|
|
|
|
BOOL
|
|
AuthzReportSecurityEvent(
|
|
IN DWORD dwFlags,
|
|
IN AUTHZ_SECURITY_EVENT_PROVIDER_HANDLE hEventProvider,
|
|
IN DWORD dwAuditId,
|
|
IN PSID pUserSid OPTIONAL,
|
|
IN DWORD dwCount,
|
|
...
|
|
)
|
|
|
|
/**
|
|
|
|
Routine Description:
|
|
|
|
Allows a client to generate an audit.
|
|
|
|
Arguments:
|
|
|
|
dwFlags - APF_AuditSuccess APF_AuditFailure
|
|
|
|
hEventProvider - handle to the provider registered.
|
|
|
|
dwAuditId - the ID of the audit
|
|
|
|
pUserSid - the SID that the audit should appear as being generated by in
|
|
the eventlog
|
|
|
|
dwCount - the number of APF type-value pairs that follow in the VA section
|
|
|
|
Return Value:
|
|
|
|
Boolean: TRUE on success; FALSE on failure. Extended information available with GetLastError().
|
|
|
|
**/
|
|
|
|
{
|
|
BOOL b;
|
|
AUDIT_PARAMS AuditParams = {0};
|
|
AUDIT_PARAM ParamArray[SE_MAX_AUDIT_PARAMETERS] = {0};
|
|
|
|
va_list(arglist);
|
|
|
|
AuditParams.Count = (USHORT)dwCount;
|
|
AuditParams.Parameters = ParamArray;
|
|
|
|
va_start (arglist, dwCount);
|
|
|
|
b = AuthzpInitializeAuditParamsV(
|
|
dwFlags | AUTHZP_INIT_PARAMS_SKIP_HEADER,
|
|
&AuditParams,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
(USHORT)dwCount,
|
|
arglist
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
b = AuthzReportSecurityEventFromParams(
|
|
dwFlags,
|
|
hEventProvider,
|
|
dwAuditId,
|
|
pUserSid,
|
|
&AuditParams
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
return b;
|
|
}
|
|
|
|
BOOL
|
|
AuthzReportSecurityEventFromParams(
|
|
IN DWORD dwFlags,
|
|
IN AUTHZ_SECURITY_EVENT_PROVIDER_HANDLE hEventProvider,
|
|
IN DWORD dwAuditId,
|
|
IN PSID pUserSid OPTIONAL,
|
|
IN PAUDIT_PARAMS pParams
|
|
)
|
|
|
|
/**
|
|
|
|
Routine Description:
|
|
|
|
This generates an audit from the passed parameter array.
|
|
|
|
Arguments:
|
|
|
|
dwFlags - APF_AuditSuccess APF_AuditFailure
|
|
|
|
hEventProvider - handle to the provider registered
|
|
|
|
dwAuditId - the ID of the audit
|
|
|
|
pUserSid - the SID that the audit should appear as being generated by in. If NULL the
|
|
effective token is used.
|
|
|
|
pParams - array of audit parameters.
|
|
|
|
Return Value:
|
|
|
|
Boolean: TRUE on success; FALSE on failure. Extended information available with GetLastError().
|
|
|
|
**/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
LUID Luid;
|
|
BOOL b = TRUE;
|
|
BOOL bFreeSid = FALSE;
|
|
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
|
|
if (NULL == hEventProvider || NULL == pParams)
|
|
{
|
|
b = FALSE;
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (NULL == pUserSid)
|
|
{
|
|
bFreeSid = TRUE;
|
|
b = AuthzpGetThreadTokenInfo(
|
|
&pUserSid,
|
|
&Luid
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
//
|
|
// Failed to get the thread token, try for the process
|
|
// token.
|
|
//
|
|
|
|
b = AuthzpGetProcessTokenInfo(
|
|
&pUserSid,
|
|
&Luid
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
RpcTryExcept {
|
|
|
|
Status = LsarAdtReportSecurityEvent(
|
|
0,
|
|
hEventProvider,
|
|
dwAuditId,
|
|
pUserSid,
|
|
pParams
|
|
);
|
|
|
|
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
|
|
|
|
Status = RpcExceptionCode();
|
|
|
|
} RpcEndExcept;
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
b = FALSE;
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (bFreeSid && pUserSid)
|
|
{
|
|
AuthzpFree(pUserSid);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
|
|
|
|
DWORD
|
|
DeleteKeyRecursivelyW(
|
|
IN HKEY hkey,
|
|
IN LPCWSTR pwszSubKey
|
|
)
|
|
{
|
|
DWORD dwRet;
|
|
HKEY hkSubKey;
|
|
|
|
// Open the subkey so we can enumerate any children
|
|
dwRet = RegOpenKeyExW(hkey, pwszSubKey, 0, MAXIMUM_ALLOWED, &hkSubKey);
|
|
if (ERROR_SUCCESS == dwRet)
|
|
{
|
|
DWORD dwIndex;
|
|
WCHAR wszSubKeyName[MAX_PATH + 1];
|
|
DWORD cchSubKeyName = ARRAYSIZE(wszSubKeyName);
|
|
|
|
// I can't just call RegEnumKey with an ever-increasing index, because
|
|
// I'm deleting the subkeys as I go, which alters the indices of the
|
|
// remaining subkeys in an implementation-dependent way. In order to
|
|
// be safe, I have to count backwards while deleting the subkeys.
|
|
|
|
// Find out how many subkeys there are
|
|
dwRet = RegQueryInfoKeyW(hkSubKey, NULL, NULL, NULL,
|
|
&dwIndex, // The # of subkeys -- all we need
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
|
|
|
if (NO_ERROR == dwRet)
|
|
{
|
|
// dwIndex is now the count of subkeys, but it needs to be
|
|
// zero-based for RegEnumKey, so I'll pre-decrement, rather
|
|
// than post-decrement.
|
|
while (ERROR_SUCCESS == RegEnumKeyW(hkSubKey, --dwIndex, wszSubKeyName, cchSubKeyName))
|
|
{
|
|
DeleteKeyRecursivelyW(hkSubKey, wszSubKeyName);
|
|
}
|
|
}
|
|
|
|
RegCloseKey(hkSubKey);
|
|
|
|
if (pwszSubKey)
|
|
{
|
|
dwRet = RegDeleteKeyW(hkey, pwszSubKey);
|
|
}
|
|
else
|
|
{
|
|
// we want to delete all the values by hand
|
|
cchSubKeyName = ARRAYSIZE(wszSubKeyName);
|
|
while (ERROR_SUCCESS == RegEnumValueW(hkey, 0, wszSubKeyName, &cchSubKeyName, NULL, NULL, NULL, NULL))
|
|
{
|
|
// avoid looping infinitely when we cant delete the value
|
|
if (RegDeleteValueW(hkey, wszSubKeyName))
|
|
break;
|
|
|
|
cchSubKeyName = ARRAYSIZE(wszSubKeyName);
|
|
}
|
|
}
|
|
}
|
|
|
|
return dwRet;
|
|
}
|
|
|