diff options
author | Andrew G. Morgan <morgan@kernel.org> | 2021-08-22 20:58:04 -0700 |
---|---|---|
committer | Andrew G. Morgan <morgan@kernel.org> | 2021-08-22 21:13:56 -0700 |
commit | 4f45bcc83545efdb4ffc5b9c05e1dbabe196339d (patch) | |
tree | b72863f7b40f62a16b9c1446a4bf33d6e1e2f9a9 /libcap | |
parent | 596850bf55899c0217aa53fcff99491fbecdc2b2 (diff) | |
download | libcap-4f45bcc83545efdb4ffc5b9c05e1dbabe196339d.tar.gz |
Add cap_iab_{compare,get_pid} functions to libcap; --iab to getpcaps.
This brings libcap back to parity with the Go 'cap' package. We
provide a CAP_IAB_DIFFERS(result, vector) macro to evaluate the result
of cap_iab_compare().
Extend the getpcaps arguments to include --iab. This causes the utility
to explore the IAB tuple for the specified process. When used, this
outputs a text representation in a similar format to that of the
'captree' (Go) utility.
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
Diffstat (limited to 'libcap')
-rw-r--r-- | libcap/cap_file.c | 2 | ||||
-rw-r--r-- | libcap/cap_flag.c | 23 | ||||
-rw-r--r-- | libcap/cap_text.c | 103 | ||||
-rw-r--r-- | libcap/include/sys/capability.h | 3 |
4 files changed, 126 insertions, 5 deletions
diff --git a/libcap/cap_file.c b/libcap/cap_file.c index 84ae3e1..95c0572 100644 --- a/libcap/cap_file.c +++ b/libcap/cap_file.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1997,2007,2016 Andrew G Morgan <morgan@kernel.org> * - * This file deals with setting capabilities on files. + * This file deals with getting/setting capabilities from/on files. */ #ifndef _DEFAULT_SOURCE diff --git a/libcap/cap_flag.c b/libcap/cap_flag.c index 51799b0..1f561f7 100644 --- a/libcap/cap_flag.c +++ b/libcap/cap_flag.c @@ -65,13 +65,10 @@ int cap_set_flag(cap_t cap_d, cap_flag_t set, } } return 0; - } else { - _cap_debug("invalid arguments"); errno = EINVAL; return -1; - } } @@ -283,3 +280,23 @@ int cap_iab_fill(cap_iab_t iab, cap_iab_vector_t vec, return 0; } + +/* + * cap_iab_compare compares two iab tuples. + */ +int cap_iab_compare(cap_iab_t a, cap_iab_t b) +{ + int j, result; + if (!(good_cap_iab_t(a) && good_cap_iab_t(b))) { + _cap_debug("invalid arguments"); + errno = EINVAL; + return -1; + } + for (j=0, result=0; j<_LIBCAP_CAPABILITY_U32S; j++) { + result |= + (a->i[j] == b->i[j] ? 0 : (1 << CAP_IAB_INH)) | + (a->a[j] == b->a[j] ? 0 : (1 << CAP_IAB_AMB)) | + (a->nb[j] == b->nb[j] ? 0 : (1 << CAP_IAB_BOUND)); + } + return result; +} diff --git a/libcap/cap_text.c b/libcap/cap_text.c index 25c5ef5..17072f7 100644 --- a/libcap/cap_text.c +++ b/libcap/cap_text.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-8,2007-8,2019 Andrew G Morgan <morgan@kernel.org> + * 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 @@ -609,3 +609,104 @@ cleanup: 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; +} + +#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]; + + if (asprintf(&path, "/proc/%d/status", 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; +} diff --git a/libcap/include/sys/capability.h b/libcap/include/sys/capability.h index d172ddc..f98da5a 100644 --- a/libcap/include/sys/capability.h +++ b/libcap/include/sys/capability.h @@ -119,6 +119,8 @@ extern int cap_fill(cap_t, cap_flag_t, cap_flag_t); #define CAP_DIFFERS(result, flag) (((result) & (1 << (flag))) != 0) extern int cap_compare(cap_t, cap_t); +#define CAP_IAB_DIFFERS(result, vector) (((result) & (1 << (vector))) != 0) +extern int cap_iab_compare(cap_iab_t, cap_iab_t); extern cap_flag_value_t cap_iab_get_vector(cap_iab_t, cap_iab_vector_t, cap_value_t); @@ -185,6 +187,7 @@ extern int cap_setuid(uid_t uid); extern int cap_setgroups(gid_t gid, size_t ngroups, const gid_t groups[]); extern cap_iab_t cap_iab_get_proc(void); +extern cap_iab_t cap_iab_get_pid(pid_t); extern int cap_iab_set_proc(cap_iab_t iab); typedef struct cap_launch_s *cap_launch_t; |