DevSecOps

13 fev, 2014

Serviços rodando sob contas de domínio devem acrescentar o serviço dnscache como dependência

Publicidade

Ano passado, tive que investigar um deadlock em uma máquina Windows Server 2012. Qualquer tentativa de iniciar ou parar um serviço nessa máquina congelava e não voltava mais. Eu não conhecia o recurso “Analyze Wait Chain” no Gerenciador de Tarefas (novo desde o Windows 7), mas acontece que ele economiza muito tempo. Esse recurso utiliza a API de depuração Wait Chain Traversal e é, na verdade, o recurso favorito do Matt Pietrek no Windows 7. Simplesmente usando o Gerenciador de Tarefas, pude ver quantos programas estavam esperando pelo serviço “Service e Controller app”, que é o SCM (o Service and Controller Manager):

wait-chain-scm

O services.exe com PID 588 é o serviço SCM “Service and Controller app”. Apenas um entre todos que estavam bloqueando as threads no SCM estavam esperando pela thread 1072, e a thread 1072 estava esperando pelo serviço LSA. Tirei um dump do PID 588, e essas threads bloqueadas tinham esta pequena pilha:

ntdll!ZwWaitForSingleObject
ntdll!RtlpWaitOnCriticalSection
ntdll!RtlpEnterCriticalSectionContended
services!ScStartServiceAndDependencies
services!RStartServiceW
rpcrt4!Invoke
...

Não sou um especialista em Windows, mas, apenas chutando os nomes das funções, posso dizer que essa é uma thread servindo uma requisição RPC para iniciar um serviço e aguardar uma seção crítica. Sabendo pela sequência de espera do Gerenciador de Tarefas que a thread bloqueada é a 1072 (poderíamos usar outras maneiras para encontrar o dono da seção crítica, mas por que se preocupar com isso?), a pilha da thread 1072 exibe que está esperando no LSA pelo logon de um usuário:

...
rpcrt4!NdrClientCall3
sspicli!SspirLogonUser
sspicli!SspipLogonUser
sspicli!LsaLogonUser
sspicli!L32pLogonUser
sspicli!LogonUserExExW
services!ScLogonService
services!ScLogonAndStartImage
services!ScStartService
services!ScStartMarkedServices
services!ScStartServiceAndDependencies
services!RStartServiceW
rpcrt4!Invoke
...

Tirando um dump do processo Isass.exe mostra que há apenas uma thread ativa:

...
rpcrt4!NdrClientCall2
sechost!RStartServiceW
sechost!StartServiceW
dnsapi!StartDnsServiceOnDemand
dnsapi!Query_PrivateExW
dnsapi!Query_Shim
dnsapi!DnsQuery_UTF8
netlogon!NetpSrvOpen
netlogon!NetpDcGetDcNext
netlogon!NetpDcGetNameSiteIp
netlogon!NetpDcGetNameIp
netlogon!NetpDcGetName
netlogon!DsIGetDcName
netlogon!DsrGetDcNameEx2
kerberos!KerbGetKdcBinding
kerberos!KerbMakeSocketCallEx
kerberos!KerbMakeSocketCall
kerberos!KerbGetAuthenticationTicketEx
kerberos!KerbGetTicketGrantingTicket
kerberos!LsaApLogonUserEx2
lsasrv!NegLogonUserEx2Worker
lsasrv!NegLogonUserEx2
lsasrv!LsapCallAuthPackageForLogon
lsasrv!LsapAuApiDispatchLogonUser
lsasrv!SspiExLogonUser
sspisrv!SspirLogonUser
rpcrt4!Invoke
...

Então aí está, o LSA está tentando iniciar um serviço de cliente DNS (também chamado de dnscache) para responder a uma requisição de logon de um usuário. Essa chamada volta ao SCM e bloqueia a seção crítica mantida pela thread 1072. Essa é uma lista de espera circular, caracterizando um deadlock. Nosso servidor estava travado nesse estado por vários dias antes da investigação.

Ao analisar o problema, fica claro que qualquer serviço executado sob uma conta de domínio pode passar por esse problema durante a inicialização da máquina. A conta de domínio é necessária porque é, na verdade, um domínio que aciona o LSA para buscar o DNS para encontrar o controle de domínio para o domínio da conta do serviço.

Eu não tinha visto esse problema antes, então provavelmente deve ser raro. Muitas coisas precisam acontecer para que ele surja. Entretanto, caso aconteçam, é difícil de investigar, ele não desaparece, corrige a si mesmo e misteriosamente some depois de um reboot. Minha recomendação é para tornar qualquer serviço rodando como uma conta de domínio, dependente do serviço dnscache. Dessa forma, o SCM se assegurará de que o serviço dnscache é iniciado antes de pedir ao LSA para criar um token de logon para o serviço que deseja iniciar, eliminando portanto a possibilidade de um deadlock. A desvantagem é que parar o serviço dnscache também irá parar o seu serviço, o que pode ser considerado desvantajoso em algumas situações.

***

Artigo traduzido pela Redação iMasters, com autorização do autor. Publicado originalmente em http://rusanu.com/2013/08/27/services-running-under-domain-account-should-add-dnscache-service-as-dependency/