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