Hi,
lpd wants to verify that it doesn't open a symbolic link, checking with
lstat(), then open()ing the file. The only reason I can see that the
code does not simply use O_NOFOLLOW is a different return value if
it encounters a symlink (maybe I am wrong here, would like to get feedback
on this assumption).
In either way, an attacker could create a regular file, waiting for the
lstat() to happen and replace it with a symlink right after the call,
before the open() function is called.
I suggest to skip lstat() and check the fstat() result later on,
continuing the train of thought of current lpd behaviour -- lpd will
just _always_ do the file check.
Also, don't assume that everything is alright if fstat() fails.
Tobias
Index: printjob.c
===================================================================
RCS file: /var/www/cvs/src/usr.sbin/lpr/lpd/printjob.c,v
retrieving revision 1.49
diff -u -p -r1.49 printjob.c
--- printjob.c 10 Dec 2013 16:38:04 -0000 1.49
+++ printjob.c 17 Jan 2014 20:35:01 -0000
@@ -539,17 +539,16 @@ print(int format, char *file)
int n, fi, fo, p[2], stopped = 0, nofile;
PRIV_START;
- if (lstat(file, &stb) < 0 || (fi = safe_open(file, O_RDONLY, 0)) < 0) {
+ if ((fi = safe_open(file, O_RDONLY, 0)) < 0) {
PRIV_END;
return(ERROR);
}
PRIV_END;
/*
- * Check to see if data file is a symbolic link. If so, it should
- * still point to the same file or someone is trying to print
- * something he shouldn't.
+ * Check if expected file was opened. If not, someone is
+ * trying to print something he shouldn't.
*/
- if (S_ISLNK(stb.st_mode) && fstat(fi, &stb) == 0 &&
+ if (fstat(fi, &stb) != 0 ||
(stb.st_dev != fdev || stb.st_ino != fino))
return(ACCESS);
if (!SF && !tof) { /* start on a fresh page */
@@ -876,18 +875,16 @@ sendfile(int type, char *file)
int sizerr, resp;
PRIV_START;
- if (lstat(file, &stb) < 0 || (f = safe_open(file, O_RDONLY, 0)) < 0) {
+ if ((f = safe_open(file, O_RDONLY, 0)) < 0) {
PRIV_END;
return(ERROR);
}
PRIV_END;
/*
- * Check to see if data file is a symbolic link. If so, it should
- * still point to the same file or someone is trying to print something
- * he shouldn't.
+ * Check if expected file was opened. If not, someone is
+ * trying to print something he shouldn't.
*/
- if (S_ISLNK(stb.st_mode) && fstat(f, &stb) == 0 &&
- (stb.st_dev != fdev || stb.st_ino != fino))
+ if (fstat(f, &stb) != 0 || (stb.st_dev != fdev || stb.st_ino != fino))
return(ACCESS);
if ((amt = snprintf(buf, sizeof(buf), "%c%lld %s\n", type,
(long long)stb.st_size, file)) >= sizeof(buf) || amt == -1)