1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
|
/*
* Copyright (c) 1997-8,2021 Andrew G. Morgan <morgan@kernel.org>
*
* 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; 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 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; i<NUMBER_OF_CAP_SETS; ++i) {
size_t j;
for (j=0; j<len_set; ) {
__u32 val;
val = cap_d->u[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; set<NUMBER_OF_CAP_SETS; ++set) {
unsigned blk;
int bno = 0;
for (blk=0; blk<(CAP_SET_SIZE/sizeof(__u32)); ++blk) {
__u32 val = 0;
if (bno != blen)
val = export->bytes[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);
}
|