aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2021-08-22 20:58:04 -0700
committerAndrew G. Morgan <morgan@kernel.org>2021-08-22 21:13:56 -0700
commit4f45bcc83545efdb4ffc5b9c05e1dbabe196339d (patch)
treeb72863f7b40f62a16b9c1446a4bf33d6e1e2f9a9
parent596850bf55899c0217aa53fcff99491fbecdc2b2 (diff)
downloadlibcap-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/Makefile3
-rw-r--r--doc/cap_iab.318
-rw-r--r--doc/cap_iab_compare.31
-rw-r--r--doc/cap_iab_get_pid.31
-rw-r--r--doc/getpcaps.87
-rw-r--r--libcap/cap_file.c2
-rw-r--r--libcap/cap_flag.c23
-rw-r--r--libcap/cap_text.c103
-rw-r--r--libcap/include/sys/capability.h3
-rw-r--r--progs/getpcaps.c22
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);
}