编写安全的代码(ACL使用方法)
?
http://www.lihuasoft.net/article/show.php?id=755
?
Windows NT/2K/XP版本的操作系統(tǒng)都支持NTFS格式的文件系統(tǒng),這是一個(gè)有安全性質(zhì)的文件系統(tǒng),你可以通過Windows的資源管理器來設(shè)置對(duì)每個(gè)目錄和文件的用戶訪問權(quán)限。這里我就不對(duì)NTFS的安全性進(jìn)行講述了,我默認(rèn)你對(duì)NTFS的文件目錄的安全設(shè)置有了一定的了解。在這里,我將向你介紹使用Windows的API函數(shù)來操縱NTFS的文件權(quán)限。
一、?????? 理論和術(shù)語
在Windows NT/2K?XP下的對(duì)象,不一定是文件系統(tǒng),還有其它的一些對(duì)象,如:進(jìn)程、命名管道、打印機(jī)、網(wǎng)絡(luò)共享、或是注冊(cè)表等等,都可以設(shè)置用戶訪問權(quán)限。在Windows系統(tǒng)中,其是用一個(gè)安全描述符(Security Descriptors)的結(jié)構(gòu)來保存其權(quán)限的設(shè)置信息,簡(jiǎn)稱為SD,其在Windows SDK中的結(jié)構(gòu)名是“SECURITY_DESCRIPTOR”,這是包括了安全設(shè)置信息的結(jié)構(gòu)體。一個(gè)安全描述符包含以下信息:
一個(gè)安全標(biāo)識(shí)符(Security identifiers),其標(biāo)識(shí)了該信息是哪個(gè)對(duì)象的,也就是用于記錄安全對(duì)象的ID。簡(jiǎn)稱為:SID。
一個(gè)DACL(Discretionary Access Control List),其指出了允許和拒絕某用戶或用戶組的存取控制列表。 當(dāng)一個(gè)進(jìn)程需要訪問安全對(duì)象,系統(tǒng)就會(huì)檢查DACL來決定進(jìn)程的訪問權(quán)。如果一個(gè)對(duì)象沒有DACL,那么就是說這個(gè)對(duì)象是任何人都可以擁有完全的訪問權(quán)限。
一個(gè)SACL(System Access Control List),其指出了在該對(duì)象上的一組存取方式(如,讀、寫、運(yùn)行等)的存取控制權(quán)限細(xì)節(jié)的列表。
還有其自身的一些控制位。
DACL和SACL構(gòu)成了整個(gè)存取控制列表Access Control List,簡(jiǎn)稱ACL,ACL中的每一項(xiàng),我們叫做ACE(Access Control Entry),ACL中的每一個(gè)ACE。
我們的程序不用直接維護(hù)SD這個(gè)結(jié)構(gòu),這個(gè)結(jié)構(gòu)由系統(tǒng)維護(hù)。我們只用使用Windows 提供的相關(guān)的API函數(shù)來取得并設(shè)置SD中的信息就行了。不過這些API函數(shù)只有Windows NT/2K/XP才支持。
安全對(duì)象Securable Object是擁有SD的Windows的對(duì)象。所有的被命名的Windows的對(duì)象都是安全對(duì)象。一些沒有命名的對(duì)象是安全對(duì)象,如:進(jìn)程和線程,也有安全描述符SD。在對(duì)大多數(shù)的創(chuàng)建安全對(duì)象的操作中都需要你傳遞一個(gè)SD的參數(shù),如:CreateFile和CreateProcess函數(shù)。另外,Windows還提供了一系列有關(guān)安全對(duì)象的安全信息的存取函數(shù),以供你取得對(duì)象上的安全設(shè)置,或修改對(duì)象上的安全設(shè)置。如:GetNamedSecurityInfo, SetNamedSecurityInfo,GetSecurityInfo, SetSecurityInfo。
下圖說明了,安全對(duì)象和DACL以及訪問者之間的聯(lián)系(來源于MSDN)。注意,DACL表中的每個(gè)ACE的順序是有意義的,如果前面的Allow(或denied)ACE通過了,那么,系統(tǒng)就不會(huì)檢查后面的ACE了。
系統(tǒng)會(huì)按照順序依次檢查所有的ACE規(guī)則,如下面的條件滿足,則退出:
1、?如果一個(gè)Access-Denied的ACE明顯地拒絕了請(qǐng)求者。
2、?如果某Access-Allowed的ACE明顯地同意了請(qǐng)求者。
3、?全部的ACE都檢查完了,但是沒有一條ACE明顯地允許或是拒絕請(qǐng)求者,那么系統(tǒng)將使用默認(rèn)值,拒絕請(qǐng)求者的訪問。
更多的理論和描述,請(qǐng)參看MSDN。
二、?????? 實(shí)踐與例程
1、? 例程一:創(chuàng)建一個(gè)有權(quán)限設(shè)置的目錄
#include <windows.h>
void main(void)
{
??SECURITY_ATTRIBUTES sa;? //和文件有關(guān)的安全結(jié)構(gòu)
??SECURITY_DESCRIPTOR sd;? //聲明一個(gè)SD
??BYTE aclBuffer[1024];
??PACL pacl=(PACL)&aclBuffer;?//聲明一個(gè)ACL,長度是1024
??BYTE sidBuffer[100];
??PSID psid=(PSID) &sidBuffer;? //聲明一個(gè)SID,長度是100
??DWORD sidBufferSize = 100;
??char domainBuffer[80];
??DWORD domainBufferSize = 80;
??SID_NAME_USE snu;
??HANDLE file;
??//初始化一個(gè)SD
??InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
??//初始化一個(gè)ACL
??InitializeAcl(pacl, 1024, ACL_REVISION);
??//查找一個(gè)用戶hchen,并取該用戶的SID
??LookupAccountName(0, "hchen", psid,
??????&sidBufferSize, domainBuffer,
??????&domainBufferSize, &snu);
??//設(shè)置該用戶的Access-Allowed的ACE,其權(quán)限為“所有權(quán)限”
AddAccessAllowedAce(pacl, ACL_REVISION, GENERIC_ALL, psid);
//把ACL設(shè)置到SD中
??SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE);
??
??//把SD放到文件安全結(jié)構(gòu)SA中
??sa.nLength = sizeof(SECURITY_ATTRIBUTES);
??sa.bInheritHandle = FALSE;
??sa.lpSecurityDescriptor = &sd;
??
??//創(chuàng)建文件
??file = CreateFile("c:\\testfile",
????0, 0, &sa, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
??CloseHandle(file);
}
這個(gè)例子我是從網(wǎng)上找來的,改了改。其中使用到的關(guān)鍵的API函數(shù),我都把其加粗了。從程序中我們可以看到,我們先初始化了一個(gè)SD和一個(gè)ACL,然后調(diào)用LookupAccountName取得用戶的SID,然后通過這個(gè)SID,對(duì)ACL中加入一個(gè)有允許訪問權(quán)限的ACE,然后再把整個(gè)ACL設(shè)置到SD中。最后,組織文件安全描述的SA結(jié)構(gòu),并調(diào)用CreateFile創(chuàng)建文件。如果你的操作系統(tǒng)是NTFS,那么,你可以看到你創(chuàng)建出來的文件的安全屬性的樣子:
這個(gè)程序旨在說明如何生成一個(gè)新的SD和ACL的用法,其有四個(gè)地方的不足和不清:
1、?對(duì)于ACL和SID的聲明采用了硬編碼的方式指定其長度。
2、?對(duì)于API函數(shù),沒有出錯(cuò)處理。
3、?沒有說明如何修改已有文件或目錄的安全設(shè)置。
4、?沒有說明安全設(shè)置的繼承性。
對(duì)于這些我將在下個(gè)例程中講述。
2、? 例程二、為目錄增加一個(gè)安全設(shè)置項(xiàng)
在我把這個(gè)例程序例出來以前,請(qǐng)?jiān)试S我多說一下。
1、? 對(duì)于文件、目錄、命令管道,我們不一定要使用GetNamedSecurityInfo和SetNamedSecurityInfo函數(shù),我們可以使用其專用函數(shù)GetFileSecurity和SetFileSecurity函數(shù)來取得或設(shè)置文件對(duì)象的SD,以設(shè)置其訪問權(quán)限。需要使用這兩個(gè)函數(shù)并不容易,正如前面我們所說的,我們還需要處理SD參數(shù),要處理SD,就需要處理DACL和ACE,以及用戶的相關(guān)SID,于是,一系統(tǒng)列的函數(shù)就被這兩個(gè)函數(shù)帶出來了。
2、? 對(duì)于上一個(gè)例子中的使用硬編碼指定SID的處理方法是。調(diào)用LookupAccountName函數(shù)時(shí),先把SID,Domain名的參數(shù)傳為空NULL,于是LookupAccountName會(huì)返回用戶的SID的長度和Domain名的長度,于是你可以根據(jù)這個(gè)長度分配內(nèi)存,然后再次調(diào)用LookupAccountName函數(shù)。于是就可以達(dá)到到態(tài)分配內(nèi)存的效果。對(duì)于ACL也一樣。
3、? 對(duì)于給文件的ACL中增加一個(gè)ACE條目,一般的做法是先取出文件上的ACL,逐條取出ACE,和現(xiàn)需要增加的ACE比較,如果有沖突,則刪除已有的ACE,把新加的ACE添置到最后。這里的最后,應(yīng)該是非繼承而來的ACE的最后。關(guān)于ACL繼承,NTFS中,你可以設(shè)置文件和目錄是否繼承于其父目錄的設(shè)置。在程序中同樣可以設(shè)置。
還是請(qǐng)看例程,這個(gè)程序比較長,來源于MSDN,我做了一點(diǎn)點(diǎn)修改,并把自己的理解加在注釋中,所以,請(qǐng)注意代碼中的注釋:
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
//使用Windows的HeapAlloc函數(shù)進(jìn)行動(dòng)態(tài)內(nèi)存分配
#define myheapalloc(x) (HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, x))
#define myheapfree(x)?(HeapFree(GetProcessHeap(), 0, x))
typedef BOOL (WINAPI *SetSecurityDescriptorControlFnPtr)(
? IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
? IN SECURITY_DESCRIPTOR_CONTROL ControlBitsOfInterest,
? IN SECURITY_DESCRIPTOR_CONTROL ControlBitsToSet);
typedef BOOL (WINAPI *AddAccessAllowedAceExFnPtr)(
?PACL pAcl,
?DWORD dwAceRevision,
?DWORD AceFlags,
?DWORD AccessMask,
?PSID pSid
);
BOOL AddAccessRights(TCHAR *lpszFileName, TCHAR *lpszAccountName,
???DWORD dwAccessMask) {
? // 聲明SID變量
? SID_NAME_USE? snuType;
? // 聲明和LookupAccountName相關(guān)的變量(注意,全為0,要在程序中動(dòng)態(tài)分配)
? TCHAR *????szDomain??? = NULL;
? DWORD?????cbDomain??? = 0;
? LPVOID???? pUserSID??? = NULL;
? DWORD?????cbUserSID???= 0;
? // 和文件相關(guān)的安全描述符 SD 的變量
? PSECURITY_DESCRIPTOR pFileSD?= NULL;?? // 結(jié)構(gòu)變量
? DWORD?????cbFileSD??? = 0;????// SD的size
? // 一個(gè)新的SD的變量,用于構(gòu)造新的ACL(把已有的ACL和需要新加的ACL整合起來)
? SECURITY_DESCRIPTOR?newSD;
? // 和ACL 相關(guān)的變量
? PACL????? pACL????? = NULL;
? BOOL????? fDaclPresent;
? BOOL????? fDaclDefaulted;
? ACL_SIZE_INFORMATION AclInfo;
? // 一個(gè)新的 ACL 變量
? PACL????? pNewACL????= NULL;?//結(jié)構(gòu)指針變量
? DWORD?????cbNewACL??? = 0;?? //ACL的size
? // 一個(gè)臨時(shí)使用的 ACE 變量
? LPVOID???? pTempAce??? = NULL;
? UINT????? CurrentAceIndex = 0;?//ACE在ACL中的位置
? UINT????? newAceIndex = 0;?//新添的ACE在ACL中的位置
? //API函數(shù)的返回值,假設(shè)所有的函數(shù)都返回失敗。
? BOOL????? fResult;
? BOOL????? fAPISuccess;
? SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION;
? // 下面的兩個(gè)函數(shù)是新的API函數(shù),僅在Windows 2000以上版本的操作系統(tǒng)支持。
? // 在此將從Advapi32.dll文件中動(dòng)態(tài)載入。如果你使用VC++ 6.0編譯程序,而且你想
? // 使用這兩個(gè)函數(shù)的靜態(tài)鏈接。則請(qǐng)為你的編譯加上:/D_WIN32_WINNT=0x0500
? // 的編譯參數(shù)。并且確保你的SDK的頭文件和lib文件是最新的。
? SetSecurityDescriptorControlFnPtr _SetSecurityDescriptorControl = NULL;
? AddAccessAllowedAceExFnPtr _AddAccessAllowedAceEx = NULL;
? __try {
???//
???// STEP 1: 通過用戶名取得SID
???//?? 在這一步中LookupAccountName函數(shù)被調(diào)用了兩次,第一次是取出所需要
???// 的內(nèi)存的大小,然后,進(jìn)行內(nèi)存分配。第二次調(diào)用才是取得了用戶的帳戶信息。
???// LookupAccountName同樣可以取得域用戶或是用戶組的信息。(請(qǐng)參看MSDN)
???//
???fAPISuccess = LookupAccountName(NULL, lpszAccountName,
??????pUserSID, &cbUserSID, szDomain, &cbDomain, &snuType);
???// 以上調(diào)用API會(huì)失敗,失敗原因是內(nèi)存不足。并把所需要的內(nèi)存大小傳出。
???// 下面是處理非內(nèi)存不足的錯(cuò)誤。
???if (fAPISuccess)
???? __leave;
???else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
???? _tprintf(TEXT("LookupAccountName() failed. Error %d\n"),
??????? GetLastError());
???? __leave;
???}
???pUserSID = myheapalloc(cbUserSID);
???if (!pUserSID) {
???? _tprintf(TEXT("HeapAlloc() failed. Error %d\n"), GetLastError());
???? __leave;
???}
???szDomain = (TCHAR *) myheapalloc(cbDomain * sizeof(TCHAR));
???if (!szDomain) {
???? _tprintf(TEXT("HeapAlloc() failed. Error %d\n"), GetLastError());
???? __leave;
???}
???fAPISuccess = LookupAccountName(NULL, lpszAccountName,
??????pUserSID, &cbUserSID, szDomain, &cbDomain, &snuType);
???if (!fAPISuccess) {
???? _tprintf(TEXT("LookupAccountName() failed. Error %d\n"),
??????? GetLastError());
???? __leave;
???}
???//
???// STEP 2: 取得文件(目錄)相關(guān)的安全描述符SD
???//?? 使用GetFileSecurity函數(shù)取得一份文件SD的拷貝,同樣,這個(gè)函數(shù)也
??? // 是被調(diào)用兩次,第一次同樣是取SD的內(nèi)存長度。注意,SD有兩種格式:自相關(guān)的
??? // (self-relative)和 完全的(absolute),GetFileSecurity只能取到“自
??? // 相關(guān)的”,而SetFileSecurity則需要完全的。這就是為什么需要一個(gè)新的SD,
??? // 而不是直接在GetFileSecurity返回的SD上進(jìn)行修改。因?yàn)椤白韵嚓P(guān)的”信息
??? // 是不完整的。
???fAPISuccess = GetFileSecurity(lpszFileName,
??????secInfo, pFileSD, 0, &cbFileSD);
???// 以上調(diào)用API會(huì)失敗,失敗原因是內(nèi)存不足。并把所需要的內(nèi)存大小傳出。
???// 下面是處理非內(nèi)存不足的錯(cuò)誤。
???if (fAPISuccess)
???? __leave;
???else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
???? _tprintf(TEXT("GetFileSecurity() failed. Error %d\n"),
??????? GetLastError());
???? __leave;
???}
???pFileSD = myheapalloc(cbFileSD);
???if (!pFileSD) {
???? _tprintf(TEXT("HeapAlloc() failed. Error %d\n"), GetLastError());
???? __leave;
???}
???fAPISuccess = GetFileSecurity(lpszFileName,
??????secInfo, pFileSD, cbFileSD, &cbFileSD);
???if (!fAPISuccess) {
???? _tprintf(TEXT("GetFileSecurity() failed. Error %d\n"),
??????? GetLastError());
???? __leave;
???}
???//
???// STEP 3: 初始化一個(gè)新的SD
???//
???if (!InitializeSecurityDescriptor(&newSD,
??????SECURITY_DESCRIPTOR_REVISION)) {
???? _tprintf(TEXT("InitializeSecurityDescriptor() failed.")
??????TEXT("Error %d\n"), GetLastError());
???? __leave;
???}
???//
???// STEP 4: 從GetFileSecurity 返回的SD中取DACL
???//
???if (!GetSecurityDescriptorDacl(pFileSD, &fDaclPresent, &pACL,
??????&fDaclDefaulted)) {
???? _tprintf(TEXT("GetSecurityDescriptorDacl() failed. Error %d\n"),
??????? GetLastError());
???? __leave;
???}
???//
???// STEP 5: 取 DACL的內(nèi)存size
???//?? GetAclInformation可以提供DACL的內(nèi)存大小。只傳入一個(gè)類型為
???// ACL_SIZE_INFORMATION的structure的參數(shù),需DACL的信息,是為了
???// 方便我們遍歷其中的ACE。
???AclInfo.AceCount = 0; // Assume NULL DACL.
???AclInfo.AclBytesFree = 0;
???AclInfo.AclBytesInUse = sizeof(ACL);
???if (pACL == NULL)
???? fDaclPresent = FALSE;
???// 如果DACL不為空,則取其信息。(大多數(shù)情況下“自關(guān)聯(lián)”的DACL為空)
???if (fDaclPresent) {??????
???? if (!GetAclInformation(pACL, &AclInfo,
??????? sizeof(ACL_SIZE_INFORMATION), AclSizeInformation)) {
??????_tprintf(TEXT("GetAclInformation() failed. Error %d\n"),
?????????GetLastError());
??????__leave;
???? }
???}
???//
???// STEP 6: 計(jì)算新的ACL的size
???//??計(jì)算的公式是:原有的DACL的size加上需要添加的一個(gè)ACE的size,以
???// 及加上一個(gè)和ACE相關(guān)的SID的size,最后減去兩個(gè)字節(jié)以獲得精確的大小。
???cbNewACL = AclInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE)
??????+ GetLengthSid(pUserSID) - sizeof(DWORD);
???//
???// STEP 7: 為新的ACL分配內(nèi)存
???//
???pNewACL = (PACL) myheapalloc(cbNewACL);
???if (!pNewACL) {
???? _tprintf(TEXT("HeapAlloc() failed. Error %d\n"), GetLastError());
???? __leave;
???}
???//
???// STEP 8: 初始化新的ACL結(jié)構(gòu)
???//
???if (!InitializeAcl(pNewACL, cbNewACL, ACL_REVISION2)) {
???? _tprintf(TEXT("InitializeAcl() failed. Error %d\n"),
??????? GetLastError());
???? __leave;
???}
???//
???// STEP 9?如果文件(目錄) DACL 有數(shù)據(jù),拷貝其中的ACE到新的DACL中
???//
???//?? 下面的代碼假設(shè)首先檢查指定文件(目錄)是否存在的DACL,如果有的話,
???// 那么就拷貝所有的ACE到新的DACL結(jié)構(gòu)中,我們可以看到其遍歷的方法是采用
???// ACL_SIZE_INFORMATION結(jié)構(gòu)中的AceCount成員來完成的。在這個(gè)循環(huán)中,
???// 會(huì)按照默認(rèn)的ACE的順序來進(jìn)行拷貝(ACE在ACL中的順序是很關(guān)鍵的),在拷
???// 貝過程中,先拷貝非繼承的ACE(我們知道ACE會(huì)從上層目錄中繼承下來)
???//
???newAceIndex = 0;
???if (fDaclPresent && AclInfo.AceCount) {
???? for (CurrentAceIndex = 0;
??????? CurrentAceIndex < AclInfo.AceCount;
??????? CurrentAceIndex++) {
??????//
??????// STEP 10: 從DACL中取ACE
??????//
??????if (!GetAce(pACL, CurrentAceIndex, &pTempAce)) {
??????? _tprintf(TEXT("GetAce() failed. Error %d\n"),
?????????? GetLastError());
??????? __leave;
??????}
??????//
??????// STEP 11: 檢查是否是非繼承的ACE
??????//?? 如果當(dāng)前的ACE是一個(gè)從父目錄繼承來的ACE,那么就退出循環(huán)。
??????// 因?yàn)?#xff0c;繼承的ACE總是在非繼承的ACE之后,而我們所要添加的ACE
??????// 應(yīng)該在已有的非繼承的ACE之后,所有的繼承的ACE之前。退出循環(huán)
??????// 正是為了要添加一個(gè)新的ACE到新的DACL中,這后,我們?cè)侔牙^承的
??????// ACE拷貝到新的DACL中。
??????//
??????if (((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags
??????? & INHERITED_ACE)
??????? break;
??????//
??????// STEP 12: 檢查要拷貝的ACE的SID是否和需要加入的ACE的SID一樣,
??????// 如果一樣,那么就應(yīng)該廢掉已存在的ACE,也就是說,同一個(gè)用戶的存取
??????// 權(quán)限的設(shè)置的ACE,在DACL中應(yīng)該唯一。這在里,跳過對(duì)同一用戶已設(shè)置
??????// 了的ACE,僅是拷貝其它用戶的ACE。
??????//
??????if (EqualSid(pUserSID,
??????? &(((ACCESS_ALLOWED_ACE *)pTempAce)->SidStart)))
??????? continue;
??????//
??????// STEP 13: 把ACE加入到新的DACL中
??????//??下面的代碼中,注意 AddAce 函數(shù)的第三個(gè)參數(shù),這個(gè)參數(shù)的意思是
??????// ACL中的索引值,意為要把ACE加到某索引位置之后,參數(shù)MAXDWORD的
???????// 意思是確保當(dāng)前的ACE是被加入到最后的位置。
??????//
??????if (!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
?????????((PACE_HEADER) pTempAce)->AceSize)) {
??????? _tprintf(TEXT("AddAce() failed. Error %d\n"),
?????????? GetLastError());
??????? __leave;
??????}
??????newAceIndex++;
???? }
???}
??//
??// STEP 14: 把一個(gè) access-allowed 的ACE 加入到新的DACL中
??//?? 前面的循環(huán)拷貝了所有的非繼承且SID為其它用戶的ACE,退出循環(huán)的第一件事
??// 就是加入我們指定的ACE。請(qǐng)注意首先先動(dòng)態(tài)裝載了一個(gè)AddAccessAllowedAceEx
??// 的API函數(shù),如果裝載不成功,就調(diào)用AddAccessAllowedAce函數(shù)。前一個(gè)函數(shù)僅
??// 在Windows 2000以后的版本支持,NT則沒有,我們?yōu)榱耸褂眯掳姹镜暮瘮?shù),我們首
??// 先先檢查一下當(dāng)前系統(tǒng)中可不可以裝載這個(gè)函數(shù),如果可以則就使用。使用動(dòng)態(tài)鏈接
??// 比使用靜態(tài)鏈接的好處是,程序運(yùn)行時(shí)不會(huì)因?yàn)闆]有這個(gè)API函數(shù)而報(bào)錯(cuò)。
??//
??// Ex版的函數(shù)多出了一個(gè)參數(shù)AceFlag(第三人參數(shù)),用這個(gè)參數(shù)我們可以來設(shè)置一
??// 個(gè)叫ACE_HEADER的結(jié)構(gòu),以便讓我們所設(shè)置的ACE可以被其子目錄所繼承下去,而
??// AddAccessAllowedAce函數(shù)不能定制這個(gè)參數(shù),在AddAccessAllowedAce函數(shù)
??// 中,其會(huì)把ACE_HEADER這個(gè)結(jié)構(gòu)設(shè)置成非繼承的。
??//
???_AddAccessAllowedAceEx = (AddAccessAllowedAceExFnPtr)
??????GetProcAddress(GetModuleHandle(TEXT("advapi32.dll")),
??????"AddAccessAllowedAceEx");
???if (_AddAccessAllowedAceEx) {
????? if (!_AddAccessAllowedAceEx(pNewACL, ACL_REVISION2,
???????CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE ,
????????dwAccessMask, pUserSID)) {
?????? _tprintf(TEXT("AddAccessAllowedAceEx() failed. Error %d\n"),
????????? GetLastError());
?????? __leave;
?????}
???}else{
?????if (!AddAccessAllowedAce(pNewACL, ACL_REVISION2,
????????dwAccessMask, pUserSID)) {
?????? _tprintf(TEXT("AddAccessAllowedAce() failed. Error %d\n"),
????????? GetLastError());
?????? __leave;
?????}
???}
???//
???// STEP 15: 按照已存在的ACE的順序拷貝從父目錄繼承而來的ACE
???//
???if (fDaclPresent && AclInfo.AceCount) {
???? for (;
???????CurrentAceIndex < AclInfo.AceCount;
???????CurrentAceIndex++) {
??????//
??????// STEP 16: 從文件(目錄)的DACL中繼續(xù)取ACE
??????//
??????if (!GetAce(pACL, CurrentAceIndex, &pTempAce)) {
??????? _tprintf(TEXT("GetAce() failed. Error %d\n"),
?????????? GetLastError());
??????? __leave;
??????}
??????//
??????// STEP 17: 把ACE加入到新的DACL中
??????//
??????if (!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
?????????((PACE_HEADER) pTempAce)->AceSize)) {
??????? _tprintf(TEXT("AddAce() failed. Error %d\n"),
?????????? GetLastError());
??????? __leave;
??????}
???? }
???}
???//
???// STEP 18: 把新的ACL設(shè)置到新的SD中
???//
???if (!SetSecurityDescriptorDacl(&newSD, TRUE, pNewACL,
??????FALSE)) {
???? _tprintf(TEXT("SetSecurityDescriptorDacl() failed. Error %d\n"),
??????? GetLastError());
???? __leave;
???}
???//
???// STEP 19: 把老的SD中的控制標(biāo)記再拷貝到新的SD中,我們使用的是一個(gè)叫
???// SetSecurityDescriptorControl() 的API函數(shù),這個(gè)函數(shù)同樣只存在于
???// Windows 2000以后的版本中,所以我們還是要?jiǎng)討B(tài)地把其從advapi32.dll
???// 中載入,如果系統(tǒng)不支持這個(gè)函數(shù),那就不拷貝老的SD的控制標(biāo)記了。
???//
???_SetSecurityDescriptorControl =(SetSecurityDescriptorControlFnPtr)
??????GetProcAddress(GetModuleHandle(TEXT("advapi32.dll")),
??????"SetSecurityDescriptorControl");
???if (_SetSecurityDescriptorControl) {
???? SECURITY_DESCRIPTOR_CONTROL controlBitsOfInterest = 0;
???? SECURITY_DESCRIPTOR_CONTROL controlBitsToSet = 0;
???? SECURITY_DESCRIPTOR_CONTROL oldControlBits = 0;
???? DWORD dwRevision = 0;
???? if (!GetSecurityDescriptorControl(pFileSD, &oldControlBits,
??????&dwRevision)) {
??????_tprintf(TEXT("GetSecurityDescriptorControl() failed.")
?????????TEXT("Error %d\n"), GetLastError());
??????__leave;
???? }
???? if (oldControlBits & SE_DACL_AUTO_INHERITED) {
??????controlBitsOfInterest =
??????? SE_DACL_AUTO_INHERIT_REQ |
??????? SE_DACL_AUTO_INHERITED ;
??????controlBitsToSet = controlBitsOfInterest;
???? }
???? else if (oldControlBits & SE_DACL_PROTECTED) {
??????controlBitsOfInterest = SE_DACL_PROTECTED;
??????controlBitsToSet = controlBitsOfInterest;
???? }????
???? if (controlBitsOfInterest) {
??????if (!_SetSecurityDescriptorControl(&newSD,
??????? controlBitsOfInterest,
??????? controlBitsToSet)) {
??????? _tprintf(TEXT("SetSecurityDescriptorControl() failed.")
?????????? TEXT("Error %d\n"), GetLastError());
??????? __leave;
??????}
???? }
???}
???//
???// STEP 20: 把新的SD設(shè)置設(shè)置到文件的安全屬性中(千山萬水啊,終于到了)
???//
???if (!SetFileSecurity(lpszFileName, secInfo,
??????&newSD)) {
???? _tprintf(TEXT("SetFileSecurity() failed. Error %d\n"),
??????? GetLastError());
???? __leave;
???}
???fResult = TRUE;
? } __finally {
???//
???// STEP 21: 釋放已分配的內(nèi)存,以免Memory Leak
???//
???if (pUserSID)?myheapfree(pUserSID);
???if (szDomain)?myheapfree(szDomain);
???if (pFileSD) myheapfree(pFileSD);
???if (pNewACL) myheapfree(pNewACL);
? }
? return fResult;
}
--------------------------------------------------------------------------------
int _tmain(int argc, TCHAR *argv[]) {
? if (argc < 3) {
???_tprintf(TEXT("usage: \"%s\" <FileName> <AccountName>\n"), argv[0]);
???return 1;
? }
? // argv[1] – 文件(目錄)名
? // argv[2] – 用戶(組)名
? // GENERIC_ALL表示所有的權(quán)限,其是一系列的NTFS權(quán)限的或
? //???NTFS的文件權(quán)限很細(xì),還請(qǐng)參看MSDN。
? if (!AddAccessRights(argv[1], argv[2], GENERIC_ALL)) {
???_tprintf(TEXT("AddAccessRights() failed.\n"));
???return 1;
? }
? else {
???_tprintf(TEXT("AddAccessRights() succeeded.\n"));
???return 0;
? }
}
三、?????? 一些相關(guān)的API函數(shù)
通過以上的示例,相信你已知道如何操作NTFS文件安全屬性了,還有一些API函數(shù)需要介紹一下。
1、?如果你要加入一個(gè)Access-Denied 的ACE,你可以使用AddAccessDeniedAce函數(shù)
2、?如果你要?jiǎng)h除一個(gè)ACE,你可以使用DeleteAce函數(shù)
3、?如果你要檢查你所設(shè)置的ACL是否合法,你可以使用IsValidAcl函數(shù),同樣,對(duì)于SD的合法也有一個(gè)叫IsValidSecurityDescriptor的函數(shù)
?
轉(zhuǎn)自:http://xue23.blog.163.com/blog/static/979344200926112725250/
總結(jié)
以上是生活随笔為你收集整理的编写安全的代码(ACL使用方法)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VC(MFC、ATL)中 得到2个SYS
- 下一篇: IPv6网络协议的安全疑云