COM 昇格モニカー

COM 昇格モニカーを使用すると、ユーザー アカウント制御 (UAC) で実行されているアプリケーションは、昇格された特権で COM クラスをアクティブ化できます。 詳細については、「最小特権に重点を置く」を参照してください。

昇格モニカーを使用する場合

昇格モニカーは、システムの日付と時刻の変更など、昇格された特権を必要とする特定の制限付き関数を実現するために COM クラスをアクティブ化するために使用されます。

昇格するには、COM クラスとそのクライアントの両方からの参加が必要です。 COM クラスは、「要件」セクションで説明されているように、レジストリ エントリに注釈を付けて昇格をサポートするように構成する必要があります。 COM クライアントは、昇格モニカーを使用して昇格を要求する必要があります。

昇格モニカーは、アプリケーションの互換性を提供するためのものではありません。 たとえば、WinWord などのレガシ COM アプリケーションを管理者特権サーバーとして実行する場合は、昇格モニカーを使用してレガシ アプリケーションのクラスをアクティブ化するのではなく、昇格を必要とするように COM クライアント実行可能ファイルを構成する必要があります。 昇格された COM クライアントがレガシ アプリケーションの CLSID を使用して CoCreateInstance を呼び出すと、クライアントの昇格された状態がサーバー プロセスにフローします。

すべての COM 機能が昇格と互換性があるわけではありません。 機能しない機能には、次のものが含まれます。

  • 昇格は、クライアントからリモート COM サーバーにフローしません。 クライアントが昇格モニカーを使用してリモート COM サーバーをアクティブ化した場合、昇格をサポートしている場合でも、サーバーは昇格されません。
  • 昇格された COM クラスが COM 呼び出し中に偽装を使用すると、偽装中に昇格された特権が失われる可能性があります。
  • 昇格された COM サーバーが実行中のオブジェクト テーブル (ROT) にクラスを登録した場合、そのクラスは管理者特権以外のクライアントでは使用できません。
  • UAC メカニズムを使用して昇格されたプロセスは、COM のアクティブ化中にユーザーごとのクラスを読み込むことはありません。 COM アプリケーションの場合、これは、アプリケーションが非特権アカウントと特権アカウントの両方で 使用される場合は、アプリケーションの COM クラスを HKEY_LOCAL_MACHINE レジストリ ハイブにインストールする必要があることを意味します。 アプリケーションの COM クラスは、アプリケーションが特権アカウントで使用されない場合にのみ、 HKEY_USERS ハイブにインストールする必要があります。
  • 管理者特権以外のアプリケーションから昇格されたアプリケーションへのドラッグ アンド ドロップは許可されません。

要件

昇格モニカーを使用して COM クラスをアクティブ化するには、起動ユーザーまたは 'Activateor としてアクティブ化' アプリケーション ID として実行するようにクラスを構成する必要があります。 クラスが他の ID で実行されるように構成されている場合、アクティブ化はエラー CO_E_RUNAS_VALUE_MUST_BE_AAAを返します。

また、このクラスには、多言語ユーザー インターフェイス (MUI) 互換の "わかりやすい" 表示名で注釈を付ける必要があります。 これには、次のレジストリ エントリが必要です。

HKEY_LOCAL_MACHINE\Software\Classes\CLSID
   {CLSID}
      LocalizedString = displayName

このエントリが見つからない場合、アクティブ化はエラー CO_E_MISSING_DISPLAYNAMEを返します。 MUI ファイルがない場合は、 RegLoadMUIStringW 関数のエラー コードが返されます。

必要に応じて、UAC ユーザー インターフェイスによって表示されるアプリケーション アイコンを指定するには、次のレジストリ キーを追加します。

HKEY_LOCAL_MACHINE\Software\Classes\CLSID
   {CLSID}
      Elevation
         IconReference = applicationIcon

IconReferenceLocalizedString と同じ形式を使用します。

@pathtobinary,-resourcenumber

さらに、アイコンを表示するには、COM コンポーネントに署名する必要があります。

COM クラスには、LUA 対応として注釈を付ける必要もあります。 これには、次のレジストリ エントリが必要です。

HKEY_LOCAL_MACHINE\Software\Classes\CLSID
   {CLSID}
      Elevation
         Enabled = 1

