Suplantar un cliente

Cuando una aplicación de usuario solicita datos de objetos en el sistema a través de un proveedor WMI, una suplantación significa que el proveedor presenta credenciales que representan el nivel de seguridad de los clientes en lugar de la de los proveedores. La suplantación impide que un cliente obtenga acceso no autorizado a la información del sistema.

En este tema se describen las secciones siguientes:

WMI normalmente se ejecuta como un servicio administrativo en un nivel de seguridad alto, mediante el contexto de seguridad LocalServer. Usar un servicio administrativo proporciona a WMI los medios para acceder a información con privilegios. Al llamar a un proveedor para obtener información, WMI pasa su identificador de seguridad (SID) al proveedor, lo que le permite acceder a la información en el mismo nivel de seguridad alto.

Durante el proceso de inicio de la aplicación WMI, el sistema operativo Windows le proporciona a la aplicación el contexto de seguridad del usuario que inició el proceso. El contexto de seguridad del usuario suele ser un nivel de seguridad inferior al de LocalServer, por lo que es posible que el usuario no tenga permiso para acceder a toda la información disponible para WMI. Cuando la aplicación de usuario solicita información dinámica, WMI le pasa el SID del usuario al proveedor correspondiente. Si se escribe adecuadamente, el proveedor intenta acceder a la información con el SID de usuario, en lugar del SID del proveedor.

Para que el proveedor suplante correctamente la aplicación cliente, tanto la aplicación cliente como el proveedor deben cumplir los siguientes criterios:

Registrar un proveedor para suplantación

WMI solo pasa el SID de una aplicación cliente a proveedores que se han registrado como proveedores de suplantación. Para permitir que un proveedor realice la suplantación, es necesario modificar el proceso de registro del proveedor.

En el procedimiento siguiente se describe cómo registrar un proveedor para suplantación. En el procedimiento se supone que ha entendido el proceso de registro. Para obtener más información sobre el proceso de registro, consulte Registrar un proveedor.

Para registrar un proveedor para suplantación

  1. Establezca la propiedad ImpersonationLevel de la clase __Win32Provider que representa al proveedor en 1.

    La propiedad ImpersonationLevel documenta si el proveedor admite la suplantación o no. Al establecer ImpersonationLevel en 0, se indica que el proveedor no suplanta al cliente y realiza todas las operaciones solicitadas en el mismo contexto de usuario que WMI. Al establecer ImpersonationLevel en 1, se indica que el proveedor usa llamadas de suplantación para comprobar las operaciones realizadas en nombre del cliente.

  2. Establezca la propiedad PerUserInitialization de la misma clase __Win32Provider enTRUE.

Nota:

Si registra un proveedor con la propiedad __Win32Providerde InitializeAsAdminFirst establecida en TRUE, el proveedor usa el token de seguridad de subprocesos de nivel de administración solo durante la fase de inicialización. Aunque no se produce un error en una llamada a CoImpersonateClient, el proveedor usa el contexto de seguridad de WMI y no del cliente.

 

En el siguiente código de ejemplo se muestra cómo registrar un proveedor para suplantación.

instance of __Win32Provider
{
    CLSID = "{FD4F53E0-65DC-11d1-AB64-00C04FD9159E}";
    ImpersonationLevel = 1;
    Name = "MS_NT_EVENTLOG_PROVIDER";
    PerUserInitialization = TRUE;
};

Establecer niveles de suplantación dentro de un proveedor

Si registra un proveedor con la propiedad de clase __Win32ProviderImpersonationLevel establecido en 1, WMI llama al proveedor para suplantar varios clientes. Para controlar estas llamadas, use las funciones COM CoImpersonateClient y CoRevertToSelf en la implementación de la interfaz IWbemServices .

La función CoImpersonateClient permite a un servidor suplantar al cliente que realizó la llamada. Al realizar una llamada a CoImpersonateClient en la implementación de IWbemServices, permite al proveedor establecer el token de subproceso del proveedor para que coincida con el token del cliente y, por tanto, suplantarle. Si no llama a CoImpersonateClient, el proveedor ejecuta código en un nivel de seguridad de administrador, lo que crea una posible vulnerabilidad de seguridad. Si el proveedor necesita actuar temporalmente como administrador o realizar la comprobación de acceso manualmente, llame a CoRevertToSelf.

A diferencia de CoImpersonateClient, CoRevertToSelf es una función COM que controla los niveles de suplantación de subprocesos. En este caso, CoRevertToSelf cambia el nivel de suplantación a la configuración de suplantación original. En general, el proveedor es inicialmente un administrador y alterna entre CoImpersonateClient y CoRevertToSelf en función de si realiza una llamada que representa al autor de la llamada o a sus propias llamadas. Es responsabilidad del proveedor realizar estas llamadas correctamente para no exponer un agujero de seguridad al usuario final. Por ejemplo, el proveedor solo debe llamar a funciones nativas de Windows dentro de la secuencia de código suplantada.

