最近两天在一家公司做兼职。刚去公司,老板就分配了一个任务。公司里有一个软件,在编辑自己产生的文件后,点击保存,文件就会自动上传到服务器进行备份,并立即删除本地的文件。给我的任务是:在Windows2000下,文件夹的属性中的安全选项卡是用户能够看到的。并且一般的用户都可以通过这个安全编辑对话框来更改文件夹的权限。问题是这样的:如果用户把文件夹的删除权限设置为拒绝,那么本地的文件只能上传到服务器,而本地的文件是删不掉的了。老板希望我想一个办法把文件夹的属性中的安全选项卡屏蔽掉。刚刚面试完,不敢说不会,只好满口答应,向老板保证,我一定会尽力去解决的。

在仔细研究了所面对的问题并上网查找了一些资料后,我觉得可以用一种变通的办法来达到同样的效果。那就是:当用户把删除权限设置为拒绝后,我就会检测到,然后再把它改过来。哈哈。不失为一种解决方案。在对Windows安全系统及编程接口完全不熟悉的情况下,我觉得能做到多少就努力做到多少吧。后来老板也很赞同我的想法。从他和我说话的表情和语气。^_^

下面就是我的代码。当然大量参考了Jeffrey Richter先生的宝书《Programming Server-Side Applications for Microsoft Windows 2000》。这是一本很好的书,可惜市面上已经买不到了。自古红颜多薄命,难道好书也像美人一样?幸好,我有英文电子版,可以解一时之急。讲解Win32安全编程机制的书也不多,这是其中的一本。


#include "stdafx.h" 
  
#include  
  <windows.h> 
  
#include  
  <Aclapi.h> 
  
#include  
  <AccCtrl.h> 
  
#include  
  <Sddl.h> 
  
#include  
  <iostream> 
  
 
  using namespace 
   std; 
#define PSIDFromPACE(pACE) ((PSID)(&((pACE)->SidStart))) 
  
typedef union _ACE_UNION 
  ... 
  { 
    ACE_HEADER         aceHeader; 
    ACCESS_ALLOWED_ACE aceAllowed; 
    ACCESS_DENIED_ACE  aceDenied; 
    SYSTEM_AUDIT_ACE   aceAudit; 
}* 
  PACE_UNION; 
void 
   DumpACL(PACL pACL); 
DWORD DelAceAndSetACL(PACL pACL, PSID psid, TCHAR * 
  pszBuf); 
void GrantDeleteRight(TCHAR *pszBuf, PSID psid, PACL pOldDACL, PACL * 
  pNewDACL); 
ULONG CalculateACLSize(PACL pACLOld, PSID* ppSidArray, int nNumSids, PACE_UNION* ppACEs, int 
   nNumACEs); 
PACE_UNION AllocateACE(ULONG bACEType, ULONG bACEFlags, ULONG lAccessMask, PSID pSID); 
ULONG GetACEInsertionIndex(PACL pDACL, PACE_UNION pACENew); 
BOOL CopyACL( PACL pACLDestination, PACL pACLSource ); 
int 
   FindACEInACL( PACL pACL, PACE_UNION pACE ); 
BOOL IsEqualACE( PACE_UNION pACE1, PACE_UNION pACE2 ); 
LPVOID AllocateTokenInformation(HANDLE hToken, TOKEN_INFORMATION_CLASS tokenClass); 
struct... 
  { 
    BYTE  lACEType; 
    PTSTR pszTypeName; 
}aceTypes[6] = ... 
  { 
    ...{ACCESS_ALLOWED_ACE_TYPE, TEXT("ACCESS_ALLOWED_ACE_TYPE")}, 
    ...{ACCESS_DENIED_ACE_TYPE, TEXT("ACCESS_DENIED_ACE_TYPE")}, 
    ...{SYSTEM_AUDIT_ACE_TYPE, TEXT("SYSTEM_AUDIT_ACE_TYPE")}, 
    ...{ACCESS_ALLOWED_OBJECT_ACE_TYPE, TEXT("ACCESS_ALLOWED_OBJECT_ACE_TYPE")}, 
    ...{ACCESS_DENIED_OBJECT_ACE_TYPE, TEXT("ACCESS_DENIED_OBJECT_ACE_TYPE")}, 
    ...{SYSTEM_AUDIT_OBJECT_ACE_TYPE, TEXT("SYSTEM_AUDIT_OBJECT_ACE_TYPE")}} 
  ; 
