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
28static 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
36static 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
43static 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
54static 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
63static 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
110cap_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
260bad:
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 */
270int 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 */
283char *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
307static 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
326char *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