# Set-UID, ambiente e descritores

Seguir a autoridade de um processo, a interpretação pela shell e os recursos herdados.

Página: https://resumos.rgo.pt/cadeiras/fsi/setuid-ambiente/

Um programa de consulta pode abrir um ficheiro com autoridade que o utilizador não tem. A segurança depende de como escolhe o programa externo, interpreta os argumentos e entrega os recursos que já abriu. A mudança de UID é apenas uma parte desse percurso.

## Ambiente em fork e execve

As variáveis de ambiente são pares de nomes e valores disponíveis ao processo. Por exemplo, `PATH` influencia a procura de executáveis em chamadas que fazem essa procura. Não é uma lista de permissões: escolher onde procurar e poder executar são decisões diferentes.

Considera um processo com `PATH=/usr/bin:/bin` e uma variável própria `MODO=consulta`:

| Operação | Ambiente resultante |
| --- | --- |
| `fork()` | O filho recebe uma cópia do ambiente do pai. |
| O filho muda `MODO` | A sua cópia muda; não altera automaticamente o ambiente do pai. |
| `execve(caminho, argv, envp)` | O novo programa recebe os pares indicados por `envp`. |

`execve` substitui o programa no mesmo processo. Não cria um filho e não escolhe automaticamente o ambiente do pai. Passar `environ` transmite o ambiente corrente; passar uma lista terminada em ponteiro nulo permite selecionar os pares. A variante com `envp=NULL` usada pelo guião é aceite em Linux, mas não é uma interface portátil para um ambiente vazio.

## UID real e UID efetivo

O UID real identifica normalmente o utilizador que iniciou o processo. O UID efetivo participa nas decisões de acesso. Ao executar um binário Set-UID, o sistema pode mudar o UID efetivo para o dono do executável, conservando o UID real. Por exemplo, um binário de root iniciado por um utilizador com UID 1000 pode ter UID real 1000 e efetivo 0.

Isto depende das condições de execução. Montagens `nosuid`, `no_new_privs` e outras restrições podem impedir o efeito Set-UID. Também não se deve extrapolar de um binário para um script interpretado: Linux ignora Set-UID no script.

O ambiente continua a exigir análise. O carregador dinâmico filtra variáveis como `LD_PRELOAD` em execução segura; isso não significa que todas as variáveis externas sejam removidas. O programa privilegiado precisa de controlar os valores que usa para decidir operações.

### UID guardado e redução de privilégios

O **UID guardado**, ou saved set-user-ID, permite certas mudanças posteriores do UID efetivo. Em Linux com glibc atual, `seteuid(u)` muda apenas o efetivo, preservando o real e o guardado. Já `setuid(u)`, quando o processo tem a autoridade `CAP_SETUID`, muda os três. Sem essa autoridade, `setuid` não tem o mesmo efeito geral: as mudanças permitidas dependem dos UIDs atuais.

Segue duas alternativas independentes, ambas a partir de um binário Set-UID de root iniciado pelo utilizador 1000. As chamadas indicadas têm sucesso, e não há outra autoridade que permita mudar UIDs:

| Estado | Real | Efetivo | Guardado |
| --- | --- | --- | --- |
| Inicial | 1000 | 0 | 0 |
| Após `seteuid(1000)` | 1000 | 1000 | 0 |
| Após `setuid(1000)` a partir do inicial | 1000 | 1000 | 1000 |

Na primeira alternativa, `seteuid(0)` pode recuperar o efetivo 0 porque este continua guardado. Na segunda, essa chamada já não é permitida apenas pelos UIDs: o 0 deixou de ser real, efetivo ou guardado. Uma redução temporária não cumpre um pedido de abandono permanente de privilégios.

Verifica sempre o resultado das chamadas, mesmo a partir de root. Analisa separadamente grupos, Linux capabilities e recursos já abertos. Mudar os três UIDs não fecha descritores, como veremos a seguir.

## PATH e interpretação pela shell

`system("ls")` pede a uma shell que interprete `ls`. A procura pode usar `PATH`: se uma pasta controlada pelo requerente vier primeiro, o nome pode selecionar outro executável. Fixar o formato do comando C não fixa o programa finalmente escolhido.

Agora o serviço usa um caminho absoluto, mas concatena um nome de ficheiro:

```
/* Esquema de uma chamada, não um programa completo. */
system("/bin/cat relatorio; printf MARCA");
```

