1 /*
2  * Copyright (c) 1997,2007-8,2020 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(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 	    "\n"
25 	    " -h          this message and exit status 0\n"
26 	    " -q          quietly\n"
27 	    " -v          validate supplied capability matches file\n"
28 	    " -n <rootid> write a user namespace limited capability\n"
29 	    " --license   display the license info\n"
30 	);
31     exit(status);
32 }
33 
34 #define MAXCAP  2048
35 
read_caps(int quiet,const char * filename,char * buffer)36 static int read_caps(int quiet, const char *filename, char *buffer)
37 {
38     int i = MAXCAP;
39 
40     if (!quiet) {
41 	fprintf(stderr,	"Please enter caps for file [empty line to end]:\n");
42     }
43     while (i > 0) {
44 	int j = read(STDIN_FILENO, buffer, i);
45 
46 	if (j < 0) {
47 	    fprintf(stderr, "\n[Error - aborting]\n");
48 	    exit(1);
49 	}
50 
51 	if (j==0 || buffer[0] == '\n') {
52 	    /* we're done */
53 	    break;
54 	}
55 
56 	/* move on... */
57 
58 	i -= j;
59 	buffer += j;
60     }
61 
62     /* <NUL> terminate */
63     buffer[0] = '\0';
64 
65     return (i < MAXCAP ? 0:-1);
66 }
67 
main(int argc,char ** argv)68 int main(int argc, char **argv)
69 {
70     int tried_to_cap_setfcap = 0;
71     char buffer[MAXCAP+1];
72     int retval, quiet = 0, verify = 0;
73     cap_t mycaps;
74     cap_value_t capflag;
75     uid_t rootid = 0, f_rootid;
76 
77     if (argc < 2) {
78 	usage(1);
79     }
80 
81     mycaps = cap_get_proc();
82     if (mycaps == NULL) {
83 	fprintf(stderr, "warning - unable to get process capabilities"
84 		" (old libcap?)\n");
85     }
86 
87     while (--argc > 0) {
88 	const char *text;
89 	cap_t cap_d;
90 
91 	if (!strcmp(*++argv, "-q")) {
92 	    quiet = 1;
93 	    continue;
94 	}
95 	if (!strcmp("--license", *argv)) {
96 	    printf(
97 		"%s has a you choose license: BSD 3-clause or GPL2\n"
98 		"Copyright (c) 1997,2007-8,2020 Andrew G. Morgan"
99 		" <morgan@kernel.org>\n", argv[0]);
100 	    exit(0);
101 	}
102 	if (!strcmp(*argv, "-h")) {
103 	    usage(0);
104 	}
105 	if (!strcmp(*argv, "-v")) {
106 	    verify = 1;
107 	    continue;
108 	}
109 	if (!strcmp(*argv, "-n")) {
110 	    if (argc < 2) {
111 		fprintf(stderr, "usage: .. -n <rootid> .. - rootid!=0 file caps");
112 		exit(1);
113 	    }
114 	    --argc;
115 	    rootid = (uid_t) atoi(*++argv);
116 	    if (rootid+1 < 2) {
117 		fprintf(stderr, "invalid rootid!=0 of '%s'", *argv);
118 		exit(1);
119 	    }
120 	    continue;
121 	}
122 
123 	if (!strcmp(*argv, "-r")) {
124 	    cap_d = NULL;
125 	} else {
126 	    if (!strcmp(*argv,"-")) {
127 		retval = read_caps(quiet, *argv, buffer);
128 		if (retval)
129 		    usage(1);
130 		text = buffer;
131 	    } else {
132 		text = *argv;
133 	    }
134 
135 	    cap_d = cap_from_text(text);
136 	    if (cap_d == NULL) {
137 		perror("fatal error");
138 		usage(1);
139 	    }
140 	    if (cap_set_nsowner(cap_d, rootid)) {
141 		perror("unable to set nsowner");
142 		exit(1);
143 	    }
144 #ifdef DEBUG
145 	    {
146 		ssize_t length;
147 		const char *result;
148 
149 		result = cap_to_text(cap_d, &length);
150 		fprintf(stderr, "caps set to: [%s]\n", result);
151 	    }
152 #endif
153 	}
154 
155 	if (--argc <= 0)
156 	    usage(1);
157 	/*
158 	 * Set the filesystem capability for this file.
159 	 */
160 	if (verify) {
161 	    cap_t cap_on_file;
162 	    int cmp;
163 
164 	    if (cap_d == NULL) {
165 		cap_d = cap_from_text("=");
166 	    }
167 
168 	    cap_on_file = cap_get_file(*++argv);
169 
170 	    if (cap_on_file == NULL) {
171 		cap_on_file = cap_from_text("=");
172 	    }
173 
174 	    cmp = cap_compare(cap_on_file, cap_d);
175 	    f_rootid = cap_get_nsowner(cap_on_file);
176 	    cap_free(cap_on_file);
177 
178 	    if (cmp != 0 || rootid != f_rootid) {
179 		if (!quiet) {
180 		    if (rootid != f_rootid) {
181 			printf("nsowner[got=%d, want=%d],", f_rootid, rootid);
182 		    }
183 		    printf("%s differs in [%s%s%s]\n", *argv,
184 			   CAP_DIFFERS(cmp, CAP_PERMITTED) ? "p" : "",
185 			   CAP_DIFFERS(cmp, CAP_INHERITABLE) ? "i" : "",
186 			   CAP_DIFFERS(cmp, CAP_EFFECTIVE) ? "e" : "");
187 		}
188 		exit(1);
189 	    }
190 	    if (!quiet) {
191 		printf("%s: OK\n", *argv);
192 	    }
193 	} else {
194 	    if (!tried_to_cap_setfcap) {
195 		capflag = CAP_SETFCAP;
196 
197 		/*
198 		 * Raise the effective CAP_SETFCAP.
199 		 */
200 		if (cap_set_flag(mycaps, CAP_EFFECTIVE, 1, &capflag, CAP_SET)
201 		    != 0) {
202 		    perror("unable to manipulate CAP_SETFCAP - "
203 			   "try a newer libcap?");
204 		    exit(1);
205 		}
206 		if (cap_set_proc(mycaps) != 0) {
207 		    perror("unable to set CAP_SETFCAP effective capability");
208 		    exit(1);
209 		}
210 		tried_to_cap_setfcap = 1;
211 	    }
212 	    retval = cap_set_file(*++argv, cap_d);
213 	    if (retval != 0) {
214 		int explained = 0;
215 		int oerrno = errno;
216 		int somebits = 0;
217 #ifdef linux
218 		cap_value_t cap;
219 		cap_flag_value_t per_state;
220 
221 		for (cap = 0;
222 		     cap_get_flag(cap_d, cap, CAP_PERMITTED, &per_state) != -1;
223 		     cap++) {
224 		    cap_flag_value_t inh_state, eff_state, combined;
225 
226 		    cap_get_flag(cap_d, cap, CAP_INHERITABLE, &inh_state);
227 		    cap_get_flag(cap_d, cap, CAP_EFFECTIVE, &eff_state);
228 		    combined = (inh_state | per_state);
229 		    somebits |= !!eff_state;
230 		    if (combined != eff_state) {
231 			explained = 1;
232 			break;
233 		    }
234 		}
235 		if (somebits && explained) {
236 		    fprintf(stderr, "NOTE: Under Linux, effective file capabilities must either be empty, or\n"
237 			    "      exactly match the union of selected permitted and inheritable bits.\n");
238 		}
239 #endif /* def linux */
240 
241 		fprintf(stderr,
242 			"Failed to set capabilities on file `%s' (%s)\n",
243 			argv[0], strerror(oerrno));
244 		if (!explained) {
245 		    usage(1);
246 		}
247 	    }
248 	}
249 	if (cap_d) {
250 	    cap_free(cap_d);
251 	}
252     }
253 
254     exit(0);
255 }
256