summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>2000-04-21 17:06:14 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>2000-04-21 17:06:14 +0000
commita9eb1c4544679cd64e3b886a7c4377d0ff3b506f (patch)
tree6db19e2fcb9819cda091b854c6faacb46752ce70
parent2d603aa8e36e73bc8fd805efa3e9b5ae53796bcd (diff)
Remove the races so that this is safe to run anytime. We open
/var/tmp/vi.recover to get an fd and user O_NOFOLLOW to following a symlink. Once we have a file handle we can use it to safely chdir to the right place and form then on do operations relative to ".". Also restrict to root.
-rw-r--r--usr.bin/vi/build/recover58
1 files changed, 42 insertions, 16 deletions
diff --git a/usr.bin/vi/build/recover b/usr.bin/vi/build/recover
index 2abbc6e6235..00bbb001af8 100644
--- a/usr.bin/vi/build/recover
+++ b/usr.bin/vi/build/recover
@@ -1,77 +1,102 @@
#!/usr/bin/perl -w
#
-# $OpenBSD: recover,v 1.5 2000/04/20 15:24:24 millert Exp $
+# $OpenBSD: recover,v 1.6 2000/04/21 17:06:13 millert Exp $
#
# Script to (safely) recover nvi edit sessions.
-# NOTE: Assumes we are running *before* users may start processes.
-# If that is not the case then the chown and chmod below are not safe.
#
use Fcntl;
+require 'sys/syscall.ph';
$recoverdir = $ARGV[0] || "/var/tmp/vi.recover";
$sendmail = "/usr/sbin/sendmail";
-# Make the recovery dir if it does not exist.
-if (!lstat($recoverdir)) {
+die "Sorry, $0 must be run as root\n" if $>;
+
+# Make the recovery dir if it does not already exist.
+if (!sysopen(DIR, $recoverdir, O_RDONLY|O_NOFOLLOW) || !stat(DIR)) {
+ die "Warning! $recoverdir is a symbolic link! (ignoring)\n"
+ if -l $recoverdir;
mkdir($recoverdir, 01777) || die "Unable to create $recoverdir: $!\n";
chmod(01777, $recoverdir);
exit(0);
}
+#
# Sanity check the vi recovery dir
-if (-l _) {
- die "Warning! $recoverdir is a symbolic link! (ignoring)\n";
-} elsif (! -d _) {
- die "Warning! $recoverdir is not a directory! (ignoring)\n";
-} elsif (! -O _) {
+# Perl doesn't support fchdir, fchmod, or fchown so we call
+# fchdir(2) via the syscall interface and then just modify ".".
+#
+die "Warning! $recoverdir is not a directory! (ignoring)\n"
+ unless -d _;
+die "$0: can't chdir to $recoverdir: $!\n"
+ unless syscall(&SYS_fchdir, fileno(DIR)) == 0;
+if (! -O _) {
warn "Warning! $recoverdir is not owned by root! (fixing)\n";
- chown 0, 0, $recoverdir;
+ chown(0, 0, ".");
}
if (((stat(_))[2] & 07777) != 01777) {
warn "Warning! $recoverdir is not mode 01777! (fixing)\n";
- chmod(01777, $recoverdir);
+ chmod(01777, ".");
}
-chdir($recoverdir) || die "$0: can't chdir to $recoverdir: $!\n";
# Check editor backup files.
opendir(RECDIR, ".") || die "$0: can't open $recoverdir: $!\n";
foreach $file (readdir(RECDIR)) {
next unless $file =~ /^vi\./;
+ #
# Unmodified vi editor backup files either have the
# execute bit set or are zero length. Delete them.
# Anything that is not a normal file gets deleted too.
+ #
lstat($file) || die "$0: can't stat $file: $!\n";
if (-x _ || ! -s _ || ! -f _) {
unlink($file) unless -d _;
}
}
-# It is possible to get incomplete recovery files, if the editor crashes
+#
+# It is possible to get incomplete recovery files if the editor crashes
# at the right time.
+#
rewinddir(RECDIR);
foreach $file (readdir(RECDIR)) {
next unless $file =~ /^recover\./;
+ if (!sysopen(RECFILE, $file, O_RDONLY|O_NOFOLLOW)) {
+ warn "$0: can't open $file: $!\n";
+ next;
+ }
+
+ #
# Delete anything that is not a regular file as that is either
# filesystem corruption from fsck or an exploit attempt.
- lstat($file) || die "$0: can't stat $file: $!\n";
+ #
+ if (!stat(RECFILE)) {
+ warn "$0: can't stat $file: $!\n";
+ close(RECFILE);
+ next;
+ }
if (! -f _ || ! -s _) {
unlink($file) unless -d _;
+ close(RECFILE);
next;
}
+ #
# Slurp in the recover.* file and search for X-vi-recover-path
# (which should point to an existing vi.* file).
- sysopen(RECFILE, $file, O_RDONLY) || die "$0: can't open $file: $!\n";
+ #
@recfile = <RECFILE>;
close(RECFILE);
@backups = grep(s/^X-vi-recover-path:\s*(.*)[\r\n]*$/$1/, @recfile);
+ #
# Delete any recovery files that are zero length, corrupted,
# or that have no corresponding backup file. Else send mail
# to the user.
+ #
if ($#backups != 0) {
unlink($file);
} elsif (! -s $backups[0]) {
@@ -84,5 +109,6 @@ foreach $file (readdir(RECDIR)) {
}
}
closedir(RECDIR);
+close(DIR);
exit(0);