Home | History | Annotate | Download | only in progs
      1 /*
      2  * Copyright (c) 1997,2007-8 Andrew G. Morgan  <morgan (at) kernel.org>
      3  *
      4  * This sets/verifies the capabilities of a given file.
      5  */
      6 
      7 #include <errno.h>
      8 #include <stdio.h>
      9 #include <string.h>
     10 #include <stdlib.h>
     11 #include <sys/capability.h>
     12 #include <unistd.h>
     13 
     14 static void usage(void)
     15 {
     16     fprintf(stderr,
     17 	    "usage: setcap [-q] [-v] (-r|-|<caps>) <filename> "
     18 	    "[ ... (-r|-|<capsN>) <filenameN> ]\n"
     19 	    "\n"
     20 	    " Note <filename> must be a regular (non-symlink) file.\n"
     21 	);
     22     exit(1);
     23 }
     24 
     25 #define MAXCAP  2048
     26 
     27 static int read_caps(int quiet, const char *filename, char *buffer)
     28 {
     29     int i = MAXCAP;
     30 
     31     if (!quiet) {
     32 	fprintf(stderr,	"Please enter caps for file [empty line to end]:\n");
     33     }
     34     while (i > 0) {
     35 	int j = read(STDIN_FILENO, buffer, i);
     36 
     37 	if (j < 0) {
     38 	    fprintf(stderr, "\n[Error - aborting]\n");
     39 	    exit(1);
     40 	}
     41 
     42 	if (j==0 || buffer[0] == '\n') {
     43 	    /* we're done */
     44 	    break;
     45 	}
     46 
     47 	/* move on... */
     48 
     49 	i -= j;
     50 	buffer += j;
     51     }
     52 
     53     /* <NUL> terminate */
     54     buffer[0] = '\0';
     55 
     56     return (i < MAXCAP ? 0:-1);
     57 }
     58 
     59 int main(int argc, char **argv)
     60 {
     61     int tried_to_cap_setfcap = 0;
     62     char buffer[MAXCAP+1];
     63     int retval, quiet=0, verify=0;
     64     cap_t mycaps;
     65     cap_value_t capflag;
     66 
     67     if (argc < 3) {
     68 	usage();
     69     }
     70 
     71     mycaps = cap_get_proc();
     72     if (mycaps == NULL) {
     73 	fprintf(stderr, "warning - unable to get process capabilities"
     74 		" (old libcap?)\n");
     75     }
     76 
     77     while (--argc > 0) {
     78 	const char *text;
     79 	cap_t cap_d;
     80 
     81 	if (!strcmp(*++argv, "-q")) {
     82 	    quiet = 1;
     83 	    continue;
     84 	}
     85 	if (!strcmp(*argv, "-v")) {
     86 	    verify = 1;
     87 	    continue;
     88 	}
     89 
     90 	if (!strcmp(*argv, "-r")) {
     91 	    cap_d = NULL;
     92 	} else {
     93 	    if (!strcmp(*argv,"-")) {
     94 		retval = read_caps(quiet, *argv, buffer);
     95 		if (retval)
     96 		    usage();
     97 		text = buffer;
     98 	    } else {
     99 		text = *argv;
    100 	    }
    101 
    102 	    cap_d = cap_from_text(text);
    103 	    if (cap_d == NULL) {
    104 		perror("fatal error");
    105 		usage();
    106 	    }
    107 #ifdef DEBUG
    108 	    {
    109 		ssize_t length;
    110 		const char *result;
    111 
    112 		result = cap_to_text(cap_d, &length);
    113 		fprintf(stderr, "caps set to: [%s]\n", result);
    114 	    }
    115 #endif
    116 	}
    117 
    118 	if (--argc <= 0)
    119 	    usage();
    120 	/*
    121 	 * Set the filesystem capability for this file.
    122 	 */
    123 	if (verify) {
    124 	    cap_t cap_on_file;
    125 	    int cmp;
    126 
    127 	    if (cap_d == NULL) {
    128 		cap_d = cap_from_text("=");
    129 	    }
    130 
    131 	    cap_on_file = cap_get_file(*++argv);
    132 
    133 	    if (cap_on_file == NULL) {
    134 		cap_on_file = cap_from_text("=");
    135 	    }
    136 
    137 	    cmp = cap_compare(cap_on_file, cap_d);
    138 	    cap_free(cap_on_file);
    139 
    140 	    if (cmp != 0) {
    141 		if (!quiet) {
    142 		    printf("%s differs in [%s%s%s]\n", *argv,
    143 			   CAP_DIFFERS(cmp, CAP_PERMITTED) ? "p" : "",
    144 			   CAP_DIFFERS(cmp, CAP_INHERITABLE) ? "i" : "",
    145 			   CAP_DIFFERS(cmp, CAP_EFFECTIVE) ? "e" : "");
    146 		}
    147 		exit(1);
    148 	    }
    149 	    if (!quiet) {
    150 		printf("%s: OK\n", *argv);
    151 	    }
    152 	} else {
    153 	    if (!tried_to_cap_setfcap) {
    154 		capflag = CAP_SETFCAP;
    155 
    156 		/*
    157 		 * Raise the effective CAP_SETFCAP.
    158 		 */
    159 		if (cap_set_flag(mycaps, CAP_EFFECTIVE, 1, &capflag, CAP_SET)
    160 		    != 0) {
    161 		    perror("unable to manipulate CAP_SETFCAP - "
    162 			   "try a newer libcap?");
    163 		    exit(1);
    164 		}
    165 		if (cap_set_proc(mycaps) != 0) {
    166 		    perror("unable to set CAP_SETFCAP effective capability");
    167 		    exit(1);
    168 		}
    169 		tried_to_cap_setfcap = 1;
    170 	    }
    171 	    retval = cap_set_file(*++argv, cap_d);
    172 	    if (retval != 0) {
    173 		int explained = 0;
    174 		int oerrno = errno;
    175 #ifdef linux
    176 		cap_value_t cap;
    177 		cap_flag_value_t per_state;
    178 
    179 		for (cap = 0;
    180 		     cap_get_flag(cap_d, cap, CAP_PERMITTED, &per_state) != -1;
    181 		     cap++) {
    182 		    cap_flag_value_t inh_state, eff_state;
    183 
    184 		    cap_get_flag(cap_d, cap, CAP_INHERITABLE, &inh_state);
    185 		    cap_get_flag(cap_d, cap, CAP_EFFECTIVE, &eff_state);
    186 		    if ((inh_state | per_state) != eff_state) {
    187 			fprintf(stderr, "NOTE: Under Linux, effective file capabilities must either be empty, or\n"
    188 				"      exactly match the union of selected permitted and inheritable bits.\n");
    189 			explained = 1;
    190 			break;
    191 		    }
    192 		}
    193 #endif /* def linux */
    194 
    195 		fprintf(stderr,
    196 			"Failed to set capabilities on file `%s' (%s)\n",
    197 			argv[0], strerror(oerrno));
    198 		if (!explained) {
    199 		    usage();
    200 		}
    201 	    }
    202 	}
    203 	if (cap_d) {
    204 	    cap_free(cap_d);
    205 	}
    206     }
    207 
    208     exit(0);
    209 }
    210