xref: /aosp_15_r20/external/libcap/progs/setcap.c (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
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