=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/file/file.c,v retrieving revision 1.65 retrieving revision 1.66 diff -c -r1.65 -r1.66 *** src/usr.bin/file/file.c 2017/11/30 11:10:07 1.65 --- src/usr.bin/file/file.c 2018/01/15 19:45:51 1.66 *************** *** 1,4 **** ! /* $OpenBSD: file.c,v 1.65 2017/11/30 11:10:07 bentley Exp $ */ /* * Copyright (c) 2015 Nicholas Marriott --- 1,4 ---- ! /* $OpenBSD: file.c,v 1.66 2018/01/15 19:45:51 brynet Exp $ */ /* * Copyright (c) 2015 Nicholas Marriott *************** *** 17,29 **** --- 17,35 ---- */ #include + #include #include + #include + #include #include + #include + #include #include #include #include #include + #include #include #include #include *************** *** 36,67 **** #include "magic.h" #include "xmalloc.h" ! struct input_file { ! struct magic *m; ! const char *path; ! struct stat sb; ! int fd; ! int error; ! char link_path[PATH_MAX]; ! int link_error; ! int link_target; ! 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 void read_link(struct input_file *, const char *); static void test_file(struct input_file *, size_t); static int try_stat(struct input_file *); --- 42,87 ---- #include "magic.h" #include "xmalloc.h" ! struct input_msg { ! int idx; ! struct stat sb; ! int error; ! char link_path[PATH_MAX]; ! int link_error; ! int link_target; ! }; ! 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 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 __dead void child(int, pid_t, int, char **); + static void test_file(struct input_file *, size_t); static int try_stat(struct input_file *); *************** *** 78,83 **** --- 98,106 ---- 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,111 **** int main(int argc, char **argv) { ! int opt, idx; ! char *home, *magicpath; struct passwd *pw; ! FILE *magicfp = NULL; ! struct magic *m; ! struct input_file *inf = NULL; ! size_t len, width = 0; ! if (pledge("stdio rpath getpw id", NULL) == -1) ! err(1, "pledge"); for (;;) { opt = getopt_long(argc, argv, "bchiLsW", longopts, NULL); --- 119,134 ---- int main(int argc, char **argv) { ! int opt, pair[2], fd, idx; ! char *home; struct passwd *pw; ! struct imsgbuf ibuf; ! struct imsg imsg; ! struct input_msg msg; ! struct input_ack *ack; ! pid_t pid, parent; ! tzset(); for (;;) { opt = getopt_long(argc, argv, "bchiLsW", longopts, NULL); *************** *** 145,150 **** --- 168,174 ---- } else if (argc == 0) usage(); + magicfp = NULL; if (geteuid() != 0 && !issetugid()) { home = getenv("HOME"); if (home == NULL || *home == '\0') { *************** *** 168,248 **** 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]); ! } } ! tzset(); ! if (pledge("stdio getpw 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", NULL) == -1) ! err(1, "pledge"); ! ! m = magic_load(magicfp, magicpath, cflag || Wflag); ! if (cflag) { ! magic_dump(m); ! exit(0); } - 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); } ! exit(0); } ! static void ! prepare_input(struct input_file *inf, const char *path) { int fd, mode, error; ! inf->path = path; if (strcmp(path, "-") == 0) { ! if (fstat(STDIN_FILENO, &inf->sb) == -1) { ! inf->error = errno; ! inf->fd = -1; ! return; } ! inf->fd = STDIN_FILENO; ! return; } if (Lflag) ! error = stat(path, &inf->sb); else ! error = lstat(path, &inf->sb); if (error == -1) { ! inf->error = errno; ! inf->fd = -1; ! return; } ! /* We don't need them, so don't open directories or symlinks. */ ! mode = inf->sb.st_mode; if (!S_ISDIR(mode) && !S_ISLNK(mode)) { fd = open(path, O_RDONLY|O_NONBLOCK); if (fd == -1 && (errno == ENFILE || errno == EMFILE)) --- 192,270 ---- if (magicfp == NULL) err(1, "%s", magicpath); ! 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]); ! fclose(magicfp); ! magicfp = NULL; ! if (cflag) ! goto wait_for_child; ! 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); ! 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); } ! wait_for_child: ! close(pair[0]); ! while (wait(NULL) == -1 && errno != ECHILD) { ! if (errno != EINTR) ! err(1, "wait"); } ! _exit(0); /* let the child flush */ } ! static int ! prepare_message(struct input_msg *msg, int idx, const char *path) { int fd, mode, error; ! memset(msg, 0, sizeof *msg); ! msg->idx = idx; if (strcmp(path, "-") == 0) { ! if (fstat(STDIN_FILENO, &msg->sb) == -1) { ! msg->error = errno; ! return (-1); } ! return (STDIN_FILENO); } if (Lflag) ! error = stat(path, &msg->sb); else ! error = lstat(path, &msg->sb); if (error == -1) { ! msg->error = errno; ! return (-1); } ! /* ! * 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,262 **** } else fd = -1; if (S_ISLNK(mode)) ! read_link(inf, path); ! inf->fd = fd; } static void ! read_link(struct input_file *inf, const char *path) { struct stat sb; char lpath[PATH_MAX]; char *copy, *root; --- 272,318 ---- } else fd = -1; if (S_ISLNK(mode)) ! 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_msg *msg, const char *path) + { struct stat sb; char lpath[PATH_MAX]; char *copy, *root; *************** *** 265,289 **** size = readlink(path, lpath, sizeof lpath - 1); if (size == -1) { ! inf->link_error = errno; return; } lpath[size] = '\0'; if (*lpath == '/') ! strlcpy(inf->link_path, lpath, sizeof inf->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); else { ! used = snprintf(inf->link_path, sizeof inf->link_path, "%s/%s", root, lpath); ! if (used < 0 || (size_t)used >= sizeof inf->link_path) { ! inf->link_error = ENAMETOOLONG; free(copy); return; } --- 321,345 ---- size = readlink(path, lpath, sizeof lpath - 1); if (size == -1) { ! msg->link_error = errno; return; } lpath[size] = '\0'; if (*lpath == '/') ! 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(msg->link_path, lpath, sizeof msg->link_path); else { ! used = snprintf(msg->link_path, sizeof msg->link_path, "%s/%s", root, lpath); ! if (used < 0 || (size_t)used >= sizeof msg->link_path) { ! msg->link_error = ENAMETOOLONG; free(copy); return; } *************** *** 293,301 **** } if (!Lflag && stat(path, &sb) == -1) ! inf->link_target = errno; } static void * fill_buffer(int fd, size_t size, size_t *used) { --- 349,431 ---- } if (!Lflag && stat(path, &sb) == -1) ! 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 * fill_buffer(int fd, size_t size, size_t *used) { *************** *** 330,343 **** { size_t used; ! if (inf->sb.st_size == 0 && S_ISREG(inf->sb.st_mode)) return (0); /* empty file */ ! if (inf->sb.st_size == 0 || inf->sb.st_size > FILE_READ_SIZE) inf->size = FILE_READ_SIZE; else ! inf->size = inf->sb.st_size; ! if (!S_ISREG(inf->sb.st_mode)) goto try_read; inf->base = mmap(NULL, inf->size, PROT_READ, MAP_PRIVATE, inf->fd, 0); --- 460,473 ---- { size_t used; ! if (inf->msg->sb.st_size == 0 && S_ISREG(inf->msg->sb.st_mode)) return (0); /* empty file */ ! if (inf->msg->sb.st_size == 0 || inf->msg->sb.st_size > FILE_READ_SIZE) inf->size = FILE_READ_SIZE; else ! inf->size = inf->msg->sb.st_size; ! 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,372 **** static int try_stat(struct input_file *inf) { ! if (inf->error != 0) { xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path, ! strerror(inf->error)); return (1); } if (sflag || strcmp(inf->path, "-") == 0) { ! switch (inf->sb.st_mode & S_IFMT) { case S_IFIFO: if (strcmp(inf->path, "-") != 0) break; --- 490,502 ---- static int try_stat(struct input_file *inf) { ! if (inf->msg->error != 0) { xasprintf(&inf->result, "cannot stat '%s' (%s)", inf->path, ! strerror(inf->msg->error)); return (1); } if (sflag || strcmp(inf->path, "-") == 0) { ! switch (inf->msg->sb.st_mode & S_IFMT) { case S_IFIFO: if (strcmp(inf->path, "-") != 0) break; *************** *** 377,405 **** } } ! if (iflag && (inf->sb.st_mode & S_IFMT) != S_IFREG) { xasprintf(&inf->result, "application/x-not-regular-file"); return (1); } ! switch (inf->sb.st_mode & S_IFMT) { case S_IFDIR: xasprintf(&inf->result, "directory"); return (1); case S_IFLNK: ! if (inf->link_error != 0) { xasprintf(&inf->result, "unreadable symlink '%s' (%s)", ! inf->path, strerror(inf->link_error)); return (1); } ! if (inf->link_target == ELOOP) xasprintf(&inf->result, "symbolic link in a loop"); ! else if (inf->link_target != 0) { xasprintf(&inf->result, "broken symbolic link to '%s'", ! inf->link_path); } else { xasprintf(&inf->result, "symbolic link to '%s'", ! inf->link_path); } return (1); case S_IFSOCK: --- 507,535 ---- } } ! if (iflag && (inf->msg->sb.st_mode & S_IFMT) != S_IFREG) { xasprintf(&inf->result, "application/x-not-regular-file"); return (1); } ! switch (inf->msg->sb.st_mode & S_IFMT) { case S_IFDIR: xasprintf(&inf->result, "directory"); return (1); case S_IFLNK: ! if (inf->msg->link_error != 0) { xasprintf(&inf->result, "unreadable symlink '%s' (%s)", ! inf->path, strerror(inf->msg->link_error)); return (1); } ! if (inf->msg->link_target == ELOOP) xasprintf(&inf->result, "symbolic link in a loop"); ! else if (inf->msg->link_target != 0) { xasprintf(&inf->result, "broken symbolic link to '%s'", ! inf->msg->link_path); } else { xasprintf(&inf->result, "symbolic link to '%s'", ! inf->msg->link_path); } return (1); case S_IFSOCK: *************** *** 407,419 **** return (1); case S_IFBLK: xasprintf(&inf->result, "block special (%ld/%ld)", ! (long)major(inf->sb.st_rdev), ! (long)minor(inf->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)); return (1); case S_IFIFO: xasprintf(&inf->result, "fifo (named pipe)"); --- 537,549 ---- return (1); case S_IFBLK: xasprintf(&inf->result, "block special (%ld/%ld)", ! (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->msg->sb.st_rdev), ! (long)minor(inf->msg->sb.st_rdev)); return (1); case S_IFIFO: xasprintf(&inf->result, "fifo (named pipe)"); *************** *** 440,455 **** { char tmp[256] = ""; ! if (inf->sb.st_size == 0 && S_ISREG(inf->sb.st_mode)) return (0); /* empty file */ if (inf->fd != -1) return (0); ! if (inf->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)) strlcat(tmp, "executable, ", sizeof tmp); ! if (S_ISREG(inf->sb.st_mode)) strlcat(tmp, "regular file, ", sizeof tmp); strlcat(tmp, "no read permission", sizeof tmp); --- 570,585 ---- { char tmp[256] = ""; ! 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->msg->sb.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) strlcat(tmp, "writable, ", sizeof tmp); ! if (inf->msg->sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) strlcat(tmp, "executable, ", sizeof tmp); ! if (S_ISREG(inf->msg->sb.st_mode)) strlcat(tmp, "regular file, ", sizeof tmp); strlcat(tmp, "no read permission", sizeof tmp);