O apelido de elevação COM

O moniker de elevação COM permite que aplicativos em execução sob controle de conta de usuário (UAC) ativem classes COM com privilégios elevados. Para obter mais informações, consulte Foco no privilégio mínimo.

Quando usar o apelido de elevação

O moniker de elevação é usado para ativar uma classe COM para realizar uma função específica e limitada que requer privilégios elevados, como alterar a data e a hora do sistema.

A elevação requer a participação de uma classe COM e de seu cliente. A classe COM deve ser configurada para oferecer suporte à elevação anotando sua entrada do Registro, conforme descrito na seção Requisitos. O cliente COM deve solicitar elevação usando o moniker de elevação.

O moniker de elevação não se destina a fornecer compatibilidade de aplicativos. Por exemplo, se você deseja executar um aplicativo COM herdado, como o WinWord, como um servidor elevado, você deve configurar o executável do cliente COM para exigir elevação, em vez de ativar a classe do aplicativo herdado com o moniker de elevação. Quando o cliente COM elevado chama CoCreateInstance usando o CLSID do aplicativo herdado, o estado elevado do cliente fluirá para o processo do servidor.

Nem toda a funcionalidade COM é compatível com elevação. A funcionalidade que não funcionará inclui:

  • A elevação não flui de um cliente para um servidor COM remoto. Se um cliente ativar um servidor COM remoto com o moniker de elevação, o servidor não será elevado, mesmo que ofereça suporte à elevação.
  • Se uma classe COM elevada usa representação durante uma chamada COM, ela pode perder seus privilégios elevados durante a representação.
  • Se um servidor COM elevado registrar uma classe na tabela de objetos em execução (ROT), a classe não estará disponível para clientes não elevados.
  • Um processo elevado usando o mecanismo UAC não carrega classes por usuário durante ativações COM. Para aplicativos COM, isso significa que as classes COM do aplicativo devem ser instaladas no hive do Registro HKEY_LOCAL_MACHINE se o aplicativo deve ser usado por contas não privilegiadas e privilegiadas. As classes COM do aplicativo só precisam ser instaladas no hive HKEY_USERS se o aplicativo nunca for usado por contas privilegiadas.
  • Não é permitido arrastar e soltar de aplicativos não elevados para elevados.

Requisitos

Para usar o moniker de elevação para ativar uma classe COM, a classe deve ser configurada para ser executada como o usuário de inicialização ou a identidade do aplicativo 'Ativar como Ativador'. Se a classe estiver configurada para ser executada sob qualquer outra identidade, a ativação retornará a CO_E_RUNAS_VALUE_MUST_BE_AAA de erro.

A classe também deve ser anotada com um nome de exibição "amigável" que seja compatível com MUI (interface do usuário multilíngue). Isso requer a seguinte entrada do Registro:

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

Se essa entrada estiver ausente, a ativação retornará a CO_E_MISSING_DISPLAYNAME de erro. Se o arquivo MUI estiver ausente, o código de erro da função RegLoadMUIStringW será retornado.

Opcionalmente, para especificar um ícone de aplicativo a ser exibido pela interface do usuário do UAC, adicione a seguinte chave do Registro:

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

IconReference usa o mesmo formato que LocalizedString:

@pathtobinary,-resourcenumber

Além disso, o componente COM deve ser assinado para que o ícone seja exibido.

A classe COM também deve ser anotada como LUA-Enabled. Isso requer a seguinte entrada do Registro:

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

Se essa entrada estiver ausente, a ativação retornará a CO_E_ELEVATION_DISABLED de erro.

Observe que essas entradas devem existir na colmeia HKEY_LOCAL_MACHINE, não na colmeia HKEY_CURRENT_USER ou HKEY_USERS. Isso impede que os usuários elevem classes COM que eles também não tinham privilégios para registrar.

O apelido de elevação e a interface do usuário de elevação

Se o cliente já estiver elevado, o uso do moniker de elevação não fará com que a interface do usuário de elevação seja exibida.

Como usar o apelido de elevação

O moniker de elevação é um moniker COM padrão, semelhante aos monikers de sessão, partição ou fila. Ele direciona uma solicitação de ativação para um servidor especificado com o nível de elevação especificado. O CLSID a ser ativado aparece na cadeia de caracteres de apelido.

O moniker de elevação suporta os seguintes tokens de nível de execução:

  1. Administrador
  2. O mais alto

A sintaxe para isso é a seguinte:

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

A sintaxe anterior usa o moniker "new" para retornar uma instância da classe COM especificada por guid. Observe que o moniker "novo" usa internamente a interface IClassFactory para obter um objeto de classe e, em seguida, chama IClassFactory::CreateInstance nele.

O moniker de elevação também pode ser usado para obter um objeto de classe, que implementa IClassFactory. Em seguida, o chamador chama CreateInstance para obter uma instância de objeto. A sintaxe para isso é a seguinte:

Elevation:Administrator!clsid:{guid}

Código de exemplo

O exemplo de código a seguir mostra como usar o moniker de elevação. Ele pressupõe que você já inicializou COM no thread atual.

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 é novo no Windows Vista. É derivado de BIND_OPTS2.

