You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
256 lines
5.5 KiB
256 lines
5.5 KiB
/*
|
|
* Copyright (c) 1997,2007-8,2020 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(int status)
|
|
{
|
|
fprintf(stderr,
|
|
"usage: setcap [-h] [-q] [-v] [-n <rootid>] (-r|-|<caps>) <filename> "
|
|
"[ ... (-r|-|<capsN>) <filenameN> ]\n"
|
|
"\n"
|
|
" Note <filename> must be a regular (non-symlink) file.\n"
|
|
" -r remove capability from file\n"
|
|
" - read capability text from stdin\n"
|
|
" <capsN> cap_from_text(3) formatted file capability\n"
|
|
"\n"
|
|
" -h this message and exit status 0\n"
|
|
" -q quietly\n"
|
|
" -v validate supplied capability matches file\n"
|
|
" -n <rootid> write a user namespace limited capability\n"
|
|
" --license display the license info\n"
|
|
);
|
|
exit(status);
|
|
}
|
|
|
|
#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;
|
|
uid_t rootid = 0, f_rootid;
|
|
|
|
if (argc < 2) {
|
|
usage(1);
|
|
}
|
|
|
|
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("--license", *argv)) {
|
|
printf(
|
|
"%s has a you choose license: BSD 3-clause or GPL2\n"
|
|
"Copyright (c) 1997,2007-8,2020 Andrew G. Morgan"
|
|
" <morgan@kernel.org>\n", argv[0]);
|
|
exit(0);
|
|
}
|
|
if (!strcmp(*argv, "-h")) {
|
|
usage(0);
|
|
}
|
|
if (!strcmp(*argv, "-v")) {
|
|
verify = 1;
|
|
continue;
|
|
}
|
|
if (!strcmp(*argv, "-n")) {
|
|
if (argc < 2) {
|
|
fprintf(stderr, "usage: .. -n <rootid> .. - rootid!=0 file caps");
|
|
exit(1);
|
|
}
|
|
--argc;
|
|
rootid = (uid_t) atoi(*++argv);
|
|
if (rootid+1 < 2) {
|
|
fprintf(stderr, "invalid rootid!=0 of '%s'", *argv);
|
|
exit(1);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (!strcmp(*argv, "-r")) {
|
|
cap_d = NULL;
|
|
} else {
|
|
if (!strcmp(*argv,"-")) {
|
|
retval = read_caps(quiet, *argv, buffer);
|
|
if (retval)
|
|
usage(1);
|
|
text = buffer;
|
|
} else {
|
|
text = *argv;
|
|
}
|
|
|
|
cap_d = cap_from_text(text);
|
|
if (cap_d == NULL) {
|
|
perror("fatal error");
|
|
usage(1);
|
|
}
|
|
if (cap_set_nsowner(cap_d, rootid)) {
|
|
perror("unable to set nsowner");
|
|
exit(1);
|
|
}
|
|
#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(1);
|
|
/*
|
|
* 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);
|
|
f_rootid = cap_get_nsowner(cap_on_file);
|
|
cap_free(cap_on_file);
|
|
|
|
if (cmp != 0 || rootid != f_rootid) {
|
|
if (!quiet) {
|
|
if (rootid != f_rootid) {
|
|
printf("nsowner[got=%d, want=%d],", f_rootid, rootid);
|
|
}
|
|
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) {
|
|
int explained = 0;
|
|
int oerrno = errno;
|
|
int somebits = 0;
|
|
#ifdef linux
|
|
cap_value_t cap;
|
|
cap_flag_value_t per_state;
|
|
|
|
for (cap = 0;
|
|
cap_get_flag(cap_d, cap, CAP_PERMITTED, &per_state) != -1;
|
|
cap++) {
|
|
cap_flag_value_t inh_state, eff_state, combined;
|
|
|
|
cap_get_flag(cap_d, cap, CAP_INHERITABLE, &inh_state);
|
|
cap_get_flag(cap_d, cap, CAP_EFFECTIVE, &eff_state);
|
|
combined = (inh_state | per_state);
|
|
somebits |= !!eff_state;
|
|
if (combined != eff_state) {
|
|
explained = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (somebits && explained) {
|
|
fprintf(stderr, "NOTE: Under Linux, effective file capabilities must either be empty, or\n"
|
|
" exactly match the union of selected permitted and inheritable bits.\n");
|
|
}
|
|
#endif /* def linux */
|
|
|
|
fprintf(stderr,
|
|
"Failed to set capabilities on file `%s' (%s)\n",
|
|
argv[0], strerror(oerrno));
|
|
if (!explained) {
|
|
usage(1);
|
|
}
|
|
}
|
|
}
|
|
if (cap_d) {
|
|
cap_free(cap_d);
|
|
}
|
|
}
|
|
|
|
exit(0);
|
|
}
|