struct... 
  { 
    ULONG lACEFlag; 
    PTSTR pszFlagName; 
}aceFlags[7] = ... 
  { 
    ...{INHERITED_ACE, TEXT("INHERITED_ACE")}, 
    ...{CONTAINER_INHERIT_ACE, TEXT("CONTAINER_INHERIT_ACE")}, 
    ...{OBJECT_INHERIT_ACE, TEXT("OBJECT_INHERIT_ACE")}, 
    ...{INHERIT_ONLY_ACE, TEXT("INHERIT_ONLY_ACE")}, 
    ...{NO_PROPAGATE_INHERIT_ACE, TEXT("NO_PROPAGATE_INHERIT_ACE")}, 
    ...{FAILED_ACCESS_ACE_FLAG, TEXT("FAILED_ACCESS_ACE_FLAG")}, 
    ...{SUCCESSFUL_ACCESS_ACE_FLAG, TEXT("SUCCESSFUL_ACCESS_ACE_FLAG")}} 
  ; 
int _tmain(int argc, _TCHAR* 
   argv[]) 
... 
  { 
    SECURITY_ATTRIBUTES sa; 
    SECURITY_DESCRIPTOR sd; 
    PSID psid = NULL; 
    // 获得当前用户的SID和默认的DACL。
    HANDLE hToken; 
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))...{ 
        // Error
        _tprintf(_T("OpenProcessToken Failed.")); 
        return 1; 
    }
    TOKEN_USER* ptUser = (TOKEN_USER*)AllocateTokenInformation(hToken, TokenUser); 
    if (ptUser != NULL)...{ 
        psid = ptUser->User.Sid; 
    }
    TOKEN_DEFAULT_DACL* ptDACL = (TOKEN_DEFAULT_DACL*)AllocateTokenInformation(hToken, TokenDefaultDacl);  
    if (ptDACL != NULL)...{ 
        DumpACL(ptDACL->DefaultDacl); 
    }
    // 初始化sd。使用当前用户的默认DACL。
    InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); 
    SetSecurityDescriptorDacl(&sd, TRUE, ptDACL->DefaultDacl, FALSE); 
    // 初始化sa。
    sa.nLength= sizeof(SECURITY_ATTRIBUTES); 
    sa.bInheritHandle = FALSE; 
    sa.lpSecurityDescriptor = &sd; 
    // 创建一个指定文件夹名和具有上面安全属性的文件夹。
    TCHAR szPathBuf[MAX_PATH]; 
    _tprintf(_T("Input the directory: ")); 
    _tscanf(_T("%s"), szPathBuf); 
    CreateDirectory(szPathBuf, &sa); 
    DWORD dwRes = 0; 
    PACL pOldDACL = NULL, pNewDACL = NULL; 
    PSECURITY_DESCRIPTOR pSD = NULL; 
    // Get a pointer to the existing DACL.
    dwRes = GetNamedSecurityInfo(szPathBuf, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pOldDACL, NULL, &pSD); 
    if (ERROR_SUCCESS != dwRes) ...{ 
        _tprintf(_T("GetNamedSecurityInfo Error %u "), dwRes); 
        return 1; 
    }
    DumpACL(pOldDACL); 
    if (DelAceAndSetACL(pOldDACL, psid, szPathBuf) == ERROR_SUCCESS) ...{ 
        GrantDeleteRight(szPathBuf, psid, pOldDACL, &pNewDACL); 
        DumpACL(pNewDACL); 
    }
    else ...{ 
        GrantDeleteRight(szPathBuf, psid, pOldDACL, &pNewDACL); 
        DumpACL(pNewDACL); 
    }
    LocalFree(pSD); 
    return 0; 
} 
  
DWORD DelAceAndSetACL(PACL pACL, PSID psid, TCHAR  
  * 
  pszBuf) 
