|
|
//depot/Lab02_N/Net/sfm/afp/server/access.c#2 - edit change 2054 (text)
/*
Copyright (c) 1992 Microsoft Corporation
Module Name:
access.c
Abstract:
This module contains the routines for handling access related stuff.
Author:
Jameel Hyder (microsoft!jameelh)
Revision History: 20 Sep 1992 Initial Version
Notes: Tab stop: 4 --*/
#define FILENUM FILE_ACCESS
#include <afp.h>
#include <fdparm.h>
#include <pathmap.h>
#define _ACCESS_LOCALS
#include <access.h>
#include <client.h>
#include <secutil.h>
#include <seposix.h>
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, afpIsUserMemberOfGroup)
#pragma alloc_text( PAGE, AfpGetUserAndPrimaryGroupSids)
#pragma alloc_text( PAGE, AfpMakeSecurityDescriptorForUser)
#pragma alloc_text( PAGE, AfpGetAfpPermissions)
#pragma alloc_text( PAGE, afpMoveAces)
#pragma alloc_text( PAGE, AfpSetAfpPermissions)
#pragma alloc_text( PAGE, afpPermissions2NtMask)
#pragma alloc_text( PAGE, afpAddAceToAcl)
#if DBG
#pragma alloc_text( PAGE, AfpDumpSid)
#pragma alloc_text( PAGE, AfpDumpSidnMask)
#endif
#endif
/*** afpIsUserMemberOfGroup
* * Determine if the User is member of the given group, if it is a group. */ LOCAL BOOLEAN afpIsUserMemberOfGroup( IN PTOKEN_GROUPS pGroups, IN PSID pSidGroup ) { DWORD i; BOOLEAN IsAMember = False;
PAGED_CODE( );
ASSERT ((pGroups != NULL) && (pSidGroup != NULL));
AfpDumpSid("afpIsUserMemberOfGroup: Checking", pSidGroup); DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("afpIsUserMemberOfGroup: # of groups is %d\n", pGroups->GroupCount)); for (i = 0; i < pGroups->GroupCount; i++) { AfpDumpSid("afpIsUserMemberOfGroup: Checking with", pGroups->Groups[i].Sid); if (RtlEqualSid(pSidGroup, pGroups->Groups[i].Sid)) { IsAMember = True; DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("afpIsUserMemberOfGroup: Yes !!\n")); break; } }
return IsAMember; }
/*** AfpGetUserAndPrimaryGroupSids
* * Get the Sids corres. to the user and his primary group. */ NTSTATUS AfpGetUserAndPrimaryGroupSids( IN PSDA pSda ) { DWORD i, j; NTSTATUS Status = STATUS_SUCCESS; DWORD SidLength, SizeNeeded, ExtraSpace, Offset; PSID_AND_ATTRIBUTES pSidnAttr; PTOKEN_GROUPS pGroups; BYTE GrpsBuffer[1024]; BYTE Buffer[256]; // We should not need a buffer larger
// than this for User SID_AND_ATTRIBUTES
PAGED_CODE( );
do { pGroups = (PTOKEN_GROUPS)GrpsBuffer; pSda->sda_pGroups = NULL; if (pSda->sda_ClientType == SDA_CLIENT_GUEST) { pSda->sda_UserSid = &AfpSidWorld; pSda->sda_GroupSid = &AfpSidWorld; // Primary group of Guest is also 'World'
break; }
pSidnAttr = (PSID_AND_ATTRIBUTES)Buffer;
// Get the Owner Sid out of the User token and copy it into the Sda
Status = NtQueryInformationToken(pSda->sda_UserToken, TokenOwner, pSidnAttr, sizeof(Buffer), &SizeNeeded);
ASSERT (NT_SUCCESS(Status)); if (!NT_SUCCESS(Status)) { break; }
AfpDumpSid("AfpGetUserAndPrimaryGroupSids: LOGON Owner Sid", pSidnAttr->Sid);
SidLength = RtlLengthSid(pSidnAttr->Sid);
pSda->sda_UserSid = (PSID)ALLOC_ACCESS_MEM(SidLength); if (pSda->sda_UserSid == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; break; } RtlCopyMemory(pSda->sda_UserSid, pSidnAttr->Sid, SidLength);
// Get the primary group of this user
Status = NtQueryInformationToken(pSda->sda_UserToken, TokenPrimaryGroup, pSidnAttr, sizeof(Buffer), &SizeNeeded);
ASSERT (NT_SUCCESS(Status)); if (!NT_SUCCESS(Status)) { break; }
AfpDumpSid("AfpGetUserAndPrimaryGroupSids: LOGON Group Sid", pSidnAttr->Sid);
SidLength = RtlLengthSid(pSidnAttr->Sid); pSda->sda_GroupSid = (PSID)ALLOC_ACCESS_MEM(SidLength); if (pSda->sda_GroupSid == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; break; } RtlCopyMemory(pSda->sda_GroupSid, pSidnAttr->Sid, SidLength);
// Get the User Sid out of the User token. This will be added to the
// list of groups that we query later, if this is different from
// the Owner Sid (which is now in sda_UserSid).
Status = NtQueryInformationToken(pSda->sda_UserToken, TokenUser, pSidnAttr, sizeof(Buffer), &SizeNeeded);
ASSERT (NT_SUCCESS(Status)); if (!NT_SUCCESS(Status)) { break; }
AfpDumpSid("AfpGetUserAndPrimaryGroupSids: LOGON User Sid", pSidnAttr->Sid);
// Get the list of groups this user is member of
SizeNeeded = sizeof(GrpsBuffer); do { if (Status != STATUS_SUCCESS) { if (pGroups != (PTOKEN_GROUPS)GrpsBuffer) AfpFreeMemory(pGroups);
if ((pGroups = (PTOKEN_GROUPS)ALLOC_ACCESS_MEM(SizeNeeded)) == NULL) { Status = AFP_ERR_MISC; if (pSda->sda_ClientType == SDA_CLIENT_ADMIN) { Status = STATUS_INSUFFICIENT_RESOURCES; } break; } } Status = NtQueryInformationToken(pSda->sda_UserToken, TokenGroups, pGroups, SizeNeeded, &SizeNeeded); } while ((Status != STATUS_SUCCESS) && ((Status == STATUS_BUFFER_TOO_SMALL) || (Status == STATUS_BUFFER_OVERFLOW) || (Status == STATUS_MORE_ENTRIES)));
if (!NT_SUCCESS(Status)) { AFPLOG_ERROR(AFPSRVMSG_USER_GROUPS, Status, NULL, 0, NULL); break; }
// Allocate enough memory to copy the group information in the sda. If
// the User and Owner Sids in the user token are not the same then we
// want to add the user sid to the list of groups. This is especially
// the case where an ADMIN logs on but his Owner Sid is Administrators.
// Also fix up the pointers appropriately !!!
ExtraSpace = 0; Offset = 0; j = 0; if (!RtlEqualSid(pSidnAttr->Sid, pSda->sda_UserSid)) { ExtraSpace = (RtlLengthSid(pSidnAttr->Sid) + sizeof(pSidnAttr->Attributes)); Offset = sizeof(SID_AND_ATTRIBUTES); j = 1; }
if ((pSda->sda_pGroups = (PTOKEN_GROUPS)AfpAllocPagedMemory(2*SizeNeeded+2*ExtraSpace)) == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; break; }
// If we are not copying the User Sid in sda_pGroups, then copy pGroups to sda_pGroups
// directly and then fixup the individual pSid pointers. If we are then make the User
// Sid as the first one in the list and copy the actual sid at the tail end of the
// buffer.
pSda->sda_pGroups->GroupCount = pGroups->GroupCount; RtlCopyMemory(&pSda->sda_pGroups->Groups[j], &pGroups->Groups[0], SizeNeeded - sizeof(DWORD)); // DWORD accounts for GroupCount
if (ExtraSpace > 0) { pSda->sda_pGroups->Groups[0].Sid = (PSID)((PBYTE)(pSda->sda_pGroups) + SizeNeeded); RtlCopyMemory(pSda->sda_pGroups->Groups[0].Sid, pSidnAttr->Sid, RtlLengthSid(pSidnAttr->Sid));
pSda->sda_pGroups->Groups[0].Attributes = pSidnAttr->Attributes; pSda->sda_pGroups->GroupCount ++;
AfpDumpSid("AfpGetUserAndPrimaryGroupSids: Member of ", pSda->sda_pGroups->Groups[0].Sid); } for (i = 0; i < pGroups->GroupCount; i++, j++) { pSda->sda_pGroups->Groups[j].Sid = (PSID)((PBYTE)(pGroups->Groups[i].Sid) - (PBYTE)pGroups + (PBYTE)(pSda->sda_pGroups) + Offset); AfpDumpSid("AfpGetUserAndPrimaryGroupSids: Member of ", pSda->sda_pGroups->Groups[j].Sid); DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("AfpGetUserAndPrimaryGroupSids: Attributes %lx\n", pSda->sda_pGroups->Groups[j].Attributes)); } } while (False);
if (pGroups != (PTOKEN_GROUPS)GrpsBuffer) AfpFreeMemory(pGroups);
return Status; }
/*** AfpMakeSecurityDescriptorForUser
* * Create a security descriptor for a user. The security descriptor has the * Owner Sid, Primary Group Sid and Aces for the User alone. */ AFPSTATUS AfpMakeSecurityDescriptorForUser( IN PSID OwnerSid, IN PSID GroupSid, OUT PISECURITY_DESCRIPTOR * ppSecDesc ) { AFPSTATUS Status = AFP_ERR_MISC; PISECURITY_DESCRIPTOR pSecDesc; int DaclSize; PACCESS_ALLOWED_ACE pAce;
PAGED_CODE( );
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("AfpMakeSecurityDescriptorForUser: Entered\n"));
do { // Allocate a security descriptor
pSecDesc = (PISECURITY_DESCRIPTOR)ALLOC_ACCESS_MEM(sizeof(SECURITY_DESCRIPTOR));
*ppSecDesc = pSecDesc; if (pSecDesc == NULL) break;
// Initialize the security descriptor
RtlCreateSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION);
pSecDesc->Control = SE_DACL_PRESENT;
// Set the owner and group Ids in the descriptor
pSecDesc->Owner = OwnerSid; pSecDesc->Group = GroupSid;
// Determine the size of the Dacl needed. The sizeof(DWORD) offsets the
// SidStart field in the ACE. There are 7 aces in this security descriptor:
//
// 2 for the owner (owner+inherit for owner)
// 2 for Administrators (one inherit)
// 2 for world (1 for world and 1 inherit for world).
// 2 for system
DaclSize = sizeof(ACL) + 2*(sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + RtlLengthSid(OwnerSid)) + 2*(sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + sizeof(AfpSidWorld)) + 2*(sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + AfpSizeSidAdmins) + 2*(sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) + RtlLengthSid(&AfpSidSystem));
if ((pSecDesc->Dacl = (PACL)ALLOC_ACCESS_MEM(DaclSize)) == NULL) break;
// Initialize the ACL with one ACE corres. to Owner getting all the
// privileges. Add another ace which is identical to the first ace but is
// a inheritance ACE.
// JH - Add another ace for world with minumum permissions and for administrators
// with FullControl
RtlCreateAcl(pSecDesc->Dacl, DaclSize, ACL_REVISION);
// we will be adding to this as we add aces, so set it to the min here
pSecDesc->Dacl->AclSize = sizeof(ACL);
pAce = (PACCESS_ALLOWED_ACE)((PBYTE)pSecDesc->Dacl + sizeof(ACL));
// Add the ALLOWED_ACE and the corres. inherit Ace for owner
pAce = afpAddAceToAcl(pSecDesc->Dacl, pAce, (AFP_READ_ACCESS | AFP_WRITE_ACCESS | AFP_OWNER_ACCESS | FILE_DELETE_CHILD), OwnerSid, True);
if (AfpSidAdmins != NULL) { // Add the ALLOWED_ACE and the corres. inherit Ace for 'Administrators'
pAce = afpAddAceToAcl(pSecDesc->Dacl, pAce, (AFP_READ_ACCESS | AFP_WRITE_ACCESS | AFP_OWNER_ACCESS | FILE_DELETE_CHILD), AfpSidAdmins, True); }
// Add a min. permission ace for world, but only if the owner is
// not world already
if (!RtlEqualSid(OwnerSid, &AfpSidWorld)) { pAce = afpAddAceToAcl(pSecDesc->Dacl, pAce, (AFP_MIN_ACCESS), &AfpSidWorld, True); }
// Now add Aces for System
pAce = afpAddAceToAcl(pSecDesc->Dacl, pAce, AFP_READ_ACCESS | AFP_WRITE_ACCESS | AFP_OWNER_ACCESS, &AfpSidSystem, True); Status = AFP_ERR_NONE; } while (False);
// Do any cleanup on error
if (!NT_SUCCESS(Status) && (pSecDesc != NULL)) { if (pSecDesc->Dacl != NULL) AfpFreeMemory(pSecDesc->Dacl); AfpFreeMemory(pSecDesc); }
return Status;
}
/*** AfpGetAfpPermissions
* * Read the security descriptor for this directory and obtain the SIDs for * Owner and Primary group. Determine if this user is a member of the directory * primary group. Finally obtain Owner,Group and World permissions. * * OwnerId, GroupId and permissions will always be valid if this call succeeds. */ NTSTATUS AfpGetAfpPermissions( IN PSDA pSda, IN HANDLE DirHandle, IN OUT PFILEDIRPARM pFDParm ) { NTSTATUS Status = STATUS_SUCCESS; DWORD SizeNeeded; PISECURITY_DESCRIPTOR pSecDesc = NULL; PBYTE pAbsSecDesc = NULL; // Used in conversion of
// sec descriptor to
// absolute format
BOOLEAN SawOwnerAce = False, SawGroupAce = False, SawWorldAce = False, SawDenyAceForUser = False, CheckUserRights; #ifdef PROFILING
TIME TimeS, TimeE, TimeD; #endif
PAGED_CODE( );
#ifdef PROFILING
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_GetPermsCount); AfpGetPerfCounter(&TimeS); #endif
// Read the security descriptor for this directory and determine the
// rights for owner/group/world.We want to optimize on how much memory
// we need to read this in. Its a pain to make a call just to get that.
// So just make a guess. If that turns out to be short then do the exact
// allocation.
do { // 4096 has been emperically chosen
SizeNeeded = 4096 - POOL_OVERHEAD; do { if (pSecDesc != NULL) { AfpFreeMemory(pSecDesc); } if ((pSecDesc = (PSECURITY_DESCRIPTOR)ALLOC_ACCESS_MEM(SizeNeeded)) == NULL) { Status = AFP_ERR_MISC; if (pSda->sda_ClientType == SDA_CLIENT_ADMIN) { Status = STATUS_INSUFFICIENT_RESOURCES; } break; } Status = NtQuerySecurityObject(DirHandle, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, pSecDesc, SizeNeeded, &SizeNeeded); } while ((Status != STATUS_SUCCESS) && ((Status == STATUS_BUFFER_TOO_SMALL) || (Status == STATUS_BUFFER_OVERFLOW) || (Status == STATUS_MORE_ENTRIES)));
if (!NT_SUCCESS(Status)) { break; }
// If the security descriptor is in self-relative form, convert to absolute
pSecDesc = (PISECURITY_DESCRIPTOR)((PBYTE)pSecDesc); if (pSecDesc->Control & SE_SELF_RELATIVE) {
DWORD AbsoluteSizeNeeded;
// An absolute SD is not necessarily the same size as a relative
// SD, so an in-place conversion may not be possible.
AbsoluteSizeNeeded = SizeNeeded; Status = RtlSelfRelativeToAbsoluteSD2(pSecDesc, &AbsoluteSizeNeeded); if (Status == STATUS_BUFFER_TOO_SMALL) { // Allocate a new buffer in which to store the absolute
// security descriptor, copy the contents of the relative
// descriptor in and try again
pAbsSecDesc = (PBYTE)ALLOC_ACCESS_MEM(AbsoluteSizeNeeded); if (pAbsSecDesc == NULL) { Status = STATUS_NO_MEMORY; DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_ERR, ("AfpGetAfpPermissions: LocalAlloc error\n")); } else { RtlCopyMemory(pAbsSecDesc, pSecDesc, SizeNeeded); Status = RtlSelfRelativeToAbsoluteSD2 (pAbsSecDesc, &AbsoluteSizeNeeded); if (NT_SUCCESS(Status)) { // We don't need relative form anymore,
// we will work with the Absolute form
if (pSecDesc != NULL) { AfpFreeMemory(pSecDesc); } pSecDesc = (PISECURITY_DESCRIPTOR)pAbsSecDesc; } else { // We cannot use Absolute Form, throw it away
AfpFreeMemory(pAbsSecDesc); pAbsSecDesc = NULL; } }
} if (!NT_SUCCESS(Status)) { DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_ERR, ("AfpGetAfpPermissions: RtlSelfRelativeToAbsoluteSD2: returned error %lx\n", Status)); break; } }
// Now determine if the user is a member of the directories primary group.
pFDParm->_fdp_OwnerId = 0; pFDParm->_fdp_GroupId = 0; pFDParm->_fdp_UserIsOwner = False; pFDParm->_fdp_UserIsMemberOfDirGroup = False;
if (pSecDesc->Owner != NULL) { AfpDumpSid("AfpGetAfpPermissions: OwnerSid", pSecDesc->Owner);
pFDParm->_fdp_UserIsOwner = (RtlEqualSid(pSecDesc->Owner, pSda->sda_UserSid) || ((pSda->sda_ClientType != SDA_CLIENT_GUEST) && (pSda->sda_ClientType != SDA_CLIENT_ADMIN) && afpIsUserMemberOfGroup(pSda->sda_pGroups, pSecDesc->Owner))); DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("AfpGetAfpPermissions: User %s Owner\n", pFDParm->_fdp_UserIsOwner ? "is" : "isnt"));
if (!NT_SUCCESS(Status = AfpSidToMacId(pSecDesc->Owner, &pFDParm->_fdp_OwnerId))) { // If we cant map the Sid, return Id SE_NULL_POSIX_ID
pFDParm->_fdp_OwnerId = SE_NULL_POSIX_ID; Status = AFP_ERR_NONE; } }
if (pSecDesc->Group != NULL) { AfpDumpSid("AfpGetAfpPermissions: GroupSid", pSecDesc->Group);
if (!pFDParm->_fdp_UserIsOwner) pFDParm->_fdp_UserIsMemberOfDirGroup = (RtlEqualSid(pSecDesc->Group, pSda->sda_UserSid) || ((pSda->sda_ClientType != SDA_CLIENT_GUEST) && (pSda->sda_ClientType != SDA_CLIENT_ADMIN) && afpIsUserMemberOfGroup(pSda->sda_pGroups, pSecDesc->Group)));
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("AfpGetAfpPermissions: User %s member of PrimaryGroup\n", pFDParm->_fdp_UserIsMemberOfDirGroup ? "is" : "isnt"));
if (!NT_SUCCESS(Status = AfpSidToMacId(pSecDesc->Group, &pFDParm->_fdp_GroupId))) { // If we cant map the Sid, return Id SE_NULL_POSIX_ID
pFDParm->_fdp_GroupId = SE_NULL_POSIX_ID; Status = AFP_ERR_NONE; } }
// Walk through the ACL list and determine Owner/Group/World and User
// permissions. For Owner/Group and User, if the specific ace's are
// not present then they inherit the world permissions.
//
// A NULL Acl => All rights to everyone. An empty Acl on the other
// hand => no access for anyone.
pFDParm->_fdp_UserRights = 0; pFDParm->_fdp_WorldRights = 0;
if ((pSecDesc->Control & SE_DACL_PRESENT) && (pSecDesc->Dacl != NULL)) { USHORT i; PSID pSid; PACL pAcl; PACCESS_ALLOWED_ACE pAce;
ASSERT (pSecDesc->Dacl != NULL); pAcl = pSecDesc->Dacl; pAce = (PACCESS_ALLOWED_ACE)((PBYTE)pAcl + sizeof(ACL)); CheckUserRights = ((pSda->sda_ClientType != SDA_CLIENT_GUEST) && (pSda->sda_ClientType != SDA_CLIENT_ADMIN)); DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("AfpGetAfpPermissions: # of aces %d\n", pSecDesc->Dacl->AceCount)); for (i = 0; i < pSecDesc->Dacl->AceCount; i++) { pSid = (PSID)(&pAce->SidStart);
// Ignore inherit aces, Aces for CreatorOwner, CreatorGroup & system
if ((pAce->Header.AceFlags & INHERIT_ONLY_ACE) || RtlEqualSid(pSid, &AfpSidSystem)) { AfpDumpSidnMask("AfpGetAfpPermissions: Skipping", pSid, pAce->Mask, pAce->Header.AceType, pAce->Header.AceFlags); } else { AfpDumpSidnMask("AfpGetAfpPermissions: ACE", pSid, pAce->Mask, pAce->Header.AceType, pAce->Header.AceFlags);
if (!SawOwnerAce && (pSecDesc->Owner != NULL) && RtlEqualSid(pSid, pSecDesc->Owner)) { AfpAccessMask2AfpPermissions(pFDParm->_fdp_OwnerRights, pAce->Mask, pAce->Header.AceType); DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("%s Ace Mask %lx, Owner Permission: %x\n", (pAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) ? "Allow" : "Deny", pAce->Mask, pFDParm->_fdp_OwnerRights)); SawOwnerAce = True; }
if (!SawGroupAce && (pSecDesc->Group != NULL) && RtlEqualSid(pSid, pSecDesc->Group)) { AfpAccessMask2AfpPermissions(pFDParm->_fdp_GroupRights, pAce->Mask, pAce->Header.AceType); DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("%s Ace Mask %lx, Group Permission: %x\n", (pAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) ? "Allow" : "Deny", pAce->Mask, pFDParm->_fdp_GroupRights)); SawGroupAce = True; }
if (!SawWorldAce && RtlEqualSid(pSid, (PSID)&AfpSidWorld)) { AfpAccessMask2AfpPermissions(pFDParm->_fdp_WorldRights, pAce->Mask, pAce->Header.AceType); DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("%s Ace Mask %lx, World Permission: %x\n", (pAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) ? "Allow" : "Deny", pAce->Mask, pFDParm->_fdp_WorldRights)); SawWorldAce = True; }
if (CheckUserRights && !SawDenyAceForUser && (RtlEqualSid(pSid, pSda->sda_UserSid) || afpIsUserMemberOfGroup(pSda->sda_pGroups, pSid))) { BYTE UserRights;
UserRights = 0; AfpAccessMask2AfpPermissions(UserRights, pAce->Mask, ACCESS_ALLOWED_ACE_TYPE);
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("%s Ace Mask %lx, User Permission: %x\n", (pAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) ? "Allow" : "Deny", pAce->Mask, pFDParm->_fdp_WorldRights));
if (pAce->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) { pFDParm->_fdp_UserRights |= UserRights; } else { SawDenyAceForUser = True; pFDParm->_fdp_UserRights &= ~UserRights; } } }
pAce = (PACCESS_ALLOWED_ACE)((PBYTE)pAce + pAce->Header.AceSize); } } else // Security descriptor not present, party time
{ pFDParm->_fdp_WorldRights = DIR_ACCESS_ALL; pFDParm->_fdp_UserRights = DIR_ACCESS_ALL | DIR_ACCESS_OWNER; }
if (!SawGroupAce) pFDParm->_fdp_GroupRights = pFDParm->_fdp_WorldRights;
// If this is a standalone server and the primary group of the
// directory is MACHINE\None, do not return this information to
// the caller.
if (AfpServerIsStandalone && (pSecDesc->Group != NULL) && RtlEqualSid(pSecDesc->Group, AfpSidNone)) { pFDParm->_fdp_GroupRights = 0; pFDParm->_fdp_GroupId = 0; }
if (pSda->sda_ClientType == SDA_CLIENT_GUEST) pFDParm->_fdp_UserRights = pFDParm->_fdp_WorldRights;
// Make sure we do not give a user anything if we saw a deny ace for him.
if (!SawOwnerAce && !SawDenyAceForUser) pFDParm->_fdp_OwnerRights = pFDParm->_fdp_WorldRights; } while (False);
if (pSecDesc != NULL) AfpFreeMemory(pSecDesc);
#ifdef PROFILING
AfpGetPerfCounter(&TimeE); TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart; INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_GetPermsTime, TimeD, &AfpStatisticsLock); #endif
return Status; }
/*** afpMoveAces
* * Move a bunch of aces from the old security descriptor to the new security * descriptor. */ LOCAL PACCESS_ALLOWED_ACE afpMoveAces( IN PACL pOldDacl, IN PACCESS_ALLOWED_ACE pAceStart, IN PSID pSidOldOwner, IN PSID pSidNewOwner, IN PSID pSidOldGroup, IN PSID pSidNewGroup, IN BOOLEAN DenyAces, IN OUT PACL pNewDacl ) { USHORT i; PACCESS_ALLOWED_ACE pAceOld; PSID pSidAce;
PAGED_CODE( );
for (i = 0, pAceOld = (PACCESS_ALLOWED_ACE)((PBYTE)pOldDacl + sizeof(ACL)); i < pOldDacl->AceCount; i++, pAceOld = (PACCESS_ALLOWED_ACE)((PBYTE)pAceOld + pAceOld->Header.AceSize)) { // Note: All deny aces are ahead of the grant aces.
if (DenyAces && (pAceOld->Header.AceType != ACCESS_DENIED_ACE_TYPE)) break;
if (!DenyAces && (pAceOld->Header.AceType == ACCESS_DENIED_ACE_TYPE)) continue;
pSidAce = (PSID)(&pAceOld->SidStart); if (!RtlEqualSid(pSidAce, &AfpSidWorld) && !RtlEqualSid(pSidAce, &AfpSidSystem) && (AfpSidAdmins != NULL) && !RtlEqualSid(pSidAce, AfpSidAdmins) && !RtlEqualSid(pSidAce, pSidOldOwner) && !RtlEqualSid(pSidAce, pSidNewOwner) && !RtlEqualSid(pSidAce, pSidOldGroup) && !RtlEqualSid(pSidAce, pSidNewGroup)) { RtlCopyMemory(pAceStart, pAceOld, pAceOld->Header.AceSize); pNewDacl->AclSize += pAceOld->Header.AceSize; pNewDacl->AceCount ++; pAceStart = (PACCESS_ALLOWED_ACE)((PBYTE)pAceStart + pAceStart->Header.AceSize); } } return pAceStart; }
/*** AfpSetAfpPermissions
* * Set the permissions on this directory. Also optionally set the owner and * group ids. For setting the owner and group ids verify if the user has the * needed access. This access is however not good enough. We check for this * access but do the actual setting of the permissions in the special server * context (RESTORE privilege is needed). */ AFPSTATUS AfpSetAfpPermissions( IN HANDLE DirHandle, IN DWORD Bitmap, IN PFILEDIRPARM pFDParm ) { AFPSTATUS Status = STATUS_SUCCESS; DWORD SizeNeeded; PISECURITY_DESCRIPTOR pSecDesc; PBYTE pAbsSecDesc = NULL; // Used in conversion of
// sec descriptor to
// absolute format
SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION; PSID pSidOwner = NULL, pSidGroup = NULL; PSID pSidOldOwner, pSidOldGroup; BOOLEAN SawOwnerAce = False, SawGroupAce = False; BOOLEAN OwnerIsWorld = False, GroupIsWorld = False; BOOLEAN fDir = IsDir(pFDParm); PACL pDaclNew = NULL; PACCESS_ALLOWED_ACE pAce; LONG SizeNewDacl; #ifdef PROFILING
TIME TimeS, TimeE, TimeD; #endif
PAGED_CODE( );
#ifdef PROFILING
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_SetPermsCount); AfpGetPerfCounter(&TimeS); #endif
do { // Read the security descriptor for this directory
SizeNeeded = 4096 - POOL_OVERHEAD; pSecDesc = NULL;
do { if (pSecDesc != NULL) { AfpFreeMemory(pSecDesc); }
SizeNewDacl = SizeNeeded; if ((pSecDesc = (PSECURITY_DESCRIPTOR)ALLOC_ACCESS_MEM(SizeNeeded)) == NULL) { Status = AFP_ERR_MISC; break; }
Status = NtQuerySecurityObject(DirHandle, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, pSecDesc, SizeNeeded, &SizeNeeded); } while ((Status != STATUS_SUCCESS) && ((Status == STATUS_BUFFER_TOO_SMALL) || (Status == STATUS_BUFFER_OVERFLOW) || (Status == STATUS_MORE_ENTRIES)));
if (!NT_SUCCESS(Status)) { Status = AfpIoConvertNTStatusToAfpStatus(Status); break; }
pSecDesc = (PISECURITY_DESCRIPTOR)((PBYTE)pSecDesc); // If the security descriptor is in self-relative form, convert to absolute
if (pSecDesc->Control & SE_SELF_RELATIVE) { DWORD AbsoluteSizeNeeded;
// An absolute SD is not necessarily the same size as a relative
// SD, so an in-place conversion may not be possible.
AbsoluteSizeNeeded = SizeNeeded; Status = RtlSelfRelativeToAbsoluteSD2(pSecDesc, &AbsoluteSizeNeeded); if (Status == STATUS_BUFFER_TOO_SMALL) { // Allocate a new buffer in which to store the absolute
// security descriptor, copy the contents of the relative
// descriptor in and try again
pAbsSecDesc = (PBYTE)ALLOC_ACCESS_MEM(AbsoluteSizeNeeded); if (pAbsSecDesc == NULL) { Status = STATUS_NO_MEMORY; DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_ERR, ("AfpSetAfpPermissions: LocalAlloc error\n")); } else { RtlCopyMemory(pAbsSecDesc, pSecDesc, SizeNeeded); Status = RtlSelfRelativeToAbsoluteSD2 (pAbsSecDesc, &AbsoluteSizeNeeded); if (NT_SUCCESS(Status)) { // We don't need relative form anymore,
// we will work with the Absolute form
if (pSecDesc != NULL) { AfpFreeMemory(pSecDesc); } pSecDesc = (PISECURITY_DESCRIPTOR)pAbsSecDesc; } else { // We cannot use Absolute Form, throw it away
AfpFreeMemory(pAbsSecDesc); pAbsSecDesc = NULL; } } }
if (!NT_SUCCESS(Status)) { DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_ERR, ("AfpSetAfpPermissions: RtlSelfRelativeToAbsoluteSD2: returned error %lx\n", Status)); break; } SizeNeeded = AbsoluteSizeNeeded; } SizeNewDacl = SizeNeeded;
// Save the old Owner and Group Sids
pSidOldOwner = pSecDesc->Owner; pSidOldGroup = pSecDesc->Group;
// Convert the owner/group ids, if any to be set to their corres. sids
if (Bitmap & DIR_BITMAP_OWNERID) { DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("AfpSetAfpPermissions: Setting Owner to ID %lx\n", pFDParm->_fdp_OwnerId));
if (AfpMacIdToSid(pFDParm->_fdp_OwnerId, &pSidOwner) != STATUS_SUCCESS) { Status = AFP_ERR_MISC; break; }
// Don't allow owner sid to be set as the NULL sid, or
// to what it is presently set to
if (!RtlEqualSid(pSecDesc->Owner, pSidOwner) && !RtlEqualSid(&AfpSidNull, pSidOwner)) { AfpDumpSid("AfpSetAfpPermissions: Setting Owner Sid to ", pSidOwner); pSecDesc->Owner = pSidOwner; SecInfo |= OWNER_SECURITY_INFORMATION; } }
if (Bitmap & DIR_BITMAP_GROUPID) { DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("AfpSetAfpPermissions: Setting Group to ID %lx\n", pFDParm->_fdp_GroupId));
if (AfpMacIdToSid(pFDParm->_fdp_GroupId, &pSidGroup) != STATUS_SUCCESS) { Status = AFP_ERR_MISC; break; }
// Don't allow group sid to be set as the NULL or None sid, or
// to what it is presently set to
if (!RtlEqualSid(pSecDesc->Group, pSidGroup) && !RtlEqualSid(&AfpSidNull, pSidGroup) && (!AfpServerIsStandalone || !RtlEqualSid(AfpSidNone, pSidGroup))) { AfpDumpSid("AfpSetAfpPermissions: Setting Group Sid to ", pSidGroup); pSecDesc->Group = pSidGroup; SecInfo |= GROUP_SECURITY_INFORMATION; }
}
// If either the owner or group or both is 'EveryOne' then coalesce the
// permissions
if (RtlEqualSid(pSecDesc->Owner, pSecDesc->Group)) { pFDParm->_fdp_OwnerRights |= pFDParm->_fdp_GroupRights; pFDParm->_fdp_GroupRights |= pFDParm->_fdp_OwnerRights; }
if (RtlEqualSid(pSecDesc->Owner, &AfpSidWorld)) { pFDParm->_fdp_WorldRights |= (pFDParm->_fdp_OwnerRights | DIR_ACCESS_OWNER); OwnerIsWorld = True; }
if (RtlEqualSid(pSecDesc->Group, &AfpSidWorld)) { pFDParm->_fdp_WorldRights |= pFDParm->_fdp_GroupRights; GroupIsWorld = True; }
// Construct the new Dacl. This consists of Aces for World, Owner and Group
// followed by Old Aces for everybody else, but with Aces for World, OldOwner
// and OldGroup stripped out. First determine space for the new Dacl and
// allocated space for the new Dacl. Lets be exteremely conservative. We
// have two aces each for owner/group/world.
//
// JH - Add aces for DomainAdmins too.
SizeNewDacl += (RtlLengthSid(pSecDesc->Owner) + sizeof(ACCESS_ALLOWED_ACE) + RtlLengthSid(pSecDesc->Group) + sizeof(ACCESS_ALLOWED_ACE) + AfpSizeSidAdmins + sizeof(ACCESS_ALLOWED_ACE) + sizeof(AfpSidSystem) + sizeof(ACCESS_ALLOWED_ACE) + sizeof(AfpSidWorld) + sizeof(ACCESS_ALLOWED_ACE)) * 3;
if ((pDaclNew = (PACL)ALLOC_ACCESS_MEM(SizeNewDacl)) == NULL) { Status = AFP_ERR_MISC; break; }
RtlCreateAcl(pDaclNew, SizeNewDacl, ACL_REVISION);
// we will be adding to this as we add aces, so set it to the min here
pDaclNew->AclSize = sizeof(ACL);
pAce = (PACCESS_ALLOWED_ACE)((PBYTE)pDaclNew + sizeof(ACL));
// At this time the Acl list is empty, i.e. no access for anybody
// Start off by copying the Deny Aces from the original Dacl list
// weeding out the Aces for World, old and new owner, new and old
// group, creator owner and creator group
if (pSecDesc->Dacl != NULL) { pAce = afpMoveAces(pSecDesc->Dacl, pAce, pSidOldOwner, pSecDesc->Owner, pSidOldGroup, pSecDesc->Group, True, pDaclNew);
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("AfpSetAfpPermissions: Added %d Deny Aces\n", pDaclNew->AceCount));
ASSERT(((PBYTE)pAce - (PBYTE)pDaclNew) < SizeNewDacl); }
// Now add Aces for System, World, Admins, Group & Owner - in that order
pAce = afpAddAceToAcl(pDaclNew, pAce, AFP_READ_ACCESS | AFP_WRITE_ACCESS | AFP_OWNER_ACCESS, &AfpSidSystem, fDir);
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("AfpSetAfpPermissions: Added Aces for System (%d)\n", pDaclNew->AceCount));
ASSERT(((PBYTE)pAce - (PBYTE)pDaclNew) < SizeNewDacl);
// Now add Ace for World
pAce = afpAddAceToAcl(pDaclNew, pAce, afpPermissions2NtMask(pFDParm->_fdp_WorldRights), &AfpSidWorld, fDir);
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("AfpSetAfpPermissions: Added Aces for World (%d)\n", pDaclNew->AceCount));
ASSERT(((PBYTE)pAce - (PBYTE)pDaclNew) < SizeNewDacl);
if (AfpSidAdmins != NULL) { // Add the ALLOWED_ACE and the corres. inherit Ace for
// 'Domain Admins' on PDC/BDC, or 'MACHINE\Administrators' for
// standalone server
pAce = afpAddAceToAcl(pDaclNew, pAce, (AFP_READ_ACCESS | AFP_WRITE_ACCESS | AFP_OWNER_ACCESS | FILE_DELETE_CHILD), AfpSidAdmins, fDir); }
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("AfpSetAfpPermissions: Added Aces for Admins (%d)\n", pDaclNew->AceCount));
// Now add Ace for Group
if (!GroupIsWorld && !RtlEqualSid(pSecDesc->Group, &AfpSidNull) && (!AfpServerIsStandalone || !RtlEqualSid(pSecDesc->Group, AfpSidNone))) { pAce = afpAddAceToAcl(pDaclNew, pAce, afpPermissions2NtMask(pFDParm->_fdp_GroupRights), pSecDesc->Group, fDir);
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("AfpSetAfpPermissions: Added Aces for World (%d)\n", pDaclNew->AceCount));
ASSERT(((PBYTE)pAce - (PBYTE)pDaclNew) < SizeNewDacl); }
if (!OwnerIsWorld && !RtlEqualSid(pSecDesc->Owner, &AfpSidNull)) { pFDParm->_fdp_OwnerRights |= DIR_ACCESS_OWNER; pAce = afpAddAceToAcl(pDaclNew, pAce, afpPermissions2NtMask(pFDParm->_fdp_OwnerRights), pSecDesc->Owner, fDir);
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("AfpSetAfpPermissions: Added Aces for Group (%d)\n", pDaclNew->AceCount));
ASSERT(((PBYTE)pAce - (PBYTE)pDaclNew) < SizeNewDacl); }
// Now add in the Grant Aces from the original Dacl list weeding out
// the Aces for World, old and new owner, new and old group, creator
// owner and creator group
if (pSecDesc->Dacl != NULL) { pAce = afpMoveAces(pSecDesc->Dacl, pAce, pSidOldOwner, pSecDesc->Owner, pSidOldGroup, pSecDesc->Group, False, pDaclNew);
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("AfpSetAfpPermissions: Added old Grant Aces (%d)\n", pDaclNew->AceCount));
ASSERT(((PBYTE)pAce - (PBYTE)pDaclNew) < SizeNewDacl); }
// Now set the new security descriptor
pSecDesc->Dacl = pDaclNew;
// We need to impersonate the FspToken while we do this
AfpImpersonateClient(NULL); Status = NtSetSecurityObject(DirHandle, SecInfo, pSecDesc); if (!NT_SUCCESS(Status)) Status = AfpIoConvertNTStatusToAfpStatus(Status); AfpRevertBack(); } while (False);
// Free the allocated buffers before we return
if (pSecDesc != NULL) AfpFreeMemory(pSecDesc); if (pDaclNew != NULL) AfpFreeMemory(pDaclNew);
#ifdef PROFILING
AfpGetPerfCounter(&TimeE); TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart; INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_SetPermsTime, TimeD, &AfpStatisticsLock); #endif
return Status; }
/*** afpPermissions2NtMask
* * Map Afp permissions to Nt access mask. FILE_DELETE_CHILD is added ONLY * when all the Afp bits are set. This is in line with the FileManager * which only sets this bit if "Full Control" is specified. Also under * NT security model, FILE_DELETE_CHILD overrides any child access control * as far as the ability to delete that entity goes. */ LOCAL ACCESS_MASK afpPermissions2NtMask( IN BYTE AfpPermissions ) { ACCESS_MASK NtAccess = 0;
PAGED_CODE( );
if (AfpPermissions & DIR_ACCESS_OWNER) NtAccess |= AFP_OWNER_ACCESS;
if ((AfpPermissions & DIR_ACCESS_ALL) == DIR_ACCESS_ALL) NtAccess |= AFP_READ_ACCESS | AFP_WRITE_ACCESS | FILE_DELETE_CHILD; else { if (AfpPermissions & (DIR_ACCESS_READ | DIR_ACCESS_SEARCH)) NtAccess |= AFP_READ_ACCESS;
if (AfpPermissions & DIR_ACCESS_WRITE) NtAccess |= AFP_WRITE_ACCESS; } return NtAccess; }
/*** afpAddAceToAcl
* * Build an Ace corres. to the Sid(s) and mask and add these to the Acl. It is * assumed that the Acl has space for the Aces. If the mask is 0 i.e. no access * we give AFP_MIN_ACCESS. This is so that the file/dir permissions can be * queried and a belted icon is generated instead of nothing. */ LOCAL PACCESS_ALLOWED_ACE afpAddAceToAcl( IN PACL pAcl, IN PACCESS_ALLOWED_ACE pAce, IN ACCESS_MASK Mask, IN PSID pSid, IN BOOLEAN fInherit ) { USHORT SidLen;
PAGED_CODE( );
SidLen = (USHORT)RtlLengthSid(pSid);
// Add a vanilla ace
pAcl->AceCount ++; pAce->Mask = Mask | SYNCHRONIZE | AFP_MIN_ACCESS; pAce->Header.AceFlags = 0; pAce->Header.AceType = ACCESS_ALLOWED_ACE_TYPE; pAce->Header.AceSize = (USHORT)(sizeof(ACE_HEADER) + sizeof(ACCESS_MASK) + SidLen);
RtlCopyMemory((PSID)(&pAce->SidStart), pSid, SidLen);
pAcl->AclSize += pAce->Header.AceSize;
AfpDumpSidnMask("afpAddAceToAcl ", pSid, pAce->Mask, ACCESS_ALLOWED_ACE_TYPE, pAce->Header.AceFlags);
// Now add an inherit ace
if (fInherit) { pAce = (PACCESS_ALLOWED_ACE)((PBYTE)pAce + pAce->Header.AceSize); pAcl->AceCount ++; pAce->Mask = Mask | SYNCHRONIZE | AFP_MIN_ACCESS; pAce->Header.AceFlags = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE | INHERIT_ONLY_ACE; pAce->Header.AceType = ACCESS_ALLOWED_ACE_TYPE; pAce->Header.AceSize = (USHORT)(sizeof(ACE_HEADER) + sizeof(ACCESS_MASK) + SidLen); RtlCopyMemory((PSID)(&pAce->SidStart), pSid, SidLen);
pAcl->AclSize += pAce->Header.AceSize;
AfpDumpSidnMask("afpAddAceToAcl (Inherit) ", pSid, pAce->Mask, ACCESS_ALLOWED_ACE_TYPE, pAce->Header.AceFlags); }
return ((PACCESS_ALLOWED_ACE)((PBYTE)pAce + pAce->Header.AceSize)); }
#if DBG
/*** AfpDumpSid
* */ VOID AfpDumpSid( IN PBYTE pString, IN PISID pSid ) { WCHAR Buffer[128]; UNICODE_STRING SidStr;
PAGED_CODE( );
AfpSetEmptyUnicodeString(&SidStr, sizeof(Buffer), Buffer); if ((AfpDebugComponent & DBG_COMP_SECURITY) && (DBG_LEVEL_INFO >= AfpDebugLevel)) { RtlConvertSidToUnicodeString(&SidStr, pSid, False);
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("%s %ws\n", pString, SidStr.Buffer)); } }
/*** AfpDumpSidnMask
* */ VOID AfpDumpSidnMask( IN PBYTE pString, IN PISID pSid, IN DWORD Mask, IN UCHAR Type, IN UCHAR Flags ) { WCHAR Buffer[128]; UNICODE_STRING SidStr;
PAGED_CODE( );
AfpSetEmptyUnicodeString(&SidStr, sizeof(Buffer), Buffer); if ((AfpDebugComponent & DBG_COMP_SECURITY) && (DBG_LEVEL_INFO >= AfpDebugLevel)) { RtlConvertSidToUnicodeString(&SidStr, pSid, False);
DBGPRINT(DBG_COMP_SECURITY, DBG_LEVEL_INFO, ("%s Sid %ws, Mask %lx, Type %x, Flags %x\n", pString, SidStr.Buffer, Mask, Type, Flags)); } }
#endif
|