天天看點

以程式的方式操縱NTFS的檔案權限(中)

還是請看例程,這個程式比較長,來源于MSDN,我做了一點點修改,并把自己的了解加在注釋中,是以,請注意代碼中的注釋:

#include <windows.h>

#include <tchar.h>

#include <stdio.h>

//使用Windows的HeapAlloc函數進行動态記憶體配置設定

#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相關的變量(注意,全為0,要在程式中動态配置設定)

   TCHAR *        szDomain       = NULL;

   DWORD          cbDomain       = 0;

   LPVOID         pUserSID       = NULL;

   DWORD          cbUserSID      = 0;

   // 和檔案相關的安全描述符 SD 的變量

   PSECURITY_DESCRIPTOR pFileSD  = NULL;     // 結構變量

   DWORD          cbFileSD       = 0;        // SD的size

   // 一個新的SD的變量,用于構造新的ACL(把已有的ACL和需要新加的ACL整合起來)

   SECURITY_DESCRIPTOR  newSD;

   // 和ACL 相關的變量

   PACL           pACL           = NULL;

   BOOL           fDaclPresent;

   BOOL           fDaclDefaulted;

   ACL_SIZE_INFORMATION AclInfo;

   // 一個新的 ACL 變量

   PACL           pNewACL        = NULL;  //結構指針變量

   DWORD          cbNewACL       = 0;     //ACL的size

   // 一個臨時使用的 ACE 變量

   LPVOID         pTempAce       = NULL;

   UINT           CurrentAceIndex = 0;  //ACE在ACL中的位置

   UINT           newAceIndex = 0;  //新添的ACE在ACL中的位置

   //API函數的傳回值,假設所有的函數都傳回失敗。

   BOOL           fResult;

   BOOL           fAPISuccess;

   SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION;

   // 下面的兩個函數是新的API函數,僅在Windows 2000以上版本的作業系統支援。 

   // 在此将從Advapi32.dll檔案中動态載入。如果你使用VC++ 6.0編譯程式,而且你想

   // 使用這兩個函數的靜态連結。則請為你的編譯加上:/D_WIN32_WINNT=0x0500

   // 的編譯參數。并且確定你的SDK的頭檔案和lib檔案是最新的。

   SetSecurityDescriptorControlFnPtr _SetSecurityDescriptorControl = NULL;

   AddAccessAllowedAceExFnPtr _AddAccessAllowedAceEx = NULL;

   __try {

      // 

      // STEP 1: 通過使用者名取得SID

      //     在這一步中LookupAccountName函數被調用了兩次,第一次是取出所需要

      // 的記憶體的大小,然後,進行記憶體配置設定。第二次調用才是取得了使用者的帳戶資訊。

      // LookupAccountName同樣可以取得域使用者或是使用者組的資訊。(請參看MSDN)

      //

      fAPISuccess = LookupAccountName(NULL, lpszAccountName,

            pUserSID, &cbUserSID, szDomain, &cbDomain, &snuType);

      // 以上調用API會失敗,失敗原因是記憶體不足。并把所需要的記憶體大小傳出。

      // 下面是處理非記憶體不足的錯誤。

      if (fAPISuccess)

         __leave;

      else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {

         _tprintf(TEXT("LookupAccountName() failed. Error %d\n"), 

               GetLastError());

      }

      pUserSID = myheapalloc(cbUserSID);

      if (!pUserSID) {

         _tprintf(TEXT("HeapAlloc() failed. Error %d\n"), GetLastError());

      szDomain = (TCHAR *) myheapalloc(cbDomain * sizeof(TCHAR));

      if (!szDomain) {

      if (!fAPISuccess) {

      // STEP 2: 取得檔案(目錄)相關的安全描述符SD

      //     使用GetFileSecurity函數取得一份檔案SD的拷貝,同樣,這個函數也

       // 是被調用兩次,第一次同樣是取SD的記憶體長度。注意,SD有兩種格式:自相關的

       // (self-relative)和 完全的(absolute),GetFileSecurity隻能取到“自

       // 相關的”,而SetFileSecurity則需要完全的。這就是為什麼需要一個新的SD,

       // 而不是直接在GetFileSecurity傳回的SD上進行修改。因為“自相關的”資訊

       // 是不完整的。

      fAPISuccess = GetFileSecurity(lpszFileName, 

            secInfo, pFileSD, 0, &cbFileSD);

         _tprintf(TEXT("GetFileSecurity() failed. Error %d\n"), 

      pFileSD = myheapalloc(cbFileSD);

      if (!pFileSD) {

            secInfo, pFileSD, cbFileSD, &cbFileSD);

      // STEP 3: 初始化一個新的SD

      // 

      if (!InitializeSecurityDescriptor(&newSD, 

            SECURITY_DESCRIPTOR_REVISION)) {

         _tprintf(TEXT("InitializeSecurityDescriptor() failed.")

            TEXT("Error %d\n"), GetLastError());

      // STEP 4: 從GetFileSecurity 傳回的SD中取DACL

      if (!GetSecurityDescriptorDacl(pFileSD, &fDaclPresent, &pACL,

            &fDaclDefaulted)) {

         _tprintf(TEXT("GetSecurityDescriptorDacl() failed. Error %d\n"),

      // STEP 5: 取 DACL的記憶體size

      //     GetAclInformation可以提供DACL的記憶體大小。隻傳入一個類型為

      // ACL_SIZE_INFORMATION的structure的參數,需DACL的資訊,是為了

      // 友善我們周遊其中的ACE。

      AclInfo.AceCount = 0; // Assume NULL DACL.

      AclInfo.AclBytesFree = 0;

      AclInfo.AclBytesInUse = sizeof(ACL);

      if (pACL == NULL)

         fDaclPresent = FALSE;

      // 如果DACL不為空,則取其資訊。(大多數情況下“自關聯”的DACL為空)

      if (fDaclPresent) {            

         if (!GetAclInformation(pACL, &AclInfo, 

               sizeof(ACL_SIZE_INFORMATION), AclSizeInformation)) {

            _tprintf(TEXT("GetAclInformation() failed. Error %d\n"),

                  GetLastError());

            __leave;

         }

      // STEP 6: 計算新的ACL的size

      //    計算的公式是:原有的DACL的size加上需要添加的一個ACE的size,以

      // 及加上一個和ACE相關的SID的size,最後減去兩個位元組以獲得精确的大小。

      cbNewACL = AclInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) 

            + GetLengthSid(pUserSID) - sizeof(DWORD);

      // STEP 7: 為新的ACL配置設定記憶體

      pNewACL = (PACL) myheapalloc(cbNewACL);

      if (!pNewACL) {

      // STEP 8: 初始化新的ACL結構

      if (!InitializeAcl(pNewACL, cbNewACL, ACL_REVISION2)) {

         _tprintf(TEXT("InitializeAcl() failed. Error %d\n"), 

(程式未完,請看下篇)

      // STEP 9  如果檔案(目錄) DACL 有資料,拷貝其中的ACE到新的DACL中

      //     下面的代碼假設首先檢查指定檔案(目錄)是否存在的DACL,如果有的話,

      // 那麼就拷貝所有的ACE到新的DACL結構中,我們可以看到其周遊的方法是采用

      // ACL_SIZE_INFORMATION結構中的AceCount成員來完成的。在這個循環中,

      // 會按照預設的ACE的順序來進行拷貝(ACE在ACL中的順序是很關鍵的),在拷

      // 貝過程中,先拷貝非繼承的ACE(我們知道ACE會從上層目錄中繼承下來)

      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

            //     如果目前的ACE是一個從父目錄繼承來的ACE,那麼就退出循環。

            // 因為,繼承的ACE總是在非繼承的ACE之後,而我們所要添加的ACE

            // 應該在已有的非繼承的ACE之後,所有的繼承的ACE之前。退出循環

            // 正是為了要添加一個新的ACE到新的DACL中,這後,我們再把繼承的

            // ACE拷貝到新的DACL中。

            //

            if (((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags

               & INHERITED_ACE)

               break;

            // STEP 12: 檢查要拷貝的ACE的SID是否和需要加入的ACE的SID一樣,

            // 如果一樣,那麼就應該廢掉已存在的ACE,也就是說,同一個使用者的存取

            // 權限的設定的ACE,在DACL中應該唯一。這在裡,跳過對同一使用者已設定

            // 了的ACE,僅是拷貝其它使用者的ACE。

            if (EqualSid(pUserSID,

               &(((ACCESS_ALLOWED_ACE *)pTempAce)->SidStart)))

               continue;

            // STEP 13: 把ACE加入到新的DACL中

            //    下面的代碼中,注意 AddAce 函數的第三個參數,這個參數的意思是 

            // ACL中的索引值,意為要把ACE加到某索引位置之後,參數MAXDWORD的

              // 意思是確定目前的ACE是被加入到最後的位置。

            if (!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,

                  ((PACE_HEADER) pTempAce)->AceSize)) {

               _tprintf(TEXT("AddAce() failed. Error %d\n"), 

            newAceIndex++;

本文轉自 haoel 51CTO部落格,原文連結:http://blog.51cto.com/haoel/124665,如需轉載請自行聯系原作者