=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/file/file.c,v retrieving revision 1.59 retrieving revision 1.60 diff -c -r1.59 -r1.60 *** src/usr.bin/file/file.c 2017/04/18 14:16:48 1.59 --- src/usr.bin/file/file.c 2017/06/28 13:37:56 1.60 *************** *** 1,4 **** ! /* $OpenBSD: file.c,v 1.59 2017/04/18 14:16:48 nicm Exp $ */ /* * Copyright (c) 2015 Nicholas Marriott --- 1,4 ---- ! /* $OpenBSD: file.c,v 1.60 2017/06/28 13:37:56 brynet Exp $ */ /* * Copyright (c) 2015 Nicholas Marriott *************** *** 29,40 **** #include #include #include - #include #include #include #include #include - #include #include #include #include --- 29,38 ---- *************** *** 43,88 **** #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 *); --- 41,72 ---- #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 *); *************** *** 99,107 **** 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' }, --- 83,88 ---- *************** *** 120,133 **** 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(); --- 101,113 ---- int main(int argc, char **argv) { ! int opt, idx; ! char *home, *magicpath; struct passwd *pw; ! FILE *magicfp; ! struct magic *m; ! struct input_file *inf = NULL; ! size_t len, width = 0; tzset(); *************** *** 193,271 **** 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)) --- 173,244 ---- 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]); ! } } ! 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); } ! exit(0); } ! static void ! prepare_input(struct input_file *inf, const char *path) { int fd, mode, error; if (strcmp(path, "-") == 0) { ! if (fstat(STDIN_FILENO, &inf->sb) == -1) { ! inf->error = errno; ! inf->fd = -1; } ! inf->fd = STDIN_FILENO; } if (Lflag) ! error = stat(path, &inf->sb); else ! error = lstat(path, &inf->sb); if (error == -1) { ! inf->error = errno; ! inf->fd = -1; } ! /* 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)) *************** *** 273,319 **** } 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; --- 246,259 ---- } else fd = -1; if (S_ISLNK(mode)) ! read_link(inf, path); ! inf->fd = fd; ! inf->path = path; } static void ! read_link(struct input_file *inf, const char *path) { struct stat sb; char lpath[PATH_MAX]; char *copy, *root; *************** *** 322,346 **** 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; } --- 262,286 ---- 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; } *************** *** 350,432 **** } 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) { --- 290,298 ---- } if (!Lflag && stat(path, &sb) == -1) ! inf->link_target = errno; } static void * fill_buffer(int fd, size_t size, size_t *used) { *************** *** 461,474 **** { 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); --- 327,340 ---- { 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); *************** *** 491,503 **** 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; --- 357,369 ---- 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; *************** *** 508,536 **** } } ! 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: --- 374,402 ---- } } ! 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: *************** *** 538,550 **** 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)"); --- 404,416 ---- 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)"); *************** *** 571,586 **** { 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); --- 437,452 ---- { 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);