On Tue, 25 Nov 2008, Dave Whipp wrote: > Brandon S. Allbery KF8NH wrote: > > Still misunderstanding, I think. Yes, it will fail anyway, but in the > > general case you're checking to see if as a privileged process it is safe to > > operate on a given file. > > I'd actually been thinking that one would use the check in the opposite > direction: > > if need_priv() { > sudo { ... }; # might ask user for passwd > } > else { > ... > } > > But then I'm not an expert in this area: I just want an API that makes it easy > to hack simple scripts that do what I need. It was C<chmod> that I really > wanted to improve the API for; I touched C<chown> only because it was adjacent > in the POD, and it seems reasonable for the two functions/methods to have > similar look&feel.
I've been wondering about this idiom, used when copying files: my $s = POSIX::stat($filename1); POSIX_extensions::setstat($filename2, $s); where setstat() is a combination of chown, chmod and utime -- in the right order to make them work as best as possible. The type of check contemplated in this thread would then come in handy for implementing setstat(). If an unprivileged user can give away a file then that change has to be the last thing done to the file. Otherwise changing the permissions should be done last, so as to maintain the set*id bits. So I would envisage it would look a bit like this (please excuse my obvious hand-waving pseudocode): sub setstat(String|File $filename, StatBuf $stat) { use fatal :everything; if $stat.uid != any(POSIX::getuid(), POSIX::geteuid()) | $stat.gid != any(POSIX::getgid(), POSIX::getegid(), POSIX::getgroups()) { # we need to give the file away my $caps = POSIX_1e::capget(); if $caps.CAP_FOWNER { # we're privileged, so it *should* just work. POSIX::chown $filename, $stat.uid, $stat.gid; POSIX::chmod $filename, Fcntl::ST_PERM($stat.mode); POSIX::utime $filename, $stat.mtime, $stat.atime; return; } if $caps.CAP_CHOWN | !chown.need_priv() { # we have enough privilege to give the file away, but not enough to # touch it after we've done so. POSIX::chmod $filename, Fcntl::ST_PERM($stat.mode); POSIX::utime $filename, $stat.mtime, $stat.atime; POSIX::chown $filename, $stat.uid, $stat.gid; return; } if catching_exception(IO_error) { throw IO_error(EPERM, $filename, "Can't give file away"); } } # can't (or don't need to) give file away POSIX::chmod $filename, Fcntl::ST_PERM($stat.mode); POSIX::utime $filename, $stat.mtime, $stat.atime; } -Martin