TL; DR
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
Você precisa perguntar ao sistema se o usuário tem permissão de gravação. A única maneira confiável é mudar o uid efetivo, o gid efetivo e os suplementos para o usuário e usar a access(W_OK)
chamada do sistema (mesmo que tenha algumas limitações em alguns sistemas / configurações).
E lembre-se de que não ter permissão de gravação em um arquivo não garante necessariamente que você não possa modificar o conteúdo do arquivo nesse caminho.
A história mais longa
Vamos considerar o que é necessário, por exemplo, para um usuário $ ter acesso de gravação /foo/file.txt
(assumindo que nenhum /foo
e/foo/file.txt
sejam links simbólicos)?
Ele precisa:
- acesso de pesquisa a
/
(não é necessário read
)
- acesso de pesquisa a
/foo
(não é necessário read
)
- acesso de gravação a
/foo/file.txt
Você já pode ver as abordagens (como @ lcd047 ou @ apaul's ) que verificam apenas a permissão de file.txt
não funcionarão porque poderiam dizer que file.txt
é gravável, mesmo que o usuário não tenha permissão de pesquisa /
ou/foo
.
E uma abordagem como:
sudo -u "$user" find / -writeble
Também não funcionará porque não reportará os arquivos nos diretórios em que o usuário não tem acesso de leitura (em find
execução como$user
não é possível o conteúdo), mesmo que ele possa gravá-los.
Se esquecermos ACLs, sistemas de arquivos somente leitura, sinalizadores FS (como imutáveis), outras medidas de segurança (apparmor, SELinux, que podem até distinguir entre diferentes tipos de gravação) e focar apenas nos atributos tradicionais de permissão e propriedade, para obter uma com permissão (pesquisar ou escrever), isso já é bastante complicado e difícil de expressar com find
.
Você precisa:
- se o arquivo pertence a você, você precisa dessa permissão para o proprietário (ou uid 0)
- se o arquivo não pertencer a você, mas o grupo for um dos seus, será necessário ter essa permissão para o grupo (ou uid 0).
- se não pertence a você e não a nenhum dos seus grupos, as outras permissões se aplicam (a menos que seu uid seja 0).
Na find
sintaxe, aqui como um exemplo com um usuário do uid 1 e dos gids 1 e 2, seria:
find / -type d \
\( \
-user 1 \( -perm -u=x -o -prune \) -o \
\( -group 1 -o -group 2 \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) -o -type l -o \
-user 1 \( ! -perm -u=w -o -print \) -o \
\( -group 1 -o -group 2 \) \( ! -perm -g=w -o -print \) -o \
! -perm -o=w -o -print
Que alguém poda os diretórios que o usuário não tenha procurar certa para e para outros tipos de arquivos (links simbólicos excluídos como eles não são relevantes), cheques para acesso de gravação.
Se você também deseja considerar o acesso de gravação aos diretórios:
find / -type d \
\( \
-user 1 \( -perm -u=x -o -prune \) -o \
\( -group 1 -o -group 2 \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) ! -type d -o -type l -o \
-user 1 \( ! -perm -u=w -o -print \) -o \
\( -group 1 -o -group 2 \) \( ! -perm -g=w -o -print \) -o \
! -perm -o=w -o -print
Ou para uma $user
associação arbitrária e de seu grupo recuperada do banco de dados do usuário:
groups=$(id -G "$user" | sed 's/ / -o -group /g'); IFS=" "
find / -type d \
\( \
-user "$user" \( -perm -u=x -o -prune \) -o \
\( -group $groups \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) ! -type d -o -type l -o \
-user "$user" \( ! -perm -u=w -o -print \) -o \
\( -group $groups \) \( ! -perm -g=w -o -print \) -o \
! -perm -o=w -o -print
(que é 3 processos totais: id
, sed
e find
)
O melhor aqui seria descer a árvore como raiz e verificar as permissões como usuário para cada arquivo.
find / ! -type l -exec sudo -u "$user" sh -c '
for file do
[ -w "$file" ] && printf "%s\n" "$file"
done' sh {} +
(esse é um find
processo mais um sudo
e sh
processa a cada poucos milhares de arquivos [
e printf
geralmente é construído no shell).
Ou com perl
:
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
(3 processos totais: find
, sudo
e perl
).
Ou com zsh
:
files=(/**/*(D^@))
USERNAME=$user
for f ($files) {
[ -w $f ] && print -r -- $f
}
(0 processo no total, mas armazena toda a lista de arquivos na memória)
Essas soluções dependem da access(2)
chamada do sistema. Em vez de reproduzir o algoritmo que o sistema usa para verificar a permissão de acesso, pedimos que o sistema faça essa verificação com o mesmo algoritmo (que leva em conta permissões, ACLs, sinalizadores imutáveis, sistemas de arquivos somente leitura ... ), você tentaria abrir o arquivo para escrever, portanto, é o mais próximo que você vai chegar de uma solução confiável.
Para testar as soluções fornecidas aqui, com as várias combinações de usuário, grupo e permissões, você pode:
perl -e '
for $u (1,2) {
for $g (1,2,3) {
$d1="u${u}g$g"; mkdir$d1;
for $m (0..511) {
$d2=$d1.sprintf"/%03o",$m; mkdir $d2; chown $u, $g, $d2; chmod $m,$d2;
for $uu (1,2) {
for $gg (1,2,3) {
$d3="$d2/u${uu}g$gg"; mkdir $d3;
for $mm (0..511) {
$f=$d3.sprintf"/%03o",$mm;
open F, ">","$f"; close F;
chown $uu, $gg, $f; chmod $mm, $f
}
}
}
}
}
}'
Variando o usuário entre 1 e 2 e o grupo entre 1, 2 e 3 e limitando-nos aos 9 bits inferiores das permissões, já que são 9458694 arquivos criados. Isso para diretórios e novamente para arquivos.
Isso cria todas as combinações possíveis de u<x>g<y>/<mode1>/u<z>g<w>/<mode2>
. O usuário com uid 1 e gids 1 e 2 teria acesso de gravação, u2g1/010/u2g3/777
mas nãou1g2/677/u1g1/777
por exemplo.
Agora, todas essas soluções tentam identificar os caminhos dos arquivos que o usuário pode abrir para gravação, diferente dos caminhos em que o usuário pode modificar o conteúdo. Para responder a essa pergunta mais genérica, há várias coisas a serem consideradas:
- $ user pode não ter acesso de gravação,
/a/b/file
mas se ele possui file
(e tem acesso de pesquisa /a/b
e o sistema de arquivos não é somente leitura, e o arquivo não tem o sinalizador imutável e ele tem acesso de shell ao sistema), então ele seria capaz de alterar as permissões do file
e conceder a si mesmo acesso.
- A mesma coisa se ele possui,
/a/b
mas não tem acesso à pesquisa.
- $ user pode não ter acesso,
/a/b/file
porque ele não tem acesso à pesquisa /a
ou /a/b
, mas esse arquivo pode ter um link /b/c/file
físico, por exemplo, nesse caso, ele pode modificar o conteúdo /a/b/file
abrindo-o pelo /b/c/file
caminho.
- A mesma coisa com montagens de ligação . Ele pode não ter acesso à pesquisa
/a
, mas /a/b
pode ser montado em ligação/c
, para que ele possa abrir file
para escrever através de seu /c/file
outro caminho.
- Ele pode não ter permissão de gravação
/a/b/file
, mas se tiver acesso de gravação /a/b
, poderá remover ou renomear file
lá e substituí-lo por sua própria versão. Ele mudaria o conteúdo do arquivo, /a/b/file
mesmo que fosse um arquivo diferente.
- A mesma coisa se ele tem acesso de gravação para
/a
(ele poderia mudar o nome /a/b
para /a/c
, criar um novo /a/b
diretório e um novo file
nele.
Para encontrar os caminhos que $user
poderiam modificar. Para endereçar 1 ou 2, não podemos mais confiar na access(2)
chamada do sistema. Poderíamos ajustar nossa find -perm
abordagem para assumir o acesso à pesquisa nos diretórios ou gravar o acesso aos arquivos assim que você for o proprietário:
groups=$(id -G "$user" | sed 's/ / -o -group /g'); IFS=" "
find / -type d \
\( \
-user "$user" -o \
\( -group $groups \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) ! -type d -o -type l -o \
-user "$user" -print -o \
\( -group $groups \) \( ! -perm -g=w -o -print \) -o \
! -perm -o=w -o -print
Poderíamos endereçar 3 e 4, gravando os números do dispositivo e do inode ou todos os arquivos que $ user tem permissão de gravação e relatando todos os caminhos de arquivos que possuem esses números de dev + inode. Desta vez, podemos usar o mais confiável access(2)
abordagens :
Algo como:
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -0lne 'print 0+-w,$_' |
perl -l -0ne '
($w,$p) = /(.)(.*)/;
($dev,$ino) = stat$p or next;
$writable{"$dev,$ino"} = 1 if $w;
push @{$p{"$dev,$ino"}}, $p;
END {
for $i (keys %writable) {
for $p (@{$p{$i}}) {
print $p;
}
}
}'
5 e 6 são, à primeira vista, complicados pelo t
pouco das permissões. Quando aplicado em diretórios, esse é o bit de exclusão restrito que impede que usuários (outros que não sejam o proprietário do diretório) removam ou renomeiem os arquivos que eles não possuem (mesmo tendo acesso de gravação ao diretório).
Por exemplo, se voltarmos ao nosso exemplo anterior, se você tem acesso de gravação para /a
, em seguida, você deve ser capaz de mudar o nome /a/b
para /a/c
, em seguida, recriar um /a/b
diretório e uma nova file
lá. Mas se o t
bit estiver ativado /a
e você não possuir /a
, poderá fazê-lo somente se possuir /a/b
. Isso dá:
- Se você possui um diretório, conforme 1, pode conceder a si mesmo acesso de gravação e o bit t não se aplica (e você pode removê-lo de qualquer maneira), para excluir / renomear / recriar qualquer arquivo ou diretório nele, portanto todos os caminhos de arquivo abaixo são seus para reescrever qualquer conteúdo.
- Se você não o possui, mas tem acesso de gravação, então:
- Ou o
t
bit não está definido e você está no mesmo caso acima (todos os caminhos de arquivo são seus).
- ou está definido e não é possível modificar os arquivos que você não possui ou não tem acesso de gravação; portanto, com o objetivo de encontrar os caminhos de arquivo que você pode modificar, é o mesmo que não ter permissão de gravação.
Portanto, podemos resolver todos os itens 1, 2, 5 e 6 com:
find / -type d \
\( \
-user "$user" -prune -exec find {} + -o \
\( -group $groups \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) ! -type d -o -type l -o \
-user "$user" \( -type d -o -print \) -o \
\( -group $groups \) \( ! -perm -g=w -o \
-type d ! -perm -1000 -exec find {} + -o -print \) -o \
! -perm -o=w -o \
-type d ! -perm -1000 -exec find {} + -o \
-print
Como a solução para 3 e 4 é independente, você pode mesclar a saída deles para obter uma lista completa:
{
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -0lne 'print 0+-w,$_' |
perl -0lne '
($w,$p) = /(.)(.*)/;
($dev,$ino) = stat$p or next;
$writable{"$dev,$ino"} = 1 if $w;
push @{$p{"$dev,$ino"}}, $p;
END {
for $i (keys %writable) {
for $p (@{$p{$i}}) {
print $p;
}
}
}'
find / -type d \
\( \
-user "$user" -prune -exec sh -c 'exec find "$@" -print0' sh {} + -o \
\( -group $groups \) \( -perm -g=x -o -prune \) -o \
-perm -o=x -o -prune \
\) ! -type d -o -type l -o \
-user "$user" \( -type d -o -print0 \) -o \
\( -group $groups \) \( ! -perm -g=w -o \
-type d ! -perm -1000 -exec sh -c 'exec find "$@" -print0' sh {} + -o -print0 \) -o \
! -perm -o=w -o \
-type d ! -perm -1000 -exec sh -c 'exec find "$@" -print0' sh {} + -o \
-print0
} | perl -l -0ne 'print unless $seen{$_}++'
Como deve ficar claro, se você leu tudo até agora, parte dele, pelo menos, trata apenas de permissões e propriedade, e não os outros recursos que podem conceder ou restringir o acesso de gravação (FS, ACLs, sinalizador imutável, outros recursos de segurança, somente leitura ...) E, como a processamos em vários estágios, algumas dessas informações podem estar erradas se os arquivos / diretórios estiverem sendo criados / excluídos / renomeados ou se suas permissões / propriedade forem modificadas enquanto esse script estiver em execução, como em um servidor de arquivos ocupado com milhões de arquivos .
Notas de portabilidade
Todo esse código é padrão (POSIX, Unix para t
bit), exceto:
-print0
é uma extensão GNU agora também suportada por algumas outras implementações. Com find
implementações que não têm suporte para isso, você pode usar -exec printf '%s\0' {} +
e substituir -exec sh -c 'exec find "$@" -print0' sh {} +
por -exec sh -c 'exec find "$@" -exec printf "%s\0" {\} +' sh {} +
.
perl
não é um comando especificado pelo POSIX, mas está amplamente disponível. Você precisa perl-5.6.0
ou acima para -Mfiletest=access
.
zsh
não é um comando especificado pelo POSIX. Esse zsh
código acima deve funcionar com zsh-3 (1995) e acima.
sudo
não é um comando especificado pelo POSIX. O código deve funcionar com qualquer versão, desde que a configuração do sistema permita a execução perl
como o usuário especificado.