1 /*
2 * Copyright (c) 1997-8,2007-8,2019,2021 Andrew G Morgan <[email protected]>
3 * Copyright (c) 1997 Andrew Main <[email protected]>
4 *
5 * This file deals with exchanging internal and textual
6 * representations of capability sets.
7 */
8
9 #ifndef _GNU_SOURCE
10 #define _GNU_SOURCE
11 #endif
12
13 #include <stdio.h>
14
15 #define LIBCAP_PLEASE_INCLUDE_ARRAY
16 #include "libcap.h"
17
18 static char const *_cap_names[__CAP_BITS] = LIBCAP_CAP_NAMES;
19
20 #include <ctype.h>
21 #include <limits.h>
22
23 #ifdef INCLUDE_GPERF_OUTPUT
24 /* we need to include it after #define _GNU_SOURCE is set */
25 #include INCLUDE_GPERF_OUTPUT
26 #endif
27
28 /* Maximum output text length */
29 #define CAP_TEXT_SIZE (__CAP_NAME_SIZE * __CAP_MAXBITS)
30
31 /*
32 * Parse a textual representation of capabilities, returning an internal
33 * representation.
34 */
35
36 #define raise_cap_mask(flat, c) (flat)[CAP_TO_INDEX(c)] |= CAP_TO_MASK(c)
37
setbits(cap_t a,const __u32 * b,cap_flag_t set,unsigned blks)38 static void setbits(cap_t a, const __u32 *b, cap_flag_t set, unsigned blks)
39 {
40 int n;
41 for (n = blks; n--; ) {
42 a->u[n].flat[set] |= b[n];
43 }
44 }
45
clrbits(cap_t a,const __u32 * b,cap_flag_t set,unsigned blks)46 static void clrbits(cap_t a, const __u32 *b, cap_flag_t set, unsigned blks)
47 {
48 int n;
49 for (n = blks; n--; )
50 a->u[n].flat[set] &= ~b[n];
51 }
52
namcmp(char const * str,char const * nam)53 static char const *namcmp(char const *str, char const *nam)
54 {
55 while (*nam && tolower((unsigned char)*str) == *nam) {
56 str++;
57 nam++;
58 }
59 if (*nam || isalnum((unsigned char)*str) || *str == '_')
60 return NULL;
61 return str;
62 }
63
64 /*
65 * forceall forces all of the kernel named capabilities to be assigned
66 * the masked value, and zeroed otherwise. Note, if the kernel is ahead
67 * of libcap, the upper bits will be referred to by number.
68 */
forceall(__u32 * flat,__u32 value,unsigned blks)69 static void forceall(__u32 *flat, __u32 value, unsigned blks)
70 {
71 unsigned n;
72 cap_value_t cmb = cap_max_bits();
73 for (n = blks; n--; ) {
74 unsigned base = 32*n;
75 __u32 mask = 0;
76 if (cmb >= base + 32) {
77 mask = ~0;
78 } else if (cmb > base) {
79 mask = (unsigned) ((1ULL << (cmb % 32)) - 1);
80 }
81 flat[n] = value & mask;
82 }
83
84 return;
85 }
86
lookupname(char const ** strp)87 static int lookupname(char const **strp)
88 {
89 union {
90 char const *constp;
91 char *p;
92 } str;
93
94 str.constp = *strp;
95 if (isdigit(*str.constp)) {
96 unsigned long n = strtoul(str.constp, &str.p, 0);
97 if (n >= __CAP_MAXBITS)
98 return -1;
99 *strp = str.constp;
100 return n;
101 } else {
102 int c;
103 size_t len;
104
105 for (len=0; (c = str.constp[len]); ++len) {
106 if (!(isalpha(c) || (c == '_'))) {
107 break;
108 }
109 }
110
111 #ifdef GPERF_DOWNCASE
112 const struct __cap_token_s *token_info;
113
114 token_info = __cap_lookup_name(str.constp, len);
115 if (token_info != NULL) {
116 *strp = str.constp + len;
117 return token_info->index;
118 }
119 #else /* ie., ndef GPERF_DOWNCASE */
120 char const *s;
121 unsigned n = cap_max_bits();
122 if (n > __CAP_BITS) {
123 n = __CAP_BITS;
124 }
125 while (n--) {
126 if (_cap_names[n] && (s = namcmp(str.constp, _cap_names[n]))) {
127 *strp = s;
128 return n;
129 }
130 }
131 #endif /* def GPERF_DOWNCASE */
132
133 return -1; /* No definition available */
134 }
135 }
136
cap_from_text(const char * str)137 cap_t cap_from_text(const char *str)
138 {
139 cap_t res;
140 int n;
141 unsigned cap_blks;
142
143 if (str == NULL) {
144 _cap_debug("bad argument");
145 errno = EINVAL;
146 return NULL;
147 }
148
149 if (!(res = cap_init()))
150 return NULL;
151
152 switch (res->head.version) {
153 case _LINUX_CAPABILITY_VERSION_1:
154 cap_blks = _LINUX_CAPABILITY_U32S_1;
155 break;
156 case _LINUX_CAPABILITY_VERSION_2:
157 cap_blks = _LINUX_CAPABILITY_U32S_2;
158 break;
159 case _LINUX_CAPABILITY_VERSION_3:
160 cap_blks = _LINUX_CAPABILITY_U32S_3;
161 break;
162 default:
163 cap_free(res);
164 errno = EINVAL;
165 return NULL;
166 }
167
168 _cap_debug("%s", str);
169
170 for (;;) {
171 __u32 list[__CAP_BLKS];
172 char op;
173 int flags = 0, listed=0;
174
175 memset(list, 0, sizeof(__u32)*__CAP_BLKS);
176
177 /* skip leading spaces */
178 while (isspace((unsigned char)*str))
179 str++;
180 if (!*str) {
181 _cap_debugcap("e = ", *res, CAP_EFFECTIVE);
182 _cap_debugcap("i = ", *res, CAP_INHERITABLE);
183 _cap_debugcap("p = ", *res, CAP_PERMITTED);
184
185 return res;
186 }
187
188 /* identify caps specified by this clause */
189 if (isalnum((unsigned char)*str) || *str == '_') {
190 for (;;) {
191 if (namcmp(str, "all")) {
192 str += 3;
193 forceall(list, ~0, cap_blks);
194 } else {
195 n = lookupname(&str);
196 if (n == -1)
197 goto bad;
198 raise_cap_mask(list, n);
199 }
200 if (*str != ',')
201 break;
202 if (!isalnum((unsigned char)*++str) && *str != '_')
203 goto bad;
204 }
205 listed = 1;
206 } else if (*str == '+' || *str == '-') {
207 goto bad; /* require a list of capabilities */
208 } else {
209 forceall(list, ~0, cap_blks);
210 }
211
212 /* identify first operation on list of capabilities */
213 op = *str++;
214 if (op == '=' && (*str == '+' || *str == '-')) {
215 if (!listed)
216 goto bad;
217 op = (*str++ == '+' ? 'P':'M'); /* skip '=' and take next op */
218 } else if (op != '+' && op != '-' && op != '=')
219 goto bad;
220
221 /* cycle through list of actions */
222 do {
223 _cap_debug("next char = '%c'", *str);
224 if (*str && !isspace(*str)) {
225 switch (*str++) { /* Effective, Inheritable, Permitted */
226 case 'e':
227 flags |= LIBCAP_EFF;
228 break;
229 case 'i':
230 flags |= LIBCAP_INH;
231 break;
232 case 'p':
233 flags |= LIBCAP_PER;
234 break;
235 default:
236 goto bad;
237 }
238 } else if (op != '=') {
239 _cap_debug("only '=' can be followed by space");
240 goto bad;
241 }
242
243 _cap_debug("how to read?");
244 switch (op) { /* how do we interpret the caps? */
245 case '=':
246 case 'P': /* =+ */
247 case 'M': /* =- */
248 clrbits(res, list, CAP_EFFECTIVE, cap_blks);
249 clrbits(res, list, CAP_PERMITTED, cap_blks);
250 clrbits(res, list, CAP_INHERITABLE, cap_blks);
251 if (op == 'M')
252 goto minus;
253 /* fall through */
254 case '+':
255 if (flags & LIBCAP_EFF)
256 setbits(res, list, CAP_EFFECTIVE, cap_blks);
257 if (flags & LIBCAP_PER)
258 setbits(res, list, CAP_PERMITTED, cap_blks);
259 if (flags & LIBCAP_INH)
260 setbits(res, list, CAP_INHERITABLE, cap_blks);
261 break;
262 case '-':
263 minus:
264 if (flags & LIBCAP_EFF)
265 clrbits(res, list, CAP_EFFECTIVE, cap_blks);
266 if (flags & LIBCAP_PER)
267 clrbits(res, list, CAP_PERMITTED, cap_blks);
268 if (flags & LIBCAP_INH)
269 clrbits(res, list, CAP_INHERITABLE, cap_blks);
270 break;
271 }
272
273 /* new directive? */
274 if (*str == '+' || *str == '-') {
275 if (!listed) {
276 _cap_debug("for + & - must list capabilities");
277 goto bad;
278 }
279 flags = 0; /* reset the flags */
280 op = *str++;
281 if (!isalpha(*str))
282 goto bad;
283 }
284 } while (*str && !isspace(*str));
285 _cap_debug("next clause");
286 }
287
288 bad:
289 cap_free(res);
290 res = NULL;
291 errno = EINVAL;
292 return res;
293 }
294
295 /*
296 * lookup a capability name and return its numerical value
297 */
cap_from_name(const char * name,cap_value_t * value_p)298 int cap_from_name(const char *name, cap_value_t *value_p)
299 {
300 int n;
301
302 if (((n = lookupname(&name)) >= 0) && (value_p != NULL)) {
303 *value_p = (unsigned) n;
304 }
305 return -(n < 0);
306 }
307
308 /*
309 * Convert a single capability index number into a string representation
310 */
cap_to_name(cap_value_t cap)311 char *cap_to_name(cap_value_t cap)
312 {
313 char *tmp, *result;
314
315 if ((cap >= 0) && (cap < __CAP_BITS)) {
316 return _libcap_strdup(_cap_names[cap]);
317 }
318 if (asprintf(&tmp, "%u", cap) <= 0) {
319 _cap_debug("asprintf filed");
320 return NULL;
321 }
322
323 result = _libcap_strdup(tmp);
324 free(tmp);
325 return result;
326 }
327
328 /*
329 * Convert an internal representation to a textual one. The textual
330 * representation is stored in static memory. It will be overwritten
331 * on the next occasion that this function is called.
332 */
333
getstateflags(cap_t caps,int capno)334 static int getstateflags(cap_t caps, int capno)
335 {
336 int f = 0;
337
338 if (isset_cap(caps, capno, CAP_EFFECTIVE)) {
339 f |= LIBCAP_EFF;
340 }
341 if (isset_cap(caps, capno, CAP_PERMITTED)) {
342 f |= LIBCAP_PER;
343 }
344 if (isset_cap(caps, capno, CAP_INHERITABLE)) {
345 f |= LIBCAP_INH;
346 }
347
348 return f;
349 }
350
351 /*
352 * This code assumes that the longest named capability is longer than
353 * the decimal text representation of __CAP_MAXBITS. This is very true
354 * at the time of writing and likely to remain so. However, we have
355 * a test in cap_text to validate it at build time.
356 */
357 #define CAP_TEXT_BUFFER_ZONE 100
358
cap_to_text(cap_t caps,ssize_t * length_p)359 char *cap_to_text(cap_t caps, ssize_t *length_p)
360 {
361 char buf[CAP_TEXT_SIZE+CAP_TEXT_BUFFER_ZONE];
362 char *p, *base;
363 int histo[8];
364 int m, t;
365 unsigned n;
366
367 /* Check arguments */
368 if (!good_cap_t(caps)) {
369 errno = EINVAL;
370 return NULL;
371 }
372
373 _cap_debugcap("e = ", *caps, CAP_EFFECTIVE);
374 _cap_debugcap("i = ", *caps, CAP_INHERITABLE);
375 _cap_debugcap("p = ", *caps, CAP_PERMITTED);
376
377 memset(histo, 0, sizeof(histo));
378
379 /* default prevailing state to the named bits */
380 cap_value_t cmb = cap_max_bits();
381 for (n = 0; n < cmb; n++)
382 histo[getstateflags(caps, n)]++;
383
384 /* find which combination of capability sets shares the most bits
385 we bias to preferring non-set (m=0) with the >= 0 test. Failing
386 to do this causes strange things to happen with older systems
387 that don't know about bits 32+. */
388 for (m=t=7; t--; )
389 if (histo[t] >= histo[m])
390 m = t;
391
392 /* blank is not a valid capability set */
393 base = buf;
394 p = sprintf(buf, "=%s%s%s",
395 (m & LIBCAP_EFF) ? "e" : "",
396 (m & LIBCAP_INH) ? "i" : "",
397 (m & LIBCAP_PER) ? "p" : "" ) + buf;
398
399 for (t = 8; t--; ) {
400 if (t == m || !histo[t]) {
401 continue;
402 }
403 *p++ = ' ';
404 for (n = 0; n < cmb; n++) {
405 if (getstateflags(caps, n) == t) {
406 char *this_cap_name = cap_to_name(n);
407 if (this_cap_name == NULL) {
408 return NULL;
409 }
410 if ((strlen(this_cap_name) + (p - buf)) > CAP_TEXT_SIZE) {
411 cap_free(this_cap_name);
412 errno = ERANGE;
413 return NULL;
414 }
415 p += sprintf(p, "%s,", this_cap_name);
416 cap_free(this_cap_name);
417 }
418 }
419 p--;
420 n = t & ~m;
421 if (n) {
422 char op = '+';
423 if (base[0] == '=' && base[1] == ' ') {
424 /*
425 * Special case all lowered default "= foo,...+eip
426 * ..." as "foo,...=eip ...". (Equivalent but shorter.)
427 */
428 base += 2;
429 op = '=';
430 }
431 p += sprintf(p, "%c%s%s%s", op,
432 (n & LIBCAP_EFF) ? "e" : "",
433 (n & LIBCAP_INH) ? "i" : "",
434 (n & LIBCAP_PER) ? "p" : "");
435 }
436 n = ~t & m;
437 if (n) {
438 p += sprintf(p, "-%s%s%s",
439 (n & LIBCAP_EFF) ? "e" : "",
440 (n & LIBCAP_INH) ? "i" : "",
441 (n & LIBCAP_PER) ? "p" : "");
442 }
443 if (p - buf > CAP_TEXT_SIZE) {
444 errno = ERANGE;
445 return NULL;
446 }
447 }
448
449 /* capture remaining unnamed bits - which must all be +. */
450 memset(histo, 0, sizeof(histo));
451 for (n = cmb; n < __CAP_MAXBITS; n++)
452 histo[getstateflags(caps, n)]++;
453
454 for (t = 8; t-- > 1; ) {
455 if (!histo[t]) {
456 continue;
457 }
458 *p++ = ' ';
459 for (n = cmb; n < __CAP_MAXBITS; n++) {
460 if (getstateflags(caps, n) == t) {
461 char *this_cap_name = cap_to_name(n);
462 if (this_cap_name == NULL) {
463 return NULL;
464 }
465 if ((strlen(this_cap_name) + (p - buf)) > CAP_TEXT_SIZE) {
466 cap_free(this_cap_name);
467 errno = ERANGE;
468 return NULL;
469 }
470 p += sprintf(p, "%s,", this_cap_name);
471 cap_free(this_cap_name);
472 }
473 }
474 p--;
475 p += sprintf(p, "+%s%s%s",
476 (t & LIBCAP_EFF) ? "e" : "",
477 (t & LIBCAP_INH) ? "i" : "",
478 (t & LIBCAP_PER) ? "p" : "");
479 if (p - buf > CAP_TEXT_SIZE) {
480 errno = ERANGE;
481 return NULL;
482 }
483 }
484
485 _cap_debug("%s", base);
486 if (length_p) {
487 *length_p = p - base;
488 }
489
490 return (_libcap_strdup(base));
491 }
492
493 /*
494 * cap_mode_name returns a text token naming the specified mode.
495 */
cap_mode_name(cap_mode_t flavor)496 const char *cap_mode_name(cap_mode_t flavor) {
497 switch (flavor) {
498 case CAP_MODE_NOPRIV:
499 return "NOPRIV";
500 case CAP_MODE_PURE1E_INIT:
501 return "PURE1E_INIT";
502 case CAP_MODE_PURE1E:
503 return "PURE1E";
504 case CAP_MODE_UNCERTAIN:
505 return "UNCERTAIN";
506 case CAP_MODE_HYBRID:
507 return "HYBRID";
508 default:
509 return "UNKNOWN";
510 }
511 }
512
513 /*
514 * cap_iab_to_text serializes an iab into a canonical text
515 * representation.
516 */
cap_iab_to_text(cap_iab_t iab)517 char *cap_iab_to_text(cap_iab_t iab)
518 {
519 char buf[CAP_TEXT_SIZE+CAP_TEXT_BUFFER_ZONE];
520 char *p = buf;
521 cap_value_t c, cmb = cap_max_bits();
522 int first = 1;
523
524 if (good_cap_iab_t(iab)) {
525 _cap_mu_lock(&iab->mutex);
526 for (c = 0; c < cmb; c++) {
527 int keep = 0;
528 int o = c >> 5;
529 __u32 bit = 1U << (c & 31);
530 __u32 ib = iab->i[o] & bit;
531 __u32 ab = iab->a[o] & bit;
532 __u32 nbb = iab->nb[o] & bit;
533 if (!(nbb | ab | ib)) {
534 continue;
535 }
536 if (!first) {
537 *p++ = ',';
538 }
539 if (nbb) {
540 *p++ = '!';
541 keep = 1;
542 }
543 if (ab) {
544 *p++ = '^';
545 keep = 1;
546 } else if (nbb && ib) {
547 *p++ = '%';
548 }
549 if (keep || ib) {
550 if (c < __CAP_BITS) {
551 strcpy(p, _cap_names[c]);
552 } else {
553 sprintf(p, "%u", c);
554 }
555 p += strlen(p);
556 first = 0;
557 }
558 }
559 _cap_mu_unlock(&iab->mutex);
560 }
561 *p = '\0';
562 return _libcap_strdup(buf);
563 }
564
cap_iab_from_text(const char * text)565 cap_iab_t cap_iab_from_text(const char *text)
566 {
567 cap_iab_t iab = cap_iab_init();
568 if (iab == NULL) {
569 return iab;
570 }
571 if (text != NULL) {
572 unsigned flags;
573 for (flags = 0; *text; text++) {
574 /* consume prefixes */
575 switch (*text) {
576 case '!':
577 flags |= LIBCAP_IAB_NB_FLAG;
578 continue;
579 case '^':
580 flags |= LIBCAP_IAB_IA_FLAG;
581 continue;
582 case '%':
583 flags |= LIBCAP_IAB_I_FLAG;
584 continue;
585 default:
586 break;
587 }
588 if (!flags) {
589 flags = LIBCAP_IAB_I_FLAG;
590 }
591
592 /* consume cap name */
593 cap_value_t c = lookupname(&text);
594 if (c == -1) {
595 goto cleanup;
596 }
597 unsigned o = c >> 5;
598 __u32 mask = 1U << (c & 31);
599 if (flags & LIBCAP_IAB_I_FLAG) {
600 iab->i[o] |= mask;
601 }
602 if (flags & LIBCAP_IAB_A_FLAG) {
603 iab->a[o] |= mask;
604 }
605 if (flags & LIBCAP_IAB_NB_FLAG) {
606 iab->nb[o] |= mask;
607 }
608
609 /* rest should be end or comma */
610 if (*text == '\0') {
611 break;
612 }
613 if (*text != ',') {
614 goto cleanup;
615 }
616 flags = 0;
617 }
618 }
619 return iab;
620
621 cleanup:
622 cap_free(iab);
623 errno = EINVAL;
624 return NULL;
625 }
626
_parse_hex32(const char * c)627 static __u32 _parse_hex32(const char *c)
628 {
629 int i;
630 __u32 v = 0;
631 for (i=0; i < 8; i++, c++) {
632 v <<= 4;
633 if (*c == 0 || *c < '0') {
634 return 0;
635 } else if (*c <= '9') {
636 v += *c - '0';
637 } else if (*c > 'f') {
638 return 0;
639 } else if (*c >= 'a') {
640 v += *c + 10 - 'a';
641 } else if (*c < 'A') {
642 return 0;
643 } else if (*c <= 'F') {
644 v += *c + 10 - 'A';
645 } else {
646 return 0;
647 }
648 }
649 return v;
650 }
651
652 /*
653 * _parse_vec_string converts the hex dumps in /proc/<pid>/current into
654 * an array of u32s - masked as per the forceall() mask.
655 */
_parse_vec_string(__u32 * vals,const char * c,int invert)656 static __u32 _parse_vec_string(__u32 *vals, const char *c, int invert)
657 {
658 int i;
659 int words = strlen(c)/8;
660 if (words > _LIBCAP_CAPABILITY_U32S) {
661 return 0;
662 }
663 forceall(vals, ~0, words);
664 for (i = 0; i < words; i++) {
665 __u32 val = _parse_hex32(c+8*(words-1-i));
666 if (invert) {
667 val = ~val;
668 }
669 vals[i] &= val;
670 }
671 return ~0;
672 }
673
674 /*
675 * libcap believes this is the root of the mounted "/proc"
676 * filesystem. (NULL == "/proc".)
677 */
678 static char *_cap_proc_dir;
679
680 /*
681 * If the constructor is called (see cap_alloc.c) then we'll need the
682 * corresponding destructor.
683 */
_cleanup_libcap(void)684 __attribute__((destructor (300))) static void _cleanup_libcap(void)
685 {
686 if (_cap_proc_dir == NULL) {
687 return;
688 }
689 cap_free(_cap_proc_dir);
690 _cap_proc_dir = NULL;
691 }
692
693 /*
694 * cap_proc_root reads and (optionally: when root != NULL) changes
695 * libcap's notion of where the "/proc" filesystem is mounted. It
696 * defaults to the value "/proc". Note, this is a global value and not
697 * considered thread safe to write - so the client should take
698 * suitable care when changing it. Further, libcap will allocate
699 * memory for storing the replacement root, and it is this memory that
700 * is returned. So, when changing the value, the caller should
701 * cap_free(the-return-value) when done with it.
702 *
703 * A return value of NULL implies the default is in effect "/proc".
704 */
cap_proc_root(const char * root)705 char *cap_proc_root(const char *root)
706 {
707 char *old = _cap_proc_dir;
708 if (root != NULL) {
709 _cap_proc_dir = _libcap_strdup(root);
710 }
711 return old;
712 }
713
714 #define PROC_LINE_MAX (8 + 8*_LIBCAP_CAPABILITY_U32S + 100)
715 /*
716 * cap_iab_get_pid fills an IAB tuple from the content of
717 * /proc/<pid>/status. Linux doesn't support syscall access to the
718 * needed information, so we parse it out of that file.
719 */
cap_iab_get_pid(pid_t pid)720 cap_iab_t cap_iab_get_pid(pid_t pid)
721 {
722 cap_iab_t iab;
723 char *path;
724 FILE *file;
725 char line[PROC_LINE_MAX];
726 const char *proc_root = _cap_proc_dir;
727
728 if (proc_root == NULL) {
729 proc_root = "/proc";
730 }
731 if (asprintf(&path, "%s/%d/status", proc_root, pid) <= 0) {
732 return NULL;
733 }
734 file = fopen(path, "r");
735 free(path);
736 if (file == NULL) {
737 return NULL;
738 }
739
740 iab = cap_iab_init();
741 uint ok = 0;
742 if (iab != NULL) {
743 while (fgets(line, PROC_LINE_MAX-1, file) != NULL) {
744 if (strncmp("Cap", line, 3) != 0) {
745 continue;
746 }
747 if (strncmp("Inh:\t", line+3, 5) == 0) {
748 ok = (_parse_vec_string(iab->i, line+8, 0) &
749 LIBCAP_IAB_I_FLAG) | ok;
750 continue;
751 }
752 if (strncmp("Bnd:\t", line+3, 5) == 0) {
753 ok = (_parse_vec_string(iab->nb, line+8, 1) &
754 LIBCAP_IAB_NB_FLAG) | ok;
755 continue;
756 }
757 if (strncmp("Amb:\t", line+3, 5) == 0) {
758 ok = (_parse_vec_string(iab->a, line+8, 0) &
759 LIBCAP_IAB_A_FLAG) | ok;
760 continue;
761 }
762 }
763 }
764 if (ok != (LIBCAP_IAB_IA_FLAG | LIBCAP_IAB_NB_FLAG)) {
765 cap_free(iab);
766 iab = NULL;
767 }
768 fclose(file);
769 return iab;
770 }
771