Windows-Server-2003/ds/security/authz/authz.c

6937 lines
176 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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;
}