aboutsummaryrefslogtreecommitdiff
path: root/libcap/cap_extint.c
blob: 462cc65647503017cc7f2ebe7f4378bec66c83bd (plain)
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);
}