TL; DR
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
Vous devez demander au système si l'utilisateur dispose d'une autorisation d'écriture. Le seul moyen fiable est de basculer l'uid efficace, le gid efficace et les gids de supplémentation sur celui de l'utilisateur et d'utiliser l' access(W_OK)
appel système (même cela a des limites sur certains systèmes / configurations).
Et gardez à l'esprit que le fait de ne pas avoir la permission d'écrire sur un fichier ne garantit pas nécessairement que vous ne pouvez pas modifier le contenu du fichier sur ce chemin.
L'histoire la plus longue
Considérons ce qu'il faut , par exemple , pour un utilisateur $ pour avoir accès en écriture à /foo/file.txt
( en supposant qu'aucun des /foo
et /foo/file.txt
des liens symboliques)?
Il a besoin:
- accès à la recherche
/
(pas nécessaire read
)
- accès à la recherche
/foo
(pas nécessaire read
)
- accès en écriture à
/foo/file.txt
Vous pouvez déjà voir que les approches (comme @ lcd047 ou @ apaul ) qui vérifient uniquement l'autorisation de file.txt
ne fonctionneront pas car elles pourraient dire file.txt
est accessible en écriture même si l'utilisateur n'a pas l'autorisation de recherche pour /
ou /foo
.
Et une approche comme:
sudo -u "$user" find / -writeble
Ne fonctionne pas , soit parce qu'elle ne rapportera pas les fichiers dans les répertoires que l'utilisateur n'a pas d' accès en lecture (en find
cours d' exécution que $user
ne peut pas la liste de leur contenu) , même s'il peut y écrire.
Si nous oublions les ACL, les systèmes de fichiers en lecture seule, les indicateurs FS (comme immuables), d'autres mesures de sécurité (apparmor, SELinux, qui peuvent même faire la distinction entre différents types d'écriture) et nous concentrer uniquement sur les attributs de permission et de propriété traditionnels, pour obtenir un étant donné (rechercher ou écrire) la permission, c'est déjà assez compliqué et difficile à exprimer find
.
Vous avez besoin:
- si le fichier vous appartient, vous avez besoin de cette autorisation pour le propriétaire (ou avoir uid 0)
- si le fichier ne vous appartient pas, mais que le groupe est l'un des vôtres, vous devez disposer de cette autorisation pour le groupe (ou avoir uid 0).
- s'il ne vous appartient pas et n'appartient à aucun de vos groupes, les autres autorisations s'appliquent (sauf si votre UID est 0).
En find
syntaxe, ici à titre d'exemple avec un utilisateur de uid 1 et des gids 1 et 2, ce serait:
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
Celui-ci élague les répertoires que l'utilisateur n'a pas droit de recherche pour et pour d'autres types de fichiers (les liens symboliques sont exclus car ils ne sont pas pertinents), vérifie l'accès en écriture.
Si vous souhaitez également envisager l'accès en écriture aux répertoires:
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 pour un arbitraire $user
et son appartenance au groupe récupéré de la base de données utilisateur:
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
(ce qui est 3 processus au total: id
, sed
et find
)
Le mieux ici serait de descendre l'arborescence en tant que root et de vérifier les autorisations en tant qu'utilisateur pour chaque fichier.
find / ! -type l -exec sudo -u "$user" sh -c '
for file do
[ -w "$file" ] && printf "%s\n" "$file"
done' sh {} +
(c'est un find
processus plus un sudo
et sh
traiter tous les quelques milliers de fichiers, [
et printf
sont généralement construits dans le shell).
Ou avec perl
:
find / ! -type l -print0 |
sudo -u "$user" perl -Mfiletest=access -l -0ne 'print if -w'
(3 procédés au total: find
, sudo
et perl
).
Ou avec zsh
:
files=(/**/*(D^@))
USERNAME=$user
for f ($files) {
[ -w $f ] && print -r -- $f
}
(0 processus au total, mais stocke la liste complète des fichiers en mémoire)
Ces solutions reposent sur l' access(2)
appel système. C'est au lieu de reproduire l'algorithme que le système utilise pour vérifier l'autorisation d'accès, nous demandons au système de faire cette vérification avec le même algorithme (qui prend en compte les autorisations, les ACL, les indicateurs immuables, les systèmes de fichiers en lecture seule ... ) qu'il utiliserait si vous essayez d'ouvrir le fichier pour l'écriture, c'est donc le plus proche que vous obtiendrez d'une solution fiable.
Pour tester les solutions données ici, avec les différentes combinaisons d'utilisateurs, de groupes et d'autorisations, vous pouvez faire:
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
}
}
}
}
}
}'
Faire varier l'utilisateur entre 1 et 2 et le groupe entre 1, 2 et 3 et nous limiter aux 9 bits inférieurs des autorisations car c'est déjà 9458694 fichiers créés. Cela pour les répertoires et encore pour les fichiers.
Cela crée toutes les combinaisons possibles de u<x>g<y>/<mode1>/u<z>g<w>/<mode2>
. L'utilisateur avec uid 1 et gids 1 et 2 aurait un accès en écriture u2g1/010/u2g3/777
mais pas u1g2/677/u1g1/777
par exemple.
Maintenant, toutes ces solutions tentent d'identifier les chemins d'accès des fichiers que l'utilisateur peut ouvrir pour l'écriture, ce qui est différent des chemins d'accès par lesquels l'utilisateur peut modifier le contenu. Pour répondre à cette question plus générique, il y a plusieurs choses à prendre en compte:
- $ user peut ne pas avoir accès en écriture à
/a/b/file
mais s'il en possède file
(et a un accès de recherche à /a/b
, et le système de fichiers n'est pas en lecture seule, et le fichier n'a pas l'indicateur immuable, et il a un accès shell au système), il serait alors en mesure de modifier les autorisations du file
et de s'accorder l'accès.
- Même chose s'il possède
/a/b
mais n'a pas accès à la recherche.
- $ user peut ne pas avoir accès à
/a/b/file
car il n'a pas accès à la recherche de /a
ou /a/b
, mais ce fichier peut avoir un lien dur à /b/c/file
par exemple, auquel cas il peut être en mesure de modifier le contenu de /a/b/file
en l'ouvrant via son /b/c/file
chemin.
- Même chose avec les fixations . Il n'a peut-être pas accès à la recherche
/a
, mais il /a/b
peut être monté en liaison/c
, il peut donc ouvrir file
pour écrire via son /c/file
autre chemin.
- Il ne dispose peut-être pas d'autorisations d'écriture
/a/b/file
, mais s'il dispose d'un accès en écriture, /a/b
il peut le supprimer ou le renommer file
et le remplacer par sa propre version. Il modifierait le contenu du fichier /a/b/file
même s'il s'agissait d'un fichier différent.
- Même chose s'il a accès en écriture
/a
(il pourrait renommer /a/b
à /a/c
, créer un nouveau /a/b
répertoire et une nouvelle file
en elle.
Trouver les chemins qui $user
pourraient être modifiés. Pour adresser 1 ou 2, nous ne pouvons plus compter sur l' access(2)
appel système. Nous pourrions ajuster notre find -perm
approche pour supposer l'accès à la recherche aux répertoires, ou l'accès en écriture aux fichiers dès que vous en êtes le propriétaire:
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
Nous pourrions aborder 3 et 4, en enregistrant les numéros de périphérique et d'inode ou tous les fichiers $ user a une autorisation d'écriture et rapporte tous les chemins de fichiers qui ont ces numéros de dev + inode. Cette fois, nous pouvons utiliser les access(2)
approches plus fiables :
Quelque chose comme:
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 et 6 sont à première vue compliqués par le t
peu des permissions. Lorsqu'il est appliqué sur des répertoires, c'est le bit de suppression restreint qui empêche les utilisateurs (autres que le propriétaire du répertoire) de supprimer ou de renommer les fichiers qu'ils ne possèdent pas (même s'ils ont un accès en écriture au répertoire).
Par exemple, si nous revenons à notre exemple précédent, si vous avez accès en écriture à /a
, alors vous devriez être en mesure de renommer /a/b
à /a/c
, puis recréer un /a/b
répertoire et un nouveau file
là - dedans. Mais si le t
bit est activé /a
et que vous ne le possédez pas /a
, vous ne pouvez le faire que si vous le possédez /a/b
. Ça donne:
- Si vous possédez un répertoire, conformément à 1, vous pouvez vous accorder un accès en écriture et le bit t ne s'applique pas (et vous pouvez le supprimer de toute façon), vous pouvez donc supprimer / renommer / recréer n'importe quel fichier ou répertoire dedans, donc tous les chemins de fichiers en dessous sont à vous de réécrire avec n'importe quel contenu.
- Si vous ne le possédez pas mais avez un accès en écriture, alors:
- Soit le
t
bit n'est pas défini, et vous êtes dans le même cas que ci-dessus (tous les chemins d'accès aux fichiers vous appartiennent).
- ou il est défini et vous ne pouvez pas modifier les fichiers que vous ne possédez pas ou auxquels vous n'avez pas accès en écriture.
Nous pouvons donc répondre à tous les 1, 2, 5 et 6 avec:
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
Cela et la solution pour 3 et 4 sont indépendants, vous pouvez fusionner leur sortie pour obtenir une liste complète:
{
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{$_}++'
Comme cela doit être clair si vous avez tout lu jusqu'à présent, une partie au moins ne concerne que les autorisations et la propriété, pas les autres fonctionnalités qui peuvent accorder ou restreindre l'accès en écriture (FS en lecture seule, ACL, indicateur immuable, autres fonctionnalités de sécurité ...). Et comme nous les traitons en plusieurs étapes, certaines de ces informations peuvent être erronées si les fichiers / répertoires sont créés / supprimés / renommés ou leurs autorisations / propriété modifiées pendant l'exécution de ce script, comme sur un serveur de fichiers occupé avec des millions de fichiers .
Notes sur la portabilité
Tout ce code est standard (POSIX, Unix pour t
bit) sauf:
-print0
est une extension GNU désormais également prise en charge par quelques autres implémentations. Avec les find
implémentations qui ne le prennent pas en charge, vous pouvez utiliser à la -exec printf '%s\0' {} +
place et remplacer -exec sh -c 'exec find "$@" -print0' sh {} +
par -exec sh -c 'exec find "$@" -exec printf "%s\0" {\} +' sh {} +
.
perl
n'est pas une commande spécifiée par POSIX mais est largement disponible. Vous avez besoin perl-5.6.0
ou au-dessus pour -Mfiletest=access
.
zsh
n'est pas une commande spécifiée par POSIX. Ce zsh
code ci-dessus devrait fonctionner avec zsh-3 (1995) et au-dessus.
sudo
n'est pas une commande spécifiée par POSIX. Le code devrait fonctionner avec n'importe quelle version tant que la configuration du système permet de s'exécuter en perl
tant qu'utilisateur donné.