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