diff options
Diffstat (limited to 'progs/setcap.c')
-rw-r--r-- | progs/setcap.c | 185 |
1 files changed, 185 insertions, 0 deletions
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); +} |