1 /*
2 * Copyright (c) 1997-8,2007-8 Andrew G Morgan <morgan@kernel.org>
3 * Copyright (c) 1997 Andrew Main <zefram@dcs.warwick.ac.uk>
4 *
5 * This file deals with exchanging internal and textual
6 * representations of capability sets.
7 */
8
9 #define _GNU_SOURCE
10 #include <stdio.h>
11
12 #define LIBCAP_PLEASE_INCLUDE_ARRAY
13 #include "libcap.h"
14
15 #include <ctype.h>
16 #include <limits.h>
17
18 /* Maximum output text length (16 per cap) */
19 #define CAP_TEXT_SIZE (16*__CAP_MAXBITS)
20
21 /*
22 * Parse a textual representation of capabilities, returning an internal
23 * representation.
24 */
25
26 #define raise_cap_mask(flat, c) (flat)[CAP_TO_INDEX(c)] |= CAP_TO_MASK(c)
27
setbits(cap_t a,const __u32 * b,cap_flag_t set,unsigned blks)28 static void setbits(cap_t a, const __u32 *b, cap_flag_t set, unsigned blks)
29 {
30 int n;
31 for (n = blks; n--; ) {
32 a->u[n].flat[set] |= b[n];
33 }
34 }
35
clrbits(cap_t a,const __u32 * b,cap_flag_t set,unsigned blks)36 static void clrbits(cap_t a, const __u32 *b, cap_flag_t set, unsigned blks)
37 {
38 int n;
39 for (n = blks; n--; )
40 a->u[n].flat[set] &= ~b[n];
41 }
42
namcmp(char const * str,char const * nam)43 static char const *namcmp(char const *str, char const *nam)
44 {
45 while (*nam && tolower((unsigned char)*str) == *nam) {
46 str++;
47 nam++;
48 }
49 if (*nam || isalnum((unsigned char)*str) || *str == '_')
50 return NULL;
51 return str;
52 }
53
forceall(__u32 * flat,__u32 value,unsigned blks)54 static void forceall(__u32 *flat, __u32 value, unsigned blks)
55 {
56 unsigned n;
57
58 for (n = blks; n--; flat[n] = value);
59
60 return;
61 }
62
lookupname(char const ** strp)63 static int lookupname(char const **strp)
64 {
65 union {
66 char const *constp;
67 char *p;
68 } str;
69
70 str.constp = *strp;
71 if (isdigit(*str.constp)) {
72 unsigned long n = strtoul(str.constp, &str.p, 0);
73 if (n >= __CAP_MAXBITS)
74 return -1;
75 *strp = str.constp;
76 return n;
77 } else {
78 int c;
79 unsigned len;
80
81 for (len=0; (c = str.constp[len]); ++len) {
82 if (!(isalpha(c) || (c == '_'))) {
83 break;
84 }
85 }
86
87 #ifdef GPERF_DOWNCASE
88 const struct __cap_token_s *token_info;
89
90 token_info = __cap_lookup_name(str.constp, len);
91 if (token_info != NULL) {
92 *strp = str.constp + len;
93 return token_info->index;
94 }
95 #else /* ie., ndef GPERF_DOWNCASE */
96 char const *s;
97 unsigned n;
98
99 for (n = __CAP_BITS; n--; )
100 if (_cap_names[n] && (s = namcmp(str.constp, _cap_names[n]))) {
101 *strp = s;
102 return n;
103 }
104 #endif /* def GPERF_DOWNCASE */
105
106 return -1; /* No definition available */
107 }
108 }
109
cap_from_text(const char * str)110 cap_t cap_from_text(const char *str)
111 {
112 cap_t res;
113 int n;
114 unsigned cap_blks;
115
116 if (str == NULL) {
117 _cap_debug("bad argument");
118 errno = EINVAL;
119 return NULL;
120 }
121
122 if (!(res = cap_init()))
123 return NULL;
124
125 switch (res->head.version) {
126 case _LINUX_CAPABILITY_VERSION_1:
127 cap_blks = _LINUX_CAPABILITY_U32S_1;
128 break;
129 case _LINUX_CAPABILITY_VERSION_2:
130 cap_blks = _LINUX_CAPABILITY_U32S_2;
131 break;
132 case _LINUX_CAPABILITY_VERSION_3:
133 cap_blks = _LINUX_CAPABILITY_U32S_3;
134 break;
135 default:
136 errno = EINVAL;
137 return NULL;
138 }
139
140 _cap_debug("%s", str);
141
142 for (;;) {
143 __u32 list[__CAP_BLKS];
144 char op;
145 int flags = 0, listed=0;
146
147 forceall(list, 0, __CAP_BLKS);
148
149 /* skip leading spaces */
150 while (isspace((unsigned char)*str))
151 str++;
152 if (!*str) {
153 _cap_debugcap("e = ", *res, CAP_EFFECTIVE);
154 _cap_debugcap("i = ", *res, CAP_INHERITABLE);
155 _cap_debugcap("p = ", *res, CAP_PERMITTED);
156
157 return res;
158 }
159
160 /* identify caps specified by this clause */
161 if (isalnum((unsigned char)*str) || *str == '_') {
162 for (;;) {
163 if (namcmp(str, "all")) {
164 str += 3;
165 forceall(list, ~0, cap_blks);
166 } else {
167 n = lookupname(&str);
168 if (n == -1)
169 goto bad;
170 raise_cap_mask(list, n);
171 }
172 if (*str != ',')
173 break;
174 if (!isalnum((unsigned char)*++str) && *str != '_')
175 goto bad;
176 }
177 listed = 1;
178 } else if (*str == '+' || *str == '-') {
179 goto bad; /* require a list of capabilities */
180 } else {
181 forceall(list, ~0, cap_blks);
182 }
183
184 /* identify first operation on list of capabilities */
185 op = *str++;
186 if (op == '=' && (*str == '+' || *str == '-')) {
187 if (!listed)
188 goto bad;
189 op = (*str++ == '+' ? 'P':'M'); /* skip '=' and take next op */
190 } else if (op != '+' && op != '-' && op != '=')
191 goto bad;
192
193 /* cycle through list of actions */
194 do {
195 _cap_debug("next char = `%c'", *str);
196 if (*str && !isspace(*str)) {
197 switch (*str++) { /* Effective, Inheritable, Permitted */
198 case 'e':
199 flags |= LIBCAP_EFF;
200 break;
201 case 'i':
202 flags |= LIBCAP_INH;
203 break;
204 case 'p':
205 flags |= LIBCAP_PER;
206 break;
207 default:
208 goto bad;
209 }
210 } else if (op != '=') {
211 _cap_debug("only '=' can be followed by space");
212 goto bad;
213 }
214
215 _cap_debug("how to read?");
216 switch (op) { /* how do we interpret the caps? */
217 case '=':
218 case 'P': /* =+ */
219 case 'M': /* =- */
220 clrbits(res, list, CAP_EFFECTIVE, cap_blks);
221 clrbits(res, list, CAP_PERMITTED, cap_blks);
222 clrbits(res, list, CAP_INHERITABLE, cap_blks);
223 if (op == 'M')
224 goto minus;
225 /* fall through */
226 case '+':
227 if (flags & LIBCAP_EFF)
228 setbits(res, list, CAP_EFFECTIVE, cap_blks);
229 if (flags & LIBCAP_PER)
230 setbits(res, list, CAP_PERMITTED, cap_blks);
231 if (flags & LIBCAP_INH)
232 setbits(res, list, CAP_INHERITABLE, cap_blks);
233 break;
234 case '-':
235 minus:
236 if (flags & LIBCAP_EFF)
237 clrbits(res, list, CAP_EFFECTIVE, cap_blks);
238 if (flags & LIBCAP_PER)
239 clrbits(res, list, CAP_PERMITTED, cap_blks);
240 if (flags & LIBCAP_INH)
241 clrbits(res, list, CAP_INHERITABLE, cap_blks);
242 break;
243 }
244
245 /* new directive? */
246 if (*str == '+' || *str == '-') {
247 if (!listed) {
248 _cap_debug("for + & - must list capabilities");
249 goto bad;
250 }
251 flags = 0; /* reset the flags */
252 op = *str++;
253 if (!isalpha(*str))
254 goto bad;
255 }
256 } while (*str && !isspace(*str));
257 _cap_debug("next clause");
258 }
259
260 bad:
261 cap_free(res);
262 res = NULL;
263 errno = EINVAL;
264 return res;
265 }
266
267 /*
268 * lookup a capability name and return its numerical value
269 */
cap_from_name(const char * name,cap_value_t * value_p)270 int cap_from_name(const char *name, cap_value_t *value_p)
271 {
272 int n;
273
274 if (((n = lookupname(&name)) >= 0) && (value_p != NULL)) {
275 *value_p = (unsigned) n;
276 }
277 return -(n < 0);
278 }
279
280 /*
281 * Convert a single capability index number into a string representation
282 */
cap_to_name(cap_value_t cap)283 char *cap_to_name(cap_value_t cap)
284 {
285 if ((cap < 0) || (cap >= __CAP_BITS)) {
286 #if UINT_MAX != 4294967295U
287 # error Recompile with correctly sized numeric array
288 #endif
289 char *tmp, *result;
290
291 asprintf(&tmp, "%u", cap);
292 result = _libcap_strdup(tmp);
293 free(tmp);
294
295 return result;
296 } else {
297 return _libcap_strdup(_cap_names[cap]);
298 }
299 }
300
301 /*
302 * Convert an internal representation to a textual one. The textual
303 * representation is stored in static memory. It will be overwritten
304 * on the next occasion that this function is called.
305 */
306
getstateflags(cap_t caps,int capno)307 static int getstateflags(cap_t caps, int capno)
308 {
309 int f = 0;
310
311 if (isset_cap(caps, capno, CAP_EFFECTIVE)) {
312 f |= LIBCAP_EFF;
313 }
314 if (isset_cap(caps, capno, CAP_PERMITTED)) {
315 f |= LIBCAP_PER;
316 }
317 if (isset_cap(caps, capno, CAP_INHERITABLE)) {
318 f |= LIBCAP_INH;
319 }
320
321 return f;
322 }
323
324 #define CAP_TEXT_BUFFER_ZONE 100
325
cap_to_text(cap_t caps,ssize_t * length_p)326 char *cap_to_text(cap_t caps, ssize_t *length_p)
327 {
328 char buf[CAP_TEXT_SIZE+CAP_TEXT_BUFFER_ZONE];
329 char *p;
330 int histo[8];
331 int m, t;
332 unsigned n;
333 unsigned cap_maxbits, cap_blks;
334
335 /* Check arguments */
336 if (!good_cap_t(caps)) {
337 errno = EINVAL;
338 return NULL;
339 }
340
341 switch (caps->head.version) {
342 case _LINUX_CAPABILITY_VERSION_1:
343 cap_blks = _LINUX_CAPABILITY_U32S_1;
344 break;
345 case _LINUX_CAPABILITY_VERSION_2:
346 cap_blks = _LINUX_CAPABILITY_U32S_2;
347 break;
348 case _LINUX_CAPABILITY_VERSION_3:
349 cap_blks = _LINUX_CAPABILITY_U32S_3;
350 break;
351 default:
352 errno = EINVAL;
353 return NULL;
354 }
355
356 cap_maxbits = 32 * cap_blks;
357
358 _cap_debugcap("e = ", *caps, CAP_EFFECTIVE);
359 _cap_debugcap("i = ", *caps, CAP_INHERITABLE);
360 _cap_debugcap("p = ", *caps, CAP_PERMITTED);
361
362 memset(histo, 0, sizeof(histo));
363
364 /* default prevailing state to the upper - unnamed bits */
365 for (n = cap_maxbits-1; n > __CAP_BITS; n--)
366 histo[getstateflags(caps, n)]++;
367
368 /* find which combination of capability sets shares the most bits
369 we bias to preferring non-set (m=0) with the >= 0 test. Failing
370 to do this causes strange things to happen with older systems
371 that don't know about bits 32+. */
372 for (m=t=7; t--; )
373 if (histo[t] >= histo[m])
374 m = t;
375
376 /* capture remaining bits - selecting m from only the unnamed bits,
377 we maximize the likelihood that we won't see numeric capability
378 values in the text output. */
379 while (n--)
380 histo[getstateflags(caps, n)]++;
381
382 /* blank is not a valid capability set */
383 p = sprintf(buf, "=%s%s%s",
384 (m & LIBCAP_EFF) ? "e" : "",
385 (m & LIBCAP_INH) ? "i" : "",
386 (m & LIBCAP_PER) ? "p" : "" ) + buf;
387
388 for (t = 8; t--; )
389 if (t != m && histo[t]) {
390 *p++ = ' ';
391 for (n = 0; n < cap_maxbits; n++)
392 if (getstateflags(caps, n) == t) {
393 char *this_cap_name;
394
395 this_cap_name = cap_to_name(n);
396 if ((strlen(this_cap_name) + (p - buf)) > CAP_TEXT_SIZE) {
397 cap_free(this_cap_name);
398 errno = ERANGE;
399 return NULL;
400 }
401 p += sprintf(p, "%s,", this_cap_name);
402 cap_free(this_cap_name);
403 }
404 p--;
405 n = t & ~m;
406 if (n)
407 p += sprintf(p, "+%s%s%s",
408 (n & LIBCAP_EFF) ? "e" : "",
409 (n & LIBCAP_INH) ? "i" : "",
410 (n & LIBCAP_PER) ? "p" : "");
411 n = ~t & m;
412 if (n)
413 p += sprintf(p, "-%s%s%s",
414 (n & LIBCAP_EFF) ? "e" : "",
415 (n & LIBCAP_INH) ? "i" : "",
416 (n & LIBCAP_PER) ? "p" : "");
417 if (p - buf > CAP_TEXT_SIZE) {
418 errno = ERANGE;
419 return NULL;
420 }
421 }
422
423 _cap_debug("%s", buf);
424 if (length_p) {
425 *length_p = p - buf;
426 }
427
428 return (_libcap_strdup(buf));
429 }
430