2010/3/12 ????? ???????????? <olga.kryzhanovska at gmail.com>: > 2010/3/12 Mike Gerdts <mgerdts at gmail.com>: >> 2010/3/12 ????? ???????????? <olga.kryzhanovska at gmail.com>: >>> This one, right? >>> ? ? ? -i[SUFFIX], --in-place[=SUFFIX] >>> ? ? ? ? ? ? ?edit files in place (makes backup if extension supplied) >>> >>> -i copies the content to a temporary backup file, truncates the >>> original (keeping permissions, ACL flags etc) and starts processing >>> from backup to original file, right? >> >> This algorithm can introduce race conditions which could result in >> security problems and in disk full conditions can result in a >> partially written file. > > Where do you see the race?
The following immediately come to mind: 1. sed just truncated /etc/passwd and has not yet written data: 1a. The system cannot come back up because it can't do user name resolution. 1b. A user tries to log in and gets a "permission denied error" because the passwd(4) entry does not exist. 2. sed is modifying a large /etc/passwd. The first write(2) has happened but this is only part of the file. Simultaneously "rsdude" is logging in. The last line of /etc/passwd looks like the following: rsdude:x:1234:1234:Restricted Shell Dude:/home/rsdude: Normally that line would have /usr/bin/rksh at the end. Due to an unfortunate buffer boundary the shell wasn't written. passwd(4) says that this person will get /usr/bin/sh. login-shell is the user's initial shell program. If this field is empty, the default shell is /usr/bin/sh. 3. The file system was very close to being full when sed started. Suppose sed managed to create the backup copy and truncated the file but something else did a write taking the last free blocks before sed could reuse the blocks it freed up with the truncate. In this case, the file that was supposed to be modified in place was truncated. > >> A better approach would be the equivalent of: >> >> 1. ln $file $file.$suffix >> 2. newfile=$(mktemp $(dirname $file)/$(basename $file).XXXXXX >> 3. chown $user:$group $newfile >> 4. chmod $perms $newfile ; # plus more magic to do extended attributes >> 5. sed $sedprog $file > $newfile >> 6. rename $newfile $file >> >> Important points of the above are: >> >> 1. Takes no space (aside from a directory entry), preserves permissions >> 2. Secure creation of a temporary file in the same directory as the >> file to ensure that rename(2) works. ?If a symlink is involved, this >> should be done in the directory where the file really exists. >> 3 - 4. Sets the permissions properly. ?Use the same code or algorithm >> that cp -p@ uses. > > This is a hard part which I like to avoid. > 1. There is no libc function which does copy all ACL data the way cp > -p@ does. We have to copy much of cp's code. I think there is a strong case for adding such a function to libc. > 2. It is not possible to set owner/group or all ACLs if we are not in > the same user/group, yet the original file may be writable for me but > not owned by me. Truncating the file avoids the requirement to copy > the ACL or owner/group totally. Quite true. -- Mike Gerdts http://mgerdts.blogspot.com/