このエントリが見つからない場合、アクティブ化はエラー CO_E_ELEVATION_DISABLEDを返します。

これらのエントリは、HKEY_CURRENT_USER または HKEY_USERS ハイブではなく、HKEY_LOCAL_MACHINE ハイブに存在する必要があることに注意してください。 これにより、登録する権限も持っていない COM クラスをユーザーが昇格できなくなります。

昇格モニカーと昇格 UI

クライアントが既に昇格されている場合、昇格モニカーを使用しても、昇格 UI は表示されません。

昇格モニカーを使用するには

昇格モニカーは、セッション、パーティション、またはキュー モニカーに似た標準的な COM モニカーです。 指定した昇格レベルを持つ指定したサーバーにアクティブ化要求を送信します。 アクティブ化する CLSID がモニカー文字列に表示されます。

昇格モニカーは、次の実行レベル トークンをサポートしています。

  1. 管理者
  2. 最高

これに関する構文は次のようになります。

Elevation:Administrator!new:{guid}
Elevation:Highest!new:{guid}

上記の構文では、"new" モニカーを使用して、 guid で指定された COM クラスのインスタンスを返します。 "new" モニカーは内部的に IClassFactory インターフェイスを使用してクラス オブジェクトを取得し、その上で IClassFactory::CreateInstance を呼び出します。

昇格モニカーを使用して、 IClassFactory を実装するクラス オブジェクトを取得することもできます。 呼び出し元は、 CreateInstance を呼び出してオブジェクト インスタンスを取得します。 これに関する構文は次のようになります。

Elevation:Administrator!clsid:{guid}

サンプル コード

次のコード例は、昇格モニカーの使用方法を示しています。 現在のスレッドで COM が既に初期化されていることを前提としています。

HRESULT CoCreateInstanceAsAdmin(HWND hwnd, REFCLSID rclsid, REFIID riid, __out void ** ppv)
{
    BIND_OPTS3 bo;
    WCHAR  wszCLSID[50];
    WCHAR  wszMonikerName[300];

    StringFromGUID2(rclsid, wszCLSID, sizeof(wszCLSID)/sizeof(wszCLSID[0])); 
    HRESULT hr = StringCchPrintf(wszMonikerName, sizeof(wszMonikerName)/sizeof(wszMonikerName[0]), L"Elevation:Administrator!new:%s", wszCLSID);
    if (FAILED(hr))
        return hr;
    memset(&bo, 0, sizeof(bo));
    bo.cbStruct = sizeof(bo);
    bo.hwnd = hwnd;
    bo.dwClassContext  = CLSCTX_LOCAL_SERVER;
    return CoGetObject(wszMonikerName, &bo, riid, ppv);
}

BIND_OPTS3 は Windows Vista の新機能です。 それは BIND_OPTS2 から派生します。

唯一の追加は HWND フィールド、 hwnd です。 このハンドルは、必要に応じて昇格 UI の所有者になるウィンドウを表します。

hwndNULL の場合、COM は GetActiveWindow を呼び出して、現在のスレッドに関連付けられているウィンドウ ハンドルを検索します。 このケースは、クライアントがスクリプトであり、 BIND_OPTS3 構造体に入力できない場合に発生する可能性があります。 この場合、COM はスクリプト スレッドに関連付けられているウィンドウの使用を試みます。

オーバーザショルダー (OTS) の昇格

オーバーショルダー (OTS) 昇格とは、クライアントが自分の資格情報ではなく管理者の資格情報を使用して COM サーバーを実行するシナリオを指します。 ("over the shoulder" という用語は、クライアントがサーバーを実行する場合に管理者がクライアントの肩を監視していることを意味します)。

このシナリオでは、サーバーが明示的に (つまり、プログラムによって) または暗黙的に (つまり、宣言によってレジストリを使用して) CoInitializeSecurity を呼び出さない可能性があるため、サーバーへの COM 呼び出しに問題が発生する可能性があります。 このようなサーバーの場合、COM は、Standard Edition LF、SYSTEM、Builtin\Administrator のみがサーバーに COM 呼び出しを行えるセキュリティ記述子を計算します。 この配置は、OTS シナリオでは機能しません。 代わりに、サーバーは明示的または暗黙的に CoInitializeSecurity を呼び出し、INTERACTIVE グループ SID と SYSTEM を含む ACL を指定する必要があります。

