summaryrefslogtreecommitdiff
path: root/libcap
diff options
context:
space:
mode:
authorAndrew G. Morgan <morgan@kernel.org>2021-06-19 13:20:10 -0700
committerAndrew G. Morgan <morgan@kernel.org>2021-06-19 13:20:41 -0700
commit1a5a67b48cbcb83a968f973b2207c1d4b833a6dd (patch)
tree82c51031043f978ae0b4826fae0801a0b3ae586f /libcap
parent071efa09e906a3d6928b49778b1a28ad7c0db5be (diff)
downloadlibcap2-1a5a67b48cbcb83a968f973b2207c1d4b833a6dd.tar.gz
Stabilize exporting capabilities into a comparable external format
A desire for this stabilization came out of a conversation with a Google colleague, Mike Schilling. This commit unifies the default libcap/cap (Go) behavior with libcap2 with respect to the binary output of cap.Export(). Previously, libcap/cap.Export() could generate shorter binary values. [To restore that behavior, set libcap/cap.MinExtFlagSize = 0.] Looking to some point in the distant future, this also prepares libcap to retain the same exported binary representation for capabilities defineable today... That is, if the kernel were to extend the capability flags to be 96 bits, but a capability of "cap_chown=ep" would not touch any of the higher bits, an 'exported' capability in external format should not need to change. (Setting libcap/cap.MinExtFlagSize = 0, cap.Export() => 8 bytes for this capability set in external format. However, libcap and libcap/cap now both default to 29 bytes. That is libcap2 has the more significant legacy footprint.) In all cases, libcap/cap and libcap2 were previously interoperable, being able to import each others exported format. This remains true [independent of the setting of libcap/cap.MinExtFlagSize]. Addresses this bug: https://bugzilla.kernel.org/show_bug.cgi?id=213375 Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
Diffstat (limited to 'libcap')
-rw-r--r--libcap/cap_extint.c100
-rw-r--r--libcap/include/sys/capability.h7
2 files changed, 91 insertions, 16 deletions
diff --git a/libcap/cap_extint.c b/libcap/cap_extint.c
index 7d6e7ad..bf0967b 100644
--- a/libcap/cap_extint.c
+++ b/libcap/cap_extint.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997-8 Andrew G Morgan <morgan@kernel.org>
+ * Copyright (c) 1997-8,2021 Andrew G. Morgan <morgan@kernel.org>
*
* This file deals with exchanging internal and external
* representations of capability sets.
@@ -15,6 +15,11 @@
#define CAP_EXT_MAGIC_SIZE 4
const static __u8 external_magic[CAP_EXT_MAGIC_SIZE+1] = CAP_EXT_MAGIC;
+/*
+ * This is the largest size libcap can currently export.
+ * cap_size() may return something smaller depending on the
+ * content of its argument cap_t.
+ */
struct cap_ext_struct {
__u8 magic[CAP_EXT_MAGIC_SIZE];
__u8 length_of_capset;
@@ -26,11 +31,44 @@ struct cap_ext_struct {
};
/*
- * return size of external capability set
+ * minimum exported flag size: libcap2 has always exported with flags
+ * this size.
*/
+static size_t _libcap_min_ext_flag_size = CAP_SET_SIZE < 8 ? CAP_SET_SIZE : 8;
-ssize_t cap_size(cap_t caps)
+/*
+ * return size of external capability set
+ */
+ssize_t cap_size(cap_t cap_d)
{
+ if (good_cap_t(cap_d)) {
+ size_t j, used;
+ for (j=used=0; j<CAP_SET_SIZE; j+=sizeof(__u32)) {
+ int i;
+ __u32 val = 0;
+ for (i=0; i<NUMBER_OF_CAP_SETS; ++i) {
+ val |= cap_d->u[j/sizeof(__u32)].flat[i];
+ }
+ if (val == 0) {
+ continue;
+ }
+ if (val > 0x0000ffff) {
+ if (val > 0x00ffffff) {
+ used = j+4;
+ } else {
+ used = j+3;
+ }
+ } else if (val > 0x000000ff) {
+ used = j+2;
+ } else {
+ used = j+1;
+ }
+ }
+ if (used < _libcap_min_ext_flag_size) {
+ used = _libcap_min_ext_flag_size;
+ }
+ return (ssize_t)(CAP_EXT_MAGIC_SIZE + 1+ NUMBER_OF_CAP_SETS * used);
+ }
return ssizeof(struct cap_ext_struct);
}
@@ -43,42 +81,57 @@ ssize_t cap_size(cap_t caps)
ssize_t cap_copy_ext(void *cap_ext, cap_t cap_d, ssize_t length)
{
struct cap_ext_struct *result = (struct cap_ext_struct *) cap_ext;
+ ssize_t csz, len_set;
int i;
/* valid arguments? */
- if (!good_cap_t(cap_d) || length < ssizeof(struct cap_ext_struct)
- || cap_ext == NULL) {
+ if (!good_cap_t(cap_d) || cap_ext == NULL) {
errno = EINVAL;
return -1;
}
+ csz = cap_size(cap_d);
+ if (csz > length) {
+ errno = EINVAL;
+ return -1;
+ }
+ len_set = (csz - (CAP_EXT_MAGIC_SIZE+1))/NUMBER_OF_CAP_SETS;
+
/* fill external capability set */
memcpy(&result->magic, external_magic, CAP_EXT_MAGIC_SIZE);
- result->length_of_capset = CAP_SET_SIZE;
+ result->length_of_capset = len_set;
for (i=0; i<NUMBER_OF_CAP_SETS; ++i) {
size_t j;
- for (j=0; j<CAP_SET_SIZE; ) {
+ for (j=0; j<len_set; ) {
__u32 val;
val = cap_d->u[j/sizeof(__u32)].flat[i];
- result->bytes[j++][i] = val & 0xFF;
- result->bytes[j++][i] = (val >>= 8) & 0xFF;
- result->bytes[j++][i] = (val >>= 8) & 0xFF;
- result->bytes[j++][i] = (val >> 8) & 0xFF;
+ result->bytes[j++][i] = val & 0xFF;
+ if (j < len_set) {
+ result->bytes[j++][i] = (val >>= 8) & 0xFF;
+ }
+ if (j < len_set) {
+ result->bytes[j++][i] = (val >>= 8) & 0xFF;
+ }
+ if (j < len_set) {
+ result->bytes[j++][i] = (val >> 8) & 0xFF;
+ }
}
}
/* All done: return length of external representation */
- return (ssizeof(struct cap_ext_struct));
+ return csz;
}
/*
* Import an external representation to produce an internal rep.
* the internal rep should be liberated with cap_free().
+ *
+ * Note, this function assumes that cap_ext has a valid length. That
+ * is, feeding garbage to this function will likely crash the program.
*/
-
cap_t cap_copy_int(const void *cap_ext)
{
const struct cap_ext_struct *export =
@@ -121,3 +174,24 @@ cap_t cap_copy_int(const void *cap_ext)
return cap_d;
}
+/*
+ * This function is the same as cap_copy_int() although it requires an
+ * extra argument that is the length of the cap_ext data. Before
+ * running cap_copy_int() the function validates that length is
+ * consistent with the stated length. It returns NULL on error.
+ */
+cap_t cap_copy_int_check(const void *cap_ext, ssize_t length)
+{
+ const struct cap_ext_struct *export =
+ (const struct cap_ext_struct *) cap_ext;
+
+ if (length < 1+CAP_EXT_MAGIC_SIZE) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (length < 1+CAP_EXT_MAGIC_SIZE + export->length_of_capset * NUMBER_OF_CAP_SETS) {
+ errno = EINVAL;
+ return NULL;
+ }
+ return cap_copy_int(cap_ext);
+}
diff --git a/libcap/include/sys/capability.h b/libcap/include/sys/capability.h
index 300fd3c..17cdcd5 100644
--- a/libcap/include/sys/capability.h
+++ b/libcap/include/sys/capability.h
@@ -145,9 +145,10 @@ extern int cap_reset_ambient(void);
#define CAP_AMBIENT_SUPPORTED() (cap_get_ambient(CAP_CHOWN) >= 0)
/* libcap/cap_extint.c */
-extern ssize_t cap_size(cap_t);
-extern ssize_t cap_copy_ext(void *, cap_t, ssize_t);
-extern cap_t cap_copy_int(const void *);
+extern ssize_t cap_size(cap_t cap_d);
+extern ssize_t cap_copy_ext(void *cap_ext, cap_t cap_d, ssize_t length);
+extern cap_t cap_copy_int(const void *cap_ext);
+extern cap_t cap_copy_int_check(const void *cap_ext, ssize_t length);
/* libcap/cap_text.c */
extern cap_t cap_from_text(const char *);