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 | |
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>
-rw-r--r-- | doc/Makefile | 3 | ||||
-rw-r--r-- | doc/cap_iab.3 | 18 | ||||
-rw-r--r-- | doc/cap_iab_compare.3 | 1 | ||||
-rw-r--r-- | doc/cap_iab_get_pid.3 | 1 | ||||
-rw-r--r-- | doc/getpcaps.8 | 7 | ||||
-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 | ||||
-rw-r--r-- | progs/getpcaps.c | 22 |
10 files changed, 175 insertions, 8 deletions
diff --git a/doc/Makefile b/doc/Makefile index a34cee0..943dbfa 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -21,7 +21,8 @@ MAN3S = cap_init.3 cap_free.3 cap_dup.3 \ cap_launcher_set_chroot.3 cap_launcher_set_mode.3 \ cap_launcher_setgroups.3 cap_launcher_setuid.3 \ cap_launcher_set_iab.3 cap_new_launcher.3 \ - cap_iab.3 cap_iab_init.3 cap_iab_get_proc.3 cap_iab_set_proc.3 \ + cap_iab.3 cap_iab_init.3 cap_iab_compare.3 \ + cap_iab_get_proc.3 cap_iab_get_pid.3 cap_iab_set_proc.3 \ cap_iab_to_text.3 cap_iab_from_text.3 cap_iab_get_vector.3 \ cap_iab_set_vector.3 cap_iab_fill.3 \ psx_syscall.3 psx_syscall3.3 psx_syscall6.3 libpsx.3 diff --git a/doc/cap_iab.3 b/doc/cap_iab.3 index a453428..7e87a0f 100644 --- a/doc/cap_iab.3 +++ b/doc/cap_iab.3 @@ -7,6 +7,8 @@ cap_iab_t cap_iab_init(void); cap_iab_t cap_iab_get_proc(void); +cap_iab_t cap_iab_get_pid(pid_t pid); + int cap_iab_set_proc(cap_iab_t iab); char *cap_iab_to_text(cap_iab_t iab); @@ -16,6 +18,8 @@ cap_iab_t cap_iab_from_text(const char *text); cap_flag_value_t cap_iab_get_vector(cap_iab_t iab, cap_iab_vector_t vec, cap_value_t val); +int cap_iab_compare(cap_iab_t a, cap_iab_t b); + int cap_iab_set_vector(cap_iab_t iab, cap_iab_vector_t vec, cap_value_t val, cap_flag_value_t enable); @@ -75,6 +79,11 @@ returns a copy of the IAB value for the current process. The returned cap_iab_t should be freed with .BR cap_free (3). .sp +.BR cap_iab_get_pid () +returns a copy of the IAB value for the specified process. The returned +cap_iab_t should be freed with +.BR cap_free (3). +.sp .BR cap_iab_set_proc () can be used to set the IAB value carried by the current process. Such a setting will fail if the process is insufficiently capable. The @@ -107,6 +116,15 @@ for the \fIpam_cap.so\fP config file. can be used to determine the specific capability value of an IAB vector. .sp +.BR cap_iab_compare () +can be used to compare two cap_iab_t tuples. When the return value is +non-zero, the macro +.B CAP_IAB_DIFFERS +.RI ( status ", " vector ) +evaluates to non-zero if the returned status differs in its +.I vector +components. +.sp .BR cap_iab_set_vector () can be used to set a specific vector value to the enable setting. .BR cap_iab_fill () diff --git a/doc/cap_iab_compare.3 b/doc/cap_iab_compare.3 new file mode 100644 index 0000000..3e730b1 --- /dev/null +++ b/doc/cap_iab_compare.3 @@ -0,0 +1 @@ +.so man3/cap_iab.3 diff --git a/doc/cap_iab_get_pid.3 b/doc/cap_iab_get_pid.3 new file mode 100644 index 0000000..3e730b1 --- /dev/null +++ b/doc/cap_iab_get_pid.3 @@ -0,0 +1 @@ +.so man3/cap_iab.3 diff --git a/doc/getpcaps.8 b/doc/getpcaps.8 index d519357..3926a8c 100644 --- a/doc/getpcaps.8 +++ b/doc/getpcaps.8 @@ -33,11 +33,18 @@ Displays output in a somewhat ugly legacy format. .B \-\-verbose Displays usage in a legacy-like format but not quite so ugly in modern default terminal fonts. +.TP +.B \-\-iab +Displays IAB tuple capabilities from the process. The output format +here is unique. Double quotes encase the regular process capabilities +and square brackets encase the IAB tuple. .SH SEE ALSO .BR capsh (1), .BR capabilities (7), .BR getcap (8), .BR setcap (8) +and +.BR cap_iab (3). .SH AUTHOR This manual page was originally written by Robert Bihlmeyer <robbe@debian.org>, for the Debian GNU/Linux system (but may be used 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; diff --git a/progs/getpcaps.c b/progs/getpcaps.c index 5e78487..3cd3fa0 100644 --- a/progs/getpcaps.c +++ b/progs/getpcaps.c @@ -22,6 +22,7 @@ static void usage(int code) " --help, -h or --usage display this message.\n" " --verbose use a more verbose output format.\n" " --ugly or --legacy use the archaic legacy output format.\n" + " --iab show IAB of process too.\n" " --license display license info\n"); exit(code); } @@ -30,13 +31,13 @@ int main(int argc, char **argv) { int retval = 0; int verbose = 0; + int iab = 0; if (argc < 2) { usage(1); } for ( ++argv; --argc > 0; ++argv ) { - ssize_t length; int pid; cap_t cap_d; @@ -55,6 +56,9 @@ int main(int argc, char **argv) } else if (!strcmp(argv[0], "--ugly") || !strcmp(argv[0], "--legacy")) { verbose = 2; continue; + } else if (!strcmp(argv[0], "--iab")) { + iab = 1; + continue; } pid = atoi(argv[0]); @@ -66,11 +70,25 @@ int main(int argc, char **argv) retval = 1; continue; } else { - char *result = cap_to_text(cap_d, &length); + char *result = cap_to_text(cap_d, NULL); if (verbose == 1) { printf("Capabilities for '%s': %s\n", *argv, result); } else if (verbose == 2) { fprintf(stderr, "Capabilities for `%s': %s\n", *argv, result); + } else if (iab) { + cap_iab_t iab_val = cap_iab_get_pid(pid); + if (iab_val == NULL) { + fprintf(stderr, "no IAB value for %d\n", pid); + exit(1); + } + char *iab_text = cap_iab_to_text(iab_val); + if (iab_text == NULL) { + perror("no text for IAB"); + exit(1); + } + printf("%s: \"%s\" [%s]\n", *argv, result, iab_text); + cap_free(iab_text); + cap_free(iab_val); } else { printf("%s: %s\n", *argv, result); } |