Realización de comprobaciones de acceso

Una comprobación de acceso determina si un descriptor de seguridad concede un conjunto especificado de derechos de acceso al cliente o subproceso identificado mediante un token de acceso. Puede llamar a la función de seguridad AccessCheck desde aplicaciones cliente de WMI o proveedores escritos en C++ o C#. Los scripts y las aplicaciones de Visual Basic no pueden realizar comprobaciones de acceso mediante el método que se describe aquí.

Las aplicaciones cliente deben realizar una comprobación de acceso para determinar la identidad de la devolución de llamada al devolver los resultados al receptor proporcionado por la llamada asincrónica del cliente.

Cuando los proveedores no pueden suplantar la aplicación cliente o el script que solicita datos, deben realizar comprobaciones de acceso en las situaciones siguientes:

  • Cuando se accede a recursos que no están protegidos por listas de control de acceso (ACL).
  • Cuando el cliente se ha conectado en el nivel de suplantación de RPC_C_LEVEL_IDENTIFY.

Nota

Las aplicaciones de C++ y C# pueden controlar si las comprobaciones de acceso se realizan mediante un proceso independiente. Los scripts y las aplicaciones de Visual Basic pueden leer o cambiar una clave del registro para asegurarse de que WMI realiza comprobaciones de acceso. Para obtener más información, vea Establecimiento de la seguridad en una llamada asincrónica.

 

El ejemplo de código de este tema requiere las siguientes referencias e instrucciones #include para compilarse correctamente.

#include <lmcons.h>
#define _WIN32_DCOM
#define SECURITY_WIN32
#include <wbemidl.h>
#include <security.h>
#include <safestr.h>
#pragma comment(lib, "wbemuuid.lib")
#pragma comment(lib, "Secur32.lib")

En el ejemplo de código siguiente se muestra cómo comprobar que el token de seguridad del subproceso de una aplicación cliente contiene los permisos adecuados para un descriptor de seguridad especificado. La función toma la cadena "domain\user" y devuelve el SID. Si se produce un error en la llamada, la función devuelve NULL; de lo contrario, el autor de la llamada debe liberar el puntero devuelto.

BYTE * GetSid(LPWSTR pwcUserName)
{
    DWORD dwSidSize = 0, dwDomainSize = 0;
    SID_NAME_USE use;

    // first call is to get the size
    BOOL bRet = LookupAccountNameW(

      NULL,            // system name
      pwcUserName,     // account name
      NULL,            // security identifier
      &dwSidSize,      // size of security identifier
      NULL,            // domain name
      &dwDomainSize,   // size of domain name
      &use             // SID-type indicator
      );    

    if(bRet == FALSE && ERROR_INSUFFICIENT_BUFFER 
        != GetLastError())\
        return NULL;

    BYTE * buff = new BYTE[dwSidSize];

    if(buff == NULL)
        return NULL;

    WCHAR * pwcDomain = new WCHAR[dwDomainSize];

    if(pwcDomain == NULL)

    {
        delete [] buff;
        return FALSE;
    }

    // Call to LookupAccountNameW actually gets the SID
    bRet = LookupAccountNameW(

      NULL,           // system name
      pwcUserName,    // account name
      buff,           // security identifier
      &dwSidSize,     // size of security identifier
      pwcDomain,      // domain name
      &dwDomainSize,  // size of domain name
      &use            // SID-type indicator
      );    

    delete [] pwcDomain;

    if(bRet == FALSE)
    {
        delete [] buff;
        return NULL;
    }

    return buff;
}

// This returns true if the caller is coming 
//   from the expected computer in the expected domain.

BOOL IsAllowed(LPWSTR pwsExpectedDomain, 
   LPWSTR pwsExpectedMachine)
{

    WCHAR wCallerName[UNLEN + 1];
    DWORD nSize = UNLEN + 1;

// Impersonate the caller and get its name

    HRESULT hr = CoImpersonateClient();
    if(FAILED(hr))

        return FALSE;

    BOOL bRet = GetUserNameExW(NameSamCompatible, 
       wCallerName, &nSize);

    CoRevertToSelf();

    if(bRet == FALSE)

        return FALSE;


    // take the expected domain and lan manager 
    //   style name and create a SID.  In actual
    // production code, it would be more efficient 
    //   to do this only when necessary

    WCHAR wExpectedName[UNLEN + 1];

    HRESULT hrCopyCat;
    hrCopyCat = StringCchCopy(wExpectedName,
        sizeof(pwsExpectedDomain)*sizeof(WCHAR)+1, 
        pwsExpectedDomain);
    if (FAILED(hrCopyCat))
    {
        return FALSE;
    }
    hrCopyCat = 
        StringCchCat(wExpectedName,sizeof(wExpectedName)
        + 2*sizeof(WCHAR)+1, L"\\");
    if (FAILED(hrCopyCat))
    {
        return FALSE;
    }
    hrCopyCat = StringCchCat(wExpectedName,sizeof(wExpectedName)
        + sizeof(pwsExpectedMachine)*sizeof(WCHAR)+1, 
        pwsExpectedMachine);
    if (FAILED(hrCopyCat))
    {
        return FALSE;
    }
    hrCopyCat = StringCchCat(wExpectedName,sizeof(wExpectedName)
        + sizeof(WCHAR)+1, L"$");
    if (FAILED(hrCopyCat))
    {
        return FALSE;
    }
  

    // convert the two names to SIDs and compare.  
    // Note that SIDs are used since 
    //   the format of the names might vary.  

    BYTE * pCaller = GetSid(wCallerName);

    if(pCaller == NULL)

        return FALSE;

    BYTE * pExpected = GetSid(wExpectedName);

    if(pExpected == NULL)
    {
        delete [] pCaller;

        return FALSE;
    }

    bRet = EqualSid((PSID)pCaller, (PSID)pExpected);

    delete [] pCaller;
    delete [] pExpected;

    return bRet;
}

Elección del registro correcto

Mantenimiento de la seguridad de WMI

Protección del proveedor

Acceso a espacios de nombres WMI