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