#! /usr/bin/perl -w use strict; use File::Find (); my $verbose; sub usage { print STDERR "@_: $!\n" if (@_); print STDERR "usage: $0 filesystem [...]\n"; exit(2); } our @capdb; sub read_capdb { my($fs) = @_; my $found = 0; local *F; if (!open(F, "<$fs/.capabilities")) { print STDERR "$fs/.capabilities: $!\n" if ($verbose); } else { my $caps; for (my $i = 0; read(F, $caps, 3 * 128 / 8); ++$i) { my($e0, $e1, $e2, $e3, $i0, $i1, $i2, $i3, $p0, $p1, $p2, $p3) = unpack('LLLLLLLLLLLL', $caps); if ($e0 || $i0 || $p0) { $capdb[$i] = [$e0, $i0, $p0]; $found = 1; } } close(F); } return $found; } my @cap_names = ( 'cap_chown', 'cap_dac_override', 'cap_dac_read_search', 'cap_fowner', 'cap_fsetid', 'cap_kill', 'cap_setgid', 'cap_setuid', 'cap_setpcap', 'cap_linux_immutable', 'cap_net_bind_service', 'cap_net_broadcast', 'cap_net_admin', 'cap_net_raw', 'cap_ipc_lock', 'cap_ipc_owner', 'cap_sys_module', 'cap_sys_rawio', 'cap_sys_chroot', 'cap_sys_ptrace', 'cap_sys_pacct', 'cap_sys_admin', 'cap_sys_boot', 'cap_sys_nice', 'cap_sys_resource', 'cap_sys_time', 'cap_sys_tty_config', 'cap_mknod', 'cap_lease', 'cap_setfcap', ); sub int_to_name { my($c) = @_; return 'all' if ($c == 0xffffffff); my $s; for (my $b = 0; $b < 32; ++$b) { if ($c & (1 << $b)) { $s .= ',' if ($s); $s .= "$cap_names[$b]"; } } return $s; } sub cap_to_text { my($e0, $i0, $p0) = @_; my $caps; if ($e0) { my $names = int_to_name($e0); $caps = "$names=e"; } if ($i0) { my $names = int_to_name($i0); if (!$e0) { $caps = "$names=i"; } else { $caps .= ($i0 == $e0) ? "i" : ":$names=i"; } } if ($p0) { my $names = int_to_name($p0); if (!$e0 && !$i0) { $caps = "$names=p"; } else { $caps .= ($p0 == $i0 || (!$i0 && $p0 == $e0)) ? "p" : ":$names=p"; } } return $caps; } use vars qw/*name *dir *prune/; *name = *File::Find::name; *dir = *File::Find::dir; *prune = *File::Find::prune; sub wanted { my($dev, $ino, $mode, $nlink, $uid, $gid); if ((($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) && !($File::Find::prune |= ($dev != $File::Find::topdev)) && -f _ && $capdb[$ino]) { my $caps = cap_to_text(@{$capdb[$ino]}); print "chcap $caps $File::Find::dir/$_\n"; } } usage() if ($#ARGV < 0); foreach my $fs (@ARGV) { local @capdb; usage($fs) if (! -d $fs); if (read_capdb($fs)) { File::Find::find({wanted => \&wanted}, $fs); } }