Home | History | Annotate | Download | only in libcap
      1 /*
      2  * Copyright (c) 1997-8,2007-8 Andrew G Morgan <morgan (at) kernel.org>
      3  * Copyright (c) 1997 Andrew Main <zefram (at) 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 
     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 
     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 
     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 
     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 
     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 
    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  */
    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  */
    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 
    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 
    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