乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      關(guān)于Windows安全權(quán)限的學習

       DavinTang 2011-01-19
      本篇文章內(nèi)容主要參考了 陳皓以程序的方式操NTFS的文件權(quán) 》,然后加入了一些自己學習和理解的東西。

      在NTFS文件系統(tǒng)出現(xiàn)后,在Windows系統(tǒng)(2K/XP/Vista..)下的對象,包括文件系統(tǒng),進程、命名管道、打印機、網(wǎng)絡共享、或是注冊表等等,都可以設置用戶訪問權(quán)限。

      Windows系統(tǒng)中,其是用一個安全描述符(Security Descriptors)的結(jié)構(gòu)來保存其權(quán)限的設置信息,簡稱為SD,其在Windows SDK中的結(jié)構(gòu)名是SECURITY_DESCRIPTOR,這是包括了安全設置信息的結(jié)構(gòu)體,其結(jié)構(gòu)體內(nèi)容定義如下:

      typedef struct _SECURITY_DESCRIPTOR {
        UCHAR  Revision;
        UCHAR  Sbz1;
        SECURITY_DESCRIPTOR_CONTROL  Control;
        PSID  Owner;
        PSID  Group;
        PACL  Sacl;
        PACL  Dacl;
      } SECURITY_DESCRIPTOR, 
      *PISECURITY_DESCRIPTOR;

      一個安全描述符包含以下安全信息:

      • 兩個安全標識符(Security identifiers),簡稱為SID,分別是OwnerSid和GroupSid. 所謂SID就是每次當我們創(chuàng)建一個用戶或一個組的時候,系統(tǒng)會分配給改用戶或組一個唯一SID,當你重新安裝系統(tǒng)后,也會得到一個唯一的SID。SID是唯一的,不隨用戶的刪除而分配到另外的用戶使用。
        請記住,SID永遠都是唯一的SIF是由計算機名、當前時間、當前用戶態(tài)線程的CPU耗費時間的總和三個參數(shù)決定以保證它的唯一性。
          例:   S-1-5-21-1763234323-3212657521-1234321321-500
      • 一個DACLDiscretionary Access Control List),其指出了允許和拒絕某用戶或用戶組的存取控制列表。 當一個進程需要訪問安全對象,系統(tǒng)就會檢查DACL來決定進程的訪問權(quán)。如果一個對象沒有DACL,那么就是說這個對象是任何人都可以擁有完全的訪問權(quán)限。
      • 一個SACLSystem Access Control List),其指出了在該對象上的一組存取方式(如,讀、寫、運行等)的存取控制權(quán)限細節(jié)的列表。
      • 還有其自身的一些控制位。SECURITY_DESCRIPTOR_CONTROL

          DACLSACL構(gòu)成了整個存取控制列表Access Control List,簡稱ACL,ACL中的每一項,我們叫做ACEAccess Control Entry),ACL中的每一個ACE。

          ACL結(jié)構(gòu)體的內(nèi)容如下:

         

      typedef struct _ACL {
          BYTE  AclRevision;
          BYTE  Sbz1;
          WORD   AclSize;
          WORD   AceCount;
          WORD   Sbz2;
      } ACL;
      typedef ACL 
      *PACL;
         

          我們的程序不用直接維護SD這個結(jié)構(gòu),這個結(jié)構(gòu)由系統(tǒng)維護。我們只用使用Windows 提供的相關(guān)的API函數(shù)來取得并設置SD中的信息就行了。不過這些API函數(shù)只有Windows NT/2K/XP才支持。

          Windows提供了一系列的安全信息的存取,控制函數(shù),如 GetNamedSecurityInfo, SetNamedSecurityInfoGetSecurityInfo, SetSecurityInfo等。

      下圖說明了,安全對象和DACL以及訪問者之間的聯(lián)系(來源于MSDN)。注意,DACL表中的每個ACE的順序是有意義的,如果 前面的Allow(或denied)ACE通過了,那么,系統(tǒng)就不會檢查后面的ACE了。另外"拒絕"權(quán)限一般是優(yōu)先于其他權(quán)限的。ACE在DACL中的 順序非常重要。


        

      系統(tǒng)會按照順序依次檢查所有的ACE規(guī)則,如下面的條件滿足,則退出:

      1、  如果一個Access-Denied的ACE明顯地拒絕了請求者。

      2、  如果某Access-Allowed的ACE明顯地同意了請求者。

      3、  全部的ACE都檢查完了,但是沒有一條ACE明顯地允許或是拒絕請求者,那么系統(tǒng)將使用默認值,拒絕請求者的訪問。

      更多的理論和描述,請參看MSDN。

      實例例程

      主要實現(xiàn)2個功能,獲取目錄的安全設置和為目錄增加一個安全設置項(該程序來源于MSDN,原作者 陳皓稍作修改,并加入了注解,我也稍作了點修改)

      1、   對于文件、目錄、命令管道,我們不一定要使用GetNamedSecurityInfoSetNamedSecurityInfo函數(shù),我們可以使用其專用函數(shù)GetFileSecuritySetFileSecurity函數(shù)來取得或設置文件對象的SD,以設置其訪問權(quán)限。需要使用這兩個函數(shù)并不容易,正如前面我們所說的,我們還需要處理SD參數(shù),要處理SD,就需要處理DACL和ACE,以及用戶的相關(guān)SID,于是,一系統(tǒng)列的函數(shù)就被這兩個函數(shù)帶出來了。

      2、   對于上一個例子中的使用硬編碼指定SID的處理方法是。調(diào)用LookupAccountName函數(shù)時,先把SID,Domain名的參數(shù)傳為空 NULL,于是LookupAccountName會返回用戶的SID的長度和Domain名的長度,于是你可以根據(jù)這個長度分配內(nèi)存,然后再次調(diào)用 LookupAccountName函數(shù)。于是就可以達到到態(tài)分配內(nèi)存的效果。對于ACL也一樣。

      3、   對于給文件的ACL中增加一個ACE條目,一般的做法是先取出文件上的ACL,逐條取出ACE,和現(xiàn)需要增加的ACE比較,如果有沖突,則刪除已有的 ACE,把新加的ACE添置到最后。這里的最后,應該是非繼承而來的ACE的最后。關(guān)于ACL繼承,NTFS中,你可以設置文件和目錄是否繼承于其父目錄 的設置。在程序中同樣可以設置。

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

      // SetSD.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
      //

      #include 
      <stdio.h>
      #include 
      <bitset>
      #include 
      <tchar.h>
      #include 
      <windows.h>
      #include 
      <string>
      #include 
      <iostream>

      using std::bitset;
      using std::string;
      using std::cout;
      using std::endl;

      //使用Windows的HeapAlloc函數(shù)進行動態(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)
      //【機能概要】
      //     為文件(目錄)添加一個帳戶(組)的權(quán)限
      //【入力】
      //     TCHAR *lpszFileName     文件(目錄)
      //     TCHAR *lpszAccountName  帳戶(組)
      //       DWORD dwAccessMask       權(quán)限設置(如GENERIC_ALL,GENERIC_READ等)
      //【輸出】
      //     無
      //【輸入輸出】
      //     無
      //【返回值】
      //     BOOL
      //【例外】
      //     無
      //---------------------------------------------------------------------------
      BOOL AddAccessRights(TCHAR *lpszFileName, TCHAR *lpszAccountName, DWORD dwAccessMask) { 
          
      // 聲明SID變量
          SID_NAME_USE   snuType;
          
      // 聲明和LookupAccountName相關(guān)的變量(注意,全為0,要在程序中動態(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
          
      // 一個新的SD的變量,用于構(gòu)造新的ACL(把已有的ACL和需要新加的ACL整合起來)
          SECURITY_DESCRIPTOR  newSD;
          
      // 和ACL 相關(guān)的變量
          PACL           pACL           = NULL;
          BOOL           fDaclPresent;
          BOOL           fDaclDefaulted;
          ACL_SIZE_INFORMATION AclInfo;
          
      // 一個新的 ACL 變量
          PACL           pNewACL        = NULL;  //結(jié)構(gòu)指針變量
          DWORD          cbNewACL       = 0;     //ACL的size
          
      // 一個臨時使用的 ACE 變量
          LPVOID         pTempAce       = NULL;
          UINT           CurrentAceIndex 
      = 0;  //ACE在ACL中的位置
          UINT           newAceIndex = 0;  //新添的ACE在ACL中的位置
          
      //API函數(shù)的返回值,假設所有的函數(shù)都返回失敗。
          BOOL           fResult = FALSE;
          BOOL           fAPISuccess 
      = FALSE;
          SECURITY_INFORMATION secInfo 
      = DACL_SECURITY_INFORMATION;
          
      // 下面的兩個函數(shù)是新的API函數(shù),僅在Windows 2000以上版本的操作系統(tǒng)支持。 
          
      // 在此將從Advapi32.dll文件中動態(tài)載入。如果你使用VC++ 6.0編譯程序,而且你想
          
      // 使用這兩個函數(shù)的靜態(tài)鏈接。則請為你的編譯加上:/D_WIN32_WINNT=0x0500
          
      // 的編譯參數(shù)。并且確保你的SDK的頭文件和lib文件是最新的。
          SetSecurityDescriptorControlFnPtr _SetSecurityDescriptorControl = NULL;
          AddAccessAllowedAceExFnPtr _AddAccessAllowedAceEx 
      = NULL; 
          __try {
              
      // 
              
      // STEP 1: 通過用戶名取得SID
              
      //     在這一步中LookupAccountName函數(shù)被調(diào)用了兩次,第一次是取出所需要
              
      // 的內(nèi)存的大小,然后,進行內(nèi)存分配。第二次調(diào)用才是取得了用戶的帳戶信息。
              
      // LookupAccountName同樣可以取得域用戶或是用戶組的信息。(請參看MSDN)
              
      //
              fAPISuccess = LookupAccountName(NULL, lpszAccountName,
                  pUserSID, 
      &cbUserSID, szDomain, &cbDomain, &snuType);
              
      // 以上調(diào)用API會失敗,失敗原因是內(nèi)存不足。并把所需要的內(nèi)存大小傳出。
              
      // 下面是處理非內(nèi)存不足的錯誤。
              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的拷貝,同樣,這個函數(shù)也
              
      // 是被調(diào)用兩次,第一次同樣是取SD的內(nèi)存長度。注意,SD有兩種格式:自相關(guān)的
              
      // (self-relative)和 完全的(absolute),GetFileSecurity只能取到“自
              
      // 相關(guān)的”,而SetFileSecurity則需要完全的。這就是為什么需要一個新的SD,
              
      // 而不是直接在GetFileSecurity返回的SD上進行修改。因為“自相關(guān)的”信息
              
      // 是不完整的。
              fAPISuccess = GetFileSecurity(lpszFileName, 
                  secInfo, pFileSD, 
      0&cbFileSD);
              
      // 以上調(diào)用API會失敗,失敗原因是內(nèi)存不足。并把所需要的內(nèi)存大小傳出。
              
      // 下面是處理非內(nèi)存不足的錯誤。
              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: 初始化一個新的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)存大小。只傳入一個類型為
              
      // 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: 計算新的ACL的size
              
      //    計算的公式是:原有的DACL的size加上需要添加的一個ACE的size,以
              
      // 及加上一個和ACE相關(guān)的SID的size,最后減去兩個字節(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中
              
      // 
              
      //     下面的代碼假設首先檢查指定文件(目錄)是否存在的DACL,如果有的話,
              
      // 那么就拷貝所有的ACE到新的DACL結(jié)構(gòu)中,我們可以看到其遍歷的方法是采用
              
      // ACL_SIZE_INFORMATION結(jié)構(gòu)中的AceCount成員來完成的。在這個循環(huán)中,
              
      // 會按照默認的ACE的順序來進行拷貝(ACE在ACL中的順序是很關(guān)鍵的),在拷
              
      // 貝過程中,先拷貝非繼承的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,那么就退出循環(huán)。
                          
      // 因為,繼承的ACE總是在非繼承的ACE之后,而我們所要添加的ACE
                          
      // 應該在已有的非繼承的ACE之后,所有的繼承的ACE之前。退出循環(huán)
                          
      // 正是為了要添加一個新的ACE到新的DACL中,這后,我們再把繼承的
                          
      // ACE拷貝到新的DACL中。
                          
      //
                          if (((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags
                              
      & INHERITED_ACE)
                              
      break;
                          
      // 
                          
      // STEP 12: 檢查要拷貝的ACE的SID是否和需要加入的ACE的SID一樣,
                          
      // 如果一樣,那么就應該廢掉已存在的ACE,也就是說,同一個用戶的存取
                          
      // 權(quán)限的設置的ACE,在DACL中應該唯一。這在里,跳過對同一用戶已設置
                          
      // 了的ACE,僅是拷貝其它用戶的ACE。
                          
      // 
                          if (EqualSid(pUserSID,
                              
      &(((ACCESS_ALLOWED_ACE *)pTempAce)->SidStart)))
                          {
                              ACCESS_ALLOWED_ACE pTempAce2 
      = *(ACCESS_ALLOWED_ACE *)pTempAce;

                              ACCESS_DENIED_ACE pTempAce4 
      = *(ACCESS_DENIED_ACE *)pTempAce;
                              
      int a = -1;
                              
      if (pTempAce2.Header.AceType == ACCESS_ALLOWED_ACE_TYPE)
                              {
                                  a 
      = 0;
                              }
                              
      else if (pTempAce2.Header.AceType == ACCESS_DENIED_ACE_TYPE)
                              {
                                  a 
      = 1;
                              }
                              
      else
                                  a 
      = 2;
                              
      continue;
                          }
                          
      // 
                          
      // STEP 13: 把ACE加入到新的DACL中
                          
      //    下面的代碼中,注意 AddAce 函數(shù)的第三個參數(shù),這個參數(shù)的意思是 
                          
      // ACL中的索引值,意為要把ACE加到某索引位置之后,參數(shù)MAXDWORD的
                          
      // 意思是確保當前的ACE是被加入到最后的位置。
                          
      //
                          if (!AddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
                              ((PACE_HEADER) pTempAce)
      ->AceSize)) {
                                  _tprintf(TEXT(
      "AddAce() failed. Error %d\n"), 
                                      GetLastError());
                                  __leave;
                          }
                          newAceIndex
      ++;
                  }
              }

              
      // 
              
      // STEP 14: 把一個 access-allowed 的ACE 加入到新的DACL中
              
      //     前面的循環(huán)拷貝了所有的非繼承且SID為其它用戶的ACE,退出循環(huán)的第一件事
              
      // 就是加入我們指定的ACE。請注意首先先動態(tài)裝載了一個AddAccessAllowedAceEx
              
      // 的API函數(shù),如果裝載不成功,就調(diào)用AddAccessAllowedAce函數(shù)。前一個函數(shù)僅
              
      // 在Windows 2000以后的版本支持,NT則沒有,我們?yōu)榱耸褂眯掳姹镜暮瘮?shù),我們首
              
      // 先先檢查一下當前系統(tǒng)中可不可以裝載這個函數(shù),如果可以則就使用。使用動態(tài)鏈接
              
      // 比使用靜態(tài)鏈接的好處是,程序運行時不會因為沒有這個API函數(shù)而報錯。
              
      // 
              
      // Ex版的函數(shù)多出了一個參數(shù)AceFlag(第三人參數(shù)),用這個參數(shù)我們可以來設置一
              
      // 個叫ACE_HEADER的結(jié)構(gòu),以便讓我們所設置的ACE可以被其子目錄所繼承下去,而 
              
      // AddAccessAllowedAce函數(shù)不能定制這個參數(shù),在AddAccessAllowedAce函數(shù)
              
      // 中,其會把ACE_HEADER這個結(jié)構(gòu)設置成非繼承的。
              
      // 
              _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
              
      // 
              bitset<32> bit(dwAccessMask);
              
      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設置到新的SD中
              
      // 
              if (!SetSecurityDescriptorDacl(&newSD, TRUE, pNewACL, 
                  FALSE)) {
                      _tprintf(TEXT(
      "SetSecurityDescriptorDacl() failed. Error %d\n"),
                          GetLastError());
                      __leave;
              }
              
      // 
              
      // STEP 19: 把老的SD中的控制標記再拷貝到新的SD中,我們使用的是一個叫 
              
      // SetSecurityDescriptorControl() 的API函數(shù),這個函數(shù)同樣只存在于
              
      // Windows 2000以后的版本中,所以我們還是要動態(tài)地把其從advapi32.dll 
              
      // 中載入,如果系統(tǒng)不支持這個函數(shù),那就不拷貝老的SD的控制標記了。
              
      // 
              _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設置設置到文件的安全屬性中(千山萬水啊,終于到了)
              
      // 
              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;
      }

      //【接口】
      //     BOOL GetAccountRights(TCHAR *lpszFileName, TCHAR *lpszAccountName, int (&arrRights)[32])
      //【機能概要】
      //     獲取該文件(目錄)指定帳戶(組)的權(quán)限
      //【入力】
      //     TCHAR *lpszFileName     文件(目錄)
      //     TCHAR *lpszAccountName  帳戶(組)
      //       int (&arrRights)[32]       數(shù)組引用,要求傳入?yún)?shù)必須是32個int數(shù)組
      //【輸出】
      //     無
      //【輸入輸出】
      //     無
      //【返回值】
      //     BOOL
      //【例外】
      //     無
      //---------------------------------------------------------------------------
      BOOL GetAccountRights(TCHAR *lpszFileName, TCHAR *lpszAccountName, int (&arrRights)[32]) {
          
      //將參數(shù)arrRights初始化為0
          for (int i = 0; i < 32; i++)
          {
              arrRights[i] 
      = 0;
          }
          
      // 聲明SID變量
          SID_NAME_USE   snuType;
          
      // 聲明和LookupAccountName相關(guān)的變量(注意,全為0,要在程序中動態(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
          
      // 和ACL 相關(guān)的變量
          PACL           pACL           = NULL;
          BOOL           fDaclPresent;
          BOOL           fDaclDefaulted;
          ACL_SIZE_INFORMATION AclInfo;
          
      // 一個臨時使用的 ACE 變量
          LPVOID         pTempAce       = NULL;
          UINT           CurrentAceIndex 
      = 0;  //ACE在ACL中的位置
          
      //API函數(shù)的返回值,假設所有的函數(shù)都返回失敗。
          BOOL           fResult = FALSE;
          BOOL           fAPISuccess 
      = FALSE;
          SECURITY_INFORMATION secInfo 
      = DACL_SECURITY_INFORMATION;
          __try {
              
      // 
              
      // STEP 1: 通過用戶名取得SID
              
      //     在這一步中LookupAccountName函數(shù)被調(diào)用了兩次,第一次是取出所需要
              
      // 的內(nèi)存的大小,然后,進行內(nèi)存分配。第二次調(diào)用才是取得了用戶的帳戶信息。
              
      // LookupAccountName同樣可以取得域用戶或是用戶組的信息。(請參看MSDN)
              
      //
              fAPISuccess = LookupAccountName(NULL, lpszAccountName,
                  pUserSID, 
      &cbUserSID, szDomain, &cbDomain, &snuType);
              
      // 以上調(diào)用API會失敗,失敗原因是內(nèi)存不足。并把所需要的內(nèi)存大小傳出。
              
      // 下面是處理非內(nèi)存不足的錯誤。
              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的拷貝,同樣,這個函數(shù)也
              
      // 是被調(diào)用兩次,第一次同樣是取SD的內(nèi)存長度。注意,SD有兩種格式:自相關(guān)的
              
      // (self-relative)和 完全的(absolute),GetFileSecurity只能取到“自
              
      // 相關(guān)的”,而SetFileSecurity則需要完全的。這就是為什么需要一個新的SD,
              
      // 而不是直接在GetFileSecurity返回的SD上進行修改。因為“自相關(guān)的”信息
              
      // 是不完整的。
              fAPISuccess = GetFileSecurity(lpszFileName, 
                  secInfo, pFileSD, 
      0&cbFileSD);
              
      // 以上調(diào)用API會失敗,失敗原因是內(nèi)存不足。并把所需要的內(nèi)存大小傳出。
              
      // 下面是處理非內(nèi)存不足的錯誤。
              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: 從GetFileSecurity 返回的SD中取DACL
              
      // 
              if (!GetSecurityDescriptorDacl(pFileSD, &fDaclPresent, &pACL,
                  
      &fDaclDefaulted)) {
                      _tprintf(TEXT(
      "GetSecurityDescriptorDacl() failed. Error %d\n"),
                          GetLastError());
                      __leave;
              }
              
      // 
              
      // STEP 4: 取 DACL的內(nèi)存size
              
      //     GetAclInformation可以提供DACL的內(nèi)存大小。只傳入一個類型為
              
      // 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 5  如果文件(目錄) DACL 有數(shù)據(jù),將指定帳戶的ACE的訪問權(quán)限轉(zhuǎn)換到整型數(shù)組
              
      // 
              
      //     下面的代碼假設首先檢查指定文件(目錄)是否存在的DACL,如果有的話,
              
      // 那么就將指定帳戶的ACE的訪問權(quán)限轉(zhuǎn)換到整型數(shù)組,我們可以看到其遍歷的方法
              
      // 是采用ACL_SIZE_INFORMATION結(jié)構(gòu)中的AceCount成員來完成的。在這個循環(huán)中,
              
      // 查找和指定賬戶相關(guān)的ACE
              
      // 
              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 6: 檢查要拷貝的ACE的SID是否和需要加入的ACE的SID一樣,
                          
      // 如果一樣,那么就將該ACE的訪問權(quán)限轉(zhuǎn)換到整型數(shù)組,
                          
      // 否則跳過,進行下一個循環(huán)
                          
      // 
                          int nAceType = 1;
                          
      if (EqualSid(pUserSID,
                              
      &(((ACCESS_ALLOWED_ACE *)pTempAce)->SidStart)))
                          {
                              
      if(((PACE_HEADER)pTempAce)->AceType == ACCESS_DENIED_ACE_TYPE)
                              {
                                  nAceType 
      = 2;
                              }
                              
      else
                              {
                                  nAceType 
      = 1;
                              }
                              
      //bitset類代表的整型數(shù)值的順序是從0到N-1
                              bitset<32> bitAccessMask(((ACCESS_ALLOWED_ACE*)pTempAce)->Mask);
                              
      for (int i = 0; i < 32; i++)
                              {
                                  
      if (bitAccessMask[i] != 0 && arrRights[i] != 2)
                                  {
                                      arrRights[i] 
      = nAceType;
                                  }
                              }
                          }
                          
      else
                          {
                              
      continue;
                          }
                  }
              }
              fResult 
      = TRUE;
          } __finally {
              
      // 
              
      // STEP 7: 釋放已分配的內(nèi)存,以免Memory Leak
              
      // 
              if (pUserSID)  myheapfree(pUserSID);
              
      if (szDomain)  myheapfree(szDomain);
              
      if (pFileSD) myheapfree(pFileSD);
          }
          
      return fResult;
      }

      int _tmain(int argc, TCHAR *argv[]) 
      {
          
      if (argc < 3) {
              _tprintf(TEXT(
      "usage: \"%s\" <FileName> <AccountName>\n"), argv[0]);
              
      return 1;
          }
          
          
      //關(guān)于ACCESS_MASK中各個位代表的含義請參考MSDN
          string filedesc[] = {"FILE_READ_DATA""FILE_WRITE_DATA""FILE_APPEND_DATA""FILE_READ_EA",

              
      "FILE_WRITE_EA""FILE_EXECUTE""FILE_DELETE_CHILD""FILE_READ_ATTRIBUTES",

              
      "FILE_WRITE_ATTRIBUTES"" "" "" ",

              
      " "" "" "" ",

              
      "DELETE ""READ_CONTROL""WRITE_DAC""WRITE_OWNER",

              
      "SYNCHRONIZE "" "" "," ",

              
      "ACCESS_SYSTEM_SECURITY""MAXIMUM_ALLOWED"" "," ",

              
      "GENERIC_ALL""GENERIC_EXECUTE""GENERIC_WRITE","GENERIC_READ"};

          
      string rights[] = {"Allow""Deny"};

          
      //獲取ACE中的訪問權(quán)限
          
      //ACE中的訪問權(quán)限是通過DWORD類型的ACCESS_MASK記錄的
          
      //GetAccountRights函數(shù)將ACCESS_MASK轉(zhuǎn)為了一個32個元素的整型數(shù)組,并傳出
          int arrRights[32= {0};
          
      if (!GetAccountRights(argv[1], argv[2], arrRights))
          {
              _tprintf(TEXT(
      "GetAccountRights() failed.\n"));
          }
          
      else
          {
              _tprintf(TEXT(
      "The access rights of the file is..\n"));
              
      for (int i = 0; i < 32; i++)
              {
                  
      int nTmpRight = arrRights[i];
                  
      if (nTmpRight != 0)
                  {
                      cout
      <<filedesc[i]<<":  "<<rights[nTmpRight-1]<<endl;
                  }
              }
          }

          
      // argv[1] – 文件(目錄)名
          
      // argv[2] – 用戶(組)名
          
      // GENERIC_ALL表示所有的權(quán)限,其是一系列的NTFS權(quán)限的或
          
      //      NTFS的文件權(quán)限很細,還請參看MSDN。
          if (!AddAccessRights(argv[1], argv[2], GENERIC_ALL)) {
              _tprintf(TEXT(
      "AddAccessRights() failed.\n"));
              
      return 1;
          }
          
      else {
              _tprintf(TEXT(
      "AddAccessRights() succeeded.\n"));
              
      return 0;
          }
      }



      函數(shù)AddAccessRights實現(xiàn)為文件(目錄)添加一個帳戶(組)的權(quán)限
      函數(shù)GetAccountRights實現(xiàn)獲取該文件(目錄)指定帳戶(組)的權(quán)限

      三、             一些相關(guān)的API函數(shù)

       

      通過以上的示例,相信你已知道如何操作NTFS文件安全屬性了,還有一些API函數(shù)需要介紹一下。

      1、  如果你要加入一個Access-Denied 的ACE,你可以使用AddAccessDeniedAce函數(shù)

      2、  如果你要刪除一個ACE,你可以使用DeleteAce函數(shù)

      3、  如果你要檢查你所設置的ACL是否合法,你可以使用IsValidAcl函數(shù),同樣,對于SD的合法也有一個叫IsValidSecurityDescriptor的函數(shù)

      4、  MakeAbsoluteSDMakeSelfRelativeSD兩個函數(shù)可以在兩種SD的格式中進行轉(zhuǎn)換。

      5、  使用SetSecurityDescriptorDaclSetSecurityDescriptorSacl可以方便地把ACL設置到SD中。

      6、  使用GetSecurityDescriptorDacl or GetSecurityDescriptorSacl可以方便地取得SD中的ACL結(jié)構(gòu)。

      我們把一干和SD/ACL/ACE相關(guān)的API函數(shù)叫作Low-Level Security Descriptor Functions,其詳細信息還請參看MSDN。

        本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多