... 
  { 
    DWORD dwRes = -1; 
    __try ...{ 
        ACL_SIZE_INFORMATION aclSize = ...{0}; 
        if (!GetAclInformation(pACL, &aclSize, sizeof(aclSize), AclSizeInformation)) 
            __leave; 
        for (ULONG lIndex = 0; lIndex < aclSize.AceCount; lIndex++) ...{ 
            ACCESS_ALLOWED_ACE* pACE; 
            if (!GetAce(pACL, lIndex, (PVOID*)&pACE)) 
                __leave; 
            bool flag16 = false, flag26 = false; 
            int cnt = 0; 
            ULONG lIndex2 = (ULONG)1<<31; 
            while (lIndex2) ...{ 
                ++cnt; 
                if (cnt == 16 && ((pACE->Mask & lIndex2) != 0)) ...{ 
                    flag16 = true; 
                }
                if (cnt == 26 && ((pACE->Mask & lIndex2) != 0)) ...{ 
                    flag26 = true; 
                }
                lIndex2 >>= 1; 
                if (flag16 || flag26) 
                    break; 
            }
            if ((flag16 || flag26) && pACE->Header.AceType == ACCESS_DENIED_ACE_TYPE) ...{ 
                ::DeleteAce(pACL, lIndex); 
                --lIndex; 
                --aclSize.AceCount; 
                 
                dwRes = SetNamedSecurityInfo(pszBuf, SE_FILE_OBJECT, 
                    DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, 
                    psid, NULL, pACL, NULL); 
                if (ERROR_SUCCESS != dwRes) 
                    printf("SetNamedSecurityInfo Error %u ", dwRes); 
                flag16 = flag26 = false; 
            }
        }
    }__finally ...{}
    return dwRes; 
} 
  
 
  void GrantDeleteRight(TCHAR *pszBuf, PSID psid, PACL pOldDACL, PACL * 
  pNewDACL) 
... 
  { 
    PSID psidArray[1]; 
    psidArray[0] = psid; 
    // Get the size of the new ACL
    ULONG lACLSize = CalculateACLSize(pOldDACL, psidArray, 1, NULL, 1); 
    if (lACLSize == 0)...{ 
        // Error
    }
    // Allocate memory for the ACL
    *pNewDACL = (PACL)HeapAlloc(GetProcessHeap(), 0, lACLSize); 
    if (pNewDACL == NULL) ...{ 
        // Error
    }
    // Initialize the ACL
    if (!InitializeAcl(*pNewDACL, lACLSize, ACL_REVISION))...{ 
        // Error
    }
    PACE_UNION pNewACE = AllocateACE( 
        ACCESS_ALLOWED_ACE_TYPE, 
        NULL, 
        GENERIC_ALL | FILE_DELETE_CHILD | DELETE, 
        psid 
        ); 
    // 如果ACL中有相同的ACE则返回。
    if (FindACEInACL(*pNewDACL, pNewACE) != -1) 
        return; 
    CopyACL(*pNewDACL, pOldDACL); 
    // Get location for new ACE
    ULONG lIndex = GetACEInsertionIndex(*pNewDACL, pNewACE); 
    // Add the new ACE
    if (!AddAce(*pNewDACL, ACL_REVISION, lIndex, pNewACE, pNewACE->aceHeader.AceSize)) 
        printf("Error!"); 
    DWORD dwRes = SetNamedSecurityInfo(pszBuf, SE_FILE_OBJECT, 
        DACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, 
        psid, NULL, *pNewDACL, NULL); 
    if (ERROR_SUCCESS != dwRes) 
        printf("SetNamedSecurityInfo Error %u ", dwRes); 
} 
  
 
  void DumpACL(PACL pACL)... 
  { 
    __try...{ 
        if (pACL == NULL)...{ 
            _tprintf(TEXT("NULL DACL ")); 
            __leave; 
        }
        ACL_SIZE_INFORMATION aclSize = ...{0}; 
        if (!GetAclInformation(pACL, &aclSize, sizeof(aclSize), AclSizeInformation)) 
            __leave; 
        _tprintf(TEXT("ACL ACE count: %d "), aclSize.AceCount); 
        for (ULONG lIndex = 0;lIndex < aclSize.AceCount;lIndex++)...{ 
            ACCESS_ALLOWED_ACE* pACE; 
            if (!GetAce(pACL, lIndex, (PVOID*)&pACE)) 
                __leave; 
            _tprintf(TEXT(" ACE #%d "), lIndex); 
            ULONG lIndex2 = 6; 
            PTSTR pszString = TEXT("Unknown ACE Type"); 
            while (lIndex2--)...{ 
                if(pACE->Header.AceType == aceTypes[lIndex2].lACEType) ...{ 
                    pszString = aceTypes[lIndex2].pszTypeName; 
                }
            }
            _tprintf(TEXT("  ACE Type =    %s "), pszString); 
            _tprintf(TEXT("  ACE Flags =  ")); 
            lIndex2 = 7; 
            while (lIndex2--) ...{ 
                if ((pACE->Header.AceFlags & aceFlags[lIndex2].lACEFlag) 
                    != 0) 
                    _tprintf(TEXT("   %s "),  
                    aceFlags[lIndex2].pszFlagName); 
            }
            _tprintf(TEXT("  ACE Mask (32->0) =    ")); 
            lIndex2 = (ULONG)1<<31; 
            while (lIndex2) ...{ 
                _tprintf(((pACE->Mask & lIndex2) != 0)?TEXT("1"):TEXT("0")); 
                lIndex2>>=1; 
            }
            TCHAR szName[1024]; 
            TCHAR szDom[1024]; 
            PSID pSID = PSIDFromPACE(pACE); 
            SID_NAME_USE sidUse;          
            ULONG lLen1 = 1024, lLen2 = 1024; 
            if (!LookupAccountSid(NULL, pSID, szName, &lLen1, szDom, &lLen2, &sidUse)) 
                lstrcpy(szName, TEXT("Unknown")); 
            PTSTR pszSID; 
            if (!ConvertSidToStringSid(pSID, &pszSID)) 
                __leave; 
            _tprintf(TEXT("   ACE SID =    %s (%s) "), pszSID, szName); 
            LocalFree(pszSID); 
        }
    }__finally...{}
} 
  
