Windows NTでファイルにアクセス権を設定する方法

戻る

本項目のサンプルソースは、Windows2000以降のOSでは正常に動作いたしません。
詳細は、以下のマイクロソフト公式情報を参照してください。

アクセス許可 ACE をファイルに追加する方法 (KB102102)


WindowsNTは,ファイルやディレクトリにアクセス権を設定して,アクセスできる利用者 を制限することができます.WindowsNTを1人で利用するときは,それほど必要のない 機能かもしれませんが,複数の利用者でファイルやディレクトリを共有するときや, IISなどのWebサーバーを利用するときには,大変重要な機能になります.
単純にWebサーバーを構築するだけなら,ディレクトリやファイルのプロパティ から設定すれば問題ないと思います.しかし,動的にアクセスできるファイルを変更 する必要がある場合や,CGIのようにWebサーバー関連のアプリケーションの インストーラを作成するときには,インストール先のアクセス権の設定を行う必要が あるかもしれません.
UNIXでは,chmod コマンドや,chmodシステムコールで設定を行うことができますが, WindowsNTでは,設定できる組み合わせが複雑で,そのような「簡単」APIは ありません.そこで,簡単にアクセス権を設定する関数を作成することにします.

10-1 UNIXでのファイルアクセス権

UNIXでのファイルアクセス権はシンプルで分かりやすくなっています. アクセス権の基準になるのはファイルの所有者で,所有者以外へのアクセス権は, 所有者と同じグループに所属する・しないユーザーに対するものと,その他の ユーザーの2種類しかありません.つまり,ファイルへのアクセス権は, 所有者・グループ・その他の利用者に対して,読み込み・書き込み・実行の3つを 組み合わせたものになります.
<UNIXでのファイルアクセス権>
所有者(u)
 読み取り(r)
 書き込み(w)
 実行(x)
グループ(g)
 読み取り(r)
 書き込み(w)
 実行(x)
その他(o)
 読み取り(r)
 書き込み(w)
 実行(x)
MS-DOSやWindowsのユーザーから見ると,「実行」という属性は見慣れないもの ではないかと思います.この属性は,MS-DOS の BAT, COM, EXE ファイルに相当する もので,MS-DOSではそれらのファイルを拡張子で判断しますが,UNIXでは,実行権で 判断し,例えば,file.pl(Perl), file.html(HTMLファイル), file.cgi(CGI) の ように,ファイル拡張子はあくまでも,利用者やアプリケーションがファイルの中身や 用途を推測しやすくするために利用することが多いようです.
UNIXの場合,WindowsNTのようにファイルシステムによって,アクセス権の設定が できないということはありません.

10-2 WindowsNTでのファイルアクセス権

WindowsNTのアクセス権は,MS-DOSの時代から存在するファイル属性とWindowsNTから 追加されたACL(Access Control List)を利用したものがあります.
後者は,ファイルシステムがNTFS(NT File System)でフォーマットされたディスクのみ が利用できます.

●MS-DOSから引き継いだ属性
MS-DOSから引き継いだファイル属性は,最近ではあまり重要な意味を持っていない ようににも見えます.しかし,CD-ROM内のファイルはすべて「読み込み専用」属性 を持っていますし,WindowsNTの「ごみ箱」など,システム内で利用され, 利用者が直接操作する必要のないものは,「隠しファイル」属性が設定されて います
.これらの属性は,利用者に無関係に設定・解除を行うことができます ので,セキュリティ面での意味はありません.
(* MS-DOSではCD-ROMは通常のドライブと異なり,ネットワークドライブと同じ扱い になっている.ISOのフォーマットにはMS-DOSと同じような属性はないので,CD-ROM ドライバがMS-DOSのファイルシステムのように見せかけると同時に,CD-ROMのファイル は上書きできないので便宜上「読み込み専用」属性が設定されているように見せかけて いると思われる.)

●NTFSでのファイルアクセス権
NTFSでフォーマットされたディスク内のファイルやディレクトリのプロパティを参照 すると,「セキュリティ」の項目が表示されます.
ファイルへのアクセス権は,システム内やドメイン内に存在するユーザやグループ単位 で設定することができます.設定できるグループやユーザーは所有者とは無関係に 設定することができ,アクセス権以外に監査を行うための情報を設定することも できます.グループに対してアクセス権を設定した場合,そのグループに所属している ユーザーすべてに対して設定が有効になりますので,多くのユーザーを管理している 場合は便利に利用することができます.
WindowsNTのセキュリティー機能は高度で柔軟性に富んでいます. しかし,アクセス権の設定を,自分で作成したアプリケーションで行うときには, 分かりにくく,複雑な機能の1つではないかと思います.

