summaryrefslogtreecommitdiff
path: root/libcap
diff options
context:
space:
mode:
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 *);