diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-11-03 20:31:18 -0700 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-11-03 20:31:18 -0700 |
commit | b138da4a4b9d57b850ca4d0061969f5e3299861d (patch) | |
tree | 3e20a6f4a29bfe91b2b51f416673d9fad1e0b7c7 /progs | |
download | libcap-b138da4a4b9d57b850ca4d0061969f5e3299861d.tar.gz libcap-b138da4a4b9d57b850ca4d0061969f5e3299861d.tar.bz2 libcap-b138da4a4b9d57b850ca4d0061969f5e3299861d.zip |
Imported Upstream version 2.22upstream/2.22
Diffstat (limited to 'progs')
-rw-r--r-- | progs/.gitignore | 5 | ||||
-rw-r--r-- | progs/Makefile | 38 | ||||
-rw-r--r-- | progs/capsh.c | 593 | ||||
-rw-r--r-- | progs/getcap.c | 108 | ||||
-rw-r--r-- | progs/getpcaps.c | 56 | ||||
-rw-r--r-- | progs/old/README | 1 | ||||
-rw-r--r-- | progs/old/execcap.c | 68 | ||||
-rw-r--r-- | progs/old/setpcaps.c | 124 | ||||
-rw-r--r-- | progs/old/sucap.c | 199 | ||||
-rwxr-xr-x | progs/quicktest.sh | 140 | ||||
-rw-r--r-- | progs/setcap.c | 185 |
11 files changed, 1517 insertions, 0 deletions
diff --git a/progs/.gitignore b/progs/.gitignore new file mode 100644 index 0000000..f42095f --- /dev/null +++ b/progs/.gitignore @@ -0,0 +1,5 @@ +capsh +getcap +getpcaps +setcap +verify-caps diff --git a/progs/Makefile b/progs/Makefile new file mode 100644 index 0000000..ef51dc6 --- /dev/null +++ b/progs/Makefile @@ -0,0 +1,38 @@ + +topdir=$(shell pwd)/.. +include $(topdir)/Make.Rules +# +# Programs: all of the examples that we will compile +# +PROGS=getpcaps capsh +ifeq ($(LIBATTR),yes) +PROGS += getcap setcap +endif + +BUILD=$(PROGS) + +ifneq ($(DYNAMIC),yes) +LDFLAGS += --static +endif +LDLIBS += -L../libcap -lcap + +all: $(BUILD) + +$(BUILD): %: %.o + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS) + +%.o: %.c $(INCS) + $(CC) $(IPATH) $(CFLAGS) -c $< -o $@ + +install: all + mkdir -p -m 0755 $(SBINDIR) + for p in $(PROGS) ; do \ + install -m 0755 $$p $(SBINDIR) ; \ + done +ifeq ($(RAISE_SETFCAP),yes) + $(SBINDIR)/setcap cap_setfcap=i $(SBINDIR)/setcap +endif + +clean: + $(LOCALCLEAN) + rm -f *.o $(BUILD) tcapsh ping hack.sh diff --git a/progs/capsh.c b/progs/capsh.c new file mode 100644 index 0000000..52336d7 --- /dev/null +++ b/progs/capsh.c @@ -0,0 +1,593 @@ +/* + * Copyright (c) 2008-11 Andrew G. Morgan <morgan@kernel.org> + * + * This is a simple 'bash' wrapper program that can be used to + * raise and lower both the bset and pI capabilities before invoking + * /bin/bash (hardcoded right now). + * + * The --print option can be used as a quick test whether various + * capability manipulations work as expected (or not). + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/prctl.h> +#include <sys/types.h> +#include <unistd.h> +#include <pwd.h> +#include <grp.h> +#include <errno.h> +#include <ctype.h> +#include <sys/capability.h> +#include <sys/securebits.h> +#include <sys/wait.h> +#include <sys/prctl.h> + +#define MAX_GROUPS 100 /* max number of supplementary groups for user */ + +static const cap_value_t raise_setpcap[1] = { CAP_SETPCAP }; +static const cap_value_t raise_chroot[1] = { CAP_SYS_CHROOT }; + +static char *binary(unsigned long value) +{ + static char string[8*sizeof(unsigned long) + 1]; + unsigned i; + + i = sizeof(string); + string[--i] = '\0'; + do { + string[--i] = (value & 1) ? '1' : '0'; + value >>= 1; + } while ((i > 0) && value); + return string + i; +} + +int main(int argc, char *argv[], char *envp[]) +{ + pid_t child; + unsigned i; + + child = 0; + + for (i=1; i<argc; ++i) { + if (!memcmp("--drop=", argv[i], 4)) { + char *ptr; + cap_t orig, raised_for_setpcap; + + /* + * We need to do this here because --inh=XXX may have reset + * orig and it isn't until we are within the --drop code that + * we know what the prevailing (orig) pI value is. + */ + orig = cap_get_proc(); + if (orig == NULL) { + perror("Capabilities not available"); + exit(1); + } + + raised_for_setpcap = cap_dup(orig); + if (raised_for_setpcap == NULL) { + fprintf(stderr, "BSET modification requires CAP_SETPCAP\n"); + exit(1); + } + + if (cap_set_flag(raised_for_setpcap, CAP_EFFECTIVE, 1, + raise_setpcap, CAP_SET) != 0) { + perror("unable to select CAP_SETPCAP"); + exit(1); + } + + if (strcmp("all", argv[i]+7) == 0) { + unsigned j = 0; + while (CAP_IS_SUPPORTED(j)) { + if (cap_drop_bound(j) != 0) { + char *name_ptr; + + name_ptr = cap_to_name(j); + fprintf(stderr, + "Unable to drop bounding capability [%s]\n", + name_ptr); + cap_free(name_ptr); + exit(1); + } + j++; + } + } else { + for (ptr = argv[i]+7; (ptr = strtok(ptr, ",")); ptr = NULL) { + /* find name for token */ + cap_value_t cap; + int status; + + if (cap_from_name(ptr, &cap) != 0) { + fprintf(stderr, + "capability [%s] is unknown to libcap\n", + ptr); + exit(1); + } + if (cap_set_proc(raised_for_setpcap) != 0) { + perror("unable to raise CAP_SETPCAP for BSET changes"); + exit(1); + } + status = prctl(PR_CAPBSET_DROP, cap); + if (cap_set_proc(orig) != 0) { + perror("unable to lower CAP_SETPCAP post BSET change"); + exit(1); + } + if (status) { + fprintf(stderr, "failed to drop [%s=%u]\n", ptr, cap); + exit(1); + } + } + } + cap_free(raised_for_setpcap); + cap_free(orig); + } else if (!memcmp("--inh=", argv[i], 6)) { + cap_t all, raised_for_setpcap; + char *text; + char *ptr; + + all = cap_get_proc(); + if (all == NULL) { + perror("Capabilities not available"); + exit(1); + } + if (cap_clear_flag(all, CAP_INHERITABLE) != 0) { + perror("libcap:cap_clear_flag() internal error"); + exit(1); + } + + raised_for_setpcap = cap_dup(all); + if ((raised_for_setpcap != NULL) + && (cap_set_flag(raised_for_setpcap, CAP_EFFECTIVE, 1, + raise_setpcap, CAP_SET) != 0)) { + cap_free(raised_for_setpcap); + raised_for_setpcap = NULL; + } + + text = cap_to_text(all, NULL); + cap_free(all); + if (text == NULL) { + perror("Fatal error concerning process capabilities"); + exit(1); + } + ptr = malloc(10 + strlen(argv[i]+6) + strlen(text)); + if (ptr == NULL) { + perror("Out of memory for inh set"); + exit(1); + } + if (argv[i][6] && strcmp("none", argv[i]+6)) { + sprintf(ptr, "%s %s+i", text, argv[i]+6); + } else { + strcpy(ptr, text); + } + + all = cap_from_text(ptr); + if (all == NULL) { + perror("Fatal error internalizing capabilities"); + exit(1); + } + cap_free(text); + free(ptr); + + if (raised_for_setpcap != NULL) { + /* + * This is only for the case that pP does not contain + * the requested change to pI.. Failing here is not + * indicative of the cap_set_proc(all) failing (always). + */ + (void) cap_set_proc(raised_for_setpcap); + cap_free(raised_for_setpcap); + raised_for_setpcap = NULL; + } + + if (cap_set_proc(all) != 0) { + perror("Unable to set inheritable capabilities"); + exit(1); + } + /* + * Since status is based on orig, we don't want to restore + * the previous value of 'all' again here! + */ + + cap_free(all); + } else if (!memcmp("--caps=", argv[i], 7)) { + cap_t all, raised_for_setpcap; + + raised_for_setpcap = cap_get_proc(); + if (raised_for_setpcap == NULL) { + perror("Capabilities not available"); + exit(1); + } + + if ((raised_for_setpcap != NULL) + && (cap_set_flag(raised_for_setpcap, CAP_EFFECTIVE, 1, + raise_setpcap, CAP_SET) != 0)) { + cap_free(raised_for_setpcap); + raised_for_setpcap = NULL; + } + + all = cap_from_text(argv[i]+7); + if (all == NULL) { + fprintf(stderr, "unable to interpret [%s]\n", argv[i]); + exit(1); + } + + if (raised_for_setpcap != NULL) { + /* + * This is only for the case that pP does not contain + * the requested change to pI.. Failing here is not + * indicative of the cap_set_proc(all) failing (always). + */ + (void) cap_set_proc(raised_for_setpcap); + cap_free(raised_for_setpcap); + raised_for_setpcap = NULL; + } + + if (cap_set_proc(all) != 0) { + fprintf(stderr, "Unable to set capabilities [%s]\n", argv[i]); + exit(1); + } + /* + * Since status is based on orig, we don't want to restore + * the previous value of 'all' again here! + */ + + cap_free(all); + } else if (!memcmp("--keep=", argv[i], 7)) { + unsigned value; + int set; + + value = strtoul(argv[i]+7, NULL, 0); + set = prctl(PR_SET_KEEPCAPS, value); + if (set < 0) { + fprintf(stderr, "prctl(PR_SET_KEEPCAPS, %u) failed: %s\n", + value, strerror(errno)); + exit(1); + } + } else if (!memcmp("--chroot=", argv[i], 9)) { + int status; + cap_t orig, raised_for_chroot; + + orig = cap_get_proc(); + if (orig == NULL) { + perror("Capabilities not available"); + exit(1); + } + + raised_for_chroot = cap_dup(orig); + if (raised_for_chroot == NULL) { + perror("Unable to duplicate capabilities"); + exit(1); + } + + if (cap_set_flag(raised_for_chroot, CAP_EFFECTIVE, 1, raise_chroot, + CAP_SET) != 0) { + perror("unable to select CAP_SET_SYS_CHROOT"); + exit(1); + } + + if (cap_set_proc(raised_for_chroot) != 0) { + perror("unable to raise CAP_SYS_CHROOT"); + exit(1); + } + cap_free(raised_for_chroot); + + status = chroot(argv[i]+9); + if (cap_set_proc(orig) != 0) { + perror("unable to lower CAP_SYS_CHROOT"); + exit(1); + } + /* + * Given we are now in a new directory tree, its good practice + * to start off in a sane location + */ + status = chdir("/"); + + cap_free(orig); + + if (status != 0) { + fprintf(stderr, "Unable to chroot/chdir to [%s]", argv[i]+9); + exit(1); + } + } else if (!memcmp("--secbits=", argv[i], 10)) { + unsigned value; + int status; + + value = strtoul(argv[i]+10, NULL, 0); + status = prctl(PR_SET_SECUREBITS, value); + if (status < 0) { + fprintf(stderr, "failed to set securebits to 0%o/0x%x\n", + value, value); + exit(1); + } + } else if (!memcmp("--forkfor=", argv[i], 10)) { + unsigned value; + + value = strtoul(argv[i]+10, NULL, 0); + if (value == 0) { + goto usage; + } + child = fork(); + if (child < 0) { + perror("unable to fork()"); + } else if (!child) { + sleep(value); + exit(0); + } + } else if (!memcmp("--killit=", argv[i], 9)) { + int retval, status; + pid_t result; + unsigned value; + + value = strtoul(argv[i]+9, NULL, 0); + if (!child) { + fprintf(stderr, "no forked process to kill\n"); + exit(1); + } + retval = kill(child, value); + if (retval != 0) { + perror("Unable to kill child process"); + exit(1); + } + result = waitpid(child, &status, 0); + if (result != child) { + fprintf(stderr, "waitpid didn't match child: %u != %u\n", + child, result); + exit(1); + } + if (WTERMSIG(status) != value) { + fprintf(stderr, "child terminated with odd signal (%d != %d)\n" + , value, WTERMSIG(status)); + exit(1); + } + } else if (!memcmp("--uid=", argv[i], 6)) { + unsigned value; + int status; + + value = strtoul(argv[i]+6, NULL, 0); + status = setuid(value); + if (status < 0) { + fprintf(stderr, "Failed to set uid=%u: %s\n", + value, strerror(errno)); + exit(1); + } + } else if (!memcmp("--gid=", argv[i], 6)) { + unsigned value; + int status; + + value = strtoul(argv[i]+6, NULL, 0); + status = setgid(value); + if (status < 0) { + fprintf(stderr, "Failed to set gid=%u: %s\n", + value, strerror(errno)); + exit(1); + } + } else if (!memcmp("--groups=", argv[i], 9)) { + char *ptr, *buf; + long length, max_groups; + gid_t *group_list; + int g_count; + + length = sysconf(_SC_GETGR_R_SIZE_MAX); + buf = calloc(1, length); + if (NULL == buf) { + fprintf(stderr, "No memory for [%s] operation\n", argv[i]); + exit(1); + } + + max_groups = sysconf(_SC_NGROUPS_MAX); + group_list = calloc(max_groups, sizeof(gid_t)); + if (NULL == group_list) { + fprintf(stderr, "No memory for gid list\n"); + exit(1); + } + + g_count = 0; + for (ptr = argv[i] + 9; (ptr = strtok(ptr, ",")); + ptr = NULL, g_count++) { + if (max_groups <= g_count) { + fprintf(stderr, "Too many groups specified (%d)\n", g_count); + exit(1); + } + if (!isdigit(*ptr)) { + struct group *g, grp; + getgrnam_r(ptr, &grp, buf, length, &g); + if (NULL == g) { + fprintf(stderr, "Failed to identify gid for group [%s]\n", ptr); + exit(1); + } + group_list[g_count] = g->gr_gid; + } else { + group_list[g_count] = strtoul(ptr, NULL, 0); + } + } + free(buf); + if (setgroups(g_count, group_list) != 0) { + fprintf(stderr, "Failed to setgroups.\n"); + exit(1); + } + free(group_list); + } else if (!memcmp("--user=", argv[i], 7)) { + struct passwd *pwd; + const char *user; + gid_t groups[MAX_GROUPS]; + int status, ngroups; + + user = argv[i] + 7; + pwd = getpwnam(user); + if (pwd == NULL) { + fprintf(stderr, "User [%s] not known\n", user); + exit(1); + } + ngroups = MAX_GROUPS; + status = getgrouplist(user, pwd->pw_gid, groups, &ngroups); + if (status < 1) { + perror("Unable to get group list for user"); + exit(1); + } + status = setgroups(ngroups, groups); + if (status != 0) { + perror("Unable to set group list for user"); + exit(1); + } + status = setgid(pwd->pw_gid); + if (status < 0) { + fprintf(stderr, "Failed to set gid=%u(user=%s): %s\n", + pwd->pw_gid, user, strerror(errno)); + exit(1); + } + status = setuid(pwd->pw_uid); + if (status < 0) { + fprintf(stderr, "Failed to set uid=%u(user=%s): %s\n", + pwd->pw_uid, user, strerror(errno)); + exit(1); + } + } else if (!memcmp("--decode=", argv[i], 9)) { + unsigned long long value; + unsigned cap; + const char *sep = ""; + + /* Note, if capabilities become longer than 64-bits we'll need + to fixup the following code.. */ + value = strtoull(argv[i]+9, NULL, 16); + printf("0x%016llx=", value); + + for (cap=0; (cap < 64) && (value >> cap); ++cap) { + if (value & (1ULL << cap)) { + char *ptr; + + ptr = cap_to_name(cap); + if (ptr != NULL) { + printf("%s%s", sep, ptr); + cap_free(ptr); + } else { + printf("%s%u", sep, cap); + } + sep = ","; + } + } + printf("\n"); + } else if (!memcmp("--supports=", argv[i], 11)) { + cap_value_t cap; + + if (cap_from_name(argv[i] + 11, &cap) < 0) { + fprintf(stderr, "cap[%s] not recognized by library\n", + argv[i] + 11); + exit(1); + } + if (!CAP_IS_SUPPORTED(cap)) { + fprintf(stderr, "cap[%s=%d] not supported by kernel\n", + argv[i] + 11, cap); + exit(1); + } + } else if (!strcmp("--print", argv[i])) { + unsigned cap; + int set, status, j; + cap_t all; + char *text; + const char *sep; + struct group *g; + gid_t groups[MAX_GROUPS], gid; + uid_t uid; + struct passwd *u; + + all = cap_get_proc(); + text = cap_to_text(all, NULL); + printf("Current: %s\n", text); + cap_free(text); + cap_free(all); + + printf("Bounding set ="); + sep = ""; + for (cap=0; (set = cap_get_bound(cap)) >= 0; cap++) { + char *ptr; + if (!set) { + continue; + } + + ptr = cap_to_name(cap); + if (ptr == NULL) { + printf("%s%u", sep, cap); + } else { + printf("%s%s", sep, ptr); + cap_free(ptr); + } + sep = ","; + } + printf("\n"); + set = prctl(PR_GET_SECUREBITS); + if (set >= 0) { + const char *b; + b = binary(set); /* use verilog convention for binary string */ + printf("Securebits: 0%o/0x%x/%u'b%s\n", set, set, strlen(b), b); + printf(" secure-noroot: %s (%s)\n", + (set & 1) ? "yes":"no", + (set & 2) ? "locked":"unlocked"); + printf(" secure-no-suid-fixup: %s (%s)\n", + (set & 4) ? "yes":"no", + (set & 8) ? "locked":"unlocked"); + printf(" secure-keep-caps: %s (%s)\n", + (set & 16) ? "yes":"no", + (set & 32) ? "locked":"unlocked"); + } else { + printf("[Securebits ABI not supported]\n"); + set = prctl(PR_GET_KEEPCAPS); + if (set >= 0) { + printf(" prctl-keep-caps: %s (locking not supported)\n", + set ? "yes":"no"); + } else { + printf("[Keepcaps ABI not supported]\n"); + } + } + uid = getuid(); + u = getpwuid(uid); + printf("uid=%u(%s)\n", getuid(), u ? u->pw_name : "???"); + gid = getgid(); + g = getgrgid(gid); + printf("gid=%u(%s)\n", gid, g ? g->gr_name : "???"); + printf("groups="); + status = getgroups(MAX_GROUPS, groups); + sep = ""; + for (j=0; j < status; j++) { + g = getgrgid(groups[j]); + printf("%s%u(%s)", sep, groups[j], g ? g->gr_name : "???"); + sep = ","; + } + printf("\n"); + } else if ((!strcmp("--", argv[i])) || (!strcmp("==", argv[i]))) { + argv[i] = strdup(argv[i][0] == '-' ? "/bin/bash" : argv[0]); + argv[argc] = NULL; + execve(argv[i], argv+i, envp); + fprintf(stderr, "execve /bin/bash failed!\n"); + exit(1); + } else { + usage: + printf("usage: %s [args ...]\n" + " --help this message (or try 'man capsh')\n" + " --print display capability relevant state\n" + " --decode=xxx decode a hex string to a list of caps\n" + " --supports=xxx exit 1 if capability xxx unsupported\n" + " --drop=xxx remove xxx,.. capabilities from bset\n" + " --caps=xxx set caps as per cap_from_text()\n" + " --inh=xxx set xxx,.. inheritiable set\n" + " --secbits=<n> write a new value for securebits\n" + " --keep=<n> set keep-capabability bit to <n>\n" + " --uid=<n> set uid to <n> (hint: id <username>)\n" + " --gid=<n> set gid to <n> (hint: id <username>)\n" + " --groups=g,... set the supplemental groups\n" + " --user=<name> set uid,gid and groups to that of user\n" + " --chroot=path chroot(2) to this path\n" + " --killit=<n> send signal(n) to child\n" + " --forkfor=<n> fork and make child sleep for <n> sec\n" + " == re-exec(capsh) with args as for --\n" + " -- remaing arguments are for /bin/bash\n" + " (without -- [%s] will simply exit(0))\n", + argv[0], argv[0]); + + exit(strcmp("--help", argv[i]) != 0); + } + } + + exit(0); +} diff --git a/progs/getcap.c b/progs/getcap.c new file mode 100644 index 0000000..f6debc0 --- /dev/null +++ b/progs/getcap.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 1997,2007 Andrew G. Morgan <morgan@kernel.org> + * + * This displays the capabilities of a given file. + */ + +#define _XOPEN_SOURCE 500 + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <dirent.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/capability.h> + +#include <ftw.h> + +static int verbose = 0; +static int recursive = 0; + +static void usage(void) +{ + fprintf(stderr, + "usage: getcap [-v] [-r] [-h] <filename> [<filename> ...]\n" + "\n" + "\tdisplays the capabilities on the queried file(s).\n" + ); + exit(1); +} + +static int do_getcap(const char *fname, const struct stat *stbuf, + int tflag, struct FTW* ftwbuf) +{ + cap_t cap_d; + char *result; + + if (tflag != FTW_F) { + if (verbose) { + printf("%s (Not a regular file)\n", fname); + } + return 0; + } + + cap_d = cap_get_file(fname); + if (cap_d == NULL) { + if (errno != ENODATA) { + fprintf(stderr, "Failed to get capabilities of file `%s' (%s)\n", + fname, strerror(errno)); + } else if (verbose) { + printf("%s\n", fname); + } + return 0; + } + + result = cap_to_text(cap_d, NULL); + if (!result) { + fprintf(stderr, + "Failed to get capabilities of human readable format at `%s' (%s)\n", + fname, strerror(errno)); + cap_free(cap_d); + return 0; + } + printf("%s %s\n", fname, result); + cap_free(cap_d); + cap_free(result); + + return 0; +} + +int main(int argc, char **argv) +{ + int i, c; + + while ((c = getopt(argc, argv, "rvh")) > 0) { + switch(c) { + case 'r': + recursive = 1; + break; + case 'v': + verbose = 1; + break; + default: + usage(); + } + } + + if (!argv[optind]) + usage(); + + for (i=optind; argv[i] != NULL; i++) { + struct stat stbuf; + + if (lstat(argv[i], &stbuf) != 0) { + fprintf(stderr, "%s (%s)\n", argv[i], strerror(errno)); + } else if (recursive) { + nftw(argv[i], do_getcap, 20, FTW_PHYS); + } else { + int tflag = S_ISREG(stbuf.st_mode) ? FTW_F : + (S_ISLNK(stbuf.st_mode) ? FTW_SL : FTW_NS); + do_getcap(argv[i], &stbuf, tflag, 0); + } + } + + return 0; +} diff --git a/progs/getpcaps.c b/progs/getpcaps.c new file mode 100644 index 0000000..e405a92 --- /dev/null +++ b/progs/getpcaps.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 1997,2008 Andrew G. Morgan <morgan@kernel.org> + * + * This displays the capabilities of given target process(es). + */ + +#include <sys/types.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/capability.h> + +static void usage(void) +{ + fprintf(stderr, +"usage: getcaps <pid> [<pid> ...]\n\n" +" This program displays the capabilities on the queried process(es).\n" +" The capabilities are displayed in the cap_from_text(3) format.\n\n" +"[Copyright (c) 1997-8,2007 Andrew G. Morgan <morgan@kernel.org>]\n" + ); + exit(1); +} + +int main(int argc, char **argv) +{ + int retval = 0; + + if (argc < 2) { + usage(); + } + + for ( ++argv; --argc > 0; ++argv ) { + ssize_t length; + int pid; + cap_t cap_d; + + pid = atoi(argv[0]); + + cap_d = cap_get_pid(pid); + if (cap_d == NULL) { + fprintf(stderr, "Failed to get cap's for proccess %d:" + " (%s)\n", pid, strerror(errno)); + retval = 1; + continue; + } else { + char *result = cap_to_text(cap_d, &length); + fprintf(stderr, "Capabilities for `%s': %s\n", *argv, result); + cap_free(result); + result = NULL; + cap_free(cap_d); + } + } + + return retval; +} diff --git a/progs/old/README b/progs/old/README new file mode 100644 index 0000000..75741d3 --- /dev/null +++ b/progs/old/README @@ -0,0 +1 @@ +these files are not relevant to this release diff --git a/progs/old/execcap.c b/progs/old/execcap.c new file mode 100644 index 0000000..330cc93 --- /dev/null +++ b/progs/old/execcap.c @@ -0,0 +1,68 @@ +/* + * This was written by Andrew G. Morgan <morgan@kernel.org> + * + * This is a program that is intended to exec a subsequent program. + * The purpose of this 'execcap' wrapper is to limit the inheritable + * capabilities of the exec()'d program. All environment variables + * are inherited. + */ + +#include <sys/types.h> +#include <errno.h> +#include <stdio.h> +#include <sys/capability.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> + +static void usage(void) +{ + fprintf(stderr, +"usage: execcap <caps> <command-path> [command-args...]\n\n" +" This program is a wrapper that can be used to limit the Inheritable\n" +" capabilities of a program to be executed. Note, this wrapper is\n" +" intended to assist in overcoming a lack of support for filesystem\n" +" capability attributes and should be used to launch other files.\n" +" This program should _NOT_ be made setuid-0.\n\n" +"[Copyright (c) 1998 Andrew G. Morgan <morgan@kernel.org>]\n"); + + exit(1); +} + +int main(int argc, char **argv) +{ + cap_t new_caps; + + /* this program should not be made setuid-0 */ + if (getuid() && !geteuid()) { + usage(); + } + + /* check that we have at least 2 arguments */ + if (argc < 3) { + usage(); + } + + /* parse the first argument to obtain a set of capabilities */ + new_caps = cap_from_text(argv[1]); + if (new_caps == NULL) { + fprintf(stderr, "requested capabilities were not recognized\n"); + usage(); + } + + /* set these capabilities for the current process */ + if (cap_set_proc(new_caps) != 0) { + fprintf(stderr, "unable to set capabilities: %s\n", strerror(errno)); + usage(); + } + + /* exec the program indicated by args 2 ... */ + execvp(argv[2], argv+2); + + /* if we fall through to here, our exec failed -- announce the fact */ + fprintf(stderr, "Unable to execute command: %s\n", strerror(errno)); + + usage(); + + return 0; +} diff --git a/progs/old/setpcaps.c b/progs/old/setpcaps.c new file mode 100644 index 0000000..3720fce --- /dev/null +++ b/progs/old/setpcaps.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 1997-8 Andrew G. Morgan <morgan@kernel.org> + * + * This sets the capabilities of a given process. + */ + +#include <sys/types.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#undef _POSIX_SOURCE +#include <sys/capability.h> +#include <unistd.h> + +static void usage(void) +{ + fprintf(stderr, +"usage: setcap [-q] (-|<caps>) <pid> [ ... (-|<capsN>) <pid> ]\n\n" +" This program can be used to set the process capabilities of running\n" +" processes. In order to work, it needs to be executing with CAP_SETPCAP\n" +" raised, and the only capabilities that this program can bestow on others\n" +" are a subset of its effective set. This program is mostly intended as an\n" +" example -- a safe use of CAP_SETPCAP has yet to be demonstrated!\n\n" +"[Copyright (c) 1997-8 Andrew G. Morgan <morgan@kernel.org>]\n" + ); + exit(1); +} + +#define MAXCAP 2048 + +static int read_caps(int quiet, const char *filename, char *buffer) +{ + int i=MAXCAP; + + if (!quiet) { + fprintf(stderr, "Please enter caps for file [empty line to end]:\n"); + } + while (i > 0) { + int j = read(STDIN_FILENO, buffer, i); + + if (j < 0) { + fprintf(stderr, "\n[Error - aborting]\n"); + exit(1); + } + + if (j==0 || buffer[0] == '\n') { + /* we're done */ + break; + } + + /* move on... */ + + i -= j; + buffer += j; + } + + /* <NUL> terminate */ + buffer[0] = '\0'; + + return (i < MAXCAP ? 0:-1); +} + +int main(int argc, char **argv) +{ + char buffer[MAXCAP+1]; + int retval, quiet=0; + cap_t cap_d; + + if (argc < 3) { + usage(); + } + + while (--argc > 0) { + const char *text; + pid_t pid; + + if (!strcmp(*++argv,"-q")) { + quiet = 1; + continue; + } + if (!strcmp(*argv,"-")) { + retval = read_caps(quiet, *argv, buffer); + if (retval) + usage(); + text = buffer; + } else + text = *argv; + + cap_d = cap_from_text(text); + if (cap_d == NULL) { + perror("fatal error"); + usage(); + } +#ifndef DEBUG + { + ssize_t length; + char *result; + + result = cap_to_text(cap_d, &length); + fprintf(stderr, "[caps set to:\n%s\n]\n", result); + cap_free(result); + result = NULL; + } +#endif + + if (--argc <= 0) + usage(); + + pid = atoi(*++argv); + retval = capsetp(pid, cap_d); + + if (retval != 0) { + fprintf(stderr, "Failed to set cap's on process `%d': (%s)\n", + pid, strerror(errno)); + usage(); + } +#ifndef DEBUG + fprintf(stderr, "[caps set on %d]\n", pid); +#endif + } + + return 0; +} diff --git a/progs/old/sucap.c b/progs/old/sucap.c new file mode 100644 index 0000000..366a093 --- /dev/null +++ b/progs/old/sucap.c @@ -0,0 +1,199 @@ +/* + * $Id: sucap.c,v 1.1.1.1 1999/04/17 22:16:31 morgan Exp $ + * + * This was written by Finn Arne Gangstad <finnag@guardian.no> + * + * This is a program that is intended to exec a subsequent program. + * The purpose of this 'sucap' wrapper is to change uid but keep all + * privileges. All environment variables are inherited. + */ + +#include <sys/types.h> +#include <errno.h> +#include <stdio.h> +#undef _POSIX_SOURCE +#include <sys/capability.h> +#include <pwd.h> +#define __USE_BSD +#include <grp.h> +#include <unistd.h> +#include <sys/wait.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> + +static void usage(void) +{ + fprintf(stderr, +"usage: sucap <user> <group> <command-path> [command-args...]\n\n" +" This program is a wrapper that change UID but not privileges of a\n" +" program to be executed.\n" +" Note, this wrapper is intended to assist in overcoming a lack of support\n" +" for filesystem capability attributes and should be used to launch other\n" +" files. This program should _NOT_ be made setuid-0.\n\n" +"[Copyright (c) 1998 Finn Arne Gangstad <finnag@guardian.no>]\n"); + + exit(1); +} + + +static void +wait_on_fd(int fd) +{ + /* Wait until some data is available on a file descriptor, or until + * end of file or an error is detected */ + char buf[1]; + while (read(fd, buf, sizeof(buf)) == -1 && errno == EINTR) { + /* empty loop */ + } +} + + +int main(int argc, char **argv) +{ + cap_t old_caps; + uid_t uid; + pid_t pid, parent_pid; + gid_t gid; + int pipe_fds[2]; + + /* this program should not be made setuid-0 */ + if (getuid() && !geteuid()) { + usage(); + } + + /* check that we have at least 3 arguments */ + if (argc < 4) { + usage(); + } + + /* Convert username to uid */ + { + struct passwd *pw = getpwnam(argv[1]); + if (!pw) { + fprintf(stderr, "sucap: No such user: %s\n", argv[1]); + exit(1); + } + uid = pw->pw_uid; + } + + /* Convert groupname to gid */ + { + struct group *gr = getgrnam(argv[2]); + if (!gr) { + fprintf(stderr, "sucap: No such group: %s\n", argv[2]); + exit(1); + } + gid = gr->gr_gid; + } + + /* set process group to current pid */ + if (setpgid(0, getpid())) { + perror("sucap: Failed to set process group"); + exit(1); + } + + if (pipe(pipe_fds)) { + perror("sucap: pipe() failed"); + exit(1); + } + + parent_pid = getpid(); + + old_caps = cap_init(); + if (capgetp(0, old_caps)) { + perror("sucap: capgetp"); + exit(1); + } + + { + ssize_t x; + printf("Caps: %s\n", cap_to_text(old_caps, &x)); + } + + + /* fork off a child to do the hard work */ + fflush(NULL); + pid = fork(); + if (pid == -1) { + perror("sucap: fork failed"); + exit(1); + } + + /* 1. mother process sets gid and uid + * 2. child process sets capabilities of mother process + * 3. mother process execs whatever is to be executed + */ + + if (pid) { + /* Mother process. */ + close(pipe_fds[0]); + + /* Get rid of any supplemental groups */ + if (!getuid() && setgroups(0, 0)) { + perror("sucap: setgroups failed"); + exit(1); + } + + /* Set gid and uid (this probably clears capabilities) */ + setregid(gid, gid); + setreuid(uid, uid); + + { + ssize_t x; + cap_t cap = cap_init(); + capgetp(0, cap); + printf("Caps: %s\n", cap_to_text(cap, &x)); + } + + printf("[debug] uid:%d, real uid:%d\n", geteuid(), getuid()); + + /* Signal child that we want our privileges updated */ + close(pipe_fds[1]); /* Child hangs in blocking read */ + + /* Wait for child process to set our privileges */ + { + int status = 0; + if (wait(&status) == -1) { + perror("sucap: wait failed"); + } + if (!WIFEXITED(status) || WEXITSTATUS(status)) { + fprintf(stderr, "sucap: child did not exit cleanly.\n"); + exit(1); + } + } + + { + ssize_t x; + cap_t cap = cap_init(); + capgetp(0, cap); + printf("Caps: %s\n", cap_to_text(cap, &x)); + } + +/* printf("[debug] uid:%d, real uid:%d\n", geteuid(), getuid()); */ + /* exec the program indicated by args 2 ... */ + execvp(argv[3], argv+3); + + /* if we fall through to here, our exec failed -- announce the fact */ + fprintf(stderr, "Unable to execute command: %s\n", strerror(errno)); + + usage(); + } else { + /* Child process */ + close(pipe_fds[1]); + + /* Wait for mother process to setuid */ + wait_on_fd(pipe_fds[0]); + + /* Set privileges on mother process */ + if (capsetp(parent_pid, old_caps)) { + perror("sucaps: capsetp"); + _exit(1); + } + + /* exit to signal mother process that we are ready */ + _exit(0); + } + + return 0; +} diff --git a/progs/quicktest.sh b/progs/quicktest.sh new file mode 100755 index 0000000..be3fa7d --- /dev/null +++ b/progs/quicktest.sh @@ -0,0 +1,140 @@ +#!/bin/bash +# +# Run through a series of tests to try out the various capability +# manipulations posible through exec. +# +# [Run this as root in a root-enabled process tree.] + +try_capsh () { + echo "TEST: ./capsh $*" + ./capsh "$@" + if [ $? -ne 0 ]; then + echo FAILED + return 1 + else + echo PASSED + return 0 + fi +} + +fail_capsh () { + echo -n "EXPECT FAILURE: " + try_capsh "$@" + if [ $? -eq 1 ]; then + echo "[WHICH MEANS A PASS!]" + return 0 + else + echo "Undesired result - aborting" + echo "PROBLEM TEST: $*" + exit 1 + fi +} + +pass_capsh () { + echo -n "EXPECT SUCCESS: " + try_capsh "$@" + if [ $? -eq 0 ]; then + return 0 + else + echo "Undesired result - aborting" + echo "PROBLEM TEST: $*" + exit 1 + fi +} + +pass_capsh --print + +# Make a local non-setuid-0 version of ping +cp /bin/ping . && chmod -s ./ping + +# Give it the forced capability it needs +./setcap all=ep ./ping +if [ $? -ne 0 ]; then + echo "Failed to set all capabilities on file" + exit 1 +fi +./setcap cap_net_raw=ep ./ping +if [ $? -ne 0 ]; then + echo "Failed to set single capability on ping file" + exit 1 +fi + +# Explore keep_caps support +pass_capsh --keep=0 --keep=1 --keep=0 --keep=1 --print + +rm -f tcapsh +cp capsh tcapsh +chown root.root tcapsh +chmod u+s tcapsh +ls -l tcapsh + +# leverage keep caps maintain capabilities accross a change of uid +# from setuid root to capable luser (as per wireshark/dumpcap 0.99.7) +pass_capsh --uid=500 -- -c "./tcapsh --keep=1 --caps=\"cap_net_raw,cap_net_admin=ip\" --uid=500 --caps=\"cap_net_raw,cap_net_admin=pie\" --print" + +# This fails, on 2.6.24, but shouldn't +pass_capsh --uid=500 -- -c "./tcapsh --keep=1 --caps=\"cap_net_raw,cap_net_admin=ip\" --uid=500 --forkfor=10 --caps= --print --killit=9 --print" + +rm -f tcapsh + +# only continue with these if --secbits is supported +./capsh --secbits=0x2f > /dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "unable to test securebits manipulation - assume not supported (PASS)" + rm -f ./ping + exit 0 +fi + +pass_capsh --secbits=42 --print +fail_capsh --secbits=32 --keep=1 --keep=0 --print +pass_capsh --secbits=10 --keep=0 --keep=1 --print +fail_capsh --secbits=47 -- -c "ping -c1 localhost" + +# Suppress uid=0 privilege +fail_capsh --secbits=47 --print -- -c "/bin/ping -c1 localhost" + +# suppress uid=0 privilege and test this ping +pass_capsh --secbits=0x2f --print -- -c "./ping -c1 localhost" + +# observe that the bounding set can be used to suppress this forced capability +fail_capsh --drop=cap_net_raw,cap_chown --secbits=0x2f --print -- -c "./ping -c1 localhost" + +# change the way the capability is obtained (make it inheritable) +./setcap cap_net_raw=ei ./ping + +pass_capsh --secbits=47 --inh=cap_net_raw --drop=cap_net_raw \ + --uid=500 --print -- -c "./ping -c1 localhost" + +rm -f ./ping + +# test that we do not support capabilities on setuid shell-scripts +cat > hack.sh <<EOF +#!/bin/bash +mypid=\$\$ +caps=\$(./getpcaps \$mypid 2>&1 | cut -d: -f2) +if [ "\$caps" != " =" ]; then + echo "Shell script got [\$caps] - you should upgrade your kernel" + exit 1 +else + ls -l \$0 + echo "Good, no capabilities [\$caps] for this setuid-0 shell script" +fi +exit 0 +EOF +chmod +xs hack.sh +./capsh --uid=500 --inh=none --print -- ./hack.sh +status=$? +rm -f ./hack.sh +if [ $status -ne 0 ]; then + echo "shell scripts can have capabilities (bug)" + exit 1 +fi + +# Max lockdown +pass_capsh --keep=1 --user=nobody --caps=cap_setpcap=ep \ + --drop=all --secbits=0x2f --caps= --print + +# Verify we can chroot +pass_capsh --chroot=$(/bin/pwd) +pass_capsh --chroot=$(/bin/pwd) == +fail_capsh --chroot=$(/bin/pwd) -- -c "echo oops" diff --git a/progs/setcap.c b/progs/setcap.c new file mode 100644 index 0000000..0215fc4 --- /dev/null +++ b/progs/setcap.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 1997,2007-8 Andrew G. Morgan <morgan@kernel.org> + * + * This sets/verifies the capabilities of a given file. + */ + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/capability.h> +#include <unistd.h> + +static void usage(void) +{ + fprintf(stderr, + "usage: setcap [-q] [-v] (-r|-|<caps>) <filename> " + "[ ... (-r|-|<capsN>) <filenameN> ]\n" + "\n" + " Note <filename> must be a regular (non-symlink) file.\n" + ); + exit(1); +} + +#define MAXCAP 2048 + +static int read_caps(int quiet, const char *filename, char *buffer) +{ + int i=MAXCAP; + + if (!quiet) { + fprintf(stderr, "Please enter caps for file [empty line to end]:\n"); + } + while (i > 0) { + int j = read(STDIN_FILENO, buffer, i); + + if (j < 0) { + fprintf(stderr, "\n[Error - aborting]\n"); + exit(1); + } + + if (j==0 || buffer[0] == '\n') { + /* we're done */ + break; + } + + /* move on... */ + + i -= j; + buffer += j; + } + + /* <NUL> terminate */ + buffer[0] = '\0'; + + return (i < MAXCAP ? 0:-1); +} + +int main(int argc, char **argv) +{ + int tried_to_cap_setfcap = 0; + char buffer[MAXCAP+1]; + int retval, quiet=0, verify=0; + cap_t mycaps; + cap_value_t capflag; + + if (argc < 3) { + usage(); + } + + mycaps = cap_get_proc(); + if (mycaps == NULL) { + fprintf(stderr, "warning - unable to get process capabilities" + " (old libcap?)\n"); + } + + while (--argc > 0) { + const char *text; + cap_t cap_d; + + if (!strcmp(*++argv, "-q")) { + quiet = 1; + continue; + } + if (!strcmp(*argv, "-v")) { + verify = 1; + continue; + } + + if (!strcmp(*argv, "-r")) { + cap_d = NULL; + } else { + if (!strcmp(*argv,"-")) { + retval = read_caps(quiet, *argv, buffer); + if (retval) + usage(); + text = buffer; + } else { + text = *argv; + } + + cap_d = cap_from_text(text); + if (cap_d == NULL) { + perror("fatal error"); + usage(); + } +#ifdef DEBUG + { + ssize_t length; + const char *result; + + result = cap_to_text(cap_d, &length); + fprintf(stderr, "caps set to: [%s]\n", result); + } +#endif + } + + if (--argc <= 0) + usage(); + /* + * Set the filesystem capability for this file. + */ + if (verify) { + cap_t cap_on_file; + int cmp; + + if (cap_d == NULL) { + cap_d = cap_from_text("="); + } + + cap_on_file = cap_get_file(*++argv); + + if (cap_on_file == NULL) { + cap_on_file = cap_from_text("="); + } + + cmp = cap_compare(cap_on_file, cap_d); + cap_free(cap_on_file); + + if (cmp != 0) { + if (!quiet) { + printf("%s differs in [%s%s%s]\n", *argv, + CAP_DIFFERS(cmp, CAP_PERMITTED) ? "p" : "", + CAP_DIFFERS(cmp, CAP_INHERITABLE) ? "i" : "", + CAP_DIFFERS(cmp, CAP_EFFECTIVE) ? "e" : ""); + } + exit(1); + } + if (!quiet) { + printf("%s: OK\n", *argv); + } + } else { + if (!tried_to_cap_setfcap) { + capflag = CAP_SETFCAP; + + /* + * Raise the effective CAP_SETFCAP. + */ + if (cap_set_flag(mycaps, CAP_EFFECTIVE, 1, &capflag, CAP_SET) + != 0) { + perror("unable to manipulate CAP_SETFCAP - " + "try a newer libcap?"); + exit(1); + } + if (cap_set_proc(mycaps) != 0) { + perror("unable to set CAP_SETFCAP effective capability"); + exit(1); + } + tried_to_cap_setfcap = 1; + } + retval = cap_set_file(*++argv, cap_d); + if (retval != 0) { + fprintf(stderr, + "Failed to set capabilities on file `%s' (%s)\n", + argv[0], strerror(errno)); + usage(); + } + } + if (cap_d) { + cap_free(cap_d); + } + } + + exit(0); +} |