diff options
Diffstat (limited to 'libcap')
-rw-r--r-- | libcap/cap_extint.c | 100 | ||||
-rw-r--r-- | libcap/include/sys/capability.h | 7 |
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 *); |