LPVOID AllocateTokenInformation(HANDLE hToken, TOKEN_INFORMATION_CLASS tokenClass) 
 
  ... 
  { 
    PVOID    pvBuffer = NULL; 
    __try...{ 
        BOOL fSuccess; 
        // Initial buffer size
        ULONG    lSize = 0 ; 
        do
        ...{ 
            // Do we have a size yet?
            if (lSize != 0) 
            ...{ 
                // Do we already have a buffer?
                if (pvBuffer != NULL) 
                    LocalFree(pvBuffer);// Then free it 
                // Allocate a new buffer 
                pvBuffer = LocalAlloc(LPTR, lSize) ; 
                if(pvBuffer == NULL) 
                    __leave; 
            }
            // Try again
            fSuccess = GetTokenInformation( hToken, tokenClass,  
                pvBuffer, lSize, &lSize ) ; 
            // Still not enough buffer?
        }while( !fSuccess && (GetLastError() ==
            ERROR_INSUFFICIENT_BUFFER)) ; 
        // If we failed for some other reason, back out
        if(!fSuccess) 
        ...{ 
            if(pvBuffer) 
                LocalFree(pvBuffer) ; 
            pvBuffer = NULL; 
        }
    }__finally...{}
    // Return locally allocated buffer
    return (pvBuffer) ; 
} 
  
ULONG CalculateACLSize(PACL pACLOld, PSID 
  * ppSidArray, int nNumSids, PACE_UNION* ppACEs, int 
   nNumACEs) 
