Windows Vistaで導入されたMandatory Integrity Control (MIC)を活用すれば、管理者ユーザーでも削除できないフォルダやファイルを作れます。実際に試してみました。

昇格した管理者ユーザーアカウントのプロセスはILが高なので、それよりさらに上のレベルであるシステムを設定してみたというだけのことです。

// il-system.cpp
 
#define UNICODE
#define _UNICODE
 
#include <iostream>
#include <cassert>
 
#include <windows.h>
#include <aclapi.h>
 
template<typename... Args>
SE_SID MakeSid(
  BYTE revision,
  SID_IDENTIFIER_AUTHORITY identifierAuthority,
  Args&&... args)
{
  SE_SID sid{ revision, sizeof...(args), identifierAuthority };
  DWORD subAuthority[] = { static_cast<DWORD>(args)... };
  std::copy(
    std::begin(subAuthority), std::end(subAuthority),
    sid.Sid.SubAuthority);
  return sid;
}
 
int main()
{
  auto ILSystemSid = MakeSid(S
    ID_REVISION,
    SECURITY_MANDATORY_LABEL_AUTHORITY,
    SECURITY_MANDATORY_SYSTEM_RID);
 
  constexpr auto ILSystemSidSize = sizeof (SID);
  assert(IsValidSid(&ILSystemSid));
  assert(ILSystemSidSize == GetLengthSid(&ILSystemSid));
 
  constexpr DWORD dwSaclSize =
    sizeof (ACL)
    + sizeof (SYSTEM_MANDATORY_LABEL_ACE)
    - sizeof (SYSTEM_MANDATORY_LABEL_ACE::SidStart)
    + ILSystemSidSize;
#if 1
  union
  {
    char saclBuffer[dwSaclSize];
    struct
    {
      ACL sacl;
      SYSTEM_MANDATORY_LABEL_ACE mandatoryLavelAce;
    };
  };
 
  sacl.AclRevision = ACL_REVISION;
  sacl.AclSize = sizeof saclBuffer;
  sacl.AceCount = 1;
  mandatoryLavelAce.Header.AceType = SYSTEM_MANDATORY_LABEL_ACE_TYPE;
  mandatoryLavelAce.Header.AceFlags =
    CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
  mandatoryLavelAce.Header.AceSize =
    (sizeof mandatoryLavelAce)
    - sizeof (SYSTEM_MANDATORY_LABEL_ACE::SidStart)
    + ILSystemSidSize;
  mandatoryLavelAce.Mask = SYSTEM_MANDATORY_LABEL_NO_WRITE_UP;
  memcpy(&mandatoryLavelAce.SidStart, &ILSystemSid, ILSystemSidSize);
  assert(IsValidAcl(&sacl));
#else
  InitializeAcl(&sacl, dwSaclSize, ACL_REVISION);
  if (!AddMandatoryAce(
    &sacl, ACL_REVISION,
    CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
    SYSTEM_MANDATORY_LABEL_NO_WRITE_UP,
    &ILSystemSid))
  {
    auto e = GetLastError();
    std::cout << "AddMandatoryAce: " << e << std::endl;
    return 0;
  }
#endif
 
  auto result = SetNamedSecurityInfo(
    LR"(D:\TEMP\t)",
    SE_FILE_OBJECT,
    LABEL_SECURITY_INFORMATION,
    nullptr,
    nullptr,
    nullptr,
    &sacl);
  if (result != ERROR_SUCCESS)
  {
    std::cout << "SetNamedSecurityInfo: " << result << std::endl;
  }
}

sizeof (SYSTEM_MANDATORY_LABEL_ACE::SidStart)は、C++11で追加された構文です。少し前のVisual C++だと対応していないので、そういうときは適宜修正してください。あるいは、最新のコンパイラを使ってください。あと、無名unionの中に無名structを置くことは、Visual C++の独自拡張のような気がします。ほかのコンパイラを使っているかた、ごめんなさい。

#if 1#else側は、どちらも同じであることを意図しています。どちらも実際に動かして、期待どおりに動作することを確認しました。

試し方はこうです。

  1. 上のプログラムをコンパイルしたものをil-system.exeとします。
  2. D:\TEMP\tというフォルダを作り(ソースコード内決め打ち!)、その中にファイルを作っておきます。
  3. ILがシステムな状態でil-system.exeを実行します。お手軽なのは、PsExecを使い、psexec -s il-system.exeで実行することだと思います。

すると、D:\TEMP\tは「管理者として実行」などで昇格したプロセスごときでは書き込み操作が拒否されるようになります。例えば、ファイルを追加したり、既に作成済みのファイルに追記したり、あるいは削除したりなどといった操作ができなくなります。

なお、削除するにはpsexec -s cmd /c rd /s D:\TEMP\tなどとすれば大丈夫です。

最後に、MSDNライブラリの関係する項目へのリンクを貼っておきます。


というわけで、管理者ユーザーはILが高なので、ILがシステムなオブジェクトなら操作できないことを試してみました、という話でした。

WindowsサービスなんかはみんなILがシステムです。ユーザーに触れられたくないファイルに、こういう設定をしたら良いのではないだろうかと思いました。もっとも、psexecのように、管理者(Administartors)は、そのWindowsサービスを追加・削除する権限を持っています。そのため、完全に防げるものではありません。


スポンサード リンク

この記事のカテゴリ

  • ⇒ 管理者でも削除できないフォルダ・ファイルを作る
  • ⇒ 管理者でも削除できないフォルダ・ファイルを作る