diff options
-rw-r--r-- | auth.c | 155 | ||||
-rw-r--r-- | auth.h | 8 | ||||
-rw-r--r-- | misc.c | 154 | ||||
-rw-r--r-- | misc.h | 8 |
4 files changed, 163 insertions, 162 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: auth.c,v 1.124 2017/09/12 06:32:07 djm Exp $ */ | 1 | /* $OpenBSD: auth.c,v 1.125 2018/01/08 15:21:49 markus Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
4 | * | 4 | * |
@@ -28,6 +28,7 @@ | |||
28 | #include <sys/types.h> | 28 | #include <sys/types.h> |
29 | #include <sys/stat.h> | 29 | #include <sys/stat.h> |
30 | #include <sys/socket.h> | 30 | #include <sys/socket.h> |
31 | #include <sys/wait.h> | ||
31 | 32 | ||
32 | #include <netinet/in.h> | 33 | #include <netinet/in.h> |
33 | 34 | ||
@@ -840,3 +841,155 @@ auth_get_canonical_hostname(struct ssh *ssh, int use_dns) | |||
840 | return dnsname; | 841 | return dnsname; |
841 | } | 842 | } |
842 | } | 843 | } |
844 | |||
845 | /* | ||
846 | * Runs command in a subprocess wuth a minimal environment. | ||
847 | * Returns pid on success, 0 on failure. | ||
848 | * The child stdout and stderr maybe captured, left attached or sent to | ||
849 | * /dev/null depending on the contents of flags. | ||
850 | * "tag" is prepended to log messages. | ||
851 | * NB. "command" is only used for logging; the actual command executed is | ||
852 | * av[0]. | ||
853 | */ | ||
854 | pid_t | ||
855 | subprocess(const char *tag, struct passwd *pw, const char *command, | ||
856 | int ac, char **av, FILE **child, u_int flags) | ||
857 | { | ||
858 | FILE *f = NULL; | ||
859 | struct stat st; | ||
860 | int fd, devnull, p[2], i; | ||
861 | pid_t pid; | ||
862 | char *cp, errmsg[512]; | ||
863 | u_int envsize; | ||
864 | char **child_env; | ||
865 | |||
866 | if (child != NULL) | ||
867 | *child = NULL; | ||
868 | |||
869 | debug3("%s: %s command \"%s\" running as %s (flags 0x%x)", __func__, | ||
870 | tag, command, pw->pw_name, flags); | ||
871 | |||
872 | /* Check consistency */ | ||
873 | if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 && | ||
874 | (flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) { | ||
875 | error("%s: inconsistent flags", __func__); | ||
876 | return 0; | ||
877 | } | ||
878 | if (((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) != (child == NULL)) { | ||
879 | error("%s: inconsistent flags/output", __func__); | ||
880 | return 0; | ||
881 | } | ||
882 | |||
883 | /* | ||
884 | * If executing an explicit binary, then verify the it exists | ||
885 | * and appears safe-ish to execute | ||
886 | */ | ||
887 | if (*av[0] != '/') { | ||
888 | error("%s path is not absolute", tag); | ||
889 | return 0; | ||
890 | } | ||
891 | temporarily_use_uid(pw); | ||
892 | if (stat(av[0], &st) < 0) { | ||
893 | error("Could not stat %s \"%s\": %s", tag, | ||
894 | av[0], strerror(errno)); | ||
895 | restore_uid(); | ||
896 | return 0; | ||
897 | } | ||
898 | if (safe_path(av[0], &st, NULL, 0, errmsg, sizeof(errmsg)) != 0) { | ||
899 | error("Unsafe %s \"%s\": %s", tag, av[0], errmsg); | ||
900 | restore_uid(); | ||
901 | return 0; | ||
902 | } | ||
903 | /* Prepare to keep the child's stdout if requested */ | ||
904 | if (pipe(p) != 0) { | ||
905 | error("%s: pipe: %s", tag, strerror(errno)); | ||
906 | restore_uid(); | ||
907 | return 0; | ||
908 | } | ||
909 | restore_uid(); | ||
910 | |||
911 | switch ((pid = fork())) { | ||
912 | case -1: /* error */ | ||
913 | error("%s: fork: %s", tag, strerror(errno)); | ||
914 | close(p[0]); | ||
915 | close(p[1]); | ||
916 | return 0; | ||
917 | case 0: /* child */ | ||
918 | /* Prepare a minimal environment for the child. */ | ||
919 | envsize = 5; | ||
920 | child_env = xcalloc(sizeof(*child_env), envsize); | ||
921 | child_set_env(&child_env, &envsize, "PATH", _PATH_STDPATH); | ||
922 | child_set_env(&child_env, &envsize, "USER", pw->pw_name); | ||
923 | child_set_env(&child_env, &envsize, "LOGNAME", pw->pw_name); | ||
924 | child_set_env(&child_env, &envsize, "HOME", pw->pw_dir); | ||
925 | if ((cp = getenv("LANG")) != NULL) | ||
926 | child_set_env(&child_env, &envsize, "LANG", cp); | ||
927 | |||
928 | for (i = 0; i < NSIG; i++) | ||
929 | signal(i, SIG_DFL); | ||
930 | |||
931 | if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { | ||
932 | error("%s: open %s: %s", tag, _PATH_DEVNULL, | ||
933 | strerror(errno)); | ||
934 | _exit(1); | ||
935 | } | ||
936 | if (dup2(devnull, STDIN_FILENO) == -1) { | ||
937 | error("%s: dup2: %s", tag, strerror(errno)); | ||
938 | _exit(1); | ||
939 | } | ||
940 | |||
941 | /* Set up stdout as requested; leave stderr in place for now. */ | ||
942 | fd = -1; | ||
943 | if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) | ||
944 | fd = p[1]; | ||
945 | else if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0) | ||
946 | fd = devnull; | ||
947 | if (fd != -1 && dup2(fd, STDOUT_FILENO) == -1) { | ||
948 | error("%s: dup2: %s", tag, strerror(errno)); | ||
949 | _exit(1); | ||
950 | } | ||
951 | closefrom(STDERR_FILENO + 1); | ||
952 | |||
953 | /* Don't use permanently_set_uid() here to avoid fatal() */ | ||
954 | if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { | ||
955 | error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid, | ||
956 | strerror(errno)); | ||
957 | _exit(1); | ||
958 | } | ||
959 | if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) { | ||
960 | error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid, | ||
961 | strerror(errno)); | ||
962 | _exit(1); | ||
963 | } | ||
964 | /* stdin is pointed to /dev/null at this point */ | ||
965 | if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 && | ||
966 | dup2(STDIN_FILENO, STDERR_FILENO) == -1) { | ||
967 | error("%s: dup2: %s", tag, strerror(errno)); | ||
968 | _exit(1); | ||
969 | } | ||
970 | |||
971 | execve(av[0], av, child_env); | ||
972 | error("%s exec \"%s\": %s", tag, command, strerror(errno)); | ||
973 | _exit(127); | ||
974 | default: /* parent */ | ||
975 | break; | ||
976 | } | ||
977 | |||
978 | close(p[1]); | ||
979 | if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) | ||
980 | close(p[0]); | ||
981 | else if ((f = fdopen(p[0], "r")) == NULL) { | ||
982 | error("%s: fdopen: %s", tag, strerror(errno)); | ||
983 | close(p[0]); | ||
984 | /* Don't leave zombie child */ | ||
985 | kill(pid, SIGTERM); | ||
986 | while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) | ||
987 | ; | ||
988 | return 0; | ||
989 | } | ||
990 | /* Success */ | ||
991 | debug3("%s: %s pid %ld", __func__, tag, (long)pid); | ||
992 | if (child != NULL) | ||
993 | *child = f; | ||
994 | return pid; | ||
995 | } | ||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: auth.h,v 1.93 2017/08/18 05:36:45 djm Exp $ */ | 1 | /* $OpenBSD: auth.h,v 1.94 2018/01/08 15:21:49 markus Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 4 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
@@ -221,6 +221,12 @@ void auth_debug_reset(void); | |||
221 | 221 | ||
222 | struct passwd *fakepw(void); | 222 | struct passwd *fakepw(void); |
223 | 223 | ||
224 | #define SSH_SUBPROCESS_STDOUT_DISCARD (1) /* Discard stdout */ | ||
225 | #define SSH_SUBPROCESS_STDOUT_CAPTURE (1<<1) /* Redirect stdout */ | ||
226 | #define SSH_SUBPROCESS_STDERR_DISCARD (1<<2) /* Discard stderr */ | ||
227 | pid_t subprocess(const char *, struct passwd *, | ||
228 | const char *, int, char **, FILE **, u_int flags); | ||
229 | |||
224 | int sys_auth_passwd(Authctxt *, const char *); | 230 | int sys_auth_passwd(Authctxt *, const char *); |
225 | 231 | ||
226 | #define SKEY_PROMPT "\nS/Key Password: " | 232 | #define SKEY_PROMPT "\nS/Key Password: " |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: misc.c,v 1.122 2017/12/08 02:14:33 djm Exp $ */ | 1 | /* $OpenBSD: misc.c,v 1.123 2018/01/08 15:21:49 markus Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
4 | * Copyright (c) 2005,2006 Damien Miller. All rights reserved. | 4 | * Copyright (c) 2005,2006 Damien Miller. All rights reserved. |
@@ -1740,158 +1740,6 @@ argv_assemble(int argc, char **argv) | |||
1740 | return ret; | 1740 | return ret; |
1741 | } | 1741 | } |
1742 | 1742 | ||
1743 | /* | ||
1744 | * Runs command in a subprocess wuth a minimal environment. | ||
1745 | * Returns pid on success, 0 on failure. | ||
1746 | * The child stdout and stderr maybe captured, left attached or sent to | ||
1747 | * /dev/null depending on the contents of flags. | ||
1748 | * "tag" is prepended to log messages. | ||
1749 | * NB. "command" is only used for logging; the actual command executed is | ||
1750 | * av[0]. | ||
1751 | */ | ||
1752 | pid_t | ||
1753 | subprocess(const char *tag, struct passwd *pw, const char *command, | ||
1754 | int ac, char **av, FILE **child, u_int flags) | ||
1755 | { | ||
1756 | FILE *f = NULL; | ||
1757 | struct stat st; | ||
1758 | int fd, devnull, p[2], i; | ||
1759 | pid_t pid; | ||
1760 | char *cp, errmsg[512]; | ||
1761 | u_int envsize; | ||
1762 | char **child_env; | ||
1763 | |||
1764 | if (child != NULL) | ||
1765 | *child = NULL; | ||
1766 | |||
1767 | debug3("%s: %s command \"%s\" running as %s (flags 0x%x)", __func__, | ||
1768 | tag, command, pw->pw_name, flags); | ||
1769 | |||
1770 | /* Check consistency */ | ||
1771 | if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 && | ||
1772 | (flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) { | ||
1773 | error("%s: inconsistent flags", __func__); | ||
1774 | return 0; | ||
1775 | } | ||
1776 | if (((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) != (child == NULL)) { | ||
1777 | error("%s: inconsistent flags/output", __func__); | ||
1778 | return 0; | ||
1779 | } | ||
1780 | |||
1781 | /* | ||
1782 | * If executing an explicit binary, then verify the it exists | ||
1783 | * and appears safe-ish to execute | ||
1784 | */ | ||
1785 | if (*av[0] != '/') { | ||
1786 | error("%s path is not absolute", tag); | ||
1787 | return 0; | ||
1788 | } | ||
1789 | temporarily_use_uid(pw); | ||
1790 | if (stat(av[0], &st) < 0) { | ||
1791 | error("Could not stat %s \"%s\": %s", tag, | ||
1792 | av[0], strerror(errno)); | ||
1793 | restore_uid(); | ||
1794 | return 0; | ||
1795 | } | ||
1796 | if (safe_path(av[0], &st, NULL, 0, errmsg, sizeof(errmsg)) != 0) { | ||
1797 | error("Unsafe %s \"%s\": %s", tag, av[0], errmsg); | ||
1798 | restore_uid(); | ||
1799 | return 0; | ||
1800 | } | ||
1801 | /* Prepare to keep the child's stdout if requested */ | ||
1802 | if (pipe(p) != 0) { | ||
1803 | error("%s: pipe: %s", tag, strerror(errno)); | ||
1804 | restore_uid(); | ||
1805 | return 0; | ||
1806 | } | ||
1807 | restore_uid(); | ||
1808 | |||
1809 | switch ((pid = fork())) { | ||
1810 | case -1: /* error */ | ||
1811 | error("%s: fork: %s", tag, strerror(errno)); | ||
1812 | close(p[0]); | ||
1813 | close(p[1]); | ||
1814 | return 0; | ||
1815 | case 0: /* child */ | ||
1816 | /* Prepare a minimal environment for the child. */ | ||
1817 | envsize = 5; | ||
1818 | child_env = xcalloc(sizeof(*child_env), envsize); | ||
1819 | child_set_env(&child_env, &envsize, "PATH", _PATH_STDPATH); | ||
1820 | child_set_env(&child_env, &envsize, "USER", pw->pw_name); | ||
1821 | child_set_env(&child_env, &envsize, "LOGNAME", pw->pw_name); | ||
1822 | child_set_env(&child_env, &envsize, "HOME", pw->pw_dir); | ||
1823 | if ((cp = getenv("LANG")) != NULL) | ||
1824 | child_set_env(&child_env, &envsize, "LANG", cp); | ||
1825 | |||
1826 | for (i = 0; i < NSIG; i++) | ||
1827 | signal(i, SIG_DFL); | ||
1828 | |||
1829 | if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { | ||
1830 | error("%s: open %s: %s", tag, _PATH_DEVNULL, | ||
1831 | strerror(errno)); | ||
1832 | _exit(1); | ||
1833 | } | ||
1834 | if (dup2(devnull, STDIN_FILENO) == -1) { | ||
1835 | error("%s: dup2: %s", tag, strerror(errno)); | ||
1836 | _exit(1); | ||
1837 | } | ||
1838 | |||
1839 | /* Set up stdout as requested; leave stderr in place for now. */ | ||
1840 | fd = -1; | ||
1841 | if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) != 0) | ||
1842 | fd = p[1]; | ||
1843 | else if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0) | ||
1844 | fd = devnull; | ||
1845 | if (fd != -1 && dup2(fd, STDOUT_FILENO) == -1) { | ||
1846 | error("%s: dup2: %s", tag, strerror(errno)); | ||
1847 | _exit(1); | ||
1848 | } | ||
1849 | closefrom(STDERR_FILENO + 1); | ||
1850 | |||
1851 | /* Don't use permanently_set_uid() here to avoid fatal() */ | ||
1852 | if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { | ||
1853 | error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid, | ||
1854 | strerror(errno)); | ||
1855 | _exit(1); | ||
1856 | } | ||
1857 | if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) { | ||
1858 | error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid, | ||
1859 | strerror(errno)); | ||
1860 | _exit(1); | ||
1861 | } | ||
1862 | /* stdin is pointed to /dev/null at this point */ | ||
1863 | if ((flags & SSH_SUBPROCESS_STDOUT_DISCARD) != 0 && | ||
1864 | dup2(STDIN_FILENO, STDERR_FILENO) == -1) { | ||
1865 | error("%s: dup2: %s", tag, strerror(errno)); | ||
1866 | _exit(1); | ||
1867 | } | ||
1868 | |||
1869 | execve(av[0], av, child_env); | ||
1870 | error("%s exec \"%s\": %s", tag, command, strerror(errno)); | ||
1871 | _exit(127); | ||
1872 | default: /* parent */ | ||
1873 | break; | ||
1874 | } | ||
1875 | |||
1876 | close(p[1]); | ||
1877 | if ((flags & SSH_SUBPROCESS_STDOUT_CAPTURE) == 0) | ||
1878 | close(p[0]); | ||
1879 | else if ((f = fdopen(p[0], "r")) == NULL) { | ||
1880 | error("%s: fdopen: %s", tag, strerror(errno)); | ||
1881 | close(p[0]); | ||
1882 | /* Don't leave zombie child */ | ||
1883 | kill(pid, SIGTERM); | ||
1884 | while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) | ||
1885 | ; | ||
1886 | return 0; | ||
1887 | } | ||
1888 | /* Success */ | ||
1889 | debug3("%s: %s pid %ld", __func__, tag, (long)pid); | ||
1890 | if (child != NULL) | ||
1891 | *child = f; | ||
1892 | return pid; | ||
1893 | } | ||
1894 | |||
1895 | /* Returns 0 if pid exited cleanly, non-zero otherwise */ | 1743 | /* Returns 0 if pid exited cleanly, non-zero otherwise */ |
1896 | int | 1744 | int |
1897 | exited_cleanly(pid_t pid, const char *tag, const char *cmd, int quiet) | 1745 | exited_cleanly(pid_t pid, const char *tag, const char *cmd, int quiet) |
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: misc.h,v 1.69 2017/12/05 23:59:47 dtucker Exp $ */ | 1 | /* $OpenBSD: misc.h,v 1.70 2018/01/08 15:21:49 markus Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
@@ -149,12 +149,6 @@ int argv_split(const char *, int *, char ***); | |||
149 | char *argv_assemble(int, char **argv); | 149 | char *argv_assemble(int, char **argv); |
150 | int exited_cleanly(pid_t, const char *, const char *, int); | 150 | int exited_cleanly(pid_t, const char *, const char *, int); |
151 | 151 | ||
152 | #define SSH_SUBPROCESS_STDOUT_DISCARD (1) /* Discard stdout */ | ||
153 | #define SSH_SUBPROCESS_STDOUT_CAPTURE (1<<1) /* Redirect stdout */ | ||
154 | #define SSH_SUBPROCESS_STDERR_DISCARD (1<<2) /* Discard stderr */ | ||
155 | pid_t subprocess(const char *, struct passwd *, | ||
156 | const char *, int, char **, FILE **, u_int flags); | ||
157 | |||
158 | struct stat; | 152 | struct stat; |
159 | int safe_path(const char *, struct stat *, const char *, uid_t, | 153 | int safe_path(const char *, struct stat *, const char *, uid_t, |
160 | char *, size_t); | 154 | char *, size_t); |