summaryrefslogtreecommitdiff
path: root/libcap/cap_flag.c
blob: 1f561f7dd7c025fd04bf7882f005d40abb5de1ff (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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/*
 * Copyright (c) 1997-8,2008,20 Andrew G. Morgan <morgan@kernel.org>
 *
 * This file deals with flipping of capabilities on internal
 * capability sets as specified by POSIX.1e (formerlly, POSIX 6).
 *
 * It also contains similar code for bit flipping cap_iab_t values.
 */

#include "libcap.h"

/*
 * Return the state of a specified capability flag.  The state is
 * returned as the contents of *raised.  The capability is from one of
 * the sets stored in cap_d as specified by set and value
 */

int cap_get_flag(cap_t cap_d, cap_value_t value, cap_flag_t set,
		 cap_flag_value_t *raised)
{
    /*
     * Do we have a set and a place to store its value?
     * Is it a known capability?
     */

    if (raised && good_cap_t(cap_d) && value >= 0 && value < __CAP_MAXBITS
	&& set >= 0 && set < NUMBER_OF_CAP_SETS) {
	*raised = isset_cap(cap_d,value,set) ? CAP_SET:CAP_CLEAR;
	return 0;
    } else {
	_cap_debug("invalid arguments");
	errno = EINVAL;
	return -1;
    }
}

/*
 * raise/lower a selection of capabilities
 */

int cap_set_flag(cap_t cap_d, cap_flag_t set,
		 int no_values, const cap_value_t *array_values,
		 cap_flag_value_t raise)
{
    /*
     * Do we have a set and a place to store its value?
     * Is it a known capability?
     */

    if (good_cap_t(cap_d) && no_values > 0 && no_values < __CAP_MAXBITS
	&& (set >= 0) && (set < NUMBER_OF_CAP_SETS)
	&& (raise == CAP_SET || raise == CAP_CLEAR) ) {
	int i;
	for (i=0; i<no_values; ++i) {
	    if (array_values[i] < 0 || array_values[i] >= __CAP_MAXBITS) {
		_cap_debug("weird capability (%d) - skipped", array_values[i]);
	    } else {
		int value = array_values[i];

		if (raise == CAP_SET) {
		    cap_d->raise_cap(value,set);
		} else {
		    cap_d->lower_cap(value,set);
		}
	    }
	}
	return 0;
    } else {
	_cap_debug("invalid arguments");
	errno = EINVAL;
	return -1;
    }
}

/*
 *  Reset the capability to be empty (nothing raised)
 */

int cap_clear(cap_t cap_d)
{
    if (good_cap_t(cap_d)) {
	memset(&(cap_d->u), 0, sizeof(cap_d->u));
	return 0;
    } else {
	_cap_debug("invalid pointer");
	errno = EINVAL;
	return -1;
    }
}

/*
 *  Reset the all of the capability bits for one of the flag sets
 */

int cap_clear_flag(cap_t cap_d, cap_flag_t flag)
{
    switch (flag) {
    case CAP_EFFECTIVE:
    case CAP_PERMITTED:
    case CAP_INHERITABLE:
	if (good_cap_t(cap_d)) {
	    unsigned i;

	    for (i=0; i<_LIBCAP_CAPABILITY_U32S; i++) {
		cap_d->u[i].flat[flag] = 0;
	    }
	    return 0;
	}
	/*
	 * fall through
	 */

    default:
	_cap_debug("invalid pointer");
	errno = EINVAL;
	return -1;
    }
}

/*
 * Compare two capability sets
 */
int cap_compare(cap_t a, cap_t b)
{
    unsigned i;
    int result;

    if (!(good_cap_t(a) && good_cap_t(b))) {
	_cap_debug("invalid arguments");
	errno = EINVAL;
	return -1;
    }

    for (i=0, result=0; i<_LIBCAP_CAPABILITY_U32S; i++) {
	result |=
	    ((a->u[i].flat[CAP_EFFECTIVE] != b->u[i].flat[CAP_EFFECTIVE])
	     ? LIBCAP_EFF : 0)
	    | ((a->u[i].flat[CAP_INHERITABLE] != b->u[i].flat[CAP_INHERITABLE])
	       ? LIBCAP_INH : 0)
	    | ((a->u[i].flat[CAP_PERMITTED] != b->u[i].flat[CAP_PERMITTED])
	       ? LIBCAP_PER : 0);
    }
    return result;
}

/*
 * cap_fill copies a bit-vector of capability state in a cap_t from
 * one flag to another.
 */
int cap_fill(cap_t cap_d, cap_flag_t to, cap_flag_t from)
{
    if (!good_cap_t(cap_d)) {
	errno = EINVAL;
	return -1;
    }

    if (to < CAP_EFFECTIVE || to > CAP_INHERITABLE ||
	from < CAP_EFFECTIVE || from > CAP_INHERITABLE) {
	errno = EINVAL;
	return -1;
    }

    int i;
    for (i = 0; i < _LIBCAP_CAPABILITY_U32S; i++) {
	cap_d->u[i].flat[to] = cap_d->u[i].flat[from];
    }

    return 0;
}

/*
 * cap_iab_get_vector reads the single bit value from an IAB vector set.
 */
cap_flag_value_t cap_iab_get_vector(cap_iab_t iab, cap_iab_vector_t vec,
				    cap_value_t bit)
{
    if (!good_cap_iab_t(iab) || bit >= cap_max_bits()) {
	return 0;
    }

    unsigned o = (bit >> 5);
    __u32 mask = 1u << (bit & 31);

    switch (vec) {
    case CAP_IAB_INH:
	return !!(iab->i[o] & mask);
	break;
    case CAP_IAB_AMB:
	return !!(iab->a[o] & mask);
	break;
    case CAP_IAB_BOUND:
	return !!(iab->nb[o] & mask);
	break;
    default:
	return 0;
    }
}

/*
 * cap_iab_set_vector sets the bits in an IAB to the value
 * raised. Note, setting A implies setting I too, lowering I implies
 * lowering A too.  The B bits are, however, independently settable.
 */
int cap_iab_set_vector(cap_iab_t iab, cap_iab_vector_t vec, cap_value_t bit,
		       cap_flag_value_t raised)
{
    if (!good_cap_iab_t(iab) || (raised >> 1) || bit >= cap_max_bits()) {
	errno = EINVAL;
	return -1;
    }

    unsigned o = (bit >> 5);
    __u32 on = 1u << (bit & 31);
    __u32 mask = ~on;

    switch (vec) {
    case CAP_IAB_INH:
	iab->i[o] = (iab->i[o] & mask) | (raised ? on : 0);
	iab->a[o] &= iab->i[o];
	break;
    case CAP_IAB_AMB:
	iab->a[o] = (iab->a[o] & mask) | (raised ? on : 0);
	iab->i[o] |= iab->a[o];
	break;
    case CAP_IAB_BOUND:
	iab->nb[o] = (iab->nb[o] & mask) | (raised ? on : 0);
	break;
    default:
	errno = EINVAL;
	return -1;
    }

    return 0;
}

/*
 * cap_iab_fill copies a bit-vector of capability state from a cap_t
 * to a cap_iab_t. Note, because the bounding bits in an iab are to be
 * dropped when applied, the copying process, when to a CAP_IAB_BOUND
 * vector involves inverting the bits. Also, adjusting I will mask
 * bits in A, and adjusting A may implicitly raise bits in I.
 */
int cap_iab_fill(cap_iab_t iab, cap_iab_vector_t vec,
		 cap_t cap_d, cap_flag_t flag)
{
    if (!good_cap_t(cap_d) || !good_cap_iab_t(iab)) {
	errno = EINVAL;
	return -1;
    }

    switch (flag) {
    case CAP_EFFECTIVE:
    case CAP_INHERITABLE:
    case CAP_PERMITTED:
	break;
    default:
	errno = EINVAL;
	return -1;
    }

    int i;
    for (i = 0; i < _LIBCAP_CAPABILITY_U32S; i++) {
	switch (vec) {
	case CAP_IAB_INH:
	    iab->i[i] = cap_d->u[i].flat[flag];
	    iab->a[i] &= iab->i[i];
	    break;
	case CAP_IAB_AMB:
	    iab->a[i] = cap_d->u[i].flat[flag];
	    iab->i[i] |= cap_d->u[i].flat[flag];
	    break;
	case CAP_IAB_BOUND:
	    iab->nb[i] = ~cap_d->u[i].flat[flag];
	    break;
	default:
	    errno = EINVAL;
	    return -1;
	}
    }

    return 0;
}

/*
 * cap_iab_compare compares two iab tuples.
 */
int cap_iab_compare(cap_iab_t a, cap_iab_t b)
{
    int j, result;
    if (!(good_cap_iab_t(a) && good_cap_iab_t(b))) {
	_cap_debug("invalid arguments");
	errno = EINVAL;
	return -1;
    }
    for (j=0, result=0; j<_LIBCAP_CAPABILITY_U32S; j++) {
	result |=
	    (a->i[j] == b->i[j] ? 0 : (1 << CAP_IAB_INH)) |
	    (a->a[j] == b->a[j] ? 0 : (1 << CAP_IAB_AMB)) |
	    (a->nb[j] == b->nb[j] ? 0 : (1 << CAP_IAB_BOUND));
    }
    return result;
}