/* Editor Settings: expandtabs and use 4 spaces for indentation * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: * * -*- mode: c, c-basic-offset: 4 -*- */ /* * Copyright Likewise Software * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. You should have received a copy of the GNU General * Public License along with this program. If not, see * . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ /* * Copyright (C) Likewise Software. All rights reserved. * * Module Name: * * acl_posix.c * * Abstract: * * Likewise Posix File System Driver (PVFS) * * Standard Unix perms & POSIX acls NTFS ACL implementation * * Authors: Gerald Carter */ #include "pvfs.h" /*********************************************************************** **********************************************************************/ NTSTATUS PvfsSecurityInitMapSecurityCtx( PLW_MAP_SECURITY_CONTEXT *ppContext ) { return LwMapSecurityCreateContext(ppContext); } /*********************************************************************** **********************************************************************/ VOID PvfsSecurityShutdownMapSecurityCtx( PLW_MAP_SECURITY_CONTEXT *ppContext ) { return LwMapSecurityFreeContext(ppContext); } /*********************************************************************** **********************************************************************/ static NTSTATUS PvfsSecurityAclMapFromPosix( IN OUT PSECURITY_DESCRIPTOR_RELATIVE pSecDesc, IN OUT PULONG pSecDescLen, IN PPVFS_STAT pStat ); NTSTATUS PvfsGetSecurityDescriptorPosix( IN PPVFS_CCB pCcb, IN OUT PSECURITY_DESCRIPTOR_RELATIVE pSecDesc, IN OUT PULONG pSecDescLen ) { NTSTATUS ntError = STATUS_NO_SECURITY_ON_OBJECT; PVFS_STAT Stat = {0}; ntError = PvfsSysFstat(pCcb->fd, &Stat); BAIL_ON_NT_STATUS(ntError); ntError = PvfsSecurityAclMapFromPosix(pSecDesc, pSecDescLen, &Stat); BAIL_ON_NT_STATUS(ntError); cleanup: return ntError; error: goto cleanup; } /*********************************************************************** **********************************************************************/ NTSTATUS PvfsGetSecurityDescriptorFilenamePosix( IN PCSTR pszFilename, IN OUT PSECURITY_DESCRIPTOR_RELATIVE pSecDesc, IN OUT PULONG pSecDescLen ) { NTSTATUS ntError = STATUS_NO_SECURITY_ON_OBJECT; PVFS_STAT Stat = {0}; ntError = PvfsSysStat(pszFilename, &Stat); BAIL_ON_NT_STATUS(ntError); ntError = PvfsSecurityAclMapFromPosix(pSecDesc, pSecDescLen, &Stat); BAIL_ON_NT_STATUS(ntError); cleanup: return ntError; error: goto cleanup; } /*********************************************************************** **********************************************************************/ static NTSTATUS PvfsSecuritySidMapFromUid( OUT PSID *ppUserSid, IN uid_t Uid ); static NTSTATUS PvfsSecuritySidMapFromGid( OUT PSID *ppGroupSid, IN gid_t Gid ); static NTSTATUS PvfsSecurityAclGetDacl( OUT PACL *ppDacl, IN PPVFS_STAT pStat ); static NTSTATUS PvfsSecurityAclMapFromPosix( IN OUT PSECURITY_DESCRIPTOR_RELATIVE pSecDescRelative, IN OUT PULONG pSecDescRelativeLen, IN PPVFS_STAT pStat ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc = NULL; PACL pDacl = NULL; PSID pOwnerSid = NULL; PSID pGroupSid = NULL; BAIL_ON_INVALID_PTR(pStat, ntError); BAIL_ON_INVALID_PTR(pSecDescRelativeLen, ntError); ntError= LW_RTL_ALLOCATE( &pSecDesc, VOID, SECURITY_DESCRIPTOR_ABSOLUTE_MIN_SIZE); BAIL_ON_NT_STATUS(ntError); ntError = RtlCreateSecurityDescriptorAbsolute( pSecDesc, SECURITY_DESCRIPTOR_REVISION); BAIL_ON_NT_STATUS(ntError); // Owner ntError = PvfsSecuritySidMapFromUid(&pOwnerSid, pStat->s_uid); BAIL_ON_NT_STATUS(ntError); ntError = RtlSetOwnerSecurityDescriptor(pSecDesc, pOwnerSid, FALSE); BAIL_ON_NT_STATUS(ntError); // Group ntError = PvfsSecuritySidMapFromGid(&pGroupSid, pStat->s_gid); BAIL_ON_NT_STATUS(ntError); ntError = RtlSetGroupSecurityDescriptor(pSecDesc, pGroupSid, FALSE); BAIL_ON_NT_STATUS(ntError); // DACL ntError = PvfsSecurityAclGetDacl(&pDacl, pStat); BAIL_ON_NT_STATUS(ntError); ntError = RtlSetDaclSecurityDescriptor(pSecDesc, TRUE, pDacl, FALSE); BAIL_ON_NT_STATUS(ntError); // We don't do SACLs here // All done ntError = RtlAbsoluteToSelfRelativeSD( pSecDesc, pSecDescRelative, pSecDescRelativeLen); BAIL_ON_NT_STATUS(ntError); cleanup: if (pSecDesc) { PvfsFreeAbsoluteSecurityDescriptor(&pSecDesc); } return ntError; error: LW_RTL_FREE(&pOwnerSid); LW_RTL_FREE(&pGroupSid); LW_RTL_FREE(&pDacl); LW_RTL_FREE(&pSecDesc); goto cleanup; } /*********************************************************************** **********************************************************************/ static NTSTATUS PvfsSecuritySidMapFromUid( OUT PSID *ppUserSid, IN uid_t Uid ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; BOOLEAN bInLock = FALSE; UINT32 Key = Uid % PVFS_MAX_MRU_SIZE; BAIL_ON_INVALID_PTR(ppUserSid, ntError); LWIO_LOCK_MUTEX(bInLock, &gUidMruCacheMutex); if (gUidMruCache[Key] != NULL && (gUidMruCache[Key]->UnixId.Uid == Uid)) { ntError = RtlDuplicateSid(ppUserSid, gUidMruCache[Key]->pSid); BAIL_ON_NT_STATUS(ntError); goto cleanup; } ntError = LwMapSecurityGetSidFromId( gpPvfsLwMapSecurityCtx, ppUserSid, TRUE, Uid); BAIL_ON_NT_STATUS(ntError); /* Try to cache the SID/UID pair but don't fail if we can't since we have already got the data the caller asked for */ if (gUidMruCache[Key] != NULL) { PVFS_FREE(&gUidMruCache[Key]); } ntError = PvfsAllocateMemory( (PVOID*)&gUidMruCache[Key], sizeof(PVFS_ID_CACHE)); if (ntError != STATUS_SUCCESS) { ntError = STATUS_SUCCESS; goto cleanup; } gUidMruCache[Key]->UnixId.Uid = Uid; ntError = RtlDuplicateSid(&gUidMruCache[Key]->pSid, *ppUserSid); if (ntError != STATUS_SUCCESS) { PVFS_FREE(&gUidMruCache[Key]); ntError = STATUS_SUCCESS; goto cleanup; } cleanup: LWIO_UNLOCK_MUTEX(bInLock, &gUidMruCacheMutex); return ntError; error: goto cleanup; } /*********************************************************************** **********************************************************************/ static NTSTATUS PvfsSecuritySidMapFromGid( OUT PSID *ppGroupSid, IN gid_t Gid ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; BOOLEAN bInLock = FALSE; UINT32 Key = Gid % PVFS_MAX_MRU_SIZE; BAIL_ON_INVALID_PTR(ppGroupSid, ntError); LWIO_LOCK_MUTEX(bInLock, &gGidMruCacheMutex); if (gGidMruCache[Key] != NULL && (gGidMruCache[Key]->UnixId.Gid == Gid)) { ntError = RtlDuplicateSid(ppGroupSid, gGidMruCache[Key]->pSid); BAIL_ON_NT_STATUS(ntError); goto cleanup; } ntError = LwMapSecurityGetSidFromId( gpPvfsLwMapSecurityCtx, ppGroupSid, FALSE, Gid); BAIL_ON_NT_STATUS(ntError); /* Try to cache the SID/GID pair but don't fail if we can't since we have already got the data the caller asked for */ if (gGidMruCache[Key] != NULL) { PVFS_FREE(&gGidMruCache[Key]); } ntError = PvfsAllocateMemory( (PVOID*)&gGidMruCache[Key], sizeof(PVFS_ID_CACHE)); if (ntError != STATUS_SUCCESS) { ntError = STATUS_SUCCESS; goto cleanup; } gGidMruCache[Key]->UnixId.Gid = Gid; ntError = RtlDuplicateSid(&gGidMruCache[Key]->pSid, *ppGroupSid); if (ntError != STATUS_SUCCESS) { PVFS_FREE(&gGidMruCache[Key]); ntError = STATUS_SUCCESS; goto cleanup; } cleanup: LWIO_UNLOCK_MUTEX(bInLock, &gGidMruCacheMutex); return ntError; error: goto cleanup; } /*********************************************************************** **********************************************************************/ static NTSTATUS PvfsSecuritySidMapToId( OUT PULONG pId, OUT PBOOLEAN pbIsUser, IN PSID pSid ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; BAIL_ON_INVALID_PTR(pId, ntError); BAIL_ON_INVALID_PTR(pbIsUser, ntError); ntError = LwMapSecurityGetIdFromSid( gpPvfsLwMapSecurityCtx, pbIsUser, pId, pSid); BAIL_ON_NT_STATUS(ntError); cleanup: return ntError; error: goto cleanup; } /*********************************************************************** **********************************************************************/ static VOID PvfsSecurityAccessMapFromPosix( IN OUT PACCESS_MASK pAccess, IN BOOLEAN bIsDirectory, IN mode_t Mode, IN mode_t Read, IN mode_t Write, IN mode_t Execute ); static NTSTATUS PvfsSecurityAclGetDacl( OUT PACL *ppDacl, IN PPVFS_STAT pStat ) { NTSTATUS ntError = STATUS_UNSUCCESSFUL; DWORD dwSizeDacl = 0; PSID pOwnerSid = NULL; PSID pGroupSid = NULL; PSID pEveryoneSid = NULL; DWORD dwSidCount = 0; PACL pDacl = NULL; ACCESS_MASK AccessMask = 0; BOOLEAN bIsDir = S_ISDIR(pStat->s_mode); /* Build SIDs */ ntError = PvfsSecuritySidMapFromUid(&pOwnerSid, pStat->s_uid); BAIL_ON_NT_STATUS(ntError); dwSidCount++; ntError = PvfsSecuritySidMapFromGid(&pGroupSid, pStat->s_gid); BAIL_ON_NT_STATUS(ntError); dwSidCount++; ntError = RtlAllocateSidFromCString(&pEveryoneSid, "S-1-1-0"); BAIL_ON_NT_STATUS(ntError); dwSidCount++; dwSizeDacl = ACL_HEADER_SIZE + dwSidCount * sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(pOwnerSid) + RtlLengthSid(pGroupSid) + RtlLengthSid(pEveryoneSid) - dwSidCount * sizeof(ULONG); ntError= RTL_ALLOCATE(&pDacl, VOID, dwSizeDacl); BAIL_ON_NT_STATUS(ntError); ntError = RtlCreateAcl(pDacl, dwSizeDacl, ACL_REVISION); BAIL_ON_NT_STATUS(ntError); AccessMask = 0; PvfsSecurityAccessMapFromPosix( &AccessMask, bIsDir, pStat->s_mode, S_IRUSR, S_IWUSR, S_IXUSR); AccessMask |= (READ_CONTROL|WRITE_DAC|WRITE_OWNER); ntError = RtlAddAccessAllowedAceEx( pDacl, ACL_REVISION, 0, AccessMask, pOwnerSid); BAIL_ON_NT_STATUS(ntError); AccessMask = 0; PvfsSecurityAccessMapFromPosix( &AccessMask, bIsDir, pStat->s_mode, S_IRGRP, S_IWGRP, S_IXGRP); AccessMask |= (pStat->s_mode & S_ISGID) ? WRITE_DAC : 0; ntError = RtlAddAccessAllowedAceEx( pDacl, ACL_REVISION, 0, AccessMask, pGroupSid); BAIL_ON_NT_STATUS(ntError); AccessMask = 0; PvfsSecurityAccessMapFromPosix( &AccessMask, bIsDir, pStat->s_mode, S_IROTH, S_IWOTH, S_IXOTH); ntError = RtlAddAccessAllowedAceEx( pDacl, ACL_REVISION, 0, AccessMask, pEveryoneSid); BAIL_ON_NT_STATUS(ntError); *ppDacl = pDacl; pDacl = NULL; cleanup: /* Use RtlFree for SIDs */ LW_RTL_FREE(&pOwnerSid); LW_RTL_FREE(&pGroupSid); LW_RTL_FREE(&pEveryoneSid); LW_RTL_FREE(&pDacl); return ntError; error: goto cleanup; } /*********************************************************************** **********************************************************************/ static VOID PvfsSecurityAccessMapFromPosix( IN OUT PACCESS_MASK pAccess, IN BOOLEAN bIsDirectory, IN mode_t Mode, IN mode_t Read, IN mode_t Write, IN mode_t Execute ) { ACCESS_MASK Access = *pAccess; if (Mode & Read) { Access |= FILE_GENERIC_READ; if (bIsDirectory) { Access |= FILE_LIST_DIRECTORY; } } if (Mode & Write) { Access |= (FILE_GENERIC_WRITE|DELETE); if (bIsDirectory) { Access |= (FILE_DELETE_CHILD|FILE_ADD_SUBDIRECTORY); } } if (Mode & Execute) { Access |= FILE_GENERIC_EXECUTE; if (bIsDirectory) { Access |= FILE_TRAVERSE; } } if ((Mode & Read) && (Mode & Write) && (Mode & Execute)) { /* Force the caller to decide if we need to include WRITE_OWNER|WRITE_DAC */ Access = FILE_ALL_ACCESS & ~(WRITE_OWNER|WRITE_DAC); } *pAccess = Access; return; } /*********************************************************************** **********************************************************************/ static VOID PvfsSecurityAccessMapToPosix( IN OUT mode_t *pMode, IN ACCESS_MASK Access, IN mode_t Read, IN mode_t Write, IN mode_t Execute ) { mode_t Mode = *pMode; if (Access & FILE_READ_DATA) { Mode |= Read; } if (Access & FILE_WRITE_DATA) { Mode |= Write; } if (Access & FILE_EXECUTE) { Mode |= Execute; } *pMode = Mode; return; } /*********************************************************************** **********************************************************************/ static NTSTATUS PvfsSecurityPosixSetOwner( PPVFS_CCB pCcb, PPVFS_STAT pStat, PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc ); static NTSTATUS PvfsSecurityPosixSetGroup( PPVFS_CCB pCcb, PPVFS_STAT pStat, PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc ); static NTSTATUS PvfsSecurityPosixSetDacl( PPVFS_CCB pCcb, PPVFS_STAT pStat, PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc ); static NTSTATUS PvfsSecurityPosixSetSacl( PPVFS_CCB pCcb, PPVFS_STAT pStat, PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc ); NTSTATUS PvfsSetSecurityDescriptorPosix( IN PPVFS_CCB pCcb, IN PSECURITY_DESCRIPTOR_RELATIVE pSecDescRelative, IN ULONG SecDescLen ) { NTSTATUS ntError = STATUS_SUCCESS; PSECURITY_DESCRIPTOR_ABSOLUTE pSecDescAbs = NULL; PVFS_STAT Stat = {0}; ntError = PvfsSysFstat(pCcb->fd, &Stat); BAIL_ON_NT_STATUS(ntError); ntError = PvfsSecurityAclSelfRelativeToAbsoluteSD( &pSecDescAbs, pSecDescRelative); BAIL_ON_NT_STATUS(ntError); /* The first two are sanity checks mainly and make no changes */ ntError = PvfsSecurityPosixSetOwner( pCcb, &Stat, pSecDescAbs); BAIL_ON_NT_STATUS(ntError); ntError = PvfsSecurityPosixSetSacl( pCcb, &Stat, pSecDescAbs); BAIL_ON_NT_STATUS(ntError); /* These can actually change the file/directory permissions */ ntError = PvfsSecurityPosixSetGroup( pCcb, &Stat, pSecDescAbs); BAIL_ON_NT_STATUS(ntError); ntError = PvfsSecurityPosixSetDacl( pCcb, &Stat, pSecDescAbs); BAIL_ON_NT_STATUS(ntError); /* Change group - User must be a member of that group */ cleanup: if (pSecDescAbs) { PvfsFreeAbsoluteSecurityDescriptor(&pSecDescAbs); } return ntError; error: goto cleanup; } /*********************************************************************** **********************************************************************/ static NTSTATUS PvfsSecurityPosixSetOwner( PPVFS_CCB pCcb, PPVFS_STAT pStat, PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc ) { NTSTATUS ntError = STATUS_ACCESS_DENIED; PSID pOwner = NULL; BOOLEAN bDefaulted = FALSE; BOOLEAN bIsUserSid = FALSE; ULONG Id = 0; BAIL_ON_INVALID_PTR(pSecDesc, ntError); ntError = RtlGetOwnerSecurityDescriptor(pSecDesc, &pOwner, &bDefaulted); BAIL_ON_NT_STATUS(ntError); if (pOwner) { ntError = PvfsSecuritySidMapToId(&Id, &bIsUserSid, pOwner); BAIL_ON_NT_STATUS(ntError); /* Owner SID has to be a user and has to match current owner. I don't support changing owner rights or take ownership permission here */ if (!bIsUserSid || (Id != pStat->s_uid)) { ntError = STATUS_ACCESS_DENIED; BAIL_ON_NT_STATUS(ntError); } } ntError = STATUS_SUCCESS; cleanup: return ntError; error: goto cleanup; } /*********************************************************************** **********************************************************************/ static NTSTATUS PvfsSecurityPosixSetGroup( PPVFS_CCB pCcb, PPVFS_STAT pStat, PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc ) { NTSTATUS ntError = STATUS_ACCESS_DENIED; PSID pGroup = NULL; BOOLEAN bDefaulted = FALSE; BOOLEAN bIsUserSid = FALSE; ULONG Id = 0; BAIL_ON_INVALID_PTR(pSecDesc, ntError); ntError = RtlGetGroupSecurityDescriptor(pSecDesc, &pGroup, &bDefaulted); BAIL_ON_NT_STATUS(ntError); if (pGroup == NULL) { ntError = STATUS_SUCCESS; goto cleanup; } ntError = PvfsSecuritySidMapToId(&Id, &bIsUserSid, pGroup); BAIL_ON_NT_STATUS(ntError); /* Has to be a valid group. TODO - Allow group ownership changes if the user is a member of that group. */ if (bIsUserSid) { ntError = STATUS_ACCESS_DENIED; BAIL_ON_NT_STATUS(ntError); } ntError = STATUS_SUCCESS; cleanup: return ntError; error: goto cleanup; } /*********************************************************************** **********************************************************************/ static NTSTATUS PvfsSecurityPosixSetDacl( PPVFS_CCB pCcb, PPVFS_STAT pStat, PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc ) { NTSTATUS ntError = STATUS_ACCESS_DENIED; PACL pDacl = NULL; BOOLEAN bPresent = FALSE; PVOID pAce = NULL; ULONG AceIndex = 0; mode_t Mode = 0; UCHAR InheritFlags = (OBJECT_INHERIT_ACE| CONTAINER_INHERIT_ACE| INHERIT_ONLY_ACE); BOOLEAN bDefaulted = FALSE; BOOLEAN bIsUserSid = FALSE; ULONG Id = 0; PSID pEveryoneSid = NULL; BAIL_ON_INVALID_PTR(pSecDesc, ntError); ntError = RtlGetDaclSecurityDescriptor( pSecDesc, &bPresent, &pDacl, &bDefaulted); BAIL_ON_NT_STATUS(ntError); if (!bPresent || !pDacl) { ntError = STATUS_SUCCESS; goto cleanup; } /* Set up the Everyone SID so we can check for it in the ACL */ ntError = RtlAllocateSidFromCString(&pEveryoneSid, "S-1-1-0"); BAIL_ON_NT_STATUS(ntError); for (ntError = RtlGetAce(pDacl, AceIndex, &pAce); (ntError == STATUS_SUCCESS) && pAce; ntError = RtlGetAce(pDacl, AceIndex++, &pAce)) { PACE_HEADER pAceHeader = (PACE_HEADER)pAce; PACCESS_ALLOWED_ACE pAllowedAce = NULL; PSID pSid = NULL; if (pAceHeader->AceType != ACCESS_ALLOWED_ACE_TYPE) { ntError = STATUS_ACCESS_DENIED; BAIL_ON_NT_STATUS(ntError); } /* Ignore directory inheritance for now */ if (pAceHeader->AceFlags & InheritFlags) { ntError = STATUS_ACCESS_DENIED; BAIL_ON_NT_STATUS(ntError); } pAllowedAce = (PACCESS_ALLOWED_ACE)pAce; pSid = (PSID)&pAllowedAce->SidStart; /* First check for "Everyone" */ if (RtlEqualSid(pSid, pEveryoneSid)) { PvfsSecurityAccessMapToPosix( &Mode, pAllowedAce->Mask, S_IROTH, S_IWOTH, S_IXOTH); continue; } /* Deal with User/Group SIDs */ ntError = PvfsSecuritySidMapToId(&Id, &bIsUserSid, pSid); BAIL_ON_NT_STATUS(ntError); if (bIsUserSid) { if (Id != pStat->s_uid) { ntError = STATUS_INVALID_ACL; BAIL_ON_NT_STATUS(ntError); } PvfsSecurityAccessMapToPosix( &Mode, pAllowedAce->Mask, S_IRUSR, S_IWUSR, S_IXUSR); } else { /* Group SID */ if (Id != pStat->s_gid) { ntError = STATUS_INVALID_ACL; BAIL_ON_NT_STATUS(ntError); } PvfsSecurityAccessMapToPosix( &Mode, pAllowedAce->Mask, S_IRGRP, S_IWGRP, S_IXGRP); if (pAllowedAce->Mask & WRITE_DAC) { Mode |= S_ISGID; } } } if (Mode == 0) { ntError = STATUS_INVALID_ACL; BAIL_ON_NT_STATUS(ntError); } ntError = PvfsSysFchmod(pCcb, Mode); BAIL_ON_NT_STATUS(ntError); cleanup: LW_RTL_FREE(&pEveryoneSid); return ntError; error: goto cleanup; } /*********************************************************************** **********************************************************************/ static NTSTATUS PvfsSecurityPosixSetSacl( PPVFS_CCB pCcb, PPVFS_STAT pStat, PSECURITY_DESCRIPTOR_ABSOLUTE pSecDesc ) { NTSTATUS ntError = STATUS_ACCESS_DENIED; PACL pSacl = NULL; BOOLEAN bPresent = FALSE; BOOLEAN bDefaulted = FALSE; BAIL_ON_INVALID_PTR(pSecDesc, ntError); ntError = RtlGetSaclSecurityDescriptor( pSecDesc, &bPresent, &pSacl, &bDefaulted); BAIL_ON_NT_STATUS(ntError); if (bPresent || pSacl) { ntError = STATUS_PRIVILEGE_NOT_HELD; BAIL_ON_NT_STATUS(ntError); } ntError = STATUS_SUCCESS; cleanup: return ntError; error: goto cleanup; } /* local variables: mode: c c-basic-offset: 4 indent-tabs-mode: nil tab-width: 4 end: */