Nota:

El propósito de CoImpersonateClient y CoRevertToSelf es establecer la seguridad de un proveedor. Si determina que se ha producido un error en la suplantación, debe devolver un código de finalización adecuado a WMI a través de IWbemObjectSink::SetStatus. Para obtener más información, consulte Controlar mensajes denegados de acceso en un proveedor.

 

Mantener los niveles de seguridad en un proveedor

Los proveedores no pueden llamar solamente una vez a CoImpersonateClient en una implementación de IWbemServices y suponer que las credenciales de suplantación permanecen en vigor durante la duración del proveedor. En su lugar, llame a CoImpersonateClient varias veces durante el transcurso de una implementación para evitar que WMI cambie las credenciales.

La principal preocupación por establecer la suplantación para un proveedor es la reentrada. En este contexto, la reentrada se produce cuando un proveedor realiza una llamada a WMI para obtener información y espera hasta que WMI le devuelva la llamada. En resumen, el subproceso de ejecución deja el código del proveedor, solo para volver a escribirlo en una fecha posterior. La reentrada forma parte del diseño de COM y, por lo general, no es debe preocuparse. Sin embargo, cuando el subproceso de ejecución entra en WMI, este toma los niveles de suplantación de WMI. Cuando el subproceso vuelve al proveedor, debe restablecer los niveles de suplantación con otra llamada a CoImpersonateClient.

Para protegerse de los agujeros de seguridad de su proveedor, debe realizar llamadas reentrantes a WMI solo al suplantar al cliente. Es decir, las llamadas a WMI deben realizarse después de llamar a CoImpersonateClient y antes de llamar a CoRevertToSelf. Dado que CoRevertToSelf hace que la suplantación se establezca en el nivel de usuario WMI se está ejecutando, por lo general LocalSystem, las llamadas reentrantes a WMI después de llamar a CoRevertToSelf podrían proporcionar al usuario y a cualquier proveedor llamado, muchas más funcionalidades de las que deberían tener.

Nota:

Si llama a una función del sistema u otro método de interfaz, no se garantiza que se mantenga el contexto de llamada.

 

Controlar mensajes denegados de acceso en un proveedor

La mayoría de los mensajes de error de Acceso denegado aparecen cuando un cliente solicita una clase o información a la que no tiene acceso. Si el proveedor devuelve un mensaje de error de Acceso denegado a WMI y este se lo pasa al cliente, el cliente puede deducir que existe la información. En algunas situaciones, esto puede llegar a ser una infracción de seguridad. Por lo tanto, el cliente no debe recibir este mensaje. Por lo tanto, el conjunto de clases que el proveedor habría proporcionado no debe exponerse. Por ese motivo, un proveedor de instancias dinámicas debe llamar al origen de datos subyacente para determinar cómo tratar los mensajes de Acceso denegado. Es responsabilidad del proveedor replicar esa filosofía en el entorno de WMI. Para obtener más información, consulte Hacer informes de instancias parciales y Hacer informes de enumeraciones parciales.

Al determinar cómo debe controlar el proveedor los mensajes de Acceso denegado, debe escribir y depurar el código. Durante la depuración, es conveniente distinguir entre una denegación debido a una suplantación baja y una denegación debido a un error en el código. Puede usar una prueba sencilla en el código para determinar la diferencia. Para obtener más información, consulte Depurar el código de acceso denegado.

Hacer informes de instancias parciales

Suele aparecer el mensaje Acceso denegado cuando WMI no puede proporcionar toda la información para rellenar una instancia. Por ejemplo, un cliente puede tener la autoridad para ver un objeto de unidad de disco duro, pero puede que no tenga autoridad para ver cuánto espacio está disponible en la propia unidad de disco duro. El proveedor debe determinar cómo controlar cualquier situación cuando el proveedor no puede rellenar completamente una instancia con propiedades debido a una infracción de acceso.

WMI no requiere una única respuesta a los clientes que tienen acceso parcial a una instancia. En su lugar, WMI versión 1.x permite al proveedor una de las siguientes opciones:

  • Producir un error en toda la operación con WBEM_E_ACCESS_DENIED y no devolver ninguna instancia.

    Devolver un objeto de error junto con WBEM_E_ACCESS_DENIED para describir el motivo de la denegación.

  • Devolver todas las propiedades disponibles y rellenar las propiedades no disponibles con NULL.

Nota:

Asegúrese de que devolver WBEM_E_ACCESS_DENIED no crea ninguna brecha de seguridad en su empresa.

 

Hacer informes de enumeraciones parciales

