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)

Reply via email to