10-3 WindowsNTでファイルのアクセス権を変更するには

NTFSで設定できるファイルセキュリティーは,WindowsNTのシステム内で利用されている その他のリソースやオブジェクトと同じ方法で管理されています.
ファイルへのアクセスを行うと,システムは,アクセスを行った利用者がリソースに 対してアクセスを行ってよいかをチェックします.このときに利用者のセキュリティー 識別子(SID)を持つアクセストークンを利用します.アクセストークンは,ユーザーが ログオンしたときに生成され,そこで実行されたプロセスは必ずアクセストークンを 持ちます.アクセストークンには,ユーザーSID,グループSID,特権,プロセス オーナーSIDなどが設定されています.
ファイルなどのオブジェクトには,セキュリティー記述子(SD)があり,その中に は,オブジェクトの所有者に関する情報や,アクセスを行ってよい利用者の情報が 格納されています.オブジェクトへのアクセスを行うと,利用者のSIDと,オブジェクト 内のSDにあるSIDとアクセスコントロールリスト(ACL)のエントリー(ACE)情報と 比較して,アクセスを行ってよいかをチェックします.
もし,アクセスを行うことができないときには,APIのエラーコードに ERROR_ACCESS_DENIED が設定され,処理を失敗します.

●アクセス権をオブジェクトに設定するには
新しいユーザーやグループのアクセス権をオブジェクトに設定するには, ACLに新しいACEを追加すればよいということになります.
それでは,実際にアクセス権を設定する手順について説明します.
1)SIDの取得
Win32 API LookupAccountName で設定するユーザーやグループののSIDを取得します. このAPIには,ユーザー名以外にグループ名を指定することができます.
2)ファイルに設定されているSDの取得
Win32 API GetFileSecurity で,ファイルに設定されているセキュリティー記述子 を取得します.
3)新しいACLを作成する
  • Win32 API GetSecurityDescriptorDacl で,2)で取得したSDに設定されている DACL(随意ACL)を取得します.
  • Win32 API GetAclInformation でACLのサイズを取得して現在持っているACLと 追加するACEを含めた場合のサイズを算出して,新しいACLの領域の確保と初期化を 行います.
  • 以前のACLを Win32 API GetAce で取得し,新しいACLに Win32 API AddAceで コピーします.もし,以前の情報を修正するときにはここで 行います.設定されている情報を引き継がないときには,コピーする必要は ありません.
    4)アクセス権を追加する
  • アクセス権を設定するには,Win32 API AddAccessAllowedAce を利用することが できます.しかし,ディレクトリ(コンテナオブジェクト)に設定する場合, ディレクトリ内に保存されるファイルに設定を引き継ぐことができません. このため,ディレクトリの場合は,独自に「システム許可ACE」を作成し, CONTAINER_INHERIT_ACE, OBJECT_INHERIT_ACE フラグを設定する必要があります.
    5)ファイルにアクセス権を設定する
  • Win32 API SetSecurityDescriptorDacl で,SDに新しいACLを設定します.
  • Win32 API SetFileSecurity で,SDをファイルに設定します.

    例10-1:ACEを作成する
    
        PACCESS_ALLOWED_ACE lpAce;
        WORD                wAceSize = sizeof(ACCESS_ALLOWED_ACE)
                                - sizeof(DWORD) + GetLengthSid(lpSid);
        LRESULT             lResult = ERROR_SUCCESS;
    
        /* ACEを格納するメモリを確保する */
        if((lpAce = LocalAlloc(LPTR, wAceSize)) == NULL){
            return GetLastError();
        }
    
        __try{
            /* アクセス許可ACEを作成する */
            lpAce->Mask            = dwAccessMask;
            lpAce->Header.AceType  = ACCESS_ALLOWED_ACE_TYPE;
            lpAce->Header.AceFlags = fDirectory ? ACE_FLAGS_DIR : ACE_FLAGS_FILE;
            lpAce->Header.AceSize  = wAceSize;
            CopyMemory(&lpAce->SidStart, lpSid, GetLengthSid(lpSid));
    
            if(AddAce(lpAcl, ACL_REVISION, MAXDWORD, lpAce, wAceSize) == FALSE){
                lResult = GetLastError();
            }
        }
        __finally{
            LocalFree(lpAce);
        }
    

    表10-1:ACEの種類
    構造体ACLの種類
    アクセス許可ACCESS_ALLOWED_ACE随意
    アクセス拒否ACCESS_DENIED_ACE随意
    システム監査SYSTEM_AUDIT_ACEシステム

    表10-2:セキュリティーAPI
    BOOL GetFileSecurity(LPCTSTR lpszFile, SECURITY_INFORMATION si,
     PSECURITY_DESCRIPTOR lpsd, DWORD dwsd,
     LPDWORD lpdwsdRequired)
    ファイルやディレクトリに関する指定された情報を取得する

    引数
    LPCTSTR lpszFile ... ファイル名
    SECURITY_INFORMATION si ... 要求する情報
     OWNER_SECURITY_INFORMATION オーナー識別子を参照
     GROUP_SECURITY_INFORMATION プライマリグループ識別子を参照
     DACL_SECURITY_INFORMATION オブジェクトの随意ACLを参照
     SACL_SECURITY_INFORMATION オブジェクトのシステムACLを参照
    PSECURITY_DESCRIPTOR lpsd ... セキュリティ記述子のアドレス
    DWORD dwsd ... バッファのサイズ
    LPDWORD lpdwsdRequired ... 必要なバッファサイズ

    戻り値
    正常終了 TRUE
    異常終了 FALSE
    BOOL SetFileSecurity(LPCTSTR lpszFile, SECURITY_INFORMATION si,
     PSECURITY_DESCRIPTOR lpsd)
    ファイルやディレクトリに関する指定された情報を設定する

    引数
    LPCTSTR lpszFile ... ファイル名
    SECURITY_INFORMATION si ... 要求する情報
     OWNER_SECURITY_INFORMATION オーナー識別子を参照
     GROUP_SECURITY_INFORMATION プライマリグループ識別子を参照
     DACL_SECURITY_INFORMATION オブジェクトの随意ACLを参照
     SACL_SECURITY_INFORMATION オブジェクトのシステムACLを参照
    PSECURITY_DESCRIPTOR lpsd ... セキュリティ記述子のアドレス

    戻り値
    正常終了 TRUE
    異常終了 FALSE
    BOOL GetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR lpsd,
     LPBOOL lpfDaclPresent, PACL *lppAcl, LPBOOL lpfDaclDefaulted)|
    随意アクセス制御リスト(ACL)の情報の取得を行う

    引数
    PSECURITY_DESCRIPTOR lpsd ... セキュリティ記述子
    LPBOOL lpfDaclPresent ... 随意ACLの存在を示す
    PACL* lppAcl ... ACLを指すポインタ
    LPBOOL lpfDaclDefaulted ... デフォルトの随意ACLか

    戻り値
    正常終了 TRUE
    異常終了 FALSE
    BOOL SetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR lpsd,
     LPBOOL lpfDaclPresent, PACL *lppAcl, LPBOOL lpfDaclDefaulted)
    随意アクセス制御リスト(ACL)の情報を設定を行う

    引数
    PSECURITY_DESCRIPTOR lpsd ... セキュリティ記述子
    BOOL fDaclPresent ... 随意ACLの存在を示す
    PACL lpAcl ... ACLを指すポインタ
    BOOL fDaclDefaulted ... デフォルトの随意ACLか

    戻り値
    正常終了 TRUE
    異常終了 FALSE
    BOOL InitializeAcl(PACL lpAcl, DWORD dwAcl, DWORD dwAclRevision)
    新しいACL構造体を作成する

    引数
    PACL lpAcl ... アクセス制御リストのアドレス
    DWORD dwAcl ... アクセス制御リストのサイズ
     単一のACCESS_ALLOWED_ACEを格納する
     のに十分なACLバッファのサイズ
     sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE)
     + GetLengthSid(pSid) - sizeof(DWORD)
    DWORD dwAclRevision ... アクセス制御リストのリビジョンレベル
     ACL_REVISION を設定する

    戻り値
    正常終了 TRUE
    異常終了 FALSE
    BOOL GetAce(PACL lpAcl, DWORD dwAceIndex, LPVOID *lplpvAce)
    ACL内のACEを指すポインタを取得する

    引数
    PACL lpAcl ... 取得するACEを含むACL構造体を指すポインタ
    DWORD dwAceIndex ... ポインタを取得するACEを指定
    LPVOID *lplpvAce ... ACEを指すポインタ

    戻り値
    正常終了 TRUE
    異常終了 FALSE
    BOOL AddAce(PACL lpAcl, DWORD dwAclRevision,
     DWORD dwStartingAceIndex, LPVOID lpvAceList,
     DWORD dwAceList)
    1つ以上のACEを指定されたACLに追加する

    引数
    PACL lpAcl ... アクセス制御リストのアドレス
    DWORD dwAclRevision ... ACLリビジョンレベル
    DWORD dwStartingAceIndex ... ACL内でのACEの位置のインデックス
    LPVOID lpvAceList ... 1つ以上のACEのアドレス
    DWORD dwAceList ... ACEのバッファのアドレス

    戻り値
    正常終了 TRUE
    異常終了 FALSE
    BOOL AddAccessAllowedAce(PACL lpAcl, DWORD dwAclRevision,
     DWORD dwAccessMask, PSID lpSid)
    ACL内のACEを指すポインタを取得する

    引数
    PACL lpAcl ... アクセス制御リストのアドレス
    DWORD dwAclRevision ... ACLリビジョンレベル
    DWORD dwAccessMask ... アクセスマスク
    LSID lpSid ... セキュリティ識別子のアドレス

    戻り値
    正常終了 TRUE
    異常終了 FALSE

    ●アクセスマスク
    ファイルに対してアクセス権を設定するときに,読み込み・書き込み・フルアクセス のようなアクセスを行う権限を制限するための情報が必要になります.
    ファイルやディレクトリのプロパティで,アクセス権を設定した場合, すでに組み合わされた値が定義されています.これらの組み合わせは,ACCESS_MASK の,固有アクセス権と,汎用アクセス権の組み合わせで構成されます.
    (* これらの定義は,WINNT.H にある.)

    表10-3:ファイルアクセス権
    読み込み FILE_GENERIC_READ | FILE_EXECUTE
    更新 FILE_GENERIC_WRITE | FILE_GENERIC_READ | FILE_EXECUTE | DELETE
    フルアクセス STANDARD_RIGHTS_ALL | 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

    表10-4:ACCESS_MASK の中身
    0から15固有アクセス権
    16DELETE削除アクセス
    17READ_CONTROLセキュリティ記述子の オーナー・グループまたは随意アクセス制御リストへの読み取りアクセス
    18WRITE_DAC随意アクセス制御リストへの 書き込みアクセス
    19WRITE_OWNERオーナーへの書き込みアクセス
    20SYNCHRONIZE同期アクセス
    24ACCESS_SYSTEM_SECURITYシステムセキュリティへの アクセス
    25MAXIMUM_ALLOWED最大限度の許可
    26,27予約
    28GENERIC_ALL汎用
    29GENERIC_EXECUTE汎用実行
    30GENERIC_WRITE汎用書き込み
    31GENERIC_READ汎用読み取り

    10-4 プログラムについて

    ファイルアクセス権を変更するアプリケーションです.
    アクセス権の追加は,すでに設定されているACEと同一の利用者の場合でも行われます. 不必要な設定を消すには,ファイルやディレクトリのプロパティで設定し直すか, SEC.EXE で,上書き指定でアクセス権の設定を行う必要があります.
    また,ディレクトリの場合は,サブディレクトリをサイクリックに検索して指定された ディレクトリ内のサブディレクトリとファイルについてもセキュリティの設定を行う ことができます.
    実際にどのようなアクセス権が設定されているかを,確認するには,Visual C++ や Win32 SDK のサンプルソース CHECK_SD.EXE や CACL.EXE を利用すると よいでしょう.

    ●使い方
    >SEC file_name account add|overwrite none|read|change|full [subdir]
    file_name ... ファイル名(ディレクトリ)
    account ... アカウント(グループ)
    追加状態
     add ... アクセス権を追加する
     overwrite ... アクセス権を上書きする
    アクセス状態
     none ... なし
     read ... 読み込み
     change ... 変更
     full ... フルアクセス
    サブディレクトリ
     subdir ... サブディレクトリも更新する

    ●ソースファイル
    SEC.EXE
     SEC.C (メインソース)
     SECFILE.H (ファイルアクセス権設定関数ヘッダ)
     SECFILE.C (ファイルアクセス権設定関数ソース)
     MAKEFILE (メイクファイル)