aboutsummaryrefslogtreecommitdiff
path: root/depends/libcap/libcap/cap_text.c
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2024-02-10 19:05:02 +0100
committerdec05eba <dec05eba@protonmail.com>2024-02-11 02:11:41 +0100
commit667a6b3b1bc20516d1bf7d8a4611cd3a4d3f3bc6 (patch)
tree2770b29323939cec51670dd0d1aebd39a8041914 /depends/libcap/libcap/cap_text.c
Initial commit, done
Diffstat (limited to 'depends/libcap/libcap/cap_text.c')
-rw-r--r--depends/libcap/libcap/cap_text.c770
1 files changed, 770 insertions, 0 deletions
diff --git a/depends/libcap/libcap/cap_text.c b/depends/libcap/libcap/cap_text.c
new file mode 100644
index 0000000..7566bd8
--- /dev/null
+++ b/depends/libcap/libcap/cap_text.c
@@ -0,0 +1,770 @@
+/*
+ * Copyright (c) 1997-8,2007-8,2019,2021 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.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+
+#define LIBCAP_PLEASE_INCLUDE_ARRAY
+#include "libcap.h"
+
+static char const *_cap_names[__CAP_BITS] = LIBCAP_CAP_NAMES;
+
+#include <ctype.h>
+#include <limits.h>
+
+#ifdef INCLUDE_GPERF_OUTPUT
+/* we need to include it after #define _GNU_SOURCE is set */
+#include INCLUDE_GPERF_OUTPUT
+#endif
+
+/* Maximum output text length */
+#define CAP_TEXT_SIZE (__CAP_NAME_SIZE * __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;
+}
+
+/*
+ * forceall forces all of the kernel named capabilities to be assigned
+ * the masked value, and zeroed otherwise. Note, if the kernel is ahead
+ * of libcap, the upper bits will be referred to by number.
+ */
+static void forceall(__u32 *flat, __u32 value, unsigned blks)
+{
+ unsigned n;
+ cap_value_t cmb = cap_max_bits();
+ for (n = blks; n--; ) {
+ unsigned base = 32*n;
+ __u32 mask = 0;
+ if (cmb >= base + 32) {
+ mask = ~0;
+ } else if (cmb > base) {
+ mask = (unsigned) ((1ULL << (cmb % 32)) - 1);
+ }
+ flat[n] = value & mask;
+ }
+
+ 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;
+ size_t 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 = cap_max_bits();
+ if (n > __CAP_BITS) {
+ n = __CAP_BITS;
+ }
+ while (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:
+ cap_free(res);
+ errno = EINVAL;
+ return NULL;
+ }
+
+ _cap_debug("%s", str);
+
+ for (;;) {
+ __u32 list[__CAP_BLKS];
+ char op;
+ int flags = 0, listed=0;
+
+ memset(list, 0, sizeof(__u32)*__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)
+{
+ char *tmp, *result;
+
+ if ((cap >= 0) && (cap < __CAP_BITS)) {
+ return _libcap_strdup(_cap_names[cap]);
+ }
+ if (asprintf(&tmp, "%u", cap) <= 0) {
+ _cap_debug("asprintf filed");
+ return NULL;
+ }
+
+ result = _libcap_strdup(tmp);
+ free(tmp);
+ return result;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * This code assumes that the longest named capability is longer than
+ * the decimal text representation of __CAP_MAXBITS. This is very true
+ * at the time of writing and likely to remain so. However, we have
+ * a test in cap_text to validate it at build time.
+ */
+#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, *base;
+ int histo[8];
+ int m, t;
+ unsigned n;
+
+ /* Check arguments */
+ if (!good_cap_t(caps)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ _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 named bits */
+ cap_value_t cmb = cap_max_bits();
+ for (n = 0; n < cmb; 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;
+
+ /* blank is not a valid capability set */
+ base = buf;
+ 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]) {
+ continue;
+ }
+ *p++ = ' ';
+ for (n = 0; n < cmb; n++) {
+ if (getstateflags(caps, n) == t) {
+ char *this_cap_name = cap_to_name(n);
+ if (this_cap_name == NULL) {
+ return NULL;
+ }
+ 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) {
+ char op = '+';
+ if (base[0] == '=' && base[1] == ' ') {
+ /*
+ * Special case all lowered default "= foo,...+eip
+ * ..." as "foo,...=eip ...". (Equivalent but shorter.)
+ */
+ base += 2;
+ op = '=';
+ }
+ p += sprintf(p, "%c%s%s%s", op,
+ (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;
+ }
+ }
+
+ /* capture remaining unnamed bits - which must all be +. */
+ memset(histo, 0, sizeof(histo));
+ for (n = cmb; n < __CAP_MAXBITS; n++)
+ histo[getstateflags(caps, n)]++;
+
+ for (t = 8; t-- > 1; ) {
+ if (!histo[t]) {
+ continue;
+ }
+ *p++ = ' ';
+ for (n = cmb; n < __CAP_MAXBITS; n++) {
+ if (getstateflags(caps, n) == t) {
+ char *this_cap_name = cap_to_name(n);
+ if (this_cap_name == NULL) {
+ return NULL;
+ }
+ 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--;
+ p += sprintf(p, "+%s%s%s",
+ (t & LIBCAP_EFF) ? "e" : "",
+ (t & LIBCAP_INH) ? "i" : "",
+ (t & LIBCAP_PER) ? "p" : "");
+ if (p - buf > CAP_TEXT_SIZE) {
+ errno = ERANGE;
+ return NULL;
+ }
+ }
+
+ _cap_debug("%s", base);
+ if (length_p) {
+ *length_p = p - base;
+ }
+
+ return (_libcap_strdup(base));
+}
+
+/*
+ * cap_mode_name returns a text token naming the specified mode.
+ */
+const char *cap_mode_name(cap_mode_t flavor) {
+ switch (flavor) {
+ case CAP_MODE_NOPRIV:
+ return "NOPRIV";
+ case CAP_MODE_PURE1E_INIT:
+ return "PURE1E_INIT";
+ case CAP_MODE_PURE1E:
+ return "PURE1E";
+ case CAP_MODE_UNCERTAIN:
+ return "UNCERTAIN";
+ case CAP_MODE_HYBRID:
+ return "HYBRID";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+/*
+ * cap_iab_to_text serializes an iab into a canonical text
+ * representation.
+ */
+char *cap_iab_to_text(cap_iab_t iab)
+{
+ char buf[CAP_TEXT_SIZE+CAP_TEXT_BUFFER_ZONE];
+ char *p = buf;
+ cap_value_t c, cmb = cap_max_bits();
+ int first = 1;
+
+ if (good_cap_iab_t(iab)) {
+ _cap_mu_lock(&iab->mutex);
+ for (c = 0; c < cmb; c++) {
+ int keep = 0;
+ int o = c >> 5;
+ __u32 bit = 1U << (c & 31);
+ __u32 ib = iab->i[o] & bit;
+ __u32 ab = iab->a[o] & bit;
+ __u32 nbb = iab->nb[o] & bit;
+ if (!(nbb | ab | ib)) {
+ continue;
+ }
+ if (!first) {
+ *p++ = ',';
+ }
+ if (nbb) {
+ *p++ = '!';
+ keep = 1;
+ }
+ if (ab) {
+ *p++ = '^';
+ keep = 1;
+ } else if (nbb && ib) {
+ *p++ = '%';
+ }
+ if (keep || ib) {
+ if (c < __CAP_BITS) {
+ strcpy(p, _cap_names[c]);
+ } else {
+ sprintf(p, "%u", c);
+ }
+ p += strlen(p);
+ first = 0;
+ }
+ }
+ _cap_mu_unlock(&iab->mutex);
+ }
+ *p = '\0';
+ return _libcap_strdup(buf);
+}
+
+cap_iab_t cap_iab_from_text(const char *text)
+{
+ cap_iab_t iab = cap_iab_init();
+ if (iab == NULL) {
+ return iab;
+ }
+ if (text != NULL) {
+ unsigned flags;
+ for (flags = 0; *text; text++) {
+ /* consume prefixes */
+ switch (*text) {
+ case '!':
+ flags |= LIBCAP_IAB_NB_FLAG;
+ continue;
+ case '^':
+ flags |= LIBCAP_IAB_IA_FLAG;
+ continue;
+ case '%':
+ flags |= LIBCAP_IAB_I_FLAG;
+ continue;
+ default:
+ break;
+ }
+ if (!flags) {
+ flags = LIBCAP_IAB_I_FLAG;
+ }
+
+ /* consume cap name */
+ cap_value_t c = lookupname(&text);
+ if (c == -1) {
+ goto cleanup;
+ }
+ unsigned o = c >> 5;
+ __u32 mask = 1U << (c & 31);
+ if (flags & LIBCAP_IAB_I_FLAG) {
+ iab->i[o] |= mask;
+ }
+ if (flags & LIBCAP_IAB_A_FLAG) {
+ iab->a[o] |= mask;
+ }
+ if (flags & LIBCAP_IAB_NB_FLAG) {
+ iab->nb[o] |= mask;
+ }
+
+ /* rest should be end or comma */
+ if (*text == '\0') {
+ break;
+ }
+ if (*text != ',') {
+ goto cleanup;
+ }
+ flags = 0;
+ }
+ }
+ return iab;
+
+cleanup:
+ cap_free(iab);
+ errno = EINVAL;
+ return NULL;
+}
+
+static __u32 _parse_hex32(const char *c)
+{
+ int i;
+ __u32 v = 0;
+ for (i=0; i < 8; i++, c++) {
+ v <<= 4;
+ if (*c == 0 || *c < '0') {
+ return 0;
+ } else if (*c <= '9') {
+ v += *c - '0';
+ } else if (*c > 'f') {
+ return 0;
+ } else if (*c >= 'a') {
+ v += *c + 10 - 'a';
+ } else if (*c < 'A') {
+ return 0;
+ } else if (*c <= 'F') {
+ v += *c + 10 - 'A';
+ } else {
+ return 0;
+ }
+ }
+ return v;
+}
+
+/*
+ * _parse_vec_string converts the hex dumps in /proc/<pid>/current into
+ * an array of u32s - masked as per the forceall() mask.
+ */
+static __u32 _parse_vec_string(__u32 *vals, const char *c, int invert)
+{
+ int i;
+ int words = strlen(c)/8;
+ if (words > _LIBCAP_CAPABILITY_U32S) {
+ return 0;
+ }
+ forceall(vals, ~0, words);
+ for (i = 0; i < words; i++) {
+ __u32 val = _parse_hex32(c+8*(words-1-i));
+ if (invert) {
+ val = ~val;
+ }
+ vals[i] &= val;
+ }
+ return ~0;
+}
+
+/*
+ * libcap believes this is the root of the mounted "/proc"
+ * filesystem. (NULL == "/proc".)
+ */
+static char *_cap_proc_dir;
+
+/*
+ * If the constructor is called (see cap_alloc.c) then we'll need the
+ * corresponding destructor.
+ */
+__attribute__((destructor (300))) static void _cleanup_libcap(void)
+{
+ if (_cap_proc_dir == NULL) {
+ return;
+ }
+ cap_free(_cap_proc_dir);
+ _cap_proc_dir = NULL;
+}
+
+/*
+ * cap_proc_root reads and (optionally: when root != NULL) changes
+ * libcap's notion of where the "/proc" filesystem is mounted. It
+ * defaults to the value "/proc". Note, this is a global value and not
+ * considered thread safe to write - so the client should take
+ * suitable care when changing it. Further, libcap will allocate
+ * memory for storing the replacement root, and it is this memory that
+ * is returned. So, when changing the value, the caller should
+ * cap_free(the-return-value) when done with it.
+ *
+ * A return value of NULL implies the default is in effect "/proc".
+ */
+char *cap_proc_root(const char *root)
+{
+ char *old = _cap_proc_dir;
+ if (root != NULL) {
+ _cap_proc_dir = _libcap_strdup(root);
+ }
+ return old;
+}
+
+#define PROC_LINE_MAX (8 + 8*_LIBCAP_CAPABILITY_U32S + 100)
+/*
+ * cap_iab_get_pid fills an IAB tuple from the content of
+ * /proc/<pid>/status. Linux doesn't support syscall access to the
+ * needed information, so we parse it out of that file.
+ */
+cap_iab_t cap_iab_get_pid(pid_t pid)
+{
+ cap_iab_t iab;
+ char *path;
+ FILE *file;
+ char line[PROC_LINE_MAX];
+ const char *proc_root = _cap_proc_dir;
+
+ if (proc_root == NULL) {
+ proc_root = "/proc";
+ }
+ if (asprintf(&path, "%s/%d/status", proc_root, pid) <= 0) {
+ return NULL;
+ }
+ file = fopen(path, "r");
+ free(path);
+ if (file == NULL) {
+ return NULL;
+ }
+
+ iab = cap_iab_init();
+ uint ok = 0;
+ if (iab != NULL) {
+ while (fgets(line, PROC_LINE_MAX-1, file) != NULL) {
+ if (strncmp("Cap", line, 3) != 0) {
+ continue;
+ }
+ if (strncmp("Inh:\t", line+3, 5) == 0) {
+ ok = (_parse_vec_string(iab->i, line+8, 0) &
+ LIBCAP_IAB_I_FLAG) | ok;
+ continue;
+ }
+ if (strncmp("Bnd:\t", line+3, 5) == 0) {
+ ok = (_parse_vec_string(iab->nb, line+8, 1) &
+ LIBCAP_IAB_NB_FLAG) | ok;
+ continue;
+ }
+ if (strncmp("Amb:\t", line+3, 5) == 0) {
+ ok = (_parse_vec_string(iab->a, line+8, 0) &
+ LIBCAP_IAB_A_FLAG) | ok;
+ continue;
+ }
+ }
+ }
+ if (ok != (LIBCAP_IAB_IA_FLAG | LIBCAP_IAB_NB_FLAG)) {
+ cap_free(iab);
+ iab = NULL;
+ }
+ fclose(file);
+ return iab;
+}