diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2000-04-21 17:06:14 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2000-04-21 17:06:14 +0000 |
commit | a9eb1c4544679cd64e3b886a7c4377d0ff3b506f (patch) | |
tree | 6db19e2fcb9819cda091b854c6faacb46752ce70 | |
parent | 2d603aa8e36e73bc8fd805efa3e9b5ae53796bcd (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/recover | 58 |
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); |