Revert my changes in r1.60 back to nicm@'s latest for now.
authorbrynet <brynet@openbsd.org>
Mon, 15 Jan 2018 19:45:51 +0000 (19:45 +0000)
committerbrynet <brynet@openbsd.org>
Mon, 15 Jan 2018 19:45:51 +0000 (19:45 +0000)
Fixes "file *|grep" breakage reported by espie@

ok nicm, deraadt

usr.bin/file/Makefile
usr.bin/file/file.c

index 7bb8f68..a386ab0 100644 (file)
@@ -1,10 +1,13 @@
-# $OpenBSD: Makefile,v 1.17 2017/06/28 13:37:56 brynet Exp $
+# $OpenBSD: Makefile,v 1.18 2018/01/15 19:45:51 brynet Exp $
 
 PROG=   file
 SRCS=   file.c magic-dump.c magic-load.c magic-test.c magic-common.c \
        text.c xmalloc.c
 MAN=   file.1 magic.5
 
+LDADD= -lutil
+DPADD= ${LIBUTIL}
+
 CDIAGFLAGS+= -Wno-long-long -Wall -W -Wnested-externs -Wformat=2
 CDIAGFLAGS+= -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations
 CDIAGFLAGS+= -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare
index acdc1d8..cc7ce8c 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: file.c,v 1.65 2017/11/30 11:10:07 bentley Exp $ */
+/* $OpenBSD: file.c,v 1.66 2018/01/15 19:45:51 brynet Exp $ */
 
 /*
  * Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org>
  */
 
 #include <sys/types.h>
+#include <sys/ioctl.h>
 #include <sys/mman.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
 #include <sys/stat.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
 
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
+#include <imsg.h>
 #include <libgen.h>
 #include <limits.h>
 #include <pwd.h>
 #include "magic.h"
 #include "xmalloc.h"
 