Otra infracción de acceso es cuando WMI no puede devolver toda una enumeración. Por ejemplo, un cliente puede tener acceso para ver todos los objetos de equipo de red local, pero puede que no tenga acceso para ver objetos de equipo fuera de su dominio. El proveedor debe determinar cómo controlar cualquier situación cuando no se puede completar una enumeración debido a una infracción de acceso.

Al igual que con un proveedor de instancias, WMI no requiere una única respuesta a una enumeración parcial. En su lugar, WMI versión 1.x permite al proveedor una de las siguientes opciones:

  • Devolver WBEM_S_NO_ERROR para todas las instancias a las que el proveedor puede acceder.

    Si usa esta opción, el usuario no podrá saber que algunas instancias no estaban disponibles. Varios proveedores, como los que usan el Lenguaje de consulta estructurado (SQL) con seguridad de nivel de fila, devuelven resultados parciales correctos mediante el nivel de seguridad del llamador para definir el conjunto de resultados.

  • Producir un error en toda la operación con WBEM_E_ACCESS_DENIED y no devolver ninguna instancia.

    Opcionalmente, el proveedor puede incluir un objeto de error que describa la situación para el cliente. Tenga en cuenta que algunos proveedores pueden acceder a orígenes de datos en serie y es posible que no encuentren denegaciones hasta que pasen por la enumeración.

  • Devuelve todas las instancias a las que se puede acceder, pero también devuelve el código de estado noerror WBEM_S_ACCESS_DENIED.

    El proveedor debe tener en cuenta la denegación durante la enumeración y puede seguir proporcionando instancias, terminando con el código de estado no error. El proveedor también puede optar por finalizar la enumeración en la primera denegación. La justificación de esta opción es que los distintos proveedores tienen paradigmas de recuperación diferentes. Es posible que un proveedor ya haya entregado instancias antes de detectar una infracción de acceso. Algunos proveedores pueden optar por seguir proporcionando otras instancias y otros pueden querer finalizar.

Debido a la estructura de COM, no puede calcular las referencias de ninguna información durante un error, excepto un objeto de error. Por lo tanto, no se puede devolver la información y un código de error. Si decide devolver información, debe usar un código de estado no error en su lugar.

Depurar el código denegado de acceso

Algunas aplicaciones pueden usar niveles de suplantación inferiores a RPC_C_IMP_LEVEL_IMPERSONATE. En este caso, se producirá un error en la mayoría de las llamadas de suplantación realizadas por el proveedor para la aplicación cliente. Para diseñar e implementar correctamente un proveedor, debe tener en cuenta esta idea.

De forma predeterminada, al único nivel de suplantación que puede acceder un proveedor es RPC_C_IMP_LEVEL_IDENTIFY. En los casos en los que una aplicación cliente usa RPC_C_IMP_LEVEL_IDENTIFY, CoImpersonateClient no devuelve un código de error. En su lugar, el proveedor suplanta al cliente solo con fines de identificación. Por lo tanto, la mayoría de los métodos de Windows a los que llama el proveedor devolverán un mensaje de acceso denegado. Esto es inofensivo en la práctica, ya que los usuarios no podrán hacer nada inapropiado. Sin embargo, puede ser útil durante el desarrollo del proveedor saber si el cliente realmente se ha suplantado o no.

El código requiere las siguientes referencias y #include instrucciones para compilar correctamente.

#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <wbemidl.h>

En el ejemplo de código siguiente se muestra cómo determinar si un proveedor ha suplantado correctamente una aplicación cliente.

DWORD dwImp = 0;
HANDLE hThreadTok;
DWORD dwBytesReturned;
BOOL bRes;

// You must call this before trying to open a thread token!
CoImpersonateClient();

bRes = OpenThreadToken(
    GetCurrentThread(),
    TOKEN_QUERY,
    TRUE,
    &hThreadTok
);

if (bRes == FALSE)
{
    printf("Unable to read thread token (%d)\n", GetLastError());
    return 0;
}

bRes = GetTokenInformation(
    hThreadTok,
    TokenImpersonationLevel, 
    &dwImp,
    sizeof(DWORD),
    &dwBytesReturned
);

if (!bRes)
{
    printf("Unable to read impersonation level\n");
    CloseHandle(hThreadTok);
    return 0;
}

switch (dwImp)
{
case SecurityAnonymous:
    printf("SecurityAnonymous\n");
    break;

case SecurityIdentification:
    printf("SecurityIdentification\n");
    break;

case SecurityImpersonation:
    printf("SecurityImpersonation\n");
    break;

case SecurityDelegation:
    printf("SecurityDelegation\n");
    break;

default:
    printf("Error. Unable to determine impersonation level\n");
    break;
}

CloseHandle(hThreadTok);

Desarrollo de un proveedor WMI

Establecer descriptores de seguridad del espacio de nombres

Protección del proveedor