A shell interpreta o ponto e vírgula como separador. Executa a consulta e depois outra operação. A segunda operação deste exemplo só imprime uma marca; demonstra por que tratar o argumento como parte de um comando aumenta a autoridade do requerente.

Com uma chamada direta a `execve`, o caminho do executável e os elementos de `argv` ficam separados. Um argumento `relatorio; printf MARCA` passa como um único nome, sem a shell o transformar em duas operações. Ainda é necessário autorizar o ficheiro, controlar opções aceites pelo executável e escolher um ambiente adequado. Retirar a shell não resolve qualquer uso perigoso de argumentos.

O efeito privilegiado de `system` depende também da shell. No ambiente descrito pelo guião, `dash` baixa privilégios quando deteta a diferença de UID; o laboratório compara com outra configuração. Não se conclui que toda chamada `system` num processo Set-UID executa a operação final como root.

## Descritores depois de retirar privilégios

O serviço abre `relatorio` para escrita enquanto tem autoridade. O sistema concede um descritor, aqui 3. O serviço baixa depois os seus UIDs e executa outro programa. Se o descritor não foi fechado nem marcado close-on-exec, o novo programa pode herdá-lo.

![O serviço abre o ficheiro com autoridade, obtém o descritor 3, baixa o UID e executa outro programa. O descritor permanece aberto se não tiver close-on-exec. Uma nova abertura pode ser negada, mas a escrita pelo descritor herdado continua possível.](https://resumos.rgo.pt/cadeiras/fsi/setuid-ambiente/figura-1.svg)

A nova abertura verifica as permissões atuais do processo. Uma escrita pelo descritor já aberto usa a referência obtida antes; baixar o UID não repete automaticamente a decisão da abertura. No guião, este caso chama-se **capability leaking**. O recurso concreto é o descritor aberto, não necessariamente uma Linux capability.

Se o programa seguinte não deve receber essa autoridade, fecha o descritor antes da transferência e define close-on-exec para impedir herança por `execve`. Close-on-exec não fecha o descritor por baixar o UID nem impede o programa atual de o usar. Quando possível, retira privilégios antes de abrir o recurso, verifica erros e mantém apenas a autoridade necessária.

## Evidência no LOGBOOK

O Moodle da semana 4 pede as Tasks 1–6, a comparação com `system` da Task 8 e a fuga de recurso da Task 9. Usa o ambiente isolado indicado pelo docente. Os exemplos do navegador de [programação defensiva](https://resumos.rgo.pt/cadeiras/fsi/programacao-defensiva/) não reproduzem UID, processos ou permissões POSIX.

Para cada observação, regista o estado anterior, a operação, o resultado e a condição que o explica. Em `PATH`, identifica o executável escolhido e os UIDs relevantes. Em injeção de comandos, distingue o argumento recebido da operação interpretada pela shell. Na fuga de descritor, identifica quando foi aberto e por que permanece utilizável. Uma captura sem esta ligação não explica o mecanismo.

Segue o guião para a preparação e restauração do ambiente. A comparação que altera a shell do laboratório exige repor a configuração original no fim. Não transfiras essas alterações para o sistema de trabalho.

## Exercícios

Executável fixo, argumento interpretado

Um serviço usa `system("cat relatorio")`. O requerente controla a primeira pasta de `PATH`. A equipa muda para `system("/bin/cat " + nome)`, onde `nome` é texto externo. Explica que risco a alteração retira e qual permanece. Compara o argumento `relatorio; printf MARCA` nas duas chamadas seguintes: o comando concatenado para `system` e uma chamada direta a `/bin/cat` por `execve` com esse texto num único elemento de `argv`. Não assumes que a shell conserva privilégios.

Pista

Quem escolhe o executável e quem interpreta os caracteres do argumento?

Solução

O caminho absoluto retira a procura de `cat` por `PATH`; a shell continua a interpretar o comando. Em `system`, o ponto e vírgula separa a consulta e `printf MARCA`. Em `execve`, o texto passa como um único argumento: sem shell, o ponto e vírgula não cria outra operação. Ainda são necessários autorização do objeto, controlo das opções e ambiente adequado. O UID da operação final depende também da shell e das condições de execução; não basta saber que o serviço é Set-UID.

Confere a tua resposta:

*   Distingui procura por PATH de interpretação pela shell.
*   Expliquei o efeito do ponto e vírgula em system e em argv.
*   Não tratei execve como autorização de qualquer ficheiro.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/fsi/setuid-ambiente/#path-e-interpreta%C3%A7%C3%A3o-pela-shell)

Duas formas de chegar ao mesmo ficheiro

Um serviço com UID efetivo 0 abre um ficheiro `0644` de root para escrita e recebe o descritor 3. Baixa todos os seus UIDs para 1000, com sucesso. Uma nova abertura para escrita é negada, mas o descritor continua válido. Assume permissões tradicionais, sem outras políticas ou restrições. Explica por que a escrita pelo descritor 3 ainda pode ocorrer. Se o serviço marcar o descritor close-on-exec, fica fechado ao baixar o UID? E após um `execve` bem-sucedido?

Pista

Identifica o momento em que foi concedida a referência de escrita.

Solução

A abertura verificou autoridade quando o UID efetivo era 0. Baixar os UIDs não fecha o descritor nem reaplica a decisão de abertura a cada escrita. A nova abertura usa a autoridade atual e é negada, enquanto o descritor 3 conserva a referência de escrita. Close-on-exec fecha-o num `execve` bem-sucedido, não na mudança de UID. O próprio serviço ainda o pode usar antes de `execve`; se já não precisa dele, deve fechá-lo.

Confere a tua resposta:

*   Identifiquei a abertura privilegiada como origem do descritor.
*   Distingui nova abertura de escrita pela referência existente.
*   Expliquei quando close-on-exec fecha e quando não fecha.

Erros frequentes

O recurso deste caso é um descritor. Retirar Linux capabilities não o fecha automaticamente.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/fsi/setuid-ambiente/#descritores-depois-de-retirar-privil%C3%A9gios)

Cópia do ambiente e substituição do programa

O pai tem `MODO=consulta` e cria um filho com `fork`. O filho muda a sua variável para `MODO=editar` e faz `execve` com uma lista `envp` que contém apenas `MODO=restrito`, terminada por ponteiro nulo. As operações têm sucesso. Que valor fica no pai? Que valor recebe o novo programa? A chamada a `execve` criou mais um processo?

Solução

O pai mantém `MODO=consulta`: o filho alterou a sua cópia. O novo programa recebe `MODO=restrito`, escolhido em `envp`, e não o valor anterior do filho. `fork` criou o filho; `execve` substituiu o seu programa no mesmo processo. Distinguir estes momentos permite controlar o ambiente transmitido a uma operação sensível.

Confere a tua resposta:

*   Mantive o ambiente do pai independente da alteração do filho.
*   Usei envp como ambiente do novo programa.
*   Distingui criar um filho de substituir o programa no mesmo processo.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/fsi/setuid-ambiente/#ambiente-em-fork-e-execve)

Redução temporária ou permanente

Em Linux com glibc atual, duas alternativas partem de UIDs real 1000, efetivo 0 e guardado 0. A faz `seteuid(1000)`; B, com `CAP_SETUID`, faz `setuid(1000)`. Estas chamadas têm sucesso. Depois não há Linux capabilities nem outra autoridade que permita recuperar root. Indica os três UIDs resultantes e decide se `seteuid(0)` pode ter sucesso em cada alternativa. Um descritor aberto para escrita antes da redução fica automaticamente fechado?

Pista

A regra de seteuid permite ao processo sem privilégio selecionar o UID real, efetivo ou guardado.

Solução

A fica com real 1000, efetivo 1000 e guardado 0. Pode voltar a efetivo 0 por `seteuid(0)`, usando o guardado. B fica com os três a 1000: nas hipóteses dadas, 0 já não é uma escolha autorizada e a recuperação falha. Nenhuma das mudanças fecha automaticamente o descritor. A capacidade de mudar UIDs e a referência a um ficheiro já aberto são autoridades distintas; para retirar a segunda, fecha o recurso ou controla a sua transferência.

Confere a tua resposta:

*   Segui os três UIDs em cada alternativa independente.
*   Justifiquei a recuperação apenas na alternativa com UID 0 guardado.
*   Separei perda de autoridade de mudança de UID e fecho de descritores.

[Voltar à explicação](https://resumos.rgo.pt/cadeiras/fsi/setuid-ambiente/#uid-guardado-e-redu%C3%A7%C3%A3o-de-privil%C3%A9gios)