... 
  { 
    ULONG lACLSize = 0; 
    try...{ 
        // If we are including an existing ACL, then find its size
        if (pACLOld != NULL)...{ 
            ACL_SIZE_INFORMATION aclSize; 
            if(!GetAclInformation(pACLOld, &aclSize, sizeof(aclSize), AclSizeInformation))...{ 
                goto leave; 
            }
            lACLSize = aclSize.AclBytesInUse; 
        }
        if (ppSidArray != NULL)...{ 
            // Step through each SID
            while (nNumSids--)...{ 
                // If a SID isn't valid, then we bail
                if (!IsValidSid(ppSidArray[nNumSids]))...{ 
                    lACLSize = 0; 
                    goto leave; 
                }
                // Get the SID's length
                lACLSize += GetLengthSid(ppSidArray[nNumSids]); 
                // Add the ACE structure size, minus the  
                // size of the SidStart member
                lACLSize += sizeof(ACCESS_ALLOWED_ACE) - sizeof(((ACCESS_ALLOWED_ACE*)0)->SidStart); 
            }
        }
        if (ppACEs != NULL)...{ 
            // Step through each ACE
            while (nNumACEs--)...{ 
                // Get the SIDs length
                lACLSize += ppACEs[nNumACEs]->aceHeader.AceSize; 
            }
        }
        // Add in the ACL structure itself
        lACLSize += sizeof(ACL); 
leave:; 
    }catch(...)...{ 
        // An exception means we fail the function
        lACLSize = 0; 
    }
    return (lACLSize); 
} 
  
BOOL CopyACL( PACL pACLDestination, PACL pACLSource ) 
 
  ... 
  { 
    BOOL fReturn = FALSE; 
    try ...{ 
        // Get the number of ACEs in the source ACL
        ACL_SIZE_INFORMATION aclSize; 
        if (!GetAclInformation(pACLSource, &aclSize, sizeof(aclSize), AclSizeInformation))...{ 
            goto leave; 
        }
        // Use GetAce and AddAce to copy the ACEs
        for(ULONG lIndex=0;lIndex < aclSize.AceCount;lIndex++)...{ 
            ACE_HEADER* pACE; 
            if(!GetAce(pACLSource, lIndex, (PVOID*)&pACE)) 
                goto leave; 
            if(!AddAce(pACLDestination, ACL_REVISION, MAXDWORD,  
                (PVOID*)pACE, pACE->AceSize)) 
                goto leave; 
        }
        fReturn = TRUE; 
leave:; 
    }catch(...)...{ 
    }
    return (fReturn); 
} 
  
PACE_UNION AllocateACE(ULONG bACEType, ULONG bACEFlags, ULONG lAccessMask, PSID pSID) 
 
  ... 
  { 
    PACE_UNION pReturnACE = NULL; 
    PBYTE pbBuffer = NULL; 
    try...{ 
        // Get the offset of the SID in the ACE
        ULONG lSIDOffset = (ULONG)(&((ACCESS_ALLOWED_ACE*)0)->SidStart); 
        // Get the size of the ACE without the SID
        ULONG lACEStructSize = sizeof(ACCESS_ALLOWED_ACE) - sizeof(((ACCESS_ALLOWED_ACE*)0)->SidStart); 
        // Get the length of the SID
        ULONG lSIDSize = GetLengthSid(pSID); 
        // Allocate a buffer for the ACE
        pbBuffer = (PBYTE)LocalAlloc(LPTR, lACEStructSize + lSIDSize); 
        if (pbBuffer == NULL) 
            goto leave; 
        // Copy the SID into the ACE
        if(!CopySid(lSIDSize, (PSID)(pbBuffer+lSIDOffset), pSID))...{ 
            goto leave; 
        }
        pReturnACE = (PACE_UNION) pbBuffer; 
        pReturnACE->aceHeader.AceSize = (USHORT)(lACEStructSize + lSIDSize); 
        pReturnACE->aceHeader.AceType = (BYTE)bACEType; 
        pReturnACE->aceHeader.AceFlags = (BYTE)bACEFlags; 
        pReturnACE->aceAllowed.Mask = lAccessMask; 
leave:; 
    }catch(...)...{}
    // Free the buffer in an error case
    if (pbBuffer != (PBYTE)pReturnACE)...{ 
        LocalFree(pbBuffer); 
    }
    return (pReturnACE); 
} 
  
