1 /*
2 * Copyright (c) 1997,2007-8,2020,21 Andrew G. Morgan <[email protected]>
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(int status)14 static void usage(int status)
15 {
16 fprintf(stderr,
17 "usage: setcap [-h] [-q] [-v] [-n <rootid>] (-r|-|<caps>) <filename> "
18 "[ ... (-r|-|<capsN>) <filenameN> ]\n"
19 "\n"
20 " Note <filename> must be a regular (non-symlink) file.\n"
21 " -r remove capability from file\n"
22 " - read capability text from stdin\n"
23 " <capsN> cap_from_text(3) formatted file capability\n"
24 " [ Note: capsh --suggest=\"something...\" might help you pick. ]"
25 "\n"
26 " -h this message and exit status 0\n"
27 " -q quietly\n"
28 " -v validate supplied capability matches file\n"
29 " -n <rootid> write a user namespace (!= 0) limited capability\n"
30 " --license display the license info\n"
31 );
32 exit(status);
33 }
34
35 /* parse a positive integer with some error handling */
pos_uint(const char * text,const char * prefix,int * ok)36 static unsigned long pos_uint(const char *text, const char *prefix, int *ok)
37 {
38 char *remains;
39 unsigned long value;
40 ssize_t len = strlen(text);
41
42 if (len == 0 || *text == '-') {
43 goto fail;
44 }
45 value = strtoul(text, &remains, 0);
46 if (*remains || value == 0) {
47 goto fail;
48 }
49 if (ok != NULL) {
50 *ok = 1;
51 }
52 return value;
53
54 fail:
55 if (ok == NULL) {
56 fprintf(stderr, "%s: want positive integer, got \"%s\"\n",
57 prefix, text);
58 exit(1);
59 }
60 *ok = 0;
61 return 0;
62 }
63
64 #define MAXCAP 2048
65
read_caps(int quiet,const char * filename,char * buffer)66 static int read_caps(int quiet, const char *filename, char *buffer)
67 {
68 int i = MAXCAP;
69
70 if (!quiet) {
71 fprintf(stderr, "Please enter caps for file [empty line to end]:\n");
72 }
73 while (i > 0) {
74 int j = read(STDIN_FILENO, buffer, i);
75
76 if (j < 0) {
77 fprintf(stderr, "\n[Error - aborting]\n");
78 exit(1);
79 }
80
81 if (j==0 || buffer[0] == '\n') {
82 /* we're done */
83 break;
84 }
85
86 /* move on... */
87
88 i -= j;
89 buffer += j;
90 }
91
92 /* <NUL> terminate */
93 buffer[0] = '\0';
94
95 return (i < MAXCAP ? 0:-1);
96 }
97
main(int argc,char ** argv)98 int main(int argc, char **argv)
99 {
100 int tried_to_cap_setfcap = 0;
101 char buffer[MAXCAP+1];
102 int retval, quiet = 0, verify = 0;
103 cap_t mycaps;
104 cap_value_t capflag;
105 uid_t rootid = 0, f_rootid;
106
107 if (argc < 2) {
108 usage(1);
109 }
110
111 mycaps = cap_get_proc();
112 if (mycaps == NULL) {
113 fprintf(stderr, "warning - unable to get process capabilities"
114 " (old libcap?)\n");
115 }
116
117 cap_t cap_d = NULL;
118 while (--argc > 0) {
119 const char *text;
120
121 cap_free(cap_d);
122 cap_d = NULL;
123
124 if (!strcmp(*++argv, "-q")) {
125 quiet = 1;
126 continue;
127 }
128 if (!strcmp("--license", *argv)) {
129 printf(
130 "%s see LICENSE file for details.\n"
131 "Copyright (c) 1997,2007-8,2020-21 Andrew G. Morgan"
132 " <[email protected]>\n", argv[0]);
133 exit(0);
134 }
135 if (!strcmp(*argv, "-h")) {
136 usage(0);
137 }
138 if (!strcmp(*argv, "-v")) {
139 verify = 1;
140 continue;
141 }
142 if (!strcmp(*argv, "-n")) {
143 if (argc < 2) {
144 fprintf(stderr,
145 "usage: .. -n <rootid> .. - rootid!=0 file caps");
146 exit(1);
147 }
148 --argc;
149 rootid = (uid_t) pos_uint(*++argv, "bad ns rootid", NULL);
150 continue;
151 }
152
153 if (!strcmp(*argv, "-r")) {
154 cap_free(cap_d);
155 cap_d = NULL;
156 } else {
157 if (!strcmp(*argv,"-")) {
158 retval = read_caps(quiet, *argv, buffer);
159 if (retval)
160 usage(1);
161 text = buffer;
162 } else {
163 text = *argv;
164 }
165
166 cap_d = cap_from_text(text);
167 if (cap_d == NULL) {
168 perror("fatal error");
169 usage(1);
170 }
171 if (cap_set_nsowner(cap_d, rootid)) {
172 perror("unable to set nsowner");
173 exit(1);
174 }
175 #ifdef DEBUG
176 {
177 char *result = cap_to_text(cap_d, NULL);
178 fprintf(stderr, "caps set to: [%s]\n", result);
179 cap_free(result);
180 }
181 #endif
182 }
183
184 if (--argc <= 0)
185 usage(1);
186 /*
187 * Set the filesystem capability for this file.
188 */
189 if (verify) {
190 cap_t cap_on_file;
191 int cmp;
192
193 if (cap_d == NULL) {
194 cap_d = cap_init();
195 if (cap_d == NULL) {
196 perror("unable to obtain empty capability");
197 exit(1);
198 }
199 }
200
201 cap_on_file = cap_get_file(*++argv);
202 if (cap_on_file == NULL) {
203 cap_on_file = cap_init();
204 if (cap_on_file == NULL) {
205 perror("unable to use missing capability");
206 exit(1);
207 }
208 }
209
210 cmp = cap_compare(cap_on_file, cap_d);
211 f_rootid = cap_get_nsowner(cap_on_file);
212 cap_free(cap_on_file);
213
214 if (cmp != 0 || rootid != f_rootid) {
215 if (!quiet) {
216 if (rootid != f_rootid) {
217 printf("nsowner[got=%d, want=%d],", f_rootid, rootid);
218 }
219 printf("%s differs in [%s%s%s]\n", *argv,
220 CAP_DIFFERS(cmp, CAP_PERMITTED) ? "p" : "",
221 CAP_DIFFERS(cmp, CAP_INHERITABLE) ? "i" : "",
222 CAP_DIFFERS(cmp, CAP_EFFECTIVE) ? "e" : "");
223 }
224 exit(1);
225 }
226 if (!quiet) {
227 printf("%s: OK\n", *argv);
228 }
229 } else {
230 if (!tried_to_cap_setfcap) {
231 capflag = CAP_SETFCAP;
232
233 /*
234 * Raise the effective CAP_SETFCAP.
235 */
236 if (cap_set_flag(mycaps, CAP_EFFECTIVE, 1, &capflag, CAP_SET)
237 != 0) {
238 perror("unable to manipulate CAP_SETFCAP - "
239 "try a newer libcap?");
240 exit(1);
241 }
242 if (cap_set_proc(mycaps) != 0) {
243 perror("unable to set CAP_SETFCAP effective capability");
244 exit(1);
245 }
246 tried_to_cap_setfcap = 1;
247 }
248 retval = cap_set_file(*++argv, cap_d);
249 if (retval != 0) {
250 int explained = 0;
251 int oerrno = errno;
252 int somebits = 0;
253 #ifdef linux
254 cap_value_t cap;
255 cap_flag_value_t per_state;
256
257 for (cap = 0;
258 cap_get_flag(cap_d, cap, CAP_PERMITTED, &per_state) != -1;
259 cap++) {
260 cap_flag_value_t inh_state, eff_state, combined;
261
262 cap_get_flag(cap_d, cap, CAP_INHERITABLE, &inh_state);
263 cap_get_flag(cap_d, cap, CAP_EFFECTIVE, &eff_state);
264 combined = (inh_state | per_state);
265 somebits |= !!eff_state;
266 if (combined != eff_state) {
267 explained = 1;
268 break;
269 }
270 }
271 if (somebits && explained) {
272 fprintf(stderr, "NOTE: Under Linux, effective file capabilities must either be empty, or\n"
273 " exactly match the union of selected permitted and inheritable bits.\n");
274 }
275 #endif /* def linux */
276
277 switch (oerrno) {
278 case EINVAL:
279 fprintf(stderr,
280 "Invalid file '%s' for capability operation\n",
281 argv[0]);
282 exit(1);
283 case ENODATA:
284 if (cap_d == NULL) {
285 fprintf(stderr,
286 "File '%s' has no capablity to remove\n",
287 argv[0]);
288 exit(1);
289 }
290 /* FALLTHROUGH */
291 default:
292 fprintf(stderr,
293 "Failed to set capabilities on file '%s': %s\n",
294 argv[0], strerror(oerrno));
295 exit(1);
296 }
297 }
298 }
299 }
300 if (cap_d) {
301 cap_free(cap_d);
302 }
303
304 exit(0);
305 }
306