次のコード例は、INTERACTIVE グループ SID を使用してセキュリティ記述子 (SD) を作成する方法を示しています。

BOOL GetAccessPermissionsForLUAServer(SECURITY_DESCRIPTOR **ppSD)
{
// Local call permissions to IU, SY
    LPWSTR lpszSDDL = L"O:BAG:BAD:(A;;0x3;;;IU)(A;;0x3;;;SY)";
    SECURITY_DESCRIPTOR *pSD;
    *ppSD = NULL;

    if (ConvertStringSecurityDescriptorToSecurityDescriptorW(lpszSDDL, SDDL_REVISION_1, (PSECURITY_DESCRIPTOR *)&pSD, NULL))
    {
        *ppSD = pSD;
        return TRUE;
    }

    return FALSE;
}

次のコード例は、前のコード例の SD を使用して CoInitializeSecurity を暗黙的に呼び出す方法を示しています。

// hKey is the HKCR\AppID\{GUID} key
BOOL SetAccessPermissions(HKEY hkey, PSECURITY_DESCRIPTOR pSD)
{
    BOOL bResult = FALSE;
    DWORD dwLen = GetSecurityDescriptorLength(pSD);
    LONG lResult;
    lResult = RegSetValueExA(hkey, 
        "AccessPermission",
        0,
        REG_BINARY,
        (BYTE*)pSD,
        dwLen);
    if (lResult != ERROR_SUCCESS) goto done;
    bResult = TRUE;
done:
    return bResult;
}

次のコード例は、上記の SD を使用して CoInitializeSecurity を明示的に呼び出す方法を示しています。

// Absolute SD values
PSECURITY_DESCRIPTOR pAbsSD = NULL;
DWORD AbsSdSize = 0;
PACL  pAbsAcl = NULL;
DWORD AbsAclSize = 0;
PACL  pAbsSacl = NULL;
DWORD AbsSaclSize = 0;
PSID  pAbsOwner = NULL;
DWORD AbsOwnerSize = 0;
PSID  pAbsGroup = NULL;
DWORD AbsGroupSize = 0;

MakeAbsoluteSD (
    pSD,
    pAbsSD,
    &AbsSdSize,
    pAbsAcl,
    &AbsAclSize,
    pAbsSacl,
    &AbsSaclSize,
    pAbsOwner,
    &AbsOwnerSize,
    pAbsGroup,
    &AbsGroupSize
);

if (ERROR_INSUFFICIENT_BUFFER == GetLastError())
{
    pAbsSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_FIXED, AbsSdSize);
    pAbsAcl = (PACL)LocalAlloc(LMEM_FIXED, AbsAclSize);
    pAbsSacl = (PACL)LocalAlloc(LMEM_FIXED, AbsSaclSize);
    pAbsOwner = (PSID)LocalAlloc(LMEM_FIXED, AbsOwnerSize);
    pAbsGroup = (PSID)LocalAlloc(LMEM_FIXED, AbsGroupSize);

    if ( ! (pAbsSD && pAbsAcl && pAbsSacl && pAbsOwner && pAbsGroup))
    {
        hr = E_OUTOFMEMORY;
        goto Cleanup;
    }
    if ( ! MakeAbsoluteSD(
        pSD,
        pAbsSD,
        &AbsSdSize,
        pAbsAcl,
        &AbsAclSize,
        pAbsSacl,
        &AbsSaclSize,
        pAbsOwner,
        &AbsOwnerSize,
        pAbsGroup,
        &AbsGroupSize
        ))
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        goto Cleanup;
    }
}
else
{
    hr = HRESULT_FROM_WIN32(GetLastError());
    goto Cleanup;
}

// Call CoInitializeSecurity .

COM アクセス許可と必須アクセス ラベル

Windows Vista では、セキュリティ記述子に 必須のアクセス ラベル の概念が導入されています。 このラベルは、クライアントが COM オブジェクトへの実行アクセスを取得できるかどうかを示します。 ラベルは、セキュリティ記述子のシステム アクセス制御リスト (SACL) 部分で指定されます。 Windows Vista では、COM は SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP ラベルをサポートしています。 WINDOWS Vista より前のオペレーティング システムでは、COM アクセス許可の SACL は無視されます。

