Como obter programaticamente privilégios de root?

Estou escrevendo algum software (em C ++, para Linux / Mac OSX) que é executado como um usuário não privilegiado, mas precisa de privilégios de root em algum momento (para criar um novo dispositivo virtual).

A execução deste programa como root não é uma opção (principalmente para questões de segurança) e eu preciso conhecer a identidade (uid) do usuário “real”.

Existe uma maneira de imitar o comportamento do comando “sudo” (pedir senha do usuário) para obter temporariamente privilégios de root e executar a tarefa específica? Em caso afirmativo, quais funções eu usaria?

Muito obrigado pela sua ajuda!

    Resposta original

    Você pode considerar o switch setuid no próprio executável. A Wikipedia tem um artigo sobre isso que mostra a diferença entre geteuid() e getuid() bastante eficácia, o primeiro sendo para descobrir quem você está “emulando” e o segundo para quem você “é”. O processo sudo, por exemplo, geteuid deve retornar 0 (root) e getuid id do seu usuário, no entanto, seus subprocesss realmente executar como root (você pode verificar isso com sudo id -u -r ).

    Eu não acho que haja uma maneira fácil de obter access root por meio de programação – afinal, aplicando o princípio do menor privilégio, por que você precisaria? A prática comum é executar apenas partes limitadas do código com privilégios elevados. Muitos daemons etc. também são configurados em sistemas modernos para serem executados como seus próprios usuários com a maioria dos privilégios de que precisam. É somente para operações muito específicas (assembly, etc) que os privilégios de root são realmente necessários.

    Atualização de 2013

    Minha resposta original permanece (embora meu eu de 2013 possa fazer um trabalho melhor do que o de 2010), mas se você está projetando um aplicativo que requer access root, pode considerar exatamente o tipo de access root necessário e considerar o uso de resources POSIX (man page) . Estes são diferentes da segurança baseada em capacidades implementada em L4 et al. As capacidades do POSIX permitem que seu aplicativo receba um subconjunto dos poderes da raiz. Por exemplo, o CAP_SYS_MODULE permitirá que você insira módulos do kernel, mas não lhe dará nenhum outro poder de root. Isto está em uso nas distribuições, por exemplo, o Fedora tem um recurso para remover completamente binários setuid com access root indiscriminado.

    Isso é importante porque, como programador, seu código é obviamente perfeito! Mas, as bibliotecas das quais você depende (suspiro, se você as tivesse escrito!) Podem ter vulnerabilidades nelas. Usando os resources, você pode limitar o uso dessa exploração e salvar você e sua empresa do escrutínio relacionado à segurança. Isso deixa todo mundo mais feliz.

    Se você precisa de privilégios de root toda vez, o melhor é iniciar seu programa como root e soltá-los (em um subprocess) com setuid e setgid . É o que o apache faz quando precisa se ligar à porta restrita 80.

    Se ganhar direitos de root é a exceção ao invés da regra e o programa rodar de forma interativa, outra forma é escrever um programa add_interface e executar

     sudo add_interface args 

    e deixe o sudo manipular a autenticação para você. Em vez de sudo, você pode querer usar uma interface gráfica como gksu, gksudo, kdesu ou kdesudo. Eu não tentaria implementar a input de senha segura eu mesmo; pode ser um problema complicado e você provavelmente deixará brechas na segurança e problemas de funcionalidade (você suporta leitores de impressão digital?).

    Outra alternativa é o polkit , anteriormente chamado de PolicyKit.

    Você não pode ganhar privilégios de root, você deve começar com eles e reduzir seus privilégios conforme necessário. A maneira usual de fazer isso é instalar o programa com o conjunto de bits “setuid”: ele executa o programa com o ID de usuário efetivo do proprietário do arquivo. Se você executar ls -l no sudo , verá que ele está instalado dessa maneira:

     -rwsr-xr-x 2 root root 123504 2010-02-25 18:22 /usr/bin/sudo 

    Enquanto o seu programa está sendo executado com privilégios de root, você pode chamar a chamada do sistema setuid(2) para mudar seu userid efetivo para algum usuário não privilegiado. Acredito (mas não tentei isso) que você poderia instalar seu programa como root com o bit setuid, imediatamente reduzir o privilégio e, em seguida, restaurar o privilégio conforme necessário (é possível, no entanto, que uma vez que você diminua seu privilégio você não ser capaz de restaurá-lo).

    Uma solução melhor é dividir a parte do seu programa que precisa ser executada como root e instalá-lo com o bit setuid ativado. Você, é claro, precisará tomar precauções razoáveis ​​para que não possa ser invocado fora de seu programa mestre.

    Normalmente isso é feito fazendo sua raiz suína binária.

    Uma maneira de gerenciar isso para que os ataques contra seu programa sejam difíceis é minimizar o código que é executado como root da seguinte forma:

     int privileged_server(int argc, char **argv); int unprivileged_client(int argc, char **argv, int comlink); int main(int argc, char **argv) { int sockets[2]; pid_t child; socketpair(AF_INET, SOCK_STREAM, 0); /* or is it AF_UNIX? */ child = fork(); if (child < 0) { perror("fork"); exit(3); } elseif (child == 0) { close(sockets[0]); dup2(sockets[1], 0); close(sockets[1]); dup2(0, 1); dup2(0, 2); /* or not */ _exit(privileged_server(argc, argv)); } else { close(sockets[1]); int rtn; setuid(getuid()); rtn = unprivileged_client(argc, argv, sockets[0]); wait(child); return rtn; } } 

    Agora o código não privilegiado fala com o código privilegiado através do fd comlink (que é um socket conectado). O código privilegiado correspondente usa stdin / stdout como seu final do comlink.

    O código privilegiado precisa verificar a segurança de cada operação que precisa ser feita, mas como esse código é pequeno comparado ao código sem privilégios, isso deve ser razoavelmente fácil.

    Você pode querer dar uma olhada nessas APIs:

     setuid, seteuid, setgid, setegid, ... 

    Eles estão definidos no header em sistemas Linux (não sei muito sobre o MAC, mas você deve ter um header similar lá também).

    Um problema que posso ver é que o processo deve ter privilégios suficientes para alterar seus IDs de usuário / grupo. Caso contrário, as chamadas para as funções acima resultarão em um erro com o errorno configurado para EPERM .

    Eu sugiro que você execute seu programa como o usuário root , altere o ID de usuário efetivo (usando seteuid ) para um usuário desprivilegiado no início. Então, sempre que você precisar elevar as permissions, solicite uma senha e use seteuid novamente para reverter para o usuário root .

    No OS X, você pode usar a function AuthorizationExecuteWithPrivileges . A página sobre tarefas de serviços de autorização tem algumas discussões elaboradas sobre essas funções (e relacionadas).

    Aqui está um pequeno código em C ++ para executar um programa com privilégios de administrador:

     static bool execute(const std::string &program, const std::vector &arguments) { AuthorizationRef ref; if (AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &ref) != errAuthorizationSuccess) { return false; } AuthorizationItem item = { kAuthorizationRightExecute, 0, 0, 0 }; AuthorizationRights rights = { 1, &item }; const AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights; if (AuthorizationCopyRights(ref, &rights, kAuthorizationEmptyEnvironment, flags, 0) != errAuthorizationSuccess) { AuthorizationFree(ref, kAuthorizationFlagDestroyRights); return false; } std::vector args; for (std::vector::const_iterator it = arguments.begin(); it != arguments.end(); ++it) { args.push_back(it->c_str()); } args.push_back(0); OSStatus status = AuthorizationExecuteWithPrivileges(ref, program.c_str(), kAuthorizationFlagDefaults, &args[0], 0); AuthorizationFree(ref, kAuthorizationFlagDestroyRights); return status == errAuthorizationSuccess; } 

    Você pode tentar iniciar o comando para criar o dispositivo virtual (incluindo o sudo) por meio de um shell de plano de fundo. Solicite a senha do usuário em um diálogo próprio e insira-o no shell quando o sudo o solicitar. Existem outras soluções como o uso do gksu, mas não há garantias de que elas estejam disponíveis em todas as máquinas.

    Você não executa todo o seu programa como root, mas apenas a pequena parte dele que precisa de root. Você deve criar um processo separado para isso e o sudo pode ser útil para você.