セキュリティディスクリプタとは?

この資料では Windows のセキュリティ機構を利用する上で非常に重要な、 セキュリティディスクリプタ (Security Descriptor, セキュリティ記述子) について説明します。

セキュリティディスクリプタとは?

セキュリティディスクリプタは、それぞれのオブジェクトに対して OS がどのようなアクセスを許可、 許可あるいは監査を行うか記述するものです。

Windows のセキュリティは誰がどこに対して何ができるか、ということが基本になっています。 これと対になるのは、.NET Framework のコードベースセキュリティと呼ばれるもので、 どのプログラムが何を出来るか、というものを基礎とするものです。

従ってセキュリティ・ディスクリプタには、「誰が」 「何を」 できるか書いてあると予想できるわけです。 そのように予想した上で、どこに 「誰が」 「何を」 できるか記述してあるのか図で見てみましょう。

セキュリティディスクリプタ

セキュリティディスクリプタは上図のような構成になっています。

ポイントは、所有者の SID 等の他、DACL (Discretionary Access Control List, 任意アクセス制御リスト) と SACL (System Access Control List, システムアクセス制御リスト) を含むという点です。

SACL はシステムの監査方法を記述するものです。 実際の、いわゆるアクセス制御を記述するのは DACL です。

DACL には ACE (Access Control Entry) が含まれており、その中に SID (すなわち 「誰が」) と アクセスマスク (すなわち 「何を」) が記述されています。

ちなみに、アクセスマスクは以下のようなデータ構造で、何を許可するかを表します。

ざっとググって見ると、DACL も ACE も区別せずにごちゃごちゃに説明している資料を結構見かけましたが、 名前が似ていて間違いやすいので気をつけましょう。

NULL DACL とエンプティ (Empty) DACL

さて、NULL DACL と Empty DACL についてみておきましょう。

NULL DACL は DACL そのものがセキュリティディスクリプタに存在しません。 この場合、誰でもアクセス可能という意味になります。(下図左)

一方、Empty DACL は DACL 内に ACE が存在しません。 この場合、誰もアクセスできないことを表します。(下図右)

誰でもアクセス可能誰もアクセスできない

セキュリティディスクリプタのプログラムからの利用

それでは簡単な例を使って、セキュリティディスクリプタを作成して、 それを用いて、セキュリティ設定のされたレジストリキーを作成してみましょう。

#include <windows.h>
#include <stdio.h>
#include <aclapi.h>
#include <assert.h>
#include <sddl.h>

int main( int argc, char* argv[] ) {

     DWORD dwRes;
     BOOL bRet;

     //
     // SID
     //
     
     PSID pEveryoneSID = NULL;

     SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
     bRet = AllocateAndInitializeSid(
          &SIDAuthWorld, 
          1, 
          SECURITY_WORLD_RID, 
          0, 0, 0, 0, 0, 0, 0, 
          &pEveryoneSID); 

     //
     // Set ACE in ACL
     //
     
     EXPLICIT_ACCESS ea;
     PACL pACL = NULL;

     ::ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
     ea.grfAccessPermissions = KEY_READ | DELETE;
     ea.grfAccessMode = SET_ACCESS;
     ea.grfInheritance= NO_INHERITANCE;
     ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
     ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
     ea.Trustee.ptstrName  = (LPTSTR) pEveryoneSID;      // Everyone SID
     
     dwRes = SetEntriesInAcl( 1, &ea, NULL, &pACL);

     //
     // Security Descriptor
     //
     
     PSECURITY_DESCRIPTOR pSD = NULL;
     
     pSD = (PSECURITY_DESCRIPTOR) malloc ( SECURITY_DESCRIPTOR_MIN_LENGTH );
  
     bRet = InitializeSecurityDescriptor(
          pSD, 
          SECURITY_DESCRIPTOR_REVISION ); 

     //
     // Add the ACL to the security descriptor. 
     //
     
     bRet = SetSecurityDescriptorDacl( pSD, TRUE, pACL, FALSE ); 

     //
     // Use the Security Descriptor
     //
     
     SECURITY_ATTRIBUTES sa;
     
     sa.nLength = sizeof (SECURITY_ATTRIBUTES);
     sa.lpSecurityDescriptor = pSD;
     sa.bInheritHandle = FALSE;

     HKEY hkSub = NULL;
     LONG lRes;
     DWORD dwDisposition;
          
     lRes = RegCreateKeyEx( 
          HKEY_CURRENT_USER, 
          "mykey", 
          0, 
          "", 
          0, 
          NULL, 
          &sa, 
          &hkSub, 
          &dwDisposition); 

     printf("RegCreateKeyEx result %u\n", lRes );

     //
     // Cleanup
     //
     
     FreeSid(pEveryoneSID);
     LocalFree(pACL);
     free (pSD);     
     RegCloseKey(hkSub);

     return 0;

}

順番に読んでいけばわかりやすいと思いますが、要は次のことをしています。

  1. Everyone の SID を取得 (AllocateAndInitializeSid)
  2. ACE (EXPLICIT_ACCESS) にアクセス権 (READ と DELETE) を設定
  3. DACL にそれを設定 (SetEntriesInAcl)
  4. セキュリティディスクリプタの作成 (malloc でメモリ割り当てし、InitializeSecurityDescriptor)
  5. セキュリティディスクリプタへ DACL の設定 (SetSecurityDescriptorDacl)
  6. SECURITY_ATTRIBUTES に上で作ったセキュリティディスクリプタを設定して、 RegCreateKeyEx でレジストリを作成する

もちろん、こんなサンプルコードではなく実際のプログラムで使う時は エラー処理はしてください。ざっくり書いてます。

上記コードを走らせると、下記のように確かに HKCU 以下に mykey というレジストリが作成され...

そのパーミッションは、Everyone に対する READ と DELETE に設定されていることが確認できます。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

© 2024 Web/DB プログラミング徹底解説