/* * Copyright (c) 1997-8,2021 Andrew G. Morgan * * This file deals with exchanging internal and external * representations of capability sets. */ #include "libcap.h" /* * External representation for capabilities. (exported as a fixed * length) */ #define CAP_EXT_MAGIC "\220\302\001\121" #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; /* * note, we arrange these so the caps are stacked with byte-size * resolution */ __u8 bytes[CAP_SET_SIZE][NUMBER_OF_CAP_SETS]; }; /* * 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; static ssize_t _cap_size_locked(cap_t cap_d) { size_t j, used; for (j=used=0; ju[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 size of external capability set */ ssize_t cap_size(cap_t cap_d) { size_t used; if (!good_cap_t(cap_d)) { return ssizeof(struct cap_ext_struct); } _cap_mu_lock(&cap_d->mutex); used = _cap_size_locked(cap_d); _cap_mu_unlock(&cap_d->mutex); return used; } /* * Copy the internal (cap_d) capability set into an external * representation. The external representation is portable to other * Linux architectures. */ 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) || cap_ext == NULL) { errno = EINVAL; return -1; } _cap_mu_lock(&cap_d->mutex); csz = _cap_size_locked(cap_d); if (csz > length) { errno = EINVAL; _cap_mu_unlock_return(&cap_d->mutex, -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 = len_set; for (i=0; iu[j/sizeof(__u32)].flat[i]; 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 */ _cap_mu_unlock_return(&cap_d->mutex, 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 = (const struct cap_ext_struct *) cap_ext; cap_t cap_d; int set, blen; /* Does the external representation make sense? */ if ((export == NULL) || memcmp(export->magic, external_magic, CAP_EXT_MAGIC_SIZE)) { errno = EINVAL; return NULL; } /* Obtain a new internal capability set */ if (!(cap_d = cap_init())) return NULL; blen = export->length_of_capset; for (set=0; setbytes[bno++][set]; if (bno != blen) val |= export->bytes[bno++][set] << 8; if (bno != blen) val |= export->bytes[bno++][set] << 16; if (bno != blen) val |= export->bytes[bno++][set] << 24; cap_d->u[blk].flat[set] = val; } } /* all done */ 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); }