Windows Vista の時点では、dcomcnfg.exeは COM アクセス許可の整合性レベル (IL) の変更をサポートしていません。 プログラムで設定する必要があります。

次のコード例は、すべての LOW IL クライアントからの起動/アクティブ化要求を許可するラベルを持つ COM セキュリティ記述子を作成する方法を示しています。 ラベルは、起動/アクティブ化および呼び出しのアクセス許可に対して有効であることに注意してください。 したがって、特定の IL を持つクライアントからの起動、アクティブ化、または呼び出しを禁止する COM サーバーを作成できます。 整合性レベルの詳細については、「保護モードでのインターネット エクスプローラーの理解と動作」の「Windows Vista の整合性メカニズムについて」セクションを参照してください。

BOOL GetLaunchActPermissionsWithIL (SECURITY_DESCRIPTOR **ppSD)
{
// Allow World Local Launch/Activation permissions. Label the SD for LOW IL Execute UP
    LPWSTR lpszSDDL = L"O:BAG:BAD:(A;;0xb;;;WD)S:(ML;;NX;;;LW)";
    if (ConvertStringSecurityDescriptorToSecurityDescriptorW(lpszSDDL, SDDL_REVISION_1, (PSECURITY_DESCRIPTOR *)&pSD, NULL))
    {
        *ppSD = pSD;
        return TRUE;
    }
}

BOOL SetLaunchActPermissions(HKEY hkey, PSECURITY_DESCRIPTOR pSD)
{
    
    BOOL bResult = FALSE;
    DWORD dwLen = GetSecurityDescriptorLength(pSD);
    LONG lResult;
    lResult = RegSetValueExA(hkey, 
        "LaunchPermission",
        0,
        REG_BINARY,
        (BYTE*)pSD,
        dwLen);
    if (lResult != ERROR_SUCCESS) goto done;
    bResult = TRUE;
done:
    return bResult;
};

CoCreateInstance レベルと整合性レベル

Windows Vista では、低 IL クライアントが既定で COM サーバーにバインドされないように、 CoCreateInstance の動作が変更されました。 サーバーでは、SACL を指定して、このようなバインドを明示的に許可する必要があります。 CoCreateInstance の変更点は次のとおりです。

  1. COM サーバー プロセスを起動すると、サーバー プロセス トークン内の IL がクライアントトークンまたはサーバー トークン IL に設定されます。いずれか低い方です。
  2. 既定では、COM は低 IL クライアントが任意の COM サーバーの実行中のインスタンスにバインドできないようにします。 バインドを許可するには、COM サーバーの起動/アクティブ化セキュリティ記述子に、低 IL ラベルを指定する SACL が含まれている必要があります (このようなセキュリティ記述子を作成するサンプル コードについては、前のセクションを参照してください)。

管理者特権でのサーバーと ROT 登録

COM サーバーが実行オブジェクト テーブル (ROT) に登録し、クライアントがその登録にアクセスできるようにする場合は、ROTFLAGS_ALLOWANYCLIENT フラグを使用する必要があります。 DCOM サービス コントロール マネージャー (DCOMSCM) は、このフラグに対してスプーフィング チェックを適用するため、「Activate As Activator」COM サーバーは ROTFLAGS_ALLOWANYCLIENT を指定できません。 そのため、Windows Vista 以降では、COM は 新しい レジストリ エントリのサポートを追加します。これにより、サーバーは ROT 登録を次のような任意のクライアントで使用できるようになります。

HKEY_LOCAL_MACHINE\Software\Classes\AppID
   {APPID}
      ROTFlags

このREG_DWORDエントリの有効な値は次だけです。

ROTREGFLAGS_ALLOWANYCLIENT 0x1

エントリは、 HKEY_LOCAL_MACHINE ハイブに存在する必要があります。

このエントリは、ROTFLAGS_ALLOWANYCLIENT が RunAs サーバーに提供するものと同じ機能を備えた「Activate As Activator」サーバーを提供します。

COM でのセキュリティ

保護モードの Internet Explorer の理解と機能