A única adição é um campo HWND , hwnd. Esse identificador representa uma janela que se torna o proprietário da interface do usuário de elevação, se aplicável.

Se hwnd for NULL, COM chamará GetActiveWindow para localizar um identificador de janela associado ao thread atual. Esse caso pode ocorrer se o cliente for um script, que não pode preencher uma estrutura BIND_OPTS3 . Nesse caso, COM tentará usar a janela associada ao thread de script.

Elevação sobre o ombro (OTS)

A elevação OTS (Over-the-shoulder) refere-se ao cenário em que um cliente executa um servidor COM com as credenciais de um administrador em vez de suas próprias. (O termo "por cima do ombro" significa que o administrador está vigiando o ombro do cliente enquanto o cliente executa o servidor.)

Esse cenário pode causar um problema para chamadas COM no servidor, porque o servidor pode não chamar CoInitializeSecurity explicitamente (ou seja, programaticamente) ou implicitamente (ou seja, declarativamente, usando o registro). Para esses servidores, COM calcula um descritor de segurança que permite que apenas SELF, SYSTEM e Builtin\Administrators façam chamadas COM no servidor. Esse arranjo não funcionará em cenários OTS. Em vez disso, o servidor deve chamar CoInitializeSecurity, explícita ou implicitamente, e especificar uma ACL que inclua o SID e o SYSTEM do grupo INTERATIVO.

O exemplo de código a seguir mostra como criar um descritor de segurança (SD) com o SID de grupo INTERATIVO.

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;
}

O exemplo de código a seguir mostra como chamar CoInitializeSecurity implicitamente com o SD do exemplo de código anterior:

// 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;
}

O exemplo de código a seguir mostra como chamar CoInitializeSecurity explicitamente com o SD acima:

// 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 .

Permissões COM e rótulos de acesso obrigatórios

O Windows Vista introduz a noção de rótulos de acesso obrigatórios nos descritores de segurança. O rótulo determina se os clientes podem obter acesso de execução a um objeto COM. O rótulo é especificado na parte da lista de controle de acesso do sistema (SACL) do descritor de segurança. No Windows Vista, COM oferece suporte ao rótulo SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP. SACLs nas permissões COM são ignoradas em sistemas operacionais anteriores ao Windows Vista.

A partir do Windows Vista, dcomcnfg.exe não oferece suporte à alteração do nível de integridade (IL) em permissões COM. Deve ser definido programaticamente.

O exemplo de código a seguir mostra como criar um descritor de segurança COM com um rótulo que permite solicitações de inicialização/ativação de todos os clientes LOW IL. Observe que os rótulos são válidos para as permissões Iniciar/Ativação e Chamada. Assim, é possível escrever um servidor COM que não permita lançamento, ativação ou chamadas de clientes com um determinado IL. Para obter mais informações sobre níveis de integridade, consulte a seção "Noções básicas sobre o mecanismo de integridade do Windows Vista" em Noções básicas e trabalho no modo protegido do Internet Explorer.

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 e níveis de integridade

O comportamento de CoCreateInstance foi alterado no Windows Vista, para impedir que clientes Low IL se associem a servidores COM por padrão. O servidor deve permitir explicitamente essas associações, especificando a SACL. As alterações em CoCreateInstance são as seguintes:

  1. Ao iniciar um processo de servidor COM, o IL no token de processo do servidor é definido como o token de cliente ou servidor IL, o que for menor.
  2. Por padrão, o COM impedirá que clientes de IL Baixa se associem a instâncias em execução de qualquer servidor COM. Para permitir a ligação, o descritor de segurança Iniciar/Ativação de um servidor COM deve conter uma SACL que especifique o rótulo Low IL (consulte a seção anterior para obter o código de exemplo para criar esse descritor de segurança).

Servidores elevados e registros ROT

Se um servidor COM deseja se registrar na tabela de objetos em execução (ROT) e permitir que qualquer cliente acesse o registro, ele deve usar o sinalizador ROTFLAGS_ALLOWANYCLIENT. Um servidor COM "Ativar como ativador" não pode especificar ROTFLAGS_ALLOWANYCLIENT porque o gerenciador de controle de serviço DCOM (DCOMSCM) impõe uma verificação de falsificação contra esse sinalizador. Portanto, no Windows Vista, o COM adiciona suporte para uma nova entrada do Registro que permite que o servidor estipule que seus registros ROT sejam disponibilizados para qualquer cliente:

HKEY_LOCAL_MACHINE\Software\Classes\AppID
   {APPID}
      ROTFlags

O único valor válido para esta entrada REG_DWORD é:

ROTREGFLAGS_ALLOWANYCLIENT 0x1

A entrada deve existir na colmeia HKEY_LOCAL_MACHINE .

Esta entrada fornece um servidor "Ativar como ativador" com a mesma funcionalidade que ROTFLAGS_ALLOWANYCLIENT fornece para um servidor RunAs.

Segurança em COM

Compreendendo e trabalhando no Modo Protegido do Internet Explorer