-struct input_file {
-       struct magic    *m;
+struct input_msg {
+       int             idx;
 
-       const char      *path;
-       struct stat      sb;
-       int              fd;
-       int              error;
+       struct stat     sb;
+       int             error;
 
-       char             link_path[PATH_MAX];
-       int              link_error;
-       int              link_target;
+       char            link_path[PATH_MAX];
+       int             link_error;
+       int             link_target;
+};
 
-       void            *base;
-       size_t           size;
-       int              mapped;
-       char            *result;
+struct input_ack {
+       int             idx;
+};
+
+struct input_file {
+       struct magic            *m;
+       struct input_msg        *msg;
+
+       const char              *path;
+       int                      fd;
+
+       void                    *base;
+       size_t                   size;
+       int                      mapped;
+       char                    *result;
 };
 
 extern char    *__progname;
 
 __dead void     usage(void);
 
-static void     prepare_input(struct input_file *, const char *);
+static int      prepare_message(struct input_msg *, int, const char *);
+static void     send_message(struct imsgbuf *, void *, size_t, int);
+static int      read_message(struct imsgbuf *, struct imsg *, pid_t);
+
+static void     read_link(struct input_msg *, const char *);
 
-static void     read_link(struct input_file *, const char *);
+static __dead void child(int, pid_t, int, char **);
 
 static void     test_file(struct input_file *, size_t);
 
@@ -78,6 +98,9 @@ static int     Lflag;
 static int      sflag;
 static int      Wflag;
 
+static char    *magicpath;
+static FILE    *magicfp;
+
 static struct option longopts[] = {
        { "brief",       no_argument, NULL, 'b' },
        { "dereference", no_argument, NULL, 'L' },
@@ -96,16 +119,16 @@ usage(void)
 int
 main(int argc, char **argv)
 {
-       int                      opt, idx;
-       char                    *home, *magicpath;
+       int                      opt, pair[2], fd, idx;
+       char                    *home;
        struct passwd           *pw;
-       FILE                    *magicfp = NULL;
-       struct magic            *m;
-       struct input_file       *inf = NULL;
-       size_t                   len, width = 0;
+       struct imsgbuf           ibuf;
+       struct imsg              imsg;
+       struct input_msg         msg;
+       struct input_ack        *ack;
+       pid_t                    pid, parent;
 
-       if (pledge("stdio rpath getpw id", NULL) == -1)
-               err(1, "pledge");
+       tzset();
 
        for (;;) {
                opt = getopt_long(argc, argv, "bchiLsW", longopts, NULL);
@@ -145,6 +168,7 @@ main(int argc, char **argv)
        } else if (argc == 0)
                usage();
 
+       magicfp = NULL;
        if (geteuid() != 0 && !issetugid()) {
                home = getenv("HOME");
                if (home == NULL || *home == '\0') {
@@ -168,81 +192,79 @@ main(int argc, char **argv)
        if (magicfp == NULL)
                err(1, "%s", magicpath);
 
-       if (!cflag) {
-               inf = xcalloc(argc, sizeof *inf);
-               for (idx = 0; idx < argc; idx++) {
-                       len = strlen(argv[idx]) + 1;
-                       if (len > width)
-                               width = len;
-                       prepare_input(&inf[idx], argv[idx]);
-               }
+       parent = getpid();
+       if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) != 0)
+               err(1, "socketpair");
+       switch (pid = fork()) {
+       case -1:
+               err(1, "fork");
+       case 0:
+               close(pair[0]);
+               child(pair[1], parent, argc, argv);
        }
+       close(pair[1]);
 
-       tzset();
+       fclose(magicfp);
+       magicfp = NULL;
 
-       if (pledge("stdio getpw id", NULL) == -1)
-               err(1, "pledge");
+       if (cflag)
+               goto wait_for_child;
 
-       if (geteuid() == 0) {
-               pw = getpwnam(FILE_USER);
-               if (pw == NULL)
-                       errx(1, "unknown user %s", FILE_USER);
-               if (setgroups(1, &pw->pw_gid) != 0)
-                       err(1, "setgroups");
-               if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0)
-                       err(1, "setresgid");
-               if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0)
-                       err(1, "setresuid");
-       }
-
-       if (pledge("stdio", NULL) == -1)
-               err(1, "pledge");
+       imsg_init(&ibuf, pair[0]);
+       for (idx = 0; idx < argc; idx++) {
+               fd = prepare_message(&msg, idx, argv[idx]);
+               send_message(&ibuf, &msg, sizeof msg, fd);
 
-       m = magic_load(magicfp, magicpath, cflag || Wflag);
-       if (cflag) {
-               magic_dump(m);
-               exit(0);
+               if (read_message(&ibuf, &imsg, pid) == 0)
+                       break;
+               if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof *ack)
+                       errx(1, "message too small");
+               ack = imsg.data;
+               if (ack->idx != idx)
+                       errx(1, "index not expected");
+               imsg_free(&imsg);
        }
-       fclose(magicfp);
 
-       for (idx = 0; idx < argc; idx++) {
-               inf[idx].m = m;
-               test_file(&inf[idx], width);
-               if (inf[idx].fd != -1 && inf[idx].fd != STDIN_FILENO)
-                       close(inf[idx].fd);
+wait_for_child:
+       close(pair[0]);
+       while (wait(NULL) == -1 && errno != ECHILD) {
+               if (errno != EINTR)
+                       err(1, "wait");
        }
-       exit(0);
+       _exit(0); /* let the child flush */
 }
 
-static void
-prepare_input(struct input_file *inf, const char *path)
+static int
+prepare_message(struct input_msg *msg, int idx, const char *path)
 {
        int     fd, mode, error;
 
-       inf->path = path;
+       memset(msg, 0, sizeof *msg);
+       msg->idx = idx;
 
        if (strcmp(path, "-") == 0) {
-               if (fstat(STDIN_FILENO, &inf->sb) == -1) {
-                       inf->error = errno;
-                       inf->fd = -1;
-                       return;
+               if (fstat(STDIN_FILENO, &msg->sb) == -1) {
+                       msg->error = errno;
+                       return (-1);
                }
-               inf->fd = STDIN_FILENO;
-               return;
+               return (STDIN_FILENO);
        }
 
        if (Lflag)
-               error = stat(path, &inf->sb);
+               error = stat(path, &msg->sb);
        else
-               error = lstat(path, &inf->sb);
+               error = lstat(path, &msg->sb);
        if (error == -1) {
-               inf->error = errno;
-               inf->fd = -1;
-               return;
+               msg->error = errno;
+               return (-1);
        }
 
-       /* We don't need them, so don't open directories or symlinks. */
-       mode = inf->sb.st_mode;
+       /*
+        * pledge(2) doesn't let us pass directory file descriptors around -
+        * but in fact we don't need them, so just don't open directories or
+        * symlinks (which could be to directories).
+        */
+       mode = msg->sb.st_mode;
        if (!S_ISDIR(mode) && !S_ISLNK(mode)) {
                fd = open(path, O_RDONLY|O_NONBLOCK);
                if (fd == -1 && (errno == ENFILE || errno == EMFILE))
@@ -250,12 +272,46 @@ prepare_input(struct input_file *inf, const char *path)
        } else
                fd = -1;
        if (S_ISLNK(mode))
-               read_link(inf, path);
-       inf->fd = fd;
+               read_link(msg, path);
+       return (fd);
+
+}
+
+static void
+send_message(struct imsgbuf *ibuf, void *msg, size_t msglen, int fd)
+{
+       if (imsg_compose(ibuf, -1, -1, 0, fd, msg, msglen) != 1)
+               err(1, "imsg_compose");
+       if (imsg_flush(ibuf) != 0)
+               err(1, "imsg_flush");
+}
+
+static int
+read_message(struct imsgbuf *ibuf, struct imsg *imsg, pid_t from)
+{
+       int     n;
+
+       while ((n = imsg_read(ibuf)) == -1 && errno == EAGAIN)
+               /* nothing */ ;
+       if (n == -1)
+               err(1, "imsg_read");
+       if (n == 0)
+               return (0);
+
+       if ((n = imsg_get(ibuf, imsg)) == -1)
+               err(1, "imsg_get");
+       if (n == 0)
+               return (0);
+
+       if ((pid_t)imsg->hdr.pid != from)
+               errx(1, "PIDs don't match");
+
+       return (n);
+
 }
 
 static void
-read_link(struct input_file *inf, const char *path)
+read_link(struct input_msg *msg, const char *path)
 {
        struct stat      sb;
        char             lpath[PATH_MAX];
@@ -265,25 +321,25 @@ read_link(struct input_file *inf, const char *path)
 
        size = readlink(path, lpath, sizeof lpath - 1);
        if (size == -1) {
-               inf->link_error = errno;
+               msg->link_error = errno;
                return;
        }
        lpath[size] = '\0';
 
        if (*lpath == '/')
-               strlcpy(inf->link_path, lpath, sizeof inf->link_path);
+               strlcpy(msg->link_path, lpath, sizeof msg->link_path);
        else {
                copy = xstrdup(path);
 
                root = dirname(copy);
                if (*root == '\0' || strcmp(root, ".") == 0 ||
                    strcmp (root, "/") == 0)
-                       strlcpy(inf->link_path, lpath, sizeof inf->link_path);
+                       strlcpy(msg->link_path, lpath, sizeof msg->link_path);
                else {
-                       used = snprintf(inf->link_path, sizeof inf->link_path,
+                       used = snprintf(msg->link_path, sizeof msg->link_path,
                            "%s/%s", root, lpath);
-                       if (used < 0 || (size_t)used >= sizeof inf->link_path) {
-                               inf->link_error = ENAMETOOLONG;
+                       if (used < 0 || (size_t)used >= sizeof msg->link_path) {
+                               msg->link_error = ENAMETOOLONG;
                                free(copy);
                                return;
                        }
@@ -293,7 +349,81 @@ read_link(struct input_file *inf, const char *path)
        }
 
        if (!Lflag && stat(path, &sb) == -1)
-               inf->link_target = errno;
+               msg->link_target = errno;
+}
+
+static __dead void
+child(int fd, pid_t parent, int argc, char **argv)
+{
+       struct passwd           *pw;
+       struct magic            *m;
+       struct imsgbuf           ibuf;
+       struct imsg              imsg;
+       struct input_msg        *msg;
+       struct input_ack         ack;
+       struct input_file        inf;
+       int                      i, idx;
+       size_t                   len, width = 0;
+
+       if (pledge("stdio getpw recvfd id", NULL) == -1)
+               err(1, "pledge");
+
+       if (geteuid() == 0) {
+               pw = getpwnam(FILE_USER);
+               if (pw == NULL)
+                       errx(1, "unknown user %s", FILE_USER);
+               if (setgroups(1, &pw->pw_gid) != 0)
+                       err(1, "setgroups");
+               if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0)
+                       err(1, "setresgid");
+               if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0)
+                       err(1, "setresuid");
+       }
+
+       if (pledge("stdio recvfd", NULL) == -1)
+               err(1, "pledge");
+
+       m = magic_load(magicfp, magicpath, cflag || Wflag);
+       if (cflag) {
+               magic_dump(m);
+               exit(0);
+       }
+
+       for (i = 0; i < argc; i++) {
+               len = strlen(argv[i]) + 1;
+               if (len > width)
+                       width = len;
+       }
+
+       imsg_init(&ibuf, fd);
+       for (;;) {
+               if (read_message(&ibuf, &imsg, parent) == 0)
+                       break;
+               if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof *msg)
+                       errx(1, "message too small");
+               msg = imsg.data;
+
+               idx = msg->idx;
+               if (idx < 0 || idx >= argc)
+                       errx(1, "index out of range");
+
+               memset(&inf, 0, sizeof inf);
+               inf.m = m;
+               inf.msg = msg;
+
+               inf.path = argv[idx];
+               inf.fd = imsg.fd;
+
+               test_file(&inf, width);
+
+               if (imsg.fd != -1)
+                       close(imsg.fd);
+               imsg_free(&imsg);
+
+               ack.idx = idx;
+               send_message(&ibuf, &ack, sizeof ack, -1);
+       }
+       exit(0);
 }
 
 static void *
@@ -330,14 +460,14 @@ load_file(struct input_file *inf)
 {
        size_t  used;
 
-       if (inf->sb.st_size == 0 && S_ISREG(inf->sb.st_mode))
+       if (inf->msg->sb.st_size == 0 && S_ISREG(inf->msg->sb.st_mode))
                return (0); /* empty file */
-       if (inf->sb.st_size == 0 || inf->sb.st_size > FILE_READ_SIZE)
+       if (inf->msg->sb.st_size == 0 || inf->msg->sb.st_size > FILE_READ_SIZE)
                inf->size = FILE_READ_SIZE;
        else
-               inf->size = inf->sb.st_size;
+               inf->size = inf->msg->sb.st_size;
 
-       if (!S_ISREG(inf->sb.st_mode))
+       if (!S_ISREG(inf->msg->sb.st_mode))
                goto try_read;
 
        inf->base = mmap(NULL, inf->size, PROT_READ, MAP_PRIVATE, inf->fd, 0);
@@ -360,13 +490,13 @@ try_read:
 static int
 try_stat(struct input_file *inf)
 {
-       if (inf->error != 0) {
+       if (inf->msg->error != 0) {
                xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path,
-                   strerror(inf->error));
+                   strerror(inf->msg->error));
                return (1);
        }
        if (sflag || strcmp(inf->path, "-") == 0) {
-               switch (inf->sb.st_mode & S_IFMT) {
+               switch (inf->msg->sb.st_mode & S_IFMT) {
                case S_IFIFO:
                        if (strcmp(inf->path, "-") != 0)
                                break;
@@ -377,29 +507,29 @@ try_stat(struct input_file *inf)
                }
        }
 
-       if (iflag && (inf->sb.st_mode & S_IFMT) != S_IFREG) {
+       if (iflag && (inf->msg->sb.st_mode & S_IFMT) != S_IFREG) {
                xasprintf(&inf->result, "application/x-not-regular-file");
                return (1);
        }
 
-       switch (inf->sb.st_mode & S_IFMT) {
+       switch (inf->msg->sb.st_mode & S_IFMT) {
        case S_IFDIR:
                xasprintf(&inf->result, "directory");
                return (1);
        case S_IFLNK:
-               if (inf->link_error != 0) {
+               if (inf->msg->link_error != 0) {
                        xasprintf(&inf->result, "unreadable symlink '%s' (%s)",
-                           inf->path, strerror(inf->link_error));
+                           inf->path, strerror(inf->msg->link_error));
                        return (1);
                }
-               if (inf->link_target == ELOOP)
+               if (inf->msg->link_target == ELOOP)
                        xasprintf(&inf->result, "symbolic link in a loop");
-               else if (inf->link_target != 0) {
+               else if (inf->msg->link_target != 0) {
                        xasprintf(&inf->result, "broken symbolic link to '%s'",
-                           inf->link_path);
+                           inf->msg->link_path);
                } else {
                        xasprintf(&inf->result, "symbolic link to '%s'",
-                           inf->link_path);
+                           inf->msg->link_path);
                }
                return (1);
        case S_IFSOCK:
@@ -407,13 +537,13 @@ try_stat(struct input_file *inf)
                return (1);
        case S_IFBLK:
                xasprintf(&inf->result, "block special (%ld/%ld)",
-                   (long)major(inf->sb.st_rdev),
-                   (long)minor(inf->sb.st_rdev));
+                   (long)major(inf->msg->sb.st_rdev),
+                   (long)minor(inf->msg->sb.st_rdev));
                return (1);
        case S_IFCHR:
                xasprintf(&inf->result, "character special (%ld/%ld)",
-                   (long)major(inf->sb.st_rdev),
-                   (long)minor(inf->sb.st_rdev));
+                   (long)major(inf->msg->sb.st_rdev),
+                   (long)minor(inf->msg->sb.st_rdev));
                return (1);
        case S_IFIFO:
                xasprintf(&inf->result, "fifo (named pipe)");
@@ -440,16 +570,16 @@ try_access(struct input_file *inf)
 {
        char tmp[256] = "";
 
-       if (inf->sb.st_size == 0 && S_ISREG(inf->sb.st_mode))
+       if (inf->msg->sb.st_size == 0 && S_ISREG(inf->msg->sb.st_mode))
                return (0); /* empty file */
        if (inf->fd != -1)
                return (0);
 
-       if (inf->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))
+       if (inf->msg->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))
                strlcat(tmp, "writable, ", sizeof tmp);
-       if (inf->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
+       if (inf->msg->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))
                strlcat(tmp, "executable, ", sizeof tmp);
-       if (S_ISREG(inf->sb.st_mode))
+       if (S_ISREG(inf->msg->sb.st_mode))
                strlcat(tmp, "regular file, ", sizeof tmp);
        strlcat(tmp, "no read permission", sizeof tmp);