summaryrefslogtreecommitdiff
path: root/progs
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2020-10-27 14:56:34 -0700
committerAndrew G. Morgan <morgan@kernel.org>2020-10-27 14:56:34 -0700
commit68240b124cc62744a7a412a7afc85b5c56a48e14 (patch)
tree68345d042be442ad6d3cef46838015753dbea72a /progs
parent1d03d736252237edd364142bd034715d743ace0b (diff)
downloadlibcap2-68240b124cc62744a7a412a7afc85b5c56a48e14.tar.gz
If needed search PATH for capsh (==) self-execution.
This addresses the following bug: https://bugzilla.kernel.org/show_bug.cgi?id=209873 Namely, the following didn't previously work: PATH=/sbin capsh == --print Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
Diffstat (limited to 'progs')
-rw-r--r--progs/capsh.c51
-rwxr-xr-xprogs/quicktest.sh7
2 files changed, 56 insertions, 2 deletions
diff --git a/progs/capsh.c b/progs/capsh.c
index 7bed98e..4f0b603 100644
--- a/progs/capsh.c
+++ b/progs/capsh.c
@@ -324,6 +324,49 @@ static void arg_change_amb(const char *arg_names, cap_flag_value_t set)
free(names);
}
+/*
+ * find_self locates and returns the full pathname of the named binary
+ * that is running. Importantly, it looks in the context of the
+ * prevailing CHROOT. Further, it does not fail over to invoking a
+ * shell if the target binary looks like something other than a
+ * executable. If an executable is not found, the function terminates
+ * the program with an error.
+ */
+static char *find_self(const char *arg0)
+{
+ int i;
+ char *parts, *dir, *scratch;
+ const char *path;
+
+ for (i = strlen(arg0)-1; i >= 0 && arg0[i] != '/'; i--);
+ if (i >= 0) {
+ return strdup(arg0);
+ }
+
+ path = getenv("PATH");
+ if (path == NULL) {
+ fprintf(stderr, "no PATH environment variable found for re-execing\n");
+ exit(1);
+ }
+
+ parts = strdup(path);
+ scratch = malloc(1+strlen(path));
+ if (parts == NULL || scratch == NULL) {
+ fprintf(stderr, "insufficient memory for path building\n");
+ exit(1);
+ }
+
+ for (i=0; (dir = strtok(parts, ":")); parts = NULL) {
+ sprintf(scratch, "%s/%s", dir, arg0);
+ if (access(scratch, X_OK) == 0) {
+ return scratch;
+ }
+ }
+
+ fprintf(stderr, "unable to find executable '%s' in PATH\n", arg0);
+ exit(1);
+}
+
int main(int argc, char *argv[], char *envp[])
{
pid_t child;
@@ -782,10 +825,14 @@ int main(int argc, char *argv[], char *envp[])
} else if (!strcmp("--print", argv[i])) {
arg_print();
} else if ((!strcmp("--", argv[i])) || (!strcmp("==", argv[i]))) {
- argv[i] = strdup(argv[i][0] == '-' ? shell : argv[0]);
+ if (argv[i][0] == '=') {
+ argv[i] = find_self(argv[0]);
+ } else {
+ argv[i] = strdup(shell);
+ }
argv[argc] = NULL;
execve(argv[i], argv+i, envp);
- fprintf(stderr, "execve '%s' failed!\n", shell);
+ fprintf(stderr, "execve '%s' failed!\n", argv[i]);
exit(1);
} else if (!strncmp("--shell=", argv[i], 8)) {
shell = argv[i]+8;
diff --git a/progs/quicktest.sh b/progs/quicktest.sh
index 5873317..1c21bb4 100755
--- a/progs/quicktest.sh
+++ b/progs/quicktest.sh
@@ -44,6 +44,13 @@ pass_capsh () {
pass_capsh --print
+# Validate that PATH expansion works
+PATH=$(/bin/pwd)/junk:$(/bin/pwd) capsh == == == --modes
+if [ $? -ne 0 ]; then
+ echo "Failed to execute capsh consecutively for capability manipulation"
+ exit 1
+fi
+
# Make a local non-setuid-0 version of capsh and call it privileged
cp ./tcapsh-static ./privileged && /bin/chmod -s ./privileged
if [ $? -ne 0 ]; then