From: markus Date: Mon, 8 Jan 2018 15:21:49 +0000 (+0000) Subject: move subprocess() so scp/sftp do not need uidswap.o; ok djm@ X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=879b4d6e86491b14cf1919bf424e9e7b2f821073;p=openbsd move subprocess() so scp/sftp do not need uidswap.o; ok djm@ --- diff --git a/usr.bin/ssh/auth.c b/usr.bin/ssh/auth.c index 5abae6d5d69..09066864653 100644 --- a/usr.bin/ssh/auth.c +++ b/usr.bin/ssh/auth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.124 2017/09/12 06:32:07 djm Exp $ */ +/* $OpenBSD: auth.c,v 1.125 2018/01/08 15:21:49 markus Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -706,3 +707,155 @@ auth_get_canonical_hostname(struct ssh *ssh, int use_dns) return dnsname; } } + +/* + * Runs command in a subprocess wuth a minimal environment. + * Returns pid on success, 0 on failure. + * The child stdout and stderr maybe captured, left attached or sent to + * /dev/null depending on the contents of flags. + * "tag" is prepended to log messages. + * NB. "command" is only used for logging; the actual command executed is + * av[0]. + */ +pid_t +subprocess(const char *tag, struct passwd *pw, const char *command, + int ac, char **av, FILE **child, u_int flags) +{ + FILE *f = NULL; + struct stat st; + int fd, devnull, p[2], i; + pid_t pid; + char *cp, errmsg[512]; + u_int envsize; + char **child_env; + + if (child != NULL) + *child = NULL; + + debug3("%s: %s command \"%s\" running as %s (flags 0x%x)", __func__, + tag, command, pw->pw_name, flags); + + /* Check consistency */ + if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 && + (flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) { + error("%s: inconsistent flags", __func__); + return 0; + } + if (((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) != (child == NULL)) { + error("%s: inconsistent flags/output", __func__); + return 0; + } + + /* + * If executing an explicit binary, then verify the it exists + * and appears safe-ish to execute + */ + if (*av[0] != '/') { + error("%s path is not absolute", tag); + return 0; + } + temporarily_use_uid(pw); + if (stat(av[0], &st) < 0) { + error("Could not stat %s \"%s\": %s", tag, + av[0], strerror(errno)); + restore_uid(); + return 0; + } + if (safe_path(av[0], &st, NULL, 0, errmsg, sizeof(errmsg)) != 0) { + error("Unsafe %s \"%s\": %s", tag, av[0], errmsg); + restore_uid(); + return 0; + } + /* Prepare to keep the child's stdout if requested */ + if (pipe(p) != 0) { + error("%s: pipe: %s", tag, strerror(errno)); + restore_uid(); + return 0; + } + restore_uid(); + + switch ((pid = fork())) { + case -1: /* error */ + error("%s: fork: %s", tag, strerror(errno)); + close(p[0]); + close(p[1]); + return 0; + case 0: /* child */ + /* Prepare a minimal environment for the child. */ + envsize = 5; + child_env = xcalloc(sizeof(*child_env), envsize); + child_set_env(&child_env, &envsize, "PATH", _PATH_STDPATH); + child_set_env(&child_env, &envsize, "USER", pw->pw_name); + child_set_env(&child_env, &envsize, "LOGNAME", pw->pw_name); + child_set_env(&child_env, &envsize, "HOME", pw->pw_dir); + if ((cp = getenv("LANG")) != NULL) + child_set_env(&child_env, &envsize, "LANG", cp); + + for (i = 0; i < NSIG; i++) + signal(i, SIG_DFL); + + if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { + error("%s: open %s: %s", tag, _PATH_DEVNULL, + strerror(errno)); + _exit(1); + } + if (dup2(devnull, STDIN_FILENO) == -1) { + error("%s: dup2: %s", tag, strerror(errno)); + _exit(1); + } + + /* Set up stdout as requested; leave stderr in place for now. */ + fd = -1; + if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) + fd = p[1]; + else if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0) + fd = devnull; + if (fd != -1 && dup2(fd, STDOUT_FILENO) == -1) { + error("%s: dup2: %s", tag, strerror(errno)); + _exit(1); + } + closefrom(STDERR_FILENO + 1); + + /* Don't use permanently_set_uid() here to avoid fatal() */ + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { + error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid, + strerror(errno)); + _exit(1); + } + if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) { + error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid, + strerror(errno)); + _exit(1); + } + /* stdin is pointed to /dev/null at this point */ + if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 && + dup2(STDIN_FILENO, STDERR_FILENO) == -1) { + error("%s: dup2: %s", tag, strerror(errno)); + _exit(1); + } + + execve(av[0], av, child_env); + error("%s exec \"%s\": %s", tag, command, strerror(errno)); + _exit(127); + default: /* parent */ + break; + } + + close(p[1]); + if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) + close(p[0]); + else if ((f = fdopen(p[0], "r")) == NULL) { + error("%s: fdopen: %s", tag, strerror(errno)); + close(p[0]); + /* Don't leave zombie child */ + kill(pid, SIGTERM); + while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) + ; + return 0; + } + /* Success */ + debug3("%s: %s pid %ld", __func__, tag, (long)pid); + if (child != NULL) + *child = f; + return pid; +} diff --git a/usr.bin/ssh/auth.h b/usr.bin/ssh/auth.h index ba6036e3abc..a4cb1204fd8 100644 --- a/usr.bin/ssh/auth.h +++ b/usr.bin/ssh/auth.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.h,v 1.93 2017/08/18 05:36:45 djm Exp $ */ +/* $OpenBSD: auth.h,v 1.94 2018/01/08 15:21:49 markus Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -195,4 +195,10 @@ void auth_debug_reset(void); struct passwd *fakepw(void); +#define SSH_SUBPROCESS_STDOUT_DISCARD (1) /* Discard stdout */ +#define SSH_SUBPROCESS_STDOUT_CAPTURE (1<<1) /* Redirect stdout */ +#define SSH_SUBPROCESS_STDERR_DISCARD (1<<2) /* Discard stderr */ +pid_t subprocess(const char *, struct passwd *, + const char *, int, char **, FILE **, u_int flags); + #endif diff --git a/usr.bin/ssh/misc.c b/usr.bin/ssh/misc.c index c1ad2dc3a77..acf58d2c57a 100644 --- a/usr.bin/ssh/misc.c +++ b/usr.bin/ssh/misc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.c,v 1.122 2017/12/08 02:14:33 djm Exp $ */ +/* $OpenBSD: misc.c,v 1.123 2018/01/08 15:21:49 markus Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2005,2006 Damien Miller. All rights reserved. @@ -1664,158 +1664,6 @@ argv_assemble(int argc, char **argv) return ret; } -/* - * Runs command in a subprocess wuth a minimal environment. - * Returns pid on success, 0 on failure. - * The child stdout and stderr maybe captured, left attached or sent to - * /dev/null depending on the contents of flags. - * "tag" is prepended to log messages. - * NB. "command" is only used for logging; the actual command executed is - * av[0]. - */ -pid_t -subprocess(const char *tag, struct passwd *pw, const char *command, - int ac, char **av, FILE **child, u_int flags) -{ - FILE *f = NULL; - struct stat st; - int fd, devnull, p[2], i; - pid_t pid; - char *cp, errmsg[512]; - u_int envsize; - char **child_env; - - if (child != NULL) - *child = NULL; - - debug3("%s: %s command \"%s\" running as %s (flags 0x%x)", __func__, - tag, command, pw->pw_name, flags); - - /* Check consistency */ - if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 && - (flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) { - error("%s: inconsistent flags", __func__); - return 0; - } - if (((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) != (child == NULL)) { - error("%s: inconsistent flags/output", __func__); - return 0; - } - - /* - * If executing an explicit binary, then verify the it exists - * and appears safe-ish to execute - */ - if (*av[0] != '/') { - error("%s path is not absolute", tag); - return 0; - } - temporarily_use_uid(pw); - if (stat(av[0], &st) < 0) { - error("Could not stat %s \"%s\": %s", tag, - av[0], strerror(errno)); - restore_uid(); - return 0; - } - if (safe_path(av[0], &st, NULL, 0, errmsg, sizeof(errmsg)) != 0) { - error("Unsafe %s \"%s\": %s", tag, av[0], errmsg); - restore_uid(); - return 0; - } - /* Prepare to keep the child's stdout if requested */ - if (pipe(p) != 0) { - error("%s: pipe: %s", tag, strerror(errno)); - restore_uid(); - return 0; - } - restore_uid(); - - switch ((pid = fork())) { - case -1: /* error */ - error("%s: fork: %s", tag, strerror(errno)); - close(p[0]); - close(p[1]); - return 0; - case 0: /* child */ - /* Prepare a minimal environment for the child. */ - envsize = 5; - child_env = xcalloc(sizeof(*child_env), envsize); - child_set_env(&child_env, &envsize, "PATH", _PATH_STDPATH); - child_set_env(&child_env, &envsize, "USER", pw->pw_name); - child_set_env(&child_env, &envsize, "LOGNAME", pw->pw_name); - child_set_env(&child_env, &envsize, "HOME", pw->pw_dir); - if ((cp = getenv("LANG")) != NULL) - child_set_env(&child_env, &envsize, "LANG", cp); - - for (i = 0; i < NSIG; i++) - signal(i, SIG_DFL); - - if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { - error("%s: open %s: %s", tag, _PATH_DEVNULL, - strerror(errno)); - _exit(1); - } - if (dup2(devnull, STDIN_FILENO) == -1) { - error("%s: dup2: %s", tag, strerror(errno)); - _exit(1); - } - - /* Set up stdout as requested; leave stderr in place for now. */ - fd = -1; - if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) - fd = p[1]; - else if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0) - fd = devnull; - if (fd != -1 && dup2(fd, STDOUT_FILENO) == -1) { - error("%s: dup2: %s", tag, strerror(errno)); - _exit(1); - } - closefrom(STDERR_FILENO + 1); - - /* Don't use permanently_set_uid() here to avoid fatal() */ - if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { - error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid, - strerror(errno)); - _exit(1); - } - if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) { - error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid, - strerror(errno)); - _exit(1); - } - /* stdin is pointed to /dev/null at this point */ - if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 && - dup2(STDIN_FILENO, STDERR_FILENO) == -1) { - error("%s: dup2: %s", tag, strerror(errno)); - _exit(1); - } - - execve(av[0], av, child_env); - error("%s exec \"%s\": %s", tag, command, strerror(errno)); - _exit(127); - default: /* parent */ - break; - } - - close(p[1]); - if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) - close(p[0]); - else if ((f = fdopen(p[0], "r")) == NULL) { - error("%s: fdopen: %s", tag, strerror(errno)); - close(p[0]); - /* Don't leave zombie child */ - kill(pid, SIGTERM); - while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) - ; - return 0; - } - /* Success */ - debug3("%s: %s pid %ld", __func__, tag, (long)pid); - if (child != NULL) - *child = f; - return pid; -} - /* Returns 0 if pid exited cleanly, non-zero otherwise */ int exited_cleanly(pid_t pid, const char *tag, const char *cmd, int quiet) diff --git a/usr.bin/ssh/misc.h b/usr.bin/ssh/misc.h index a81a124e624..4c8ee3d59a7 100644 --- a/usr.bin/ssh/misc.h +++ b/usr.bin/ssh/misc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: misc.h,v 1.69 2017/12/05 23:59:47 dtucker Exp $ */ +/* $OpenBSD: misc.h,v 1.70 2018/01/08 15:21:49 markus Exp $ */ /* * Author: Tatu Ylonen @@ -147,12 +147,6 @@ int argv_split(const char *, int *, char ***); char *argv_assemble(int, char **argv); int exited_cleanly(pid_t, const char *, const char *, int); -#define SSH_SUBPROCESS_STDOUT_DISCARD (1) /* Discard stdout */ -#define SSH_SUBPROCESS_STDOUT_CAPTURE (1<<1) /* Redirect stdout */ -#define SSH_SUBPROCESS_STDERR_DISCARD (1<<2) /* Discard stderr */ -pid_t subprocess(const char *, struct passwd *, - const char *, int, char **, FILE **, u_int flags); - struct stat; int safe_path(const char *, struct stat *, const char *, uid_t, char *, size_t); diff --git a/usr.bin/ssh/scp/Makefile b/usr.bin/ssh/scp/Makefile index ff6baa43a33..0e5aaeda70c 100644 --- a/usr.bin/ssh/scp/Makefile +++ b/usr.bin/ssh/scp/Makefile @@ -1,10 +1,10 @@ -# $OpenBSD: Makefile,v 1.18 2017/12/10 19:37:57 deraadt Exp $ +# $OpenBSD: Makefile,v 1.19 2018/01/08 15:21:49 markus Exp $ .PATH: ${.CURDIR}/.. SRCS= scp.c SRCS+= atomicio.c cleanup.c fatal.c log.c misc.c progressmeter.c \ - sshbuf-getput-basic.c sshbuf.c ssherr.c uidswap.c utf8.c xmalloc.c + sshbuf-getput-basic.c sshbuf.c ssherr.c utf8.c xmalloc.c PROG= scp BINOWN= root diff --git a/usr.bin/ssh/sftp/Makefile b/usr.bin/ssh/sftp/Makefile index 102c6d32a9b..4a1d597af83 100644 --- a/usr.bin/ssh/sftp/Makefile +++ b/usr.bin/ssh/sftp/Makefile @@ -1,10 +1,10 @@ -# $OpenBSD: Makefile,v 1.14 2017/12/10 19:37:57 deraadt Exp $ +# $OpenBSD: Makefile,v 1.15 2018/01/08 15:21:49 markus Exp $ .PATH: ${.CURDIR}/.. SRCS= sftp.c sftp-client.c sftp-common.c sftp-glob.c SRCS+= atomicio.c cleanup.c fatal.c log.c misc.c progressmeter.c \ - sshbuf-getput-basic.c sshbuf.c ssherr.c uidswap.c utf8.c xmalloc.c + sshbuf-getput-basic.c sshbuf.c ssherr.c utf8.c xmalloc.c PROG= sftp BINOWN= root