├── .github └── workflows │ └── build.yml ├── .gitignore ├── COPYING.openbsd ├── LICENSE ├── Makefile ├── README.md ├── execl.c ├── execv.c ├── fake_getgrent.c ├── fake_getpwent.c ├── fake_getusershell.c ├── get_new_argv.c ├── getgrent.c ├── getpwent.c ├── getusershell.c ├── libiosexec.h ├── libiosexec_private.h.in ├── posix_spawn.c ├── pwcache.c ├── system.c ├── tests ├── scripts │ ├── empty.sh │ ├── normal.sh │ ├── normalwitharg.sh │ ├── normalwithmultipleargs.sh │ └── noshebang.sh ├── t_ie_execl.c ├── t_ie_execle.c ├── t_ie_execlp.c ├── t_ie_execv.c ├── t_ie_execve.c ├── t_ie_execvp.c ├── t_ie_execvpe.c ├── t_ie_posix_spawn.c ├── t_ie_posix_spawnp.c └── t_ie_system.c ├── utils.c └── utils.h /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - 'LICENSE' 7 | - '**/*.md' 8 | - '**/*.txt' 9 | - '.gitignore' 10 | pull_request: 11 | paths-ignore: 12 | - 'LICENSE' 13 | - '**/*.md' 14 | - '**/*.txt' 15 | - '.gitignore' 16 | 17 | jobs: 18 | build: 19 | runs-on: macos-latest 20 | steps: 21 | - name: Checkout repository 22 | uses: actions/checkout@v3 23 | - name: Compile libiosexec 24 | run: | 25 | make all 26 | - name: Run tests 27 | run: | 28 | make check 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.dylib 3 | *.a 4 | *.dSYM 5 | libiosexec_private.h 6 | tests/t_ie_execl 7 | tests/t_ie_execle 8 | tests/t_ie_execlp 9 | tests/t_ie_execv 10 | tests/t_ie_execve 11 | tests/t_ie_execvp 12 | tests/t_ie_execvpe 13 | tests/t_ie_posix_spawn 14 | tests/t_ie_posix_spawnp 15 | tests/t_ie_system 16 | -------------------------------------------------------------------------------- /COPYING.openbsd: -------------------------------------------------------------------------------- 1 | Copyright (c) 1991, 1993 2 | The Regents of the University of California. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 3. Neither the name of the University nor the names of its contributors 13 | may be used to endorse or promote products derived from this software 14 | without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2021-2022 Procursus Team 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC ?= cc 2 | AR ?= ar 3 | LN ?= ln 4 | RANLIB ?= ranlib 5 | INSTALL ?= install 6 | 7 | MEMO_PREFIX ?= 8 | MEMO_SUB_PREFIX ?= /usr 9 | LIBDIR ?= $(MEMO_PREFIX)$(MEMO_SUB_PREFIX)/lib 10 | INCLUDEDIR ?= $(MEMO_PREFIX)$(MEMO_SUB_PREFIX)/include 11 | 12 | DEFAULT_INTERPRETER ?= /bin/sh 13 | 14 | SOVER := 1 15 | 16 | SRC := execl.c execv.c get_new_argv.c posix_spawn.c system.c utils.c 17 | PWD_SRC := getgrent.c getpwent.c pwcache.c getusershell.c 18 | WRAP_SRC := fake_getgrent.c fake_getpwent.c fake_getusershell.c 19 | 20 | ifeq ($(shell uname -s), Linux) 21 | CFLAGS += -fPIE -fPIC 22 | endif 23 | 24 | ifeq ($(DEBUG),1) 25 | CFLAGS += -g3 26 | endif 27 | 28 | LIBIOSEXEC_PREFIXED_ROOT ?= 0 29 | SHEBANG_REDIRECT_PATH ?= / 30 | 31 | ifeq ($(LIBIOSEXEC_PREFIXED_ROOT),1) 32 | SRC += $(PWD_SRC) 33 | else 34 | SRC += $(WRAP_SRC) 35 | endif 36 | 37 | CFLAGS += -D_PW_NAME_LEN=MAXLOGNAME -DLIBIOSEXEC_INTERNAL -DLIBIOSEXEC_PREFIXED_ROOT=$(LIBIOSEXEC_PREFIXED_ROOT) -DDEFAULT_INTERPRETER='"$(DEFAULT_INTERPRETER)"' 38 | 39 | DEFAULT_PATH ?= /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11:/usr/games 40 | 41 | all: libiosexec.$(SOVER).dylib libiosexec.a 42 | 43 | %.o: %.c libiosexec_private.h 44 | $(CC) -c $(CFLAGS) -fvisibility=hidden $< 45 | 46 | libiosexec_private.h: libiosexec_private.h.in 47 | sed -e "s|@DEFAULT_PATH@|$(shell printf "%s\n" "$(DEFAULT_PATH)" | tr ':' '\n' | sed "p; s|^|$(SHEBANG_REDIRECT_PATH)|" | tr '\n' ':' | sed 's|:$$|\n|')|" -e "s|@SHEBANG_REDIRECT_PATH@|$(SHEBANG_REDIRECT_PATH)|" $^ > $@ 48 | 49 | libiosexec.$(SOVER).dylib: $(SRC:%.c=%.o) 50 | ifeq ($(shell uname -s), Linux) 51 | $(CC) $(CFLAGS) $(LDFLAGS) -fvisibility=hidden -DLIBIOSEXEC_INTERNAL -lbsd -shared -o $@ $^ 52 | else ifeq ($(shell uname -s), Darwin) 53 | $(CC) $(CFLAGS) $(LDFLAGS) -fvisibility=hidden -DLIBIOSEXEC_INTERNAL -install_name $(LIBDIR)/$@ -shared -o $@ $^ 54 | endif 55 | 56 | libiosexec.a: $(SRC:%.c=%.o) 57 | $(AR) cru $@ $^ 58 | $(RANLIB) $@ 59 | 60 | 61 | TEST_progs := tests/t_ie_execve \ 62 | tests/t_ie_execv \ 63 | tests/t_ie_execle \ 64 | tests/t_ie_execl \ 65 | tests/t_ie_execvpe \ 66 | tests/t_ie_execvp \ 67 | tests/t_ie_execlp \ 68 | tests/t_ie_posix_spawn \ 69 | tests/t_ie_posix_spawnp \ 70 | tests/t_ie_system 71 | 72 | TEST_scripts := tests/scripts/empty.sh \ 73 | tests/scripts/normal.sh \ 74 | tests/scripts/normalwitharg.sh \ 75 | tests/scripts/normalwithmultipleargs.sh 76 | 77 | 78 | %: %.c libiosexec.a 79 | $(CC) -I. $(CFLAGS) $(LDFLAGS) -o $@ $^ 80 | 81 | check: $(TEST_progs) 82 | success=0; \ 83 | PATH="$(PATH):$(PWD)/tests/scripts"; \ 84 | for test in $^; do \ 85 | for script in $(TEST_scripts); do \ 86 | printf '%s %s... ' $$(basename $$test) $$(basename $$script); \ 87 | if $$test $$script; then \ 88 | printf 'success\n'; \ 89 | else \ 90 | success=1; \ 91 | printf 'FAILED!\n'; \ 92 | fi; \ 93 | done; \ 94 | done; \ 95 | exit $$success 96 | 97 | install: all 98 | $(INSTALL) -Dm644 libiosexec.$(SOVER).dylib $(DESTDIR)$(LIBDIR)/libiosexec.$(SOVER).dylib 99 | $(LN) -sf libiosexec.$(SOVER).dylib $(DESTDIR)$(LIBDIR)/libiosexec.dylib 100 | $(INSTALL) -Dm644 libiosexec.a $(DESTDIR)$(LIBDIR)/libiosexec.a 101 | $(INSTALL) -Dm644 libiosexec.h $(DESTDIR)$(INCLUDEDIR)/libiosexec.h 102 | 103 | clean: 104 | rm -rf libiosexec.$(SOVER).dylib libiosexec.a *.o tests/test tests/*.dSYM *.dSYM libiosexec_private.h $(TEST_progs) 105 | 106 | .PHONY: all clean install 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libiosexec 2 | A shim library that both works to allow shell scripts to execute correctly on iOS, and provides a framework for true rootless support. 3 | 4 | ## Installation 5 | A build of ``libiosexec`` is provided on [Procursus](https://github.com/ProcursusTeam/Procursus), which you can install if your device is already setup with the build system. 6 | 7 | Alternatively, you can compile ``libiosexec`` on your device using Git and Make. Run the commands below to build the project 8 | 9 | make install DESTDIR="/install/path" 10 | 11 | There are other variables you can specify when building; checkout the Makefile. 12 | 13 | ## Usage 14 | There's not that much to say here; simply define the symbols in your project/script 15 | 16 | /* Wrapper functions to make iOS shells scripts function correctly */ 17 | #include 18 | 19 | Note that this implementation follows FreeBSD/Linux behavior of not splitting the argument passed to the shebang, unlike macOS which does. 20 | -------------------------------------------------------------------------------- /execl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "libiosexec.h" 3 | #include 4 | 5 | int ie_execl(const char *path, const char *argv0, ...) { 6 | int argc; 7 | va_list ap; 8 | va_start(ap, argv0); 9 | for (argc=1; va_arg(ap, const char *); argc++); 10 | va_end(ap); 11 | { 12 | int i; 13 | char *argv[argc+1]; 14 | va_start(ap, argv0); 15 | argv[0] = (char *)argv0; 16 | 17 | for (i=1; i 3 | #elif defined __linux__ 4 | #include 5 | #include 6 | #endif 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "utils.h" 18 | #include "libiosexec.h" 19 | #include "libiosexec_private.h" 20 | 21 | extern char** environ; 22 | 23 | int ie_execve(const char* path, char* const argv[], char* const envp[]) { 24 | bool bypass_sysexecve = false; 25 | 26 | #if LIBIOSEXEC_PREFIXED_ROOT == 1 27 | if (is_shell_script(path)) { 28 | bypass_sysexecve = true; 29 | } 30 | #endif 31 | 32 | if (!bypass_sysexecve) { 33 | execve(path, argv, envp); 34 | } 35 | 36 | int execve_ret = errno; 37 | 38 | if (!bypass_sysexecve && (execve_ret != EPERM && execve_ret != ENOEXEC)) { 39 | return -1; 40 | } 41 | 42 | char** argv_new = get_new_argv(path, argv); 43 | if (argv_new == NULL) { 44 | return -1; 45 | } 46 | 47 | int ret = execve(argv_new[0], argv_new, envp); 48 | int saved_errno = errno; 49 | free_new_argv(argv_new); 50 | errno = saved_errno; 51 | return ret; 52 | } 53 | 54 | int ie_execv(const char *path, char *const argv[]) { 55 | return ie_execve(path, argv, environ); 56 | } 57 | 58 | int ie_execvpe(const char *name, char *const *argv, char *const *envp) { 59 | char **memp; 60 | int cnt; 61 | size_t lp, ln, len; 62 | char *p; 63 | int eacces = 0; 64 | char *bp, *cur, *path, buf[PATH_MAX]; 65 | 66 | /* 67 | * Do not allow null name 68 | */ 69 | if (name == NULL || *name == '\0') { 70 | errno = ENOENT; 71 | return (-1); 72 | } 73 | 74 | /* If it's an absolute or relative path name, it's easy. */ 75 | if (strchr(name, '/')) { 76 | bp = (char *)name; 77 | cur = path = NULL; 78 | goto retry; 79 | } 80 | bp = buf; 81 | 82 | /* Get the path we're searching. */ 83 | if (!(path = getenv("PATH"))) 84 | path = DEFAULT_PATH; 85 | len = strlen(path) + 1; 86 | cur = alloca(len); 87 | if (cur == NULL) { 88 | errno = ENOMEM; 89 | return (-1); 90 | } 91 | strlcpy(cur, path, len); 92 | path = cur; 93 | while ((p = strsep(&cur, ":"))) { 94 | /* 95 | * It's a SHELL path -- double, leading and trailing colons 96 | * mean the current directory. 97 | */ 98 | if (!*p) { 99 | p = "."; 100 | lp = 1; 101 | } else 102 | lp = strlen(p); 103 | ln = strlen(name); 104 | 105 | /* 106 | * If the path is too long complain. This is a possible 107 | * security issue; given a way to make the path too long 108 | * the user may execute the wrong program. 109 | */ 110 | if (lp + ln + 2 > sizeof(buf)) { 111 | struct iovec iov[3]; 112 | 113 | iov[0].iov_base = "execvp: "; 114 | iov[0].iov_len = 8; 115 | iov[1].iov_base = p; 116 | iov[1].iov_len = lp; 117 | iov[2].iov_base = ": path too long\n"; 118 | iov[2].iov_len = 16; 119 | (void)writev(STDERR_FILENO, iov, 3); 120 | continue; 121 | } 122 | bcopy(p, buf, lp); 123 | buf[lp] = '/'; 124 | bcopy(name, buf + lp + 1, ln); 125 | buf[lp + ln + 1] = '\0'; 126 | 127 | retry: (void)ie_execve(bp, argv, envp); 128 | switch(errno) { 129 | case E2BIG: 130 | goto done; 131 | case EISDIR: 132 | case ELOOP: 133 | case ENAMETOOLONG: 134 | case ENOENT: 135 | break; 136 | case ENOEXEC: 137 | for (cnt = 0; argv[cnt]; ++cnt) 138 | ; 139 | memp = alloca((cnt + 2) * sizeof(char *)); 140 | if (memp == NULL) 141 | goto done; 142 | memp[0] = "sh"; 143 | memp[1] = bp; 144 | bcopy(argv + 1, memp + 2, cnt * sizeof(char *)); 145 | (void)ie_execve(DEFAULT_INTERPRETER, memp, envp); 146 | goto done; 147 | case ENOMEM: 148 | goto done; 149 | case ENOTDIR: 150 | break; 151 | case ETXTBSY: 152 | /* 153 | * We used to retry here, but sh(1) doesn't. 154 | */ 155 | goto done; 156 | case EACCES: 157 | eacces = 1; 158 | break; 159 | default: 160 | goto done; 161 | } 162 | } 163 | if (eacces) 164 | errno = EACCES; 165 | else if (!errno) 166 | errno = ENOENT; 167 | done: 168 | return (-1); 169 | } 170 | 171 | int ie_execvp(const char *file, char *const argv[]) 172 | { 173 | return ie_execvpe(file, argv, environ); 174 | } 175 | -------------------------------------------------------------------------------- /fake_getgrent.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "libiosexec.h" 4 | 5 | struct group * 6 | ie_getgrgid(gid_t gid) { 7 | return getgrgid(gid); 8 | } 9 | 10 | int 11 | ie_getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize, struct group **result) { 12 | return getgrgid_r(gid, grp, buffer, bufsize, result); 13 | } 14 | 15 | struct group * 16 | ie_getgrnam(const char *name) { 17 | return getgrnam(name); 18 | } 19 | 20 | int 21 | ie_getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize, struct group **result) { 22 | return getgrnam_r(name, grp, buffer, bufsize, result); 23 | } 24 | 25 | struct group * 26 | ie_getgrent(void) { 27 | return getgrent(); 28 | } 29 | 30 | void 31 | ie_setgrent(void) { 32 | return setgrent(); 33 | } 34 | 35 | void 36 | ie_endgrent(void) { 37 | return endgrent(); 38 | } 39 | 40 | char * 41 | ie_group_from_gid(gid_t group, int nogroup) { 42 | return group_from_gid(group, nogroup); 43 | } 44 | -------------------------------------------------------------------------------- /fake_getpwent.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "libiosexec.h" 4 | 5 | struct passwd * 6 | ie_getpwent(void) { 7 | return getpwent(); 8 | } 9 | 10 | struct passwd * 11 | ie_getpwnam(const char *name) { 12 | return getpwnam(name); 13 | } 14 | 15 | int 16 | ie_getpwnam_r(const char *name, struct passwd *pw, char *buf, size_t buflen, struct passwd **pwretp) { 17 | return getpwnam_r(name, pw, buf, buflen, pwretp); 18 | } 19 | 20 | struct passwd * 21 | ie_getpwuid(uid_t uid) { 22 | return getpwuid(uid); 23 | } 24 | 25 | int 26 | ie_getpwuid_r(uid_t uid, struct passwd *pw, char *buf, size_t buflen, struct passwd **pwretp) { 27 | return getpwuid_r(uid, pw, buf, buflen, pwretp); 28 | } 29 | 30 | int 31 | ie_setpassent(int stayopen) { 32 | return setpassent(stayopen); 33 | } 34 | 35 | void 36 | ie_setpwent(void) { 37 | return setpwent(); 38 | } 39 | 40 | void 41 | ie_endpwent(void) { 42 | return endpwent(); 43 | } 44 | 45 | char * 46 | ie_user_from_uid(uid_t uid, int nouser) { 47 | return user_from_uid(uid, nouser); 48 | } 49 | -------------------------------------------------------------------------------- /fake_getusershell.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "libiosexec.h" 4 | 5 | char * 6 | ie_getusershell(void) 7 | { 8 | return getusershell(); 9 | } 10 | 11 | void 12 | ie_endusershell(void) 13 | { 14 | endusershell(); 15 | } 16 | 17 | void 18 | ie_setusershell(void) 19 | { 20 | setusershell(); 21 | } 22 | -------------------------------------------------------------------------------- /get_new_argv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "libiosexec.h" 8 | #include "libiosexec_private.h" 9 | 10 | #include "utils.h" 11 | 12 | // Stolen from xnu/bsd/kern/kern_exec.c 13 | #define IS_WHITESPACE(ch) ((ch == ' ') || (ch == '\t')) 14 | #define IS_EOL(ch) ((ch == '#') || (ch == '\n') || (ch == '\0')) 15 | 16 | char ** 17 | get_new_argv(const char *path, char *const argv[]) 18 | { 19 | int argc = 0, interpc = 0; 20 | char **newargv = NULL; 21 | char **interpargv = NULL; 22 | char interpbuf[512] = {0}; 23 | int fd = 0; 24 | ssize_t sz = 0; 25 | char *startp = NULL, *endp = NULL; 26 | 27 | if ((fd = open(path, O_RDONLY)) == -1) 28 | return NULL; 29 | 30 | while (argv[argc] != NULL) 31 | argc++; 32 | 33 | if ((sz = read(fd, interpbuf, 2)) == 2) { 34 | if (interpbuf[0] == '#' && interpbuf[1] == '!') { 35 | if ((sz = read(fd, interpbuf, 510)) == -1) { 36 | errno = ENOEXEC; /* If we fail to read we should bail */ 37 | return NULL; 38 | } 39 | /* Find start and end pointer first */ 40 | startp = interpbuf; 41 | for (int i = 0; i < 510; i++, startp++) { 42 | if (IS_EOL(*startp)) { 43 | errno = ENOEXEC; /* No shebang!? */ 44 | return NULL; 45 | } else if (IS_WHITESPACE(*startp)) { 46 | /* Keep looking */ 47 | } else { 48 | break; /* Found start! */ 49 | } 50 | } 51 | endp = startp; 52 | for (int i = 0; i < 510; i++, endp++) { 53 | if (IS_EOL(*endp)) { 54 | break; /* Found end */ 55 | } 56 | } 57 | if (!IS_EOL(*endp)) { 58 | errno = ENOEXEC; 59 | return NULL; 60 | } 61 | /* Walk endp back to the first non-whitespace */ 62 | while (IS_EOL(*endp) || IS_WHITESPACE(*endp)) 63 | endp--; 64 | endp++; /* Character after the last valid char */ 65 | *endp = '\0'; 66 | while (startp) { 67 | char *ch = startp; 68 | while (*ch && !IS_WHITESPACE(*ch)) 69 | ch++; 70 | if (*ch == '\0') { 71 | interpc++; 72 | interpargv = realloc(interpargv, interpc * sizeof(char *)); 73 | interpargv[interpc - 1] = strdup(startp); 74 | startp = NULL; 75 | } else { 76 | *ch = '\0'; 77 | interpc++; 78 | interpargv = realloc(interpargv, interpc * sizeof(char *)); 79 | interpargv[interpc - 1] = strdup(startp); 80 | startp = ch + 1; 81 | while (IS_WHITESPACE(*startp)) 82 | startp++; 83 | } 84 | } 85 | #if LIBIOSEXEC_PREFIXED_ROOT 86 | char *interp = interpargv[0]; 87 | if (strncmp(interp, "/noredirect", strlen("/noredirect")) == 0) { 88 | memmove(interp, interp + strlen("/noredirect"), (strlen(interp) + 1) - strlen("/noredirect")); 89 | } else if (strncmp(interp, "/bin", 4) == 0 || strncmp(interp, "/usr/bin", 8) == 0) { 90 | char *newinterp = calloc(strlen(interp) + strlen(SHEBANG_REDIRECT_PATH) + 1, 1); 91 | strcat(strcat(newinterp, SHEBANG_REDIRECT_PATH), interp); 92 | free(interp); 93 | interpargv[0] = newinterp; 94 | } 95 | #endif 96 | } 97 | } 98 | 99 | newargv = realloc(interpargv, (interpc + argc + 1) * sizeof(char *)); 100 | newargv[interpc + argc] = NULL; 101 | /* i = 1 to avoid old argv[0] being passed to interpreter */ 102 | newargv[interpc] = strdup(path); 103 | for (int i = 1; i < argc; i++) { 104 | newargv[interpc + i] = strdup(argv[i]); 105 | } 106 | 107 | finish: 108 | close(fd); 109 | return newargv; 110 | } 111 | 112 | 113 | void free_new_argv(char** argv) { 114 | for (int i = 0; argv[i] != NULL; i++) { 115 | free(argv[i]); 116 | argv[i] = NULL; 117 | } 118 | free(argv); 119 | } 120 | -------------------------------------------------------------------------------- /getgrent.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: getgrent.c,v 1.48 2019/07/02 15:54:05 deraadt Exp $ */ 2 | /* 3 | * Copyright (c) 1989, 1993 4 | * The Regents of the University of California. All rights reserved. 5 | * Portions Copyright (c) 1994, Jason Downs. All Rights Reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 3. Neither the name of the University nor the names of its contributors 16 | * may be used to endorse or promote products derived from this software 17 | * without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "libiosexec.h" 42 | 43 | #define __THREAD_NAME(name) __CONCAT(_thread_tagname_,name) 44 | 45 | #define _THREAD_PRIVATE_KEY(name) \ 46 | static void *__THREAD_NAME(name) 47 | 48 | #define _THREAD_PRIVATE_MUTEX_LOCK(name) do {} while (0) 49 | #define _THREAD_PRIVATE_MUTEX_UNLOCK(name) do {} while (0) 50 | 51 | #define _THREAD_PRIVATE(keyname, storage, error) &(storage) 52 | 53 | #define _GR_BUF_LEN (1024+200*sizeof(char*)) 54 | 55 | /* This global storage is locked for the non-rentrant functions */ 56 | _THREAD_PRIVATE_KEY(gr_storage); 57 | static struct group_storage { 58 | #define MAXGRP 200 59 | char *members[MAXGRP]; 60 | #define MAXLINELENGTH 1024 61 | char line[MAXLINELENGTH]; 62 | } gr_storage; 63 | #define GETGR_R_SIZE_MAX _GR_BUF_LEN 64 | 65 | /* File pointers are locked with the 'gr' mutex */ 66 | _THREAD_PRIVATE_KEY(gr); 67 | static FILE *_gr_fp; 68 | static struct group _gr_group; 69 | static int _gr_stayopen; 70 | static int grscan(int, gid_t, const char *, struct group *, struct group_storage *, 71 | int *); 72 | static int start_gr(void); 73 | static void endgrent_basic(void); 74 | 75 | static struct group *getgrnam_gs(const char *, struct group *, 76 | struct group_storage *); 77 | static struct group *getgrgid_gs(gid_t, struct group *, 78 | struct group_storage *); 79 | 80 | 81 | struct group * 82 | _getgrent_yp(int *foundyp) 83 | { 84 | struct group *p_gr = (struct group*)_THREAD_PRIVATE(gr, _gr_group, NULL); 85 | struct group_storage *gs = (struct group_storage *)_THREAD_PRIVATE(gr_storage, 86 | gr_storage, NULL); 87 | 88 | _THREAD_PRIVATE_MUTEX_LOCK(gr); 89 | if ((!_gr_fp && !start_gr()) || !grscan(0, 0, NULL, p_gr, gs, foundyp)) 90 | p_gr = NULL; 91 | _THREAD_PRIVATE_MUTEX_UNLOCK(gr); 92 | return (p_gr); 93 | } 94 | 95 | struct group * 96 | ie_getgrent(void) 97 | { 98 | return (_getgrent_yp(NULL)); 99 | } 100 | 101 | static struct group * 102 | getgrnam_gs(const char *name, struct group *p_gr, struct group_storage *gs) 103 | { 104 | int rval; 105 | 106 | _THREAD_PRIVATE_MUTEX_LOCK(gr); 107 | if (!start_gr()) 108 | rval = 0; 109 | else { 110 | rval = grscan(1, 0, name, p_gr, gs, NULL); 111 | if (!_gr_stayopen) 112 | endgrent_basic(); 113 | } 114 | _THREAD_PRIVATE_MUTEX_UNLOCK(gr); 115 | return(rval ? p_gr : NULL); 116 | } 117 | 118 | struct group * 119 | ie_getgrnam(const char *name) 120 | { 121 | struct group *p_gr = (struct group*)_THREAD_PRIVATE(gr,_gr_group,NULL); 122 | struct group_storage *gs = (struct group_storage *)_THREAD_PRIVATE(gr_storage, 123 | gr_storage, NULL); 124 | 125 | return getgrnam_gs(name, p_gr, gs); 126 | } 127 | 128 | int 129 | ie_getgrnam_r(const char *name, struct group *grp, char *buffer, 130 | size_t bufsize, struct group **result) 131 | { 132 | int errnosave; 133 | int ret; 134 | 135 | if (bufsize < GETGR_R_SIZE_MAX) 136 | return ERANGE; 137 | errnosave = errno; 138 | errno = 0; 139 | *result = getgrnam_gs(name, grp, (struct group_storage *)buffer); 140 | if (*result == NULL) 141 | ret = errno; 142 | else 143 | ret = 0; 144 | errno = errnosave; 145 | return ret; 146 | } 147 | 148 | static struct group * 149 | getgrgid_gs(gid_t gid, struct group *p_gr, struct group_storage *gs) 150 | { 151 | int rval; 152 | 153 | _THREAD_PRIVATE_MUTEX_LOCK(gr); 154 | if (!start_gr()) 155 | rval = 0; 156 | else { 157 | rval = grscan(1, gid, NULL, p_gr, gs, NULL); 158 | if (!_gr_stayopen) 159 | endgrent_basic(); 160 | } 161 | _THREAD_PRIVATE_MUTEX_UNLOCK(gr); 162 | return(rval ? p_gr : NULL); 163 | } 164 | 165 | struct group * 166 | ie_getgrgid(gid_t gid) 167 | { 168 | struct group *p_gr = (struct group*)_THREAD_PRIVATE(gr, _gr_group, NULL); 169 | struct group_storage *gs = (struct group_storage *)_THREAD_PRIVATE(gr_storage, 170 | gr_storage, NULL); 171 | 172 | return getgrgid_gs(gid, p_gr, gs); 173 | } 174 | 175 | int 176 | ie_getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize, 177 | struct group **result) 178 | { 179 | int errnosave; 180 | int ret; 181 | 182 | if (bufsize < GETGR_R_SIZE_MAX) 183 | return ERANGE; 184 | errnosave = errno; 185 | errno = 0; 186 | *result = getgrgid_gs(gid, grp, (struct group_storage *)buffer); 187 | if (*result == NULL) 188 | ret = errno; 189 | else 190 | ret = 0; 191 | errno = errnosave; 192 | return ret; 193 | } 194 | 195 | static int 196 | start_gr(void) 197 | { 198 | 199 | if (_gr_fp) { 200 | rewind(_gr_fp); 201 | return(1); 202 | } 203 | 204 | 205 | return((_gr_fp = fopen(_PATH_GROUP, "re")) ? 1 : 0); 206 | } 207 | 208 | void 209 | ie_setgrent(void) 210 | { 211 | int saved_errno; 212 | 213 | saved_errno = errno; 214 | setgroupent(0); 215 | errno = saved_errno; 216 | } 217 | 218 | int 219 | setgroupent(int stayopen) 220 | { 221 | int retval; 222 | 223 | _THREAD_PRIVATE_MUTEX_LOCK(gr); 224 | if (!start_gr()) 225 | retval = 0; 226 | else { 227 | _gr_stayopen = stayopen; 228 | retval = 1; 229 | } 230 | _THREAD_PRIVATE_MUTEX_UNLOCK(gr); 231 | return (retval); 232 | } 233 | 234 | static 235 | void 236 | endgrent_basic(void) 237 | { 238 | int saved_errno; 239 | 240 | if (_gr_fp) { 241 | saved_errno = errno; 242 | fclose(_gr_fp); 243 | _gr_fp = NULL; 244 | errno = saved_errno; 245 | } 246 | } 247 | 248 | void 249 | ie_endgrent(void) 250 | { 251 | _THREAD_PRIVATE_MUTEX_LOCK(gr); 252 | endgrent_basic(); 253 | _THREAD_PRIVATE_MUTEX_UNLOCK(gr); 254 | } 255 | 256 | static int 257 | grscan(int search, gid_t gid, const char *name, struct group *p_gr, 258 | struct group_storage *gs, int *foundyp) 259 | { 260 | char *cp, **m; 261 | char *bp, *endp; 262 | u_long ul; 263 | char **members; 264 | char *line; 265 | int saved_errno; 266 | 267 | if (gs == NULL) 268 | return 0; 269 | members = gs->members; 270 | line = gs->line; 271 | saved_errno = errno; 272 | 273 | for (;;) { 274 | if (!fgets(line, sizeof(gs->line), _gr_fp)) { 275 | if (feof(_gr_fp) && !ferror(_gr_fp)) 276 | errno = saved_errno; 277 | return 0; 278 | } 279 | bp = line; 280 | /* skip lines that are too big */ 281 | if (!strchr(line, '\n')) { 282 | int ch; 283 | 284 | while ((ch = getc_unlocked(_gr_fp)) != '\n' && 285 | ch != EOF) 286 | ; 287 | continue; 288 | } 289 | p_gr->gr_name = strsep(&bp, ":\n"); 290 | if (search && name && strcmp(p_gr->gr_name, name)) 291 | continue; 292 | p_gr->gr_passwd = strsep(&bp, ":\n"); 293 | if (!(cp = strsep(&bp, ":\n"))) 294 | continue; 295 | ul = strtoul(cp, &endp, 10); 296 | if (endp == cp || *endp != '\0' || ul >= GID_MAX) 297 | continue; 298 | p_gr->gr_gid = ul; 299 | if (search && name == NULL && p_gr->gr_gid != gid) 300 | continue; 301 | cp = NULL; 302 | if (bp == NULL) 303 | continue; 304 | for (m = p_gr->gr_mem = members;; bp++) { 305 | if (m == &members[MAXGRP - 1]) 306 | break; 307 | if (*bp == ',') { 308 | if (cp) { 309 | *bp = '\0'; 310 | *m++ = cp; 311 | cp = NULL; 312 | } 313 | } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') { 314 | if (cp) { 315 | *bp = '\0'; 316 | *m++ = cp; 317 | } 318 | break; 319 | } else if (cp == NULL) 320 | cp = bp; 321 | } 322 | *m = NULL; 323 | errno = saved_errno; 324 | return 1; 325 | } 326 | /* NOTREACHED */ 327 | } 328 | -------------------------------------------------------------------------------- /getpwent.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: getpwent.c,v 1.64 2021/12/07 18:13:45 deraadt Exp $ */ 2 | /* 3 | * Copyright (c) 2008 Theo de Raadt 4 | * Copyright (c) 1988, 1993 5 | * The Regents of the University of California. All rights reserved. 6 | * Portions Copyright (c) 1994, 1995, 1996, Jason Downs. All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. Neither the name of the University nor the names of its contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #include "libiosexec.h" 48 | #include "libiosexec_private.h" 49 | 50 | #define DEF_WEAK(a) 51 | 52 | #define __THREAD_NAME(name) __CONCAT(_thread_tagname_,name) 53 | #define _THREAD_PRIVATE_KEY(name) \ 54 | static void *__THREAD_NAME(name) 55 | 56 | #define _THREAD_PRIVATE_MUTEX_LOCK(name) do {} while (0) 57 | #define _THREAD_PRIVATE_MUTEX_UNLOCK(name) do {} while (0) 58 | 59 | #define _PW_BUF_LEN 1024 60 | 61 | #if 0 62 | #undef _PATH_MP_DB 63 | #define _PATH_MP_DB "/tmp/passwd/pwd.db" 64 | 65 | #undef _PATH_SMP_DB 66 | #define _PATH_SMP_DB "/tmp/passwd/spwd.db" 67 | #endif 68 | 69 | #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 70 | 71 | struct pw_storage { 72 | struct passwd pw; 73 | uid_t uid; 74 | char name[_PW_NAME_LEN + 1]; 75 | char pwbuf[_PW_BUF_LEN]; 76 | }; 77 | 78 | _THREAD_PRIVATE_KEY(pw); 79 | 80 | static DB *_pw_db; /* password database */ 81 | 82 | /* mmap'd password storage */ 83 | static struct pw_storage *_pw_storage = MAP_FAILED; 84 | 85 | /* Following are used only by setpwent(), getpwent(), and endpwent() */ 86 | static int _pw_keynum; /* key counter */ 87 | static int _pw_stayopen; /* keep fd's open */ 88 | static int _pw_flags; /* password flags */ 89 | 90 | static int __hashpw(DBT *, char *buf, size_t buflen, struct passwd *, int *); 91 | static int __initdb(int); 92 | static struct passwd *_pwhashbyname(const char *name, char *buf, 93 | size_t buflen, struct passwd *pw, int *); 94 | static struct passwd *_pwhashbyuid(uid_t uid, char *buf, 95 | size_t buflen, struct passwd *pw, int *); 96 | 97 | 98 | static struct passwd * 99 | __get_pw_buf(char **bufp, size_t *buflenp, uid_t uid, const char *name) 100 | { 101 | bool remap = true; 102 | 103 | /* Unmap the old buffer unless we are looking up the same uid/name */ 104 | if (_pw_storage != MAP_FAILED) { 105 | if (name != NULL) { 106 | if (strcmp(_pw_storage->name, name) == 0) { 107 | #ifdef PWDEBUG 108 | struct syslog_data sdata = SYSLOG_DATA_INIT; 109 | syslog_r(LOG_CRIT | LOG_CONS, &sdata, 110 | "repeated passwd lookup of user \"%s\"", 111 | name); 112 | #endif 113 | remap = false; 114 | } 115 | } else if (uid != (uid_t)-1) { 116 | if (_pw_storage->uid == uid) { 117 | #ifdef PWDEBUG 118 | struct syslog_data sdata = SYSLOG_DATA_INIT; 119 | syslog_r(LOG_CRIT | LOG_CONS, &sdata, 120 | "repeated passwd lookup of uid %u", 121 | uid); 122 | #endif 123 | remap = false; 124 | } 125 | } 126 | if (remap) 127 | munmap(_pw_storage, sizeof(*_pw_storage)); 128 | } 129 | 130 | if (remap) { 131 | _pw_storage = mmap(NULL, sizeof(*_pw_storage), 132 | PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); 133 | if (_pw_storage == MAP_FAILED) 134 | return NULL; 135 | if (name != NULL) 136 | strlcpy(_pw_storage->name, name, sizeof(_pw_storage->name)); 137 | _pw_storage->uid = uid; 138 | } 139 | 140 | *bufp = _pw_storage->pwbuf; 141 | *buflenp = sizeof(_pw_storage->pwbuf); 142 | return &_pw_storage->pw; 143 | } 144 | 145 | struct passwd * 146 | ie_getpwent(void) 147 | { 148 | char bf[1 + sizeof(_pw_keynum)]; 149 | struct passwd *pw, *ret = NULL; 150 | char *pwbuf; 151 | size_t buflen; 152 | DBT key; 153 | 154 | _THREAD_PRIVATE_MUTEX_LOCK(pw); 155 | if (!_pw_db && !__initdb(geteuid() == 0 ? true : false)) 156 | goto done; 157 | 158 | /* Allocate space for struct and strings, unmapping the old. */ 159 | if ((pw = __get_pw_buf(&pwbuf, &buflen, -1, NULL)) == NULL) 160 | goto done; 161 | 162 | 163 | ++_pw_keynum; 164 | bf[0] = _PW_KEYBYNUM; 165 | bcopy((char *)&_pw_keynum, &bf[1], sizeof(_pw_keynum)); 166 | key.data = (u_char *)bf; 167 | key.size = 1 + sizeof(_pw_keynum); 168 | if (__hashpw(&key, pwbuf, buflen, pw, &_pw_flags)) { 169 | ret = pw; 170 | goto done; 171 | } 172 | 173 | done: 174 | _THREAD_PRIVATE_MUTEX_UNLOCK(pw); 175 | return (ret); 176 | } 177 | 178 | 179 | static struct passwd * 180 | _pwhashbyname(const char *name, char *buf, size_t buflen, struct passwd *pw, 181 | int *flagsp) 182 | { 183 | char bf[1 + _PW_NAME_LEN]; 184 | size_t len; 185 | DBT key; 186 | int r; 187 | 188 | len = strlen(name); 189 | if (len > _PW_NAME_LEN) 190 | return (NULL); 191 | bf[0] = _PW_KEYBYNAME; 192 | bcopy(name, &bf[1], MINIMUM(len, _PW_NAME_LEN)); 193 | key.data = (u_char *)bf; 194 | key.size = 1 + MINIMUM(len, _PW_NAME_LEN); 195 | r = __hashpw(&key, buf, buflen, pw, flagsp); 196 | if (r) 197 | return (pw); 198 | return (NULL); 199 | } 200 | 201 | static struct passwd * 202 | _pwhashbyuid(uid_t uid, char *buf, size_t buflen, struct passwd *pw, 203 | int *flagsp) 204 | { 205 | char bf[1 + sizeof(int)]; 206 | DBT key; 207 | int r; 208 | 209 | bf[0] = _PW_KEYBYUID; 210 | bcopy(&uid, &bf[1], sizeof(uid)); 211 | key.data = (u_char *)bf; 212 | key.size = 1 + sizeof(uid); 213 | r = __hashpw(&key, buf, buflen, pw, flagsp); 214 | if (r) 215 | return (pw); 216 | return (NULL); 217 | } 218 | 219 | static int 220 | getpwnam_internal(const char *name, struct passwd *pw, char *buf, size_t buflen, 221 | struct passwd **pwretp, bool shadow, bool reentrant) 222 | { 223 | struct passwd *pwret = NULL; 224 | int flags = 0, *flagsp = &flags; 225 | int my_errno = 0; 226 | int saved_errno, tmp_errno; 227 | 228 | _THREAD_PRIVATE_MUTEX_LOCK(pw); 229 | saved_errno = errno; 230 | errno = 0; 231 | if (!_pw_db && !__initdb(shadow)) 232 | goto fail; 233 | 234 | if (!reentrant) { 235 | /* Allocate space for struct and strings, unmapping the old. */ 236 | if ((pw = __get_pw_buf(&buf, &buflen, -1, name)) == NULL) 237 | goto fail; 238 | flagsp = &_pw_flags; 239 | } 240 | 241 | if (!pwret) 242 | pwret = _pwhashbyname(name, buf, buflen, pw, flagsp); 243 | 244 | if (!_pw_stayopen) { 245 | tmp_errno = errno; 246 | (void)(_pw_db->close)(_pw_db); 247 | _pw_db = NULL; 248 | errno = tmp_errno; 249 | } 250 | fail: 251 | if (pwretp) 252 | *pwretp = pwret; 253 | if (pwret == NULL) 254 | my_errno = errno; 255 | errno = saved_errno; 256 | _THREAD_PRIVATE_MUTEX_UNLOCK(pw); 257 | return (my_errno); 258 | } 259 | 260 | int 261 | ie_getpwnam_r(const char *name, struct passwd *pw, char *buf, size_t buflen, 262 | struct passwd **pwretp) 263 | { 264 | return getpwnam_internal(name, pw, buf, buflen, pwretp, geteuid() == 0 ? true : false, true); 265 | } 266 | DEF_WEAK(getpwnam_r); 267 | 268 | struct passwd * 269 | ie_getpwnam(const char *name) 270 | { 271 | struct passwd *pw = NULL; 272 | int my_errno; 273 | 274 | my_errno = getpwnam_internal(name, NULL, NULL, 0, &pw, geteuid() == 0 ? true : false, false); 275 | if (my_errno) { 276 | pw = NULL; 277 | errno = my_errno; 278 | } 279 | return (pw); 280 | } 281 | 282 | static int 283 | getpwuid_internal(uid_t uid, struct passwd *pw, char *buf, size_t buflen, 284 | struct passwd **pwretp, bool shadow, bool reentrant) 285 | { 286 | struct passwd *pwret = NULL; 287 | int flags = 0, *flagsp = &flags; 288 | int my_errno = 0; 289 | int saved_errno, tmp_errno; 290 | 291 | _THREAD_PRIVATE_MUTEX_LOCK(pw); 292 | saved_errno = errno; 293 | errno = 0; 294 | if (!_pw_db && !__initdb(shadow)) 295 | goto fail; 296 | 297 | if (!reentrant) { 298 | /* Allocate space for struct and strings, unmapping the old. */ 299 | if ((pw = __get_pw_buf(&buf, &buflen, uid, NULL)) == NULL) 300 | goto fail; 301 | flagsp = &_pw_flags; 302 | } 303 | 304 | if (!pwret) 305 | pwret = _pwhashbyuid(uid, buf, buflen, pw, flagsp); 306 | 307 | if (!_pw_stayopen) { 308 | tmp_errno = errno; 309 | (void)(_pw_db->close)(_pw_db); 310 | _pw_db = NULL; 311 | errno = tmp_errno; 312 | } 313 | fail: 314 | if (pwretp) 315 | *pwretp = pwret; 316 | if (pwret == NULL) 317 | my_errno = errno; 318 | errno = saved_errno; 319 | _THREAD_PRIVATE_MUTEX_UNLOCK(pw); 320 | return (my_errno); 321 | } 322 | 323 | 324 | int 325 | ie_getpwuid_r(uid_t uid, struct passwd *pw, char *buf, size_t buflen, 326 | struct passwd **pwretp) 327 | { 328 | return getpwuid_internal(uid, pw, buf, buflen, pwretp, geteuid() == 0 ? true : false, true); 329 | } 330 | DEF_WEAK(ie_getpwuid_r); 331 | 332 | struct passwd * 333 | ie_getpwuid(uid_t uid) 334 | { 335 | struct passwd *pw = NULL; 336 | int my_errno; 337 | 338 | my_errno = getpwuid_internal(uid, NULL, NULL, 0, &pw, geteuid() == 0 ? true : false, false); 339 | if (my_errno) { 340 | pw = NULL; 341 | errno = my_errno; 342 | } 343 | return (pw); 344 | } 345 | 346 | int 347 | ie_setpassent(int stayopen) 348 | { 349 | _THREAD_PRIVATE_MUTEX_LOCK(pw); 350 | _pw_keynum = 0; 351 | _pw_stayopen = stayopen; 352 | _THREAD_PRIVATE_MUTEX_UNLOCK(pw); 353 | return (1); 354 | } 355 | DEF_WEAK(ie_setpassent); 356 | 357 | void 358 | ie_setpwent(void) 359 | { 360 | (void) ie_setpassent(0); 361 | } 362 | 363 | void 364 | ie_endpwent(void) 365 | { 366 | int saved_errno; 367 | 368 | _THREAD_PRIVATE_MUTEX_LOCK(pw); 369 | saved_errno = errno; 370 | _pw_keynum = 0; 371 | if (_pw_db) { 372 | (void)(_pw_db->close)(_pw_db); 373 | _pw_db = NULL; 374 | } 375 | errno = saved_errno; 376 | _THREAD_PRIVATE_MUTEX_UNLOCK(pw); 377 | } 378 | 379 | static int 380 | __initdb(int shadow) 381 | { 382 | static int warned; 383 | int saved_errno = errno; 384 | 385 | if (shadow) 386 | _pw_db = dbopen(_PATH_SMP_DB, O_RDONLY, 0, DB_HASH, NULL); 387 | if (!_pw_db) 388 | _pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL); 389 | if (_pw_db) { 390 | errno = saved_errno; 391 | return (1); 392 | } 393 | if (!warned) { 394 | saved_errno = errno; 395 | errno = saved_errno; 396 | warned = 1; 397 | } 398 | return (0); 399 | } 400 | 401 | static int 402 | __hashpw(DBT *key, char *buf, size_t buflen, struct passwd *pw, 403 | int *flagsp) 404 | { 405 | char *p, *t; 406 | DBT data; 407 | 408 | if ((_pw_db->get)(_pw_db, key, &data, 0)) 409 | return (0); 410 | p = (char *)data.data; 411 | if (data.size > buflen) { 412 | errno = ERANGE; 413 | return (0); 414 | } 415 | 416 | t = buf; 417 | #define EXPAND(e) e = t; while ((*t++ = *p++)); 418 | EXPAND(pw->pw_name); 419 | EXPAND(pw->pw_passwd); 420 | bcopy(p, (char *)&pw->pw_uid, sizeof(int)); 421 | p += sizeof(int); 422 | bcopy(p, (char *)&pw->pw_gid, sizeof(int)); 423 | p += sizeof(int); 424 | bcopy(p, (char *)&pw->pw_change, sizeof(time_t)); 425 | p += sizeof(time_t); 426 | EXPAND(pw->pw_class); 427 | EXPAND(pw->pw_gecos); 428 | EXPAND(pw->pw_dir); 429 | EXPAND(pw->pw_shell); 430 | bcopy(p, (char *)&pw->pw_expire, sizeof(time_t)); 431 | p += sizeof(time_t); 432 | 433 | /* See if there's any data left. If so, read in flags. */ 434 | if (data.size > (p - (char *)data.data)) { 435 | bcopy(p, (char *)flagsp, sizeof(int)); 436 | p += sizeof(int); 437 | } else 438 | *flagsp = _PASSWORD_NOUID|_PASSWORD_NOGID; /* default */ 439 | return (1); 440 | } 441 | -------------------------------------------------------------------------------- /getusershell.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: getusershell.c,v 1.18 2019/12/10 02:35:16 millert Exp $ */ 2 | /* 3 | * Copyright (c) 1985, 1993 4 | * The Regents of the University of California. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the University nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | 39 | #include "libiosexec.h" 40 | #include "libiosexec_private.h" 41 | 42 | #ifdef _PATH_BSHELL 43 | #undef _PATH_BSHELL 44 | #endif 45 | 46 | #ifdef _PATH_CSHELL 47 | #undef _PATH_CSHELL 48 | #endif 49 | 50 | #ifdef _PATH_KSHELL 51 | #undef _PATH_KSHELL 52 | #endif 53 | 54 | #ifdef _PATH_SHELLS 55 | #undef _PATH_SHELLS 56 | #endif 57 | 58 | #define _PATH_BSHELL SHEBANG_REDIRECT_PATH "/usr/bin/sh" 59 | #define _PATH_CSHELL SHEBANG_REDIRECT_PATH "/usr/bin/csh" 60 | #define _PATH_KSHELL SHEBANG_REDIRECT_PATH "/usr/bin/ksh" 61 | 62 | #define _PATH_SHELLS SHEBANG_REDIRECT_PATH "/etc/shells" 63 | 64 | /* 65 | * Local shells should NOT be added here. They should be added in 66 | * /etc/shells. 67 | */ 68 | 69 | static char *okshells[] = { _PATH_BSHELL, _PATH_CSHELL, _PATH_KSHELL, NULL }; 70 | static char **curshell, **shells; 71 | static char **initshells(void); 72 | static void *reallocarray(void *optr, size_t nmemb, size_t size); 73 | 74 | /* 75 | * Get a list of shells from _PATH_SHELLS, if it exists. 76 | */ 77 | char * 78 | ie_getusershell(void) 79 | { 80 | char *ret; 81 | 82 | if (curshell == NULL) 83 | curshell = initshells(); 84 | ret = *curshell; 85 | if (ret != NULL) 86 | curshell++; 87 | return (ret); 88 | } 89 | 90 | void 91 | ie_endusershell(void) 92 | { 93 | char **s; 94 | 95 | if ((s = shells)) 96 | while (*s) 97 | free(*s++); 98 | free(shells); 99 | shells = NULL; 100 | 101 | curshell = NULL; 102 | } 103 | 104 | void 105 | ie_setusershell(void) 106 | { 107 | 108 | curshell = initshells(); 109 | } 110 | 111 | static char ** 112 | initshells(void) 113 | { 114 | size_t nshells, nalloc, linesize; 115 | char *line; 116 | FILE *fp; 117 | 118 | free(shells); 119 | shells = NULL; 120 | 121 | if ((fp = fopen(_PATH_SHELLS, "re")) == NULL) 122 | return (okshells); 123 | 124 | line = NULL; 125 | nalloc = 10; // just an initial guess 126 | nshells = 0; 127 | shells = reallocarray(NULL, nalloc, sizeof (char *)); 128 | if (shells == NULL) 129 | goto fail; 130 | linesize = 0; 131 | while (getline(&line, &linesize, fp) != -1) { 132 | if (*line != '/') 133 | continue; 134 | line[strcspn(line, "#\n")] = '\0'; 135 | if (!(shells[nshells] = strdup(line))) 136 | goto fail; 137 | 138 | if (nshells + 1 == nalloc) { 139 | char **new = reallocarray(shells, nalloc * 2, sizeof(char *)); 140 | if (!new) 141 | goto fail; 142 | shells = new; 143 | nalloc *= 2; 144 | } 145 | nshells++; 146 | } 147 | free(line); 148 | shells[nshells] = NULL; 149 | (void)fclose(fp); 150 | return (shells); 151 | 152 | fail: 153 | free(line); 154 | while (nshells) 155 | free(shells[nshells--]); 156 | free(shells); 157 | shells = NULL; 158 | (void)fclose(fp); 159 | return (okshells); 160 | } 161 | 162 | 163 | // The following taken from OpenBSD reallocarray.c 164 | 165 | /* 166 | * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX 167 | * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW 168 | */ 169 | #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) 170 | 171 | static void * 172 | reallocarray(void *optr, size_t nmemb, size_t size) 173 | { 174 | if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && 175 | nmemb > 0 && SIZE_MAX / nmemb < size) { 176 | errno = ENOMEM; 177 | return NULL; 178 | } 179 | if (size == 0 || nmemb == 0) 180 | return NULL; 181 | return realloc(optr, size * nmemb); 182 | } 183 | -------------------------------------------------------------------------------- /libiosexec.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBIOSEXEC_H 2 | #define LIBIOSEXEC_H 3 | 4 | #define IOSEXEC_PUBLIC __attribute__ ((visibility ("default"))) 5 | #define IOSEXEC_HIDDEN __attribute__ ((visibility ("hidden"))) 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif // __cplusplus 10 | 11 | IOSEXEC_PUBLIC int ie_execl(const char* path, const char* arg0, ...); 12 | IOSEXEC_PUBLIC int ie_execle(const char* path, const char* arg0, ...); 13 | IOSEXEC_PUBLIC int ie_execlp(const char* file, const char* arg0, ...); 14 | 15 | IOSEXEC_PUBLIC int ie_execv(const char* path, char *const argv[]); 16 | IOSEXEC_PUBLIC int ie_execvp(const char* file, char* const argv[]); 17 | IOSEXEC_PUBLIC int ie_execvpe(const char* file, char* const argv[], char* const envp[]); 18 | IOSEXEC_PUBLIC int ie_execve(const char* path, char* const argv[], char* const envp[]); 19 | 20 | IOSEXEC_PUBLIC int ie_system(const char *command); 21 | 22 | IOSEXEC_PUBLIC char *ie_getusershell(void); 23 | IOSEXEC_PUBLIC void ie_setusershell(void); 24 | IOSEXEC_PUBLIC void ie_endusershell(void); 25 | /* 26 | * If spawn.h was already included then we need these prototypes, 27 | * otherwise the defines below will let us use the prototypes from spawn.h 28 | */ 29 | #if defined(_SPAWN_H_) || defined(LIBIOSEXEC_INTERNAL) 30 | # if defined(LIBIOSEXEC_INTERNAL) && !defined(_SPAWN_H_) 31 | # include 32 | # endif 33 | IOSEXEC_PUBLIC int ie_posix_spawn(pid_t *pid, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]); 34 | IOSEXEC_PUBLIC int ie_posix_spawnp(pid_t *pid, const char *name, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]); 35 | #endif 36 | 37 | #if defined(_PWD_H_) || defined(LIBIOSEXEC_INTERNAL) 38 | # if defined(LIBIOSEXEC_INTERNAL) && !defined(_PWD_H_) 39 | # include 40 | # endif 41 | IOSEXEC_PUBLIC struct passwd *ie_getpwent(void); 42 | IOSEXEC_PUBLIC struct passwd *ie_getpwnam(const char *name); 43 | IOSEXEC_PUBLIC int ie_getpwnam_r(const char *name, struct passwd *pw, char *buf, size_t buflen, struct passwd **pwretp); 44 | IOSEXEC_PUBLIC struct passwd *ie_getpwuid(uid_t uid); 45 | IOSEXEC_PUBLIC int ie_getpwuid_r(uid_t uid, struct passwd *pw, char *buf, size_t buflen, struct passwd **pwretp); 46 | IOSEXEC_PUBLIC int ie_setpassent(int stayopen); 47 | IOSEXEC_PUBLIC void ie_setpwent(void); 48 | IOSEXEC_PUBLIC void ie_endpwent(void); 49 | IOSEXEC_PUBLIC char *ie_user_from_uid(uid_t, int); 50 | #endif 51 | 52 | #if defined(_GRP_H_) || defined(LIBIOSEXEC_INTERNAL) 53 | # if defined(LIBIOSEXEC_INTERNAL) && !defined(_GRP_H_) 54 | # include 55 | # endif 56 | IOSEXEC_PUBLIC struct group *ie_getgrgid(gid_t gid); 57 | IOSEXEC_PUBLIC int ie_getgrgid_r(gid_t gid, struct group *grp, char *buffer, size_t bufsize, struct group **result); 58 | IOSEXEC_PUBLIC struct group *ie_getgrnam(const char *name); 59 | IOSEXEC_PUBLIC int ie_getgrnam_r(const char *name, struct group *grp, char *buffer, size_t bufsize, struct group **result); 60 | IOSEXEC_PUBLIC struct group *ie_getgrent(void); 61 | IOSEXEC_PUBLIC void ie_setgrent(void); 62 | IOSEXEC_PUBLIC void ie_endgrent(void); 63 | IOSEXEC_PUBLIC char *ie_group_from_gid(gid_t, int); 64 | #endif 65 | 66 | #if defined(__APPLE__) 67 | # include 68 | # if TARGET_OS_IPHONE 69 | # ifndef LIBIOSEXEC_INTERNAL 70 | # define execl ie_execl 71 | # define execle ie_execle 72 | # define execlp ie_execlp 73 | # define execv ie_execv 74 | # define execvp ie_execvp 75 | # define execvpe ie_execvpe 76 | # define execve ie_execve 77 | # define posix_spawn ie_posix_spawn 78 | # define posix_spawnp ie_posix_spawnp 79 | 80 | # define system ie_system 81 | 82 | # define getusershell ie_getusershell 83 | # define setusershell ie_setusershell 84 | # define endusershell ie_endusershell 85 | 86 | # define getpwent ie_getpwent 87 | # define getpwuid ie_getpwuid 88 | # define getpwuid_r ie_getpwuid_r 89 | # define getpwnam ie_getpwnam 90 | # define getpwnam_r ie_getpwnam_r 91 | # define setpassent ie_setpassent 92 | # define setpwent ie_setpwent 93 | # define endpwent ie_endpwent 94 | # define user_from_uid ie_user_from_uid 95 | 96 | # define getgrent ie_getgrent 97 | # define getgrgid ie_getgrgid 98 | # define getgrgid_r ie_getgrgid_r 99 | # define getgrnam ie_getgrnam 100 | # define getgrnam_r ie_getgrnam_r 101 | # define setgrent ie_setgrent 102 | # define endgrent ie_endgrent 103 | # define group_from_gid ie_group_from_gid 104 | # endif // LIBIOSEXEC_INTERNAL 105 | # endif // TARGET_OS_IPHONE 106 | #endif // __APPLE__ 107 | 108 | #ifdef __cplusplus 109 | } 110 | #endif // __cplusplus 111 | 112 | #endif // LIBIOSEXEC_H 113 | -------------------------------------------------------------------------------- /libiosexec_private.h.in: -------------------------------------------------------------------------------- 1 | #include "libiosexec.h" 2 | 3 | IOSEXEC_HIDDEN char** get_new_argv(const char* path, char* const argv[]); 4 | IOSEXEC_HIDDEN void free_new_argv(char** argv); 5 | // PATH_MAX for Darwin 6 | #define PATH_MAX 1024 7 | 8 | #define DEFAULT_PATH "@DEFAULT_PATH@" 9 | #define SHEBANG_REDIRECT_PATH "@SHEBANG_REDIRECT_PATH@" 10 | -------------------------------------------------------------------------------- /posix_spawn.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "utils.h" 9 | #include "libiosexec.h" 10 | #include "libiosexec_private.h" 11 | 12 | int ie_posix_spawn(pid_t *pid, const char *path, 13 | const posix_spawn_file_actions_t *file_actions, 14 | const posix_spawnattr_t *attrp, 15 | char *const argv[], 16 | char *const envp[]) { 17 | bool bypass_sysposix_spawn = false; 18 | int err; 19 | 20 | #if LIBIOSEXEC_PREFIXED_ROOT == 1 21 | if (is_shell_script(path)) { 22 | bypass_sysposix_spawn = true; 23 | } 24 | #endif 25 | 26 | if (!bypass_sysposix_spawn) 27 | err = posix_spawn(pid, path, file_actions, attrp, argv, envp); 28 | 29 | if (!bypass_sysposix_spawn && (err != EPERM && err != ENOEXEC)) { 30 | return err; 31 | } 32 | 33 | char** new_args = get_new_argv(path, argv); 34 | if (new_args == NULL) { 35 | return errno; 36 | } 37 | 38 | int ret = posix_spawn(pid, new_args[0], file_actions, attrp, new_args, envp); 39 | int saved_errno = errno; 40 | free_new_argv(new_args); 41 | errno = saved_errno; 42 | return ret; 43 | } 44 | 45 | int 46 | ie_posix_spawnp(pid_t *pid, const char *file, 47 | const posix_spawn_file_actions_t *file_actions, 48 | const posix_spawnattr_t *attrp, 49 | char *const argv[], char *const envp[]) 50 | { 51 | const char *env_path; 52 | char *bp; 53 | char *cur; 54 | char *p; 55 | char **memp; 56 | int lp; 57 | int ln; 58 | int cnt; 59 | int err = 0; 60 | int eacces = 0; 61 | struct stat sb; 62 | char path_buf[PATH_MAX]; 63 | 64 | if ((env_path = getenv("PATH")) == NULL) 65 | env_path = DEFAULT_PATH; 66 | 67 | /* If it's an absolute or relative path name, it's easy. */ 68 | if (strchr(file, '/')) { 69 | bp = (char *)file; 70 | cur = NULL; 71 | goto retry; 72 | } 73 | bp = path_buf; 74 | 75 | /* If it's an empty path name, fail in the usual POSIX way. */ 76 | if (*file == '\0') 77 | return (ENOENT); 78 | 79 | if ((cur = alloca(strlen(env_path) + 1)) == NULL) 80 | return ENOMEM; 81 | strcpy(cur, env_path); 82 | while ((p = strsep(&cur, ":")) != NULL) { 83 | /* 84 | * It's a SHELL path -- double, leading and trailing colons 85 | * mean the current directory. 86 | */ 87 | if (*p == '\0') { 88 | p = "."; 89 | lp = 1; 90 | } else { 91 | lp = strlen(p); 92 | } 93 | ln = strlen(file); 94 | 95 | /* 96 | * If the path is too long complain. This is a possible 97 | * security issue; given a way to make the path too long 98 | * the user may spawn the wrong program. 99 | */ 100 | if (lp + ln + 2 > sizeof(path_buf)) { 101 | err = ENAMETOOLONG; 102 | goto done; 103 | } 104 | bcopy(p, path_buf, lp); 105 | path_buf[lp] = '/'; 106 | bcopy(file, path_buf + lp + 1, ln); 107 | path_buf[lp + ln + 1] = '\0'; 108 | 109 | retry: err = ie_posix_spawn(pid, bp, file_actions, attrp, argv, envp); 110 | switch (err) { 111 | case E2BIG: 112 | case ENOMEM: 113 | case ETXTBSY: 114 | goto done; 115 | case ELOOP: 116 | case ENAMETOOLONG: 117 | case ENOENT: 118 | case ENOTDIR: 119 | break; 120 | case ENOEXEC: 121 | for (cnt = 0; argv[cnt]; ++cnt) 122 | ; 123 | memp = alloca((cnt + 2) * sizeof(char *)); 124 | if (memp == NULL) { 125 | /* errno = ENOMEM; XXX override ENOEXEC? */ 126 | goto done; 127 | } 128 | memp[0] = "sh"; 129 | memp[1] = bp; 130 | bcopy(argv + 1, memp + 2, cnt * sizeof(char *)); 131 | err = ie_posix_spawn(pid, "/bin/sh", file_actions, attrp, memp, envp); 132 | goto done; 133 | default: 134 | /* 135 | * EACCES may be for an inaccessible directory or 136 | * a non-executable file. Call stat() to decide 137 | * which. This also handles ambiguities for EFAULT 138 | * and EIO, and undocumented errors like ESTALE. 139 | * We hope that the race for a stat() is unimportant. 140 | */ 141 | if (stat(bp, &sb) != 0) 142 | break; 143 | if (err == EACCES) { 144 | eacces = 1; 145 | continue; 146 | } 147 | goto done; 148 | } 149 | } 150 | if (eacces) 151 | err = EACCES; 152 | else 153 | err = ENOENT; 154 | done: 155 | return (err); 156 | } 157 | -------------------------------------------------------------------------------- /pwcache.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2008-2018 Apple Inc. All rights reserved. 3 | * 4 | * @APPLE_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apple Public Source License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://www.opensource.apple.com/apsl/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPLE_LICENSE_HEADER_END@ 22 | */ 23 | #include 24 | #include 25 | #include 26 | 27 | #include "libiosexec.h" 28 | 29 | char * 30 | ie_user_from_uid(uid_t uid, int nouser) 31 | { 32 | struct passwd *pw; 33 | static char buf[16]; 34 | 35 | pw = ie_getpwuid(uid); 36 | if (pw != NULL) return pw->pw_name; 37 | 38 | if (nouser) return NULL; 39 | 40 | snprintf(buf, sizeof(buf), "%u", uid); 41 | return buf; 42 | } 43 | 44 | char * 45 | ie_group_from_gid(gid_t gid, int nogroup) 46 | { 47 | struct group *gr; 48 | static char buf[16]; 49 | 50 | gr = ie_getgrgid(gid); 51 | if (gr != NULL) return gr->gr_name; 52 | 53 | if (nogroup) return NULL; 54 | 55 | snprintf(buf, sizeof(buf), "%u", gid); 56 | return buf; 57 | } 58 | -------------------------------------------------------------------------------- /system.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1988, 1993 3 | * The Regents of the University of California. All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 4. Neither the name of the University nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | */ 29 | 30 | #pragma clang diagnostic push 31 | #pragma clang diagnostic ignored "-Wstrict-prototypes" 32 | 33 | #if defined(LIBC_SCCS) && !defined(lint) 34 | static char sccsid[] = "@(#)system.c 8.1 (Berkeley) 6/4/93"; 35 | #endif /* LIBC_SCCS and not lint */ 36 | #include 37 | __FBSDID("$FreeBSD: src/lib/libc/stdlib/system.c,v 1.11 2007/01/09 00:28:10 imp Exp $"); 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #include 50 | #define environ (*_NSGetEnviron()) 51 | 52 | #include 53 | 54 | #include 55 | 56 | #include "libiosexec.h" 57 | 58 | static pthread_mutex_t __systemfn_mutex = PTHREAD_MUTEX_INITIALIZER; 59 | 60 | int 61 | ie_system(const char *command) 62 | { 63 | pid_t pid, savedpid; 64 | int pstat, err; 65 | struct sigaction ign, intact, quitact; 66 | sigset_t newsigblock, oldsigblock, defaultsig; 67 | posix_spawnattr_t attr; 68 | short flags = POSIX_SPAWN_SETSIGMASK; 69 | const char *argv[] = {"sh", "-c", command, NULL}; 70 | 71 | if (command == NULL) { /* just checking... */ 72 | if (access(_PATH_BSHELL, F_OK) == -1) /* if no sh or no access */ 73 | return(0); 74 | else 75 | return(1); 76 | } 77 | 78 | if ((err = posix_spawnattr_init(&attr)) != 0) { 79 | errno = err; 80 | return -1; 81 | } 82 | (void)sigemptyset(&defaultsig); 83 | 84 | pthread_mutex_lock(&__systemfn_mutex); 85 | /* 86 | * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 87 | * existing signal dispositions. 88 | */ 89 | ign.sa_handler = SIG_IGN; 90 | (void)sigemptyset(&ign.sa_mask); 91 | ign.sa_flags = 0; 92 | (void)sigaction(SIGINT, &ign, &intact); 93 | if (intact.sa_handler != SIG_IGN) { 94 | sigaddset(&defaultsig, SIGINT); 95 | flags |= POSIX_SPAWN_SETSIGDEF; 96 | } 97 | (void)sigaction(SIGQUIT, &ign, &quitact); 98 | if (quitact.sa_handler != SIG_IGN) { 99 | sigaddset(&defaultsig, SIGQUIT); 100 | flags |= POSIX_SPAWN_SETSIGDEF; 101 | } 102 | (void)sigemptyset(&newsigblock); 103 | (void)sigaddset(&newsigblock, SIGCHLD); 104 | (void)sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 105 | (void)posix_spawnattr_setsigmask(&attr, &oldsigblock); 106 | if (flags & POSIX_SPAWN_SETSIGDEF) { 107 | (void)posix_spawnattr_setsigdefault(&attr, &defaultsig); 108 | } 109 | (void)posix_spawnattr_setflags(&attr, flags); 110 | 111 | err = ie_posix_spawn(&pid, _PATH_BSHELL, NULL, &attr, (char *const *)argv, environ); 112 | (void)posix_spawnattr_destroy(&attr); 113 | if (err == 0) { 114 | savedpid = pid; 115 | do { 116 | pid = wait4(savedpid, &pstat, 0, (struct rusage *)0); 117 | } while (pid == -1 && errno == EINTR); 118 | if (pid == -1) pstat = -1; 119 | } else if (err == ENOMEM || err == EAGAIN) { /* as if fork failed */ 120 | pstat = -1; 121 | } else { 122 | pstat = W_EXITCODE(127, 0); /* couldn't exec shell */ 123 | } 124 | 125 | (void)sigaction(SIGINT, &intact, NULL); 126 | (void)sigaction(SIGQUIT, &quitact, NULL); 127 | (void)sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 128 | pthread_mutex_unlock(&__systemfn_mutex); 129 | return(pstat); 130 | } 131 | 132 | #pragma clang diagnostic pop 133 | -------------------------------------------------------------------------------- /tests/scripts/empty.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | -------------------------------------------------------------------------------- /tests/scripts/normal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "normal script" >/dev/null 3 | -------------------------------------------------------------------------------- /tests/scripts/normalwitharg.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo "normal script with arguments" >/dev/null 3 | -------------------------------------------------------------------------------- /tests/scripts/normalwithmultipleargs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env -S bash -e 2 | echo "normal script with arguments" >/dev/null 3 | -------------------------------------------------------------------------------- /tests/scripts/noshebang.sh: -------------------------------------------------------------------------------- 1 | echo "No shebang script" >/dev/null 2 | -------------------------------------------------------------------------------- /tests/t_ie_execl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | int 10 | main(int argc, char **argv) 11 | { 12 | int err; 13 | if (argc != 2) 14 | return errno; 15 | 16 | err = ie_execl(argv[1], argv[1], NULL); 17 | fprintf(stderr, "ie_execl: %s\n", strerror(errno)); 18 | return errno; 19 | } 20 | -------------------------------------------------------------------------------- /tests/t_ie_execle.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | extern char **environ; 10 | 11 | int 12 | main(int argc, char **argv) 13 | { 14 | int err; 15 | if (argc != 2) 16 | return errno; 17 | 18 | err = ie_execl(argv[1], argv[1], NULL, environ); 19 | fprintf(stderr, "ie_execle: %s\n", strerror(errno)); 20 | return errno; 21 | } 22 | -------------------------------------------------------------------------------- /tests/t_ie_execlp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | int 11 | main(int argc, char **argv) 12 | { 13 | int err; 14 | if (argc != 2) 15 | return errno; 16 | 17 | char *arg = basename(argv[1]); 18 | 19 | err = ie_execlp(arg, arg, NULL); 20 | fprintf(stderr, "ie_execlp: %s\n", strerror(errno)); 21 | return errno; 22 | } 23 | -------------------------------------------------------------------------------- /tests/t_ie_execv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | int 10 | main(int argc, char **argv) 11 | { 12 | int err; 13 | if (argc != 2) 14 | return errno; 15 | 16 | char *args[2] = { argv[1], NULL }; 17 | 18 | err = ie_execv(argv[1], args); 19 | fprintf(stderr, "ie_execv: %s\n", strerror(errno)); 20 | return errno; 21 | } 22 | -------------------------------------------------------------------------------- /tests/t_ie_execve.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | extern char **environ; 10 | 11 | int 12 | main(int argc, char **argv) 13 | { 14 | int err; 15 | if (argc != 2) 16 | return errno; 17 | 18 | char *args[2] = { argv[1], NULL }; 19 | 20 | err = ie_execve(argv[1], args, environ); 21 | fprintf(stderr, "ie_execve: %s\n", strerror(errno)); 22 | return errno; 23 | } 24 | -------------------------------------------------------------------------------- /tests/t_ie_execvp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | int 11 | main(int argc, char **argv) 12 | { 13 | int err; 14 | if (argc != 2) 15 | return errno; 16 | 17 | char *args[2] = { basename(argv[1]), NULL }; 18 | 19 | err = ie_execvp(args[0], args); 20 | fprintf(stderr, "ie_execvp: %s\n", strerror(errno)); 21 | return errno; 22 | } 23 | -------------------------------------------------------------------------------- /tests/t_ie_execvpe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | extern char **environ; 11 | 12 | int 13 | main(int argc, char **argv) 14 | { 15 | int err; 16 | if (argc != 2) 17 | return errno; 18 | 19 | char *args[2] = { basename(argv[1]), NULL }; 20 | 21 | err = ie_execvpe(args[0], args, environ); 22 | fprintf(stderr, "ie_execvpe: %s\n", strerror(errno)); 23 | return errno; 24 | } 25 | -------------------------------------------------------------------------------- /tests/t_ie_posix_spawn.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | int 11 | main(int argc, char **argv) 12 | { 13 | int err; 14 | pid_t pid; 15 | if (argc != 2) 16 | exit(1); 17 | 18 | const char *args[2] = { argv[1], NULL }; 19 | 20 | err = ie_posix_spawn(&pid, args[0], NULL, NULL, (char * const *)args, NULL); 21 | 22 | if (err != 0) 23 | fprintf(stderr, "ie_posix_spawn: %s", strerror(errno)); 24 | 25 | waitpid(pid, NULL, 0); 26 | 27 | return err; 28 | } 29 | -------------------------------------------------------------------------------- /tests/t_ie_posix_spawnp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | int 12 | main(int argc, char **argv) 13 | { 14 | int err; 15 | pid_t pid; 16 | if (argc != 2) 17 | exit(1); 18 | 19 | const char *args[2] = { basename(argv[1]), NULL }; 20 | 21 | err = ie_posix_spawnp(&pid, args[0], NULL, NULL, (char * const *)args, NULL); 22 | 23 | if (err != 0) 24 | fprintf(stderr, "ie_posix_spawnp: %s", strerror(errno)); 25 | 26 | waitpid(pid, NULL, 0); 27 | 28 | return err; 29 | } 30 | -------------------------------------------------------------------------------- /tests/t_ie_system.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | int 9 | main(int argc, char **argv) 10 | { 11 | int err = 0; 12 | if (argc != 2) 13 | exit(1); 14 | 15 | err = ie_system(argv[1]); 16 | if (err == -1) 17 | fprintf(stderr, "ie_system: %s\n", strerror(errno)); 18 | else if (WEXITSTATUS(err) == 127) 19 | fprintf(stderr, "ie_system: Failed to execute shell\n"); 20 | 21 | exit(WEXITSTATUS(err)); 22 | } 23 | -------------------------------------------------------------------------------- /utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | bool 6 | is_shell_script(const char *path) 7 | { 8 | int fd = 0; 9 | ssize_t sz = 0; 10 | char buf[2] = {0}; 11 | 12 | if (access(path, R_OK) == -1) 13 | return false; 14 | 15 | if ((fd = open(path, O_RDONLY)) == -1) 16 | return false; 17 | 18 | if ((sz = read(fd, buf, 2)) != 2) { 19 | close(fd); 20 | return false; 21 | } 22 | 23 | if (buf[0] == '#' && buf[1] == '!') { 24 | close(fd); 25 | return true; 26 | } 27 | 28 | close(fd); 29 | return false; 30 | } 31 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | #include "libiosexec.h" 2 | #include 3 | 4 | IOSEXEC_HIDDEN bool is_shell_script(const char* path); 5 | --------------------------------------------------------------------------------