ULONG GetACEInsertionIndex(PACL pDACL, PACE_UNION pACENew) 
 
  ... 
  { 
    ULONG lIndex = (ULONG) -1; 
    try...{ 
        // ACE types by ACL order
        ULONG lFilterType[] = ...{ ACCESS_DENIED_ACE_TYPE, 
            ACCESS_DENIED_OBJECT_ACE_TYPE,  
            ACCESS_ALLOWED_ACE_TYPE,  
            ACCESS_ALLOWED_OBJECT_ACE_TYPE}; 
        // Determine which group the new ACE should belong to
        ULONG lNewAceGroup; 
        for(lNewAceGroup = 0; lNewAceGroup<4 ; lNewAceGroup++)...{ 
            if(pACENew->aceHeader.AceType == lFilterType[lNewAceGroup]) 
                break; 
        }
        // If group == 4, the ACE type is no good
        if(lNewAceGroup==4) 
            goto leave; 
        // If new ACE is an inherited ACE, then it goes after other ACEs
        if((pACENew->aceHeader.AceFlags & INHERITED_ACE) != 0) 
            lNewAceGroup+=4; 
        // Get ACE count
        ACL_SIZE_INFORMATION aclSize; 
        if (!GetAclInformation(pDACL, &aclSize, sizeof(aclSize), AclSizeInformation))...{ 
            goto leave; 
        }
        // Iterate through ACEs
        lIndex = 0; 
        for(lIndex = 0;lIndex < aclSize.AceCount;lIndex++)...{ 
            ACE_HEADER* pACE; 
            if(!GetAce(pDACL, lIndex, (PVOID*)&pACE)) 
                goto leave; 
            // Get the group of the ACL ACE
            ULONG lAceGroup; 
            for(lAceGroup = 0; lAceGroup<4 ; lAceGroup++)...{ 
                if(pACE->AceType == lFilterType[lAceGroup]) 
                    break; 
            }
            // Test for bad ACE
            if(lAceGroup==4)...{ 
                lIndex = (ULONG) -1; 
                goto leave; 
            }
            // Inherited adjustment
            if((pACE->AceFlags & INHERITED_ACE) != 0) 
                lAceGroup+=4; 
            // If this is the same group, then insertion point found
            if(lAceGroup>=lNewAceGroup) 
                break; 
        }
leave: ; 
    }catch(...)...{ 
    }
    return (lIndex); 
} 
  
BOOL IsEqualACE( PACE_UNION pACE1, PACE_UNION pACE2 ) 
 
  ... 
  { 
    BOOL fReturn = FALSE; 
    try ...{ 
        if(pACE1->aceHeader.AceType != pACE2->aceHeader.AceType) 
            goto leave; 
        // Get the offset of the SID in the ACE
        ULONG lSIDOffset = (ULONG)((&((ACCESS_ALLOWED_ACE*)0)->SidStart)); 
        // Get the size of the ACE without the SID
        ULONG lACEStructSize = sizeof(ACCESS_ALLOWED_ACE) - sizeof(((ACCESS_ALLOWED_ACE*)0)->SidStart); 
        PBYTE pbACE1 = (PBYTE)pACE1; 
        PBYTE pbACE2 = (PBYTE)pACE2; 
        fReturn = TRUE; 
        while(lACEStructSize--) 
            fReturn = (fReturn && ((pbACE1[lACEStructSize] == pbACE2[lACEStructSize]))); 
        // Check SIDs
        fReturn = fReturn && EqualSid((PSID)(pbACE1+lSIDOffset), 
            (PSID)(pbACE2+lSIDOffset));       
leave:; 
    }catch(...)...{ 
    }
    return (fReturn); 
} 
  
 
  int 
   FindACEInACL(PACL pACL, PACE_UNION pACE) 
... 
  { 
    int nACEIndex = -1; 
    try...{ 
        ACL_SIZE_INFORMATION aclSize; 
        if (!GetAclInformation(pACL, &aclSize, sizeof(aclSize), AclSizeInformation))...{ 
                goto leave; 
        }
        while (aclSize.AceCount--)...{ 
            PACE_UNION pACETemp; 
            if(!GetAce(pACL, aclSize.AceCount, (PVOID *)&pACETemp)) 
                goto leave; 
            if(IsEqualACE(pACETemp, pACE))...{ 
                nACEIndex = (int)aclSize.AceCount; 
                break; 
            }
        }
leave:; 
    }catch(...)...{ 
    }
    return (nACEIndex); 
}