非同期呼び出しでのセキュリティの設定

非同期呼び出しでは、"シンク" へのコールバックが元のアプリケーションまたはスクリプトによる非同期呼び出しの結果ではない可能性があるため、重大なセキュリティ リスクが発生します。 リモート接続のセキュリティは、クライアントとリモート コンピューター上のプロバイダー間の通信の暗号化に基づいています。 C++ では、CoInitializeSecurity の呼び出しで認証レベル パラメーターを使用して暗号化を設定できます。 スクリプトで、モニカー接続または SWbemSecurity オブジェクトに AuthenticationLevel を設定します。 詳細については、「VBScript を使用した既定のプロセス セキュリティ レベルの設定」を参照してください。

WMI では、コールバックが成功するまでコールバックの認証レベルが下げられるため、非同期呼び出しのセキュリティ リスクが存在します。 発信非同期呼び出しでは、クライアントは WMI への接続で認証レベルを設定できます。 WMI によってクライアント呼び出しのセキュリティ設定が取得され、同じ認証レベルでコールバックが試みられます。 コールバックは常に RPC_C_AUTHN_LEVEL_PKT_PRIVACY レベルで開始されます。 コールバックが失敗した場合、WMI では認証レベルをコールバックが成功してできるレベルに、必要に応じて RPC_C_AUTHN_LEVEL_NONE まで下げます。 認証サービスが Kerberos ではないローカル システム内の呼び出しのコンテキストでは、コールバックは常に RPC_C_AUTHN_LEVEL_NONE で返されます。

最小認証レベルは RPC_C_AUTHN_LEVEL_PKT です (スクリプトの場合は wbemAuthenticationLevelPkt)。 ただし、RPC_C_AUTHN_LEVEL_PKT_PRIVACY (wbemAuthenticationLevelPktPrivacy) などの上位レベルを指定できます。 クライアント アプリケーションまたはスクリプトで認証レベルを RPC_C_AUTHN_LEVEL_DEFAULT (wbemAuthenticationLevelDefault) に設定することをお勧めします。これにより、認証レベルをサーバーで指定されたレベルにネゴシエートできます。

HKEY_LOCAL_MACHINE\ソフトウェア\Microsoft\WBEM\CIMOM\UnsecAppAccessControlDefault レジストリ値は、コールバックで許容される認証レベルを WMI でチェックするかどうかを制御します。 これは、スクリプトまたは Visual Basic で行われた非同期呼び出しのシンク セキュリティを保護するための唯一のメカニズムです。 既定では、このレジストリ キーは 0 に設定されています。 レジストリ キーが 0 の場合、WMI では認証レベルを検証しません。 スクリプトで非同期呼び出しをセキュリティで保護するには、レジストリ キーを 1 に設定します。 C++ クライアントでは IWbemUnsecuredApartment::CreateSinkStub を呼び出して、シンクへのアクセスを制御できます。 この値は、既定で任意の場所に作成されます。

次のトピックでは、非同期呼び出しセキュリティを設定する例を示します。

C++ での非同期呼び出しセキュリティの設定

IWbemUnsecuredApartment::CreateSinkStub メソッドは IUnsecuredApartment::CreateObjectStub メソッドに似ており、コールバックを受信するために、別のプロセス Unsecapp.exe にシンクが作成されます。 ただし、CreateSinkStub メソッドには、個別のプロセスでアクセス制御を処理する方法を指定する dwFlag パラメーターがあります。

dwFlag パラメーターは、Unsecapp.exe に対して次のいずれかのアクションを指定します。

  • レジストリ キー設定を使用して、アクセスをチェックするかどうかを判断します。
  • レジストリ キーを無視し、常にアクセスをチェックします。
  • レジストリ キーを無視し、決してアクセスをチェックしません。

このトピックのコードの例を正しくコンパイルするには、次の #include ステートメントが必要です。

#include <wbemidl.h>

次の手順では、IWbemUnsecuredApartment を使用して非同期呼び出しを実行する方法について説明します。

IWbemUnsecuredApartment で非同期呼び出しを実行するには

  1. CoCreateInstance を呼び出して専用プロセスを作成します。

    次のコード例では、CoCreateInstance を呼び出して、専用プロセスを作成します。

    CLSID                    CLSID_WbemUnsecuredApartment;
    IWbemUnsecuredApartment* pUnsecApp = NULL;
    
    CoCreateInstance(CLSID_WbemUnsecuredApartment, 
                     NULL, 
                     CLSCTX_LOCAL_SERVER, 
                     IID_IWbemUnsecuredApartment, 
                     (void**)&pUnsecApp);
    
  2. シンク オブジェクトのインスタンスを作成します。

    次のコード例では、新しいシンク オブジェクトを作成します。

    CMySink* pSink = new CMySink;
    pSink->AddRef();
    
  3. シンクに対するスタブを作成します。

    スタブは、シンクから生成されるラッパー関数です。

    次のコード例では、シンク用のスタブを作成しています。

    LPCWSTR          wszReserved = NULL;           
    IWbemObjectSink* pStubSink   = NULL;
    IUnknown*        pStubUnk    = NULL; 
    
    pUnsecApp->CreateSinkStub(pSink,
                              WBEM_FLAG_UNSECAPP_CHECK_ACCESS,  //Authenticate callbacks regardless of registry key
                              wszReserved,
                              &pStubSink);
    
  4. シンク オブジェクト ポインターを解放します。

    スタブでポインターを所有するようになったため、オブジェクト ポインターを解放できます。

    次のコード例では、オブジェクト ポインターを解放しています。

    pSink->Release();
    
  5. 任意の非同期呼び出しでスタブを使用します。

    呼び出しが完了したら、ローカル参照カウントを解放します。

    次のコード例では、非同期呼び出しでスタブを使用しています。

    // pServices is an IWbemServices* object
    pServices->CreateInstanceEnumAsync(strClassName, 0, NULL, pStubSink);
    

    呼び出し後に非同期呼び出しを取り消す必要がある場合があります。 呼び出しを取り消す必要がある場合は、最初に呼び出しを行ったのと同じポインターを使用して呼び出しをキャンセルします。

    次のコード例では、非同期呼び出しを取り消す方法を示しています。

    pServices->CancelAsyncCall(pStubSink);
    
  6. 非同期呼び出しの使用が完了したら、ローカル参照カウントを解放します。

    非同期呼び出しを取り消す必要がないことを確認した後にのみ、pStubSink ポインターを解放してください。 また、WMI によって pSink シンク ポインターが解放された後は、pStubSink を解放しないでください。 pSink の後に pStubSink を解放すると、シンクとスタブの両方がメモリに永遠に保持される循環参照カウントが作成されます。 代わりに、ポインターを解放できる場所は、元の非同期呼び出しが完了したことを報告するために WMI によって行われた IWbemObjectSink::SetStatus 呼び出し内です。

  7. 完了したら、Release() への呼び出しを使用して、COM を初期化前の状態に戻します。

    次のコード例は、pUnsecApp ポインターで Release() を呼び出す方法を示しています。

    pUnsecApp->Release();
    

CoInitializeSecurity 関数とパラメーターの詳細については、COM のドキュメントを参照してください。