diff options
Diffstat (limited to 'libcap/cap_text.c')
-rw-r--r-- | libcap/cap_text.c | 429 |
1 files changed, 429 insertions, 0 deletions
diff --git a/libcap/cap_text.c b/libcap/cap_text.c new file mode 100644 index 0000000..42fb685 --- /dev/null +++ b/libcap/cap_text.c @@ -0,0 +1,429 @@ +/* + * 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)); +} |