123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- /*
- * Copyright (c) 1997-8,2007-8 Andrew G Morgan <morgan@kernel.org>
- * Copyright (c) 1997 Andrew Main <zefram@dcs.warwick.ac.uk>
- *
- * This file deals with exchanging internal and textual
- * representations of capability sets.
- */
- #define _GNU_SOURCE
- #include <stdio.h>
- #define LIBCAP_PLEASE_INCLUDE_ARRAY
- #include "libcap.h"
- #include <ctype.h>
- #include <limits.h>
- /* Maximum output text length (16 per cap) */
- #define CAP_TEXT_SIZE (16*__CAP_MAXBITS)
- /*
- * Parse a textual representation of capabilities, returning an internal
- * representation.
- */
- #define raise_cap_mask(flat, c) (flat)[CAP_TO_INDEX(c)] |= CAP_TO_MASK(c)
- static void setbits(cap_t a, const __u32 *b, cap_flag_t set, unsigned blks)
- {
- int n;
- for (n = blks; n--; ) {
- a->u[n].flat[set] |= b[n];
- }
- }
- static void clrbits(cap_t a, const __u32 *b, cap_flag_t set, unsigned blks)
- {
- int n;
- for (n = blks; n--; )
- a->u[n].flat[set] &= ~b[n];
- }
- static char const *namcmp(char const *str, char const *nam)
- {
- while (*nam && tolower((unsigned char)*str) == *nam) {
- str++;
- nam++;
- }
- if (*nam || isalnum((unsigned char)*str) || *str == '_')
- return NULL;
- return str;
- }
- static void forceall(__u32 *flat, __u32 value, unsigned blks)
- {
- unsigned n;
- for (n = blks; n--; flat[n] = value);
- return;
- }
- static int lookupname(char const **strp)
- {
- union {
- char const *constp;
- char *p;
- } str;
- str.constp = *strp;
- if (isdigit(*str.constp)) {
- unsigned long n = strtoul(str.constp, &str.p, 0);
- if (n >= __CAP_MAXBITS)
- return -1;
- *strp = str.constp;
- return n;
- } else {
- int c;
- unsigned len;
- for (len=0; (c = str.constp[len]); ++len) {
- if (!(isalpha(c) || (c == '_'))) {
- break;
- }
- }
- #ifdef GPERF_DOWNCASE
- const struct __cap_token_s *token_info;
- token_info = __cap_lookup_name(str.constp, len);
- if (token_info != NULL) {
- *strp = str.constp + len;
- return token_info->index;
- }
- #else /* ie., ndef GPERF_DOWNCASE */
- char const *s;
- unsigned n;
- for (n = __CAP_BITS; n--; )
- if (_cap_names[n] && (s = namcmp(str.constp, _cap_names[n]))) {
- *strp = s;
- return n;
- }
- #endif /* def GPERF_DOWNCASE */
- return -1; /* No definition available */
- }
- }
- cap_t cap_from_text(const char *str)
- {
- cap_t res;
- int n;
- unsigned cap_blks;
- if (str == NULL) {
- _cap_debug("bad argument");
- errno = EINVAL;
- return NULL;
- }
- if (!(res = cap_init()))
- return NULL;
- switch (res->head.version) {
- case _LINUX_CAPABILITY_VERSION_1:
- cap_blks = _LINUX_CAPABILITY_U32S_1;
- break;
- case _LINUX_CAPABILITY_VERSION_2:
- cap_blks = _LINUX_CAPABILITY_U32S_2;
- break;
- case _LINUX_CAPABILITY_VERSION_3:
- cap_blks = _LINUX_CAPABILITY_U32S_3;
- break;
- default:
- errno = EINVAL;
- return NULL;
- }
-
- _cap_debug("%s", str);
- for (;;) {
- __u32 list[__CAP_BLKS];
- char op;
- int flags = 0, listed=0;
- forceall(list, 0, __CAP_BLKS);
- /* skip leading spaces */
- while (isspace((unsigned char)*str))
- str++;
- if (!*str) {
- _cap_debugcap("e = ", *res, CAP_EFFECTIVE);
- _cap_debugcap("i = ", *res, CAP_INHERITABLE);
- _cap_debugcap("p = ", *res, CAP_PERMITTED);
- return res;
- }
- /* identify caps specified by this clause */
- if (isalnum((unsigned char)*str) || *str == '_') {
- for (;;) {
- if (namcmp(str, "all")) {
- str += 3;
- forceall(list, ~0, cap_blks);
- } else {
- n = lookupname(&str);
- if (n == -1)
- goto bad;
- raise_cap_mask(list, n);
- }
- if (*str != ',')
- break;
- if (!isalnum((unsigned char)*++str) && *str != '_')
- goto bad;
- }
- listed = 1;
- } else if (*str == '+' || *str == '-') {
- goto bad; /* require a list of capabilities */
- } else {
- forceall(list, ~0, cap_blks);
- }
- /* identify first operation on list of capabilities */
- op = *str++;
- if (op == '=' && (*str == '+' || *str == '-')) {
- if (!listed)
- goto bad;
- op = (*str++ == '+' ? 'P':'M'); /* skip '=' and take next op */
- } else if (op != '+' && op != '-' && op != '=')
- goto bad;
- /* cycle through list of actions */
- do {
- _cap_debug("next char = `%c'", *str);
- if (*str && !isspace(*str)) {
- switch (*str++) { /* Effective, Inheritable, Permitted */
- case 'e':
- flags |= LIBCAP_EFF;
- break;
- case 'i':
- flags |= LIBCAP_INH;
- break;
- case 'p':
- flags |= LIBCAP_PER;
- break;
- default:
- goto bad;
- }
- } else if (op != '=') {
- _cap_debug("only '=' can be followed by space");
- goto bad;
- }
- _cap_debug("how to read?");
- switch (op) { /* how do we interpret the caps? */
- case '=':
- case 'P': /* =+ */
- case 'M': /* =- */
- clrbits(res, list, CAP_EFFECTIVE, cap_blks);
- clrbits(res, list, CAP_PERMITTED, cap_blks);
- clrbits(res, list, CAP_INHERITABLE, cap_blks);
- if (op == 'M')
- goto minus;
- /* fall through */
- case '+':
- if (flags & LIBCAP_EFF)
- setbits(res, list, CAP_EFFECTIVE, cap_blks);
- if (flags & LIBCAP_PER)
- setbits(res, list, CAP_PERMITTED, cap_blks);
- if (flags & LIBCAP_INH)
- setbits(res, list, CAP_INHERITABLE, cap_blks);
- break;
- case '-':
- minus:
- if (flags & LIBCAP_EFF)
- clrbits(res, list, CAP_EFFECTIVE, cap_blks);
- if (flags & LIBCAP_PER)
- clrbits(res, list, CAP_PERMITTED, cap_blks);
- if (flags & LIBCAP_INH)
- clrbits(res, list, CAP_INHERITABLE, cap_blks);
- break;
- }
- /* new directive? */
- if (*str == '+' || *str == '-') {
- if (!listed) {
- _cap_debug("for + & - must list capabilities");
- goto bad;
- }
- flags = 0; /* reset the flags */
- op = *str++;
- if (!isalpha(*str))
- goto bad;
- }
- } while (*str && !isspace(*str));
- _cap_debug("next clause");
- }
- bad:
- cap_free(res);
- res = NULL;
- errno = EINVAL;
- return res;
- }
- /*
- * lookup a capability name and return its numerical value
- */
- int cap_from_name(const char *name, cap_value_t *value_p)
- {
- int n;
- if (((n = lookupname(&name)) >= 0) && (value_p != NULL)) {
- *value_p = (unsigned) n;
- }
- return -(n < 0);
- }
- /*
- * Convert a single capability index number into a string representation
- */
- char *cap_to_name(cap_value_t cap)
- {
- if ((cap < 0) || (cap >= __CAP_BITS)) {
- #if UINT_MAX != 4294967295U
- # error Recompile with correctly sized numeric array
- #endif
- char *tmp, *result;
- asprintf(&tmp, "%u", cap);
- result = _libcap_strdup(tmp);
- free(tmp);
- return result;
- } else {
- return _libcap_strdup(_cap_names[cap]);
- }
- }
- /*
- * Convert an internal representation to a textual one. The textual
- * representation is stored in static memory. It will be overwritten
- * on the next occasion that this function is called.
- */
- static int getstateflags(cap_t caps, int capno)
- {
- int f = 0;
- if (isset_cap(caps, capno, CAP_EFFECTIVE)) {
- f |= LIBCAP_EFF;
- }
- if (isset_cap(caps, capno, CAP_PERMITTED)) {
- f |= LIBCAP_PER;
- }
- if (isset_cap(caps, capno, CAP_INHERITABLE)) {
- f |= LIBCAP_INH;
- }
- return f;
- }
- #define CAP_TEXT_BUFFER_ZONE 100
- char *cap_to_text(cap_t caps, ssize_t *length_p)
- {
- char buf[CAP_TEXT_SIZE+CAP_TEXT_BUFFER_ZONE];
- char *p;
- int histo[8];
- int m, t;
- unsigned n;
- unsigned cap_maxbits, cap_blks;
- /* Check arguments */
- if (!good_cap_t(caps)) {
- errno = EINVAL;
- return NULL;
- }
- switch (caps->head.version) {
- case _LINUX_CAPABILITY_VERSION_1:
- cap_blks = _LINUX_CAPABILITY_U32S_1;
- break;
- case _LINUX_CAPABILITY_VERSION_2:
- cap_blks = _LINUX_CAPABILITY_U32S_2;
- break;
- case _LINUX_CAPABILITY_VERSION_3:
- cap_blks = _LINUX_CAPABILITY_U32S_3;
- break;
- default:
- errno = EINVAL;
- return NULL;
- }
- cap_maxbits = 32 * cap_blks;
- _cap_debugcap("e = ", *caps, CAP_EFFECTIVE);
- _cap_debugcap("i = ", *caps, CAP_INHERITABLE);
- _cap_debugcap("p = ", *caps, CAP_PERMITTED);
- memset(histo, 0, sizeof(histo));
- /* default prevailing state to the upper - unnamed bits */
- for (n = cap_maxbits-1; n > __CAP_BITS; n--)
- histo[getstateflags(caps, n)]++;
- /* find which combination of capability sets shares the most bits
- we bias to preferring non-set (m=0) with the >= 0 test. Failing
- to do this causes strange things to happen with older systems
- that don't know about bits 32+. */
- for (m=t=7; t--; )
- if (histo[t] >= histo[m])
- m = t;
- /* capture remaining bits - selecting m from only the unnamed bits,
- we maximize the likelihood that we won't see numeric capability
- values in the text output. */
- while (n--)
- histo[getstateflags(caps, n)]++;
- /* blank is not a valid capability set */
- p = sprintf(buf, "=%s%s%s",
- (m & LIBCAP_EFF) ? "e" : "",
- (m & LIBCAP_INH) ? "i" : "",
- (m & LIBCAP_PER) ? "p" : "" ) + buf;
- for (t = 8; t--; )
- if (t != m && histo[t]) {
- *p++ = ' ';
- for (n = 0; n < cap_maxbits; n++)
- if (getstateflags(caps, n) == t) {
- char *this_cap_name;
- this_cap_name = cap_to_name(n);
- if ((strlen(this_cap_name) + (p - buf)) > CAP_TEXT_SIZE) {
- cap_free(this_cap_name);
- errno = ERANGE;
- return NULL;
- }
- p += sprintf(p, "%s,", this_cap_name);
- cap_free(this_cap_name);
- }
- p--;
- n = t & ~m;
- if (n)
- p += sprintf(p, "+%s%s%s",
- (n & LIBCAP_EFF) ? "e" : "",
- (n & LIBCAP_INH) ? "i" : "",
- (n & LIBCAP_PER) ? "p" : "");
- n = ~t & m;
- if (n)
- p += sprintf(p, "-%s%s%s",
- (n & LIBCAP_EFF) ? "e" : "",
- (n & LIBCAP_INH) ? "i" : "",
- (n & LIBCAP_PER) ? "p" : "");
- if (p - buf > CAP_TEXT_SIZE) {
- errno = ERANGE;
- return NULL;
- }
- }
- _cap_debug("%s", buf);
- if (length_p) {
- *length_p = p - buf;
- }
- return (_libcap_strdup(buf));
- }
|