1 /*
2  * Copyright (c) 1997,2007-8 Andrew G. Morgan  <morgan@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 
usage(void)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 
read_caps(int quiet,const char * filename,char * buffer)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 
main(int argc,char ** argv)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 #ifdef linux
175 		cap_value_t cap;
176 		cap_flag_value_t per_state;
177 
178 		for (cap = 0;
179 		     cap_get_flag(cap_d, cap, CAP_PERMITTED, &per_state) != -1;
180 		     cap++) {
181 		    cap_flag_value_t inh_state, eff_state;
182 
183 		    cap_get_flag(cap_d, cap, CAP_INHERITABLE, &inh_state);
184 		    cap_get_flag(cap_d, cap, CAP_EFFECTIVE, &eff_state);
185 		    if ((inh_state | per_state) != eff_state) {
186 			fprintf(stderr, "NOTE: Under Linux, effective file capabilities must either be empty, or\n"
187 				"      exactly match the union of selected permitted and inheritable bits.\n");
188 			explained = 1;
189 			break;
190 		    }
191 		}
192 #endif /* def linux */
193 
194 		fprintf(stderr,
195 			"Failed to set capabilities on file `%s' (%s)\n",
196 			argv[0], strerror(errno));
197 		if (!explained) {
198 		    usage();
199 		}
200 	    }
201 	}
202 	if (cap_d) {
203 	    cap_free(cap_d);
204 	}
205     }
206 
207     exit(0);
208 }
209