On Mon, Jan 20, 2014 at 12:51:47PM -0700, Tobias Stoeckmann wrote:
> CVSROOT:      /cvs
> Module name:  src
> Changes by:   tob...@cvs.openbsd.org  2014/01/20 12:51:47
> 
> Modified files:
>       usr.sbin/lpr/lpd: printjob.c 
> 
> Log message:
> Fix race condition during symlink check.  If there was no symbolic link
> requested, use O_NOFOLLOW, otherwise make sure afterwards that the
> correct file has been referenced (device/inode supplied by 'S' line).
> 
> diff basically from and ok millert@, ok guenther@

This change has broken remote printing for me.

With a printcap like this:

lp|remote line printer:\
        :lp=:rm=10.197.84.33:rp=lp:sd=/var/spool/output:lf=/var/log/lpd-errs:

I get the following when trying to send jobs to the remote printer:

# lpr /tmp/ppuf.pdf
# lpq
ted.stsp.name: no space on remote; waiting for queue to drain
Rank   Owner      Job  Files                                                    
                                           Total Size
1st    root       87   /tmp/ppuf.pdf                                            
                                           1537701 bytes

If I revert the above commit, it works again:

# lpq 
ted.stsp.name: sending to 10.197.84.33
Rank   Owner      Job  Files                                                    
                                           Total Size
1st    stsp       90   /tmp/ppuf.pdf                                            
                                           1537701 bytes


FWIW, this diff reverts the change.
I cannot spot the problem in the diff. Any ideas?

Index: printjob.c
===================================================================
RCS file: /cvs/src/usr.sbin/lpr/lpd/printjob.c,v
retrieving revision 1.51
diff -u -p -r1.51 printjob.c
--- printjob.c  20 Jan 2014 19:52:45 -0000      1.51
+++ printjob.c  7 Feb 2014 18:23:36 -0000
@@ -226,9 +226,6 @@ again:
                        continue;
                errcnt = 0;
        restart:
-               fdev = (dev_t)-1;
-               fino = (ino_t)-1;
-
                (void)lseek(lfd, pidoff, SEEK_SET);
                if ((i = snprintf(line, sizeof(line), "%s\n", q->q_name)) >=
                    sizeof(line) || i == -1)
@@ -541,30 +538,20 @@ print(int format, char *file)
        int fd, status, serrno;
        int n, fi, fo, p[2], stopped = 0, nofile;
 
-       if (fdev != (dev_t)-1 && fino != (ino_t)-1) {
-               /* symbolic link */
-               PRIV_START;
-               fi = safe_open(file, O_RDONLY, 0);
-               PRIV_END;
-               if (fi != -1) {
-                       /*
-                        * The symbolic link should still point to the same file
-                        * or someone is trying to print something he shouldn't.
-                        */
-                       if (fstat(fi, &stb) == -1 ||
-                           stb.st_dev != fdev || stb.st_ino != fino) {
-                               close(fi);
-                               return(ACCESS);
-                       }
-               }
-       } else {
-               /* regular file */
-               PRIV_START;
-               fi = safe_open(file, O_RDONLY|O_NOFOLLOW, 0);
+       PRIV_START;
+       if (lstat(file, &stb) < 0 || (fi = safe_open(file, O_RDONLY, 0)) < 0) {
                PRIV_END;
-       }
-       if (fi == -1)
                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.
+        */
+       if (S_ISLNK(stb.st_mode) && fstat(fi, &stb) == 0 &&
+           (stb.st_dev != fdev || stb.st_ino != fino))
+               return(ACCESS);
        if (!SF && !tof) {              /* start on a fresh page */
                (void)write(ofd, FF, strlen(FF));
                tof = 1;
@@ -888,30 +875,20 @@ sendfile(int type, char *file)
        char buf[BUFSIZ];
        int sizerr, resp;
 
-       if (fdev != (dev_t)-1 && fino != (ino_t)-1) {
-               /* symbolic link */
-               PRIV_START;
-               f = safe_open(file, O_RDONLY, 0);
+       PRIV_START;
+       if (lstat(file, &stb) < 0 || (f = safe_open(file, O_RDONLY, 0)) < 0) {
                PRIV_END;
-               if (f != -1) {
-                       /*
-                        * The symbolic link should still point to the same file
-                        * or someone is trying to print something he shouldn't.
-                        */
-                       if (fstat(f, &stb) == -1 ||
-                           stb.st_dev != fdev || stb.st_ino != fino) {
-                               close(f);
-                               return(ACCESS);
-                       }
-               }
-       } else {
-               /* regular file */
-               PRIV_START;
-               f = safe_open(file, O_RDONLY|O_NOFOLLOW, 0);
-               PRIV_END;
-       }
-       if (f == -1)
                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.
+        */
+       if (S_ISLNK(stb.st_mode) && 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)
                return (ACCESS);                /* XXX hack */

Reply via email to