xref: /aosp_15_r20/external/libcap/libcap/cap_extint.c (revision 2810ac1b38eead2603277920c78344c84ddf3aff)
1 /*
2  * Copyright (c) 1997-8,2021 Andrew G. Morgan <[email protected]>
3  *
4  * This file deals with exchanging internal and external
5  * representations of capability sets.
6  */
7 
8 #include "libcap.h"
9 
10 /*
11  * External representation for capabilities. (exported as a fixed
12  * length)
13  */
14 #define CAP_EXT_MAGIC "\220\302\001\121"
15 #define CAP_EXT_MAGIC_SIZE 4
16 const static __u8 external_magic[CAP_EXT_MAGIC_SIZE+1] = CAP_EXT_MAGIC;
17 
18 /*
19  * This is the largest size libcap can currently export.
20  * cap_size() may return something smaller depending on the
21  * content of its argument cap_t.
22  */
23 struct cap_ext_struct {
24     __u8 magic[CAP_EXT_MAGIC_SIZE];
25     __u8 length_of_capset;
26     /*
27      * note, we arrange these so the caps are stacked with byte-size
28      * resolution
29      */
30     __u8 bytes[CAP_SET_SIZE][NUMBER_OF_CAP_SETS];
31 };
32 
33 /*
34  * minimum exported flag size: libcap2 has always exported with flags
35  * this size.
36  */
37 static size_t _libcap_min_ext_flag_size = CAP_SET_SIZE < 8 ? CAP_SET_SIZE : 8;
38 
_cap_size_locked(cap_t cap_d)39 static ssize_t _cap_size_locked(cap_t cap_d)
40 {
41     size_t j, used;
42     for (j=used=0; j<CAP_SET_SIZE; j+=sizeof(__u32)) {
43 	int i;
44 	__u32 val = 0;
45 	for (i=0; i<NUMBER_OF_CAP_SETS; ++i) {
46 	    val |= cap_d->u[j/sizeof(__u32)].flat[i];
47 	}
48 	if (val == 0) {
49 	    continue;
50 	}
51 	if (val > 0x0000ffff) {
52 	    if (val > 0x00ffffff) {
53 		used = j+4;
54 	    } else {
55 		used = j+3;
56 	    }
57 	} else if (val > 0x000000ff) {
58 	    used = j+2;
59 	} else {
60 	    used = j+1;
61 	}
62     }
63     if (used < _libcap_min_ext_flag_size) {
64 	used = _libcap_min_ext_flag_size;
65     }
66     return (ssize_t)(CAP_EXT_MAGIC_SIZE + 1+ NUMBER_OF_CAP_SETS * used);
67 }
68 
69 /*
70  * return size of external capability set
71  */
cap_size(cap_t cap_d)72 ssize_t cap_size(cap_t cap_d)
73 {
74     size_t used;
75     if (!good_cap_t(cap_d)) {
76 	return ssizeof(struct cap_ext_struct);
77     }
78     _cap_mu_lock(&cap_d->mutex);
79     used = _cap_size_locked(cap_d);
80     _cap_mu_unlock(&cap_d->mutex);
81     return used;
82 }
83 
84 /*
85  * Copy the internal (cap_d) capability set into an external
86  * representation.  The external representation is portable to other
87  * Linux architectures.
88  */
89 
cap_copy_ext(void * cap_ext,cap_t cap_d,ssize_t length)90 ssize_t cap_copy_ext(void *cap_ext, cap_t cap_d, ssize_t length)
91 {
92     struct cap_ext_struct *result = (struct cap_ext_struct *) cap_ext;
93     ssize_t csz, len_set;
94     int i;
95 
96     /* valid arguments? */
97     if (!good_cap_t(cap_d) || cap_ext == NULL) {
98 	errno = EINVAL;
99 	return -1;
100     }
101 
102     _cap_mu_lock(&cap_d->mutex);
103     csz = _cap_size_locked(cap_d);
104     if (csz > length) {
105 	errno = EINVAL;
106 	_cap_mu_unlock_return(&cap_d->mutex, -1);
107     }
108     len_set = (csz - (CAP_EXT_MAGIC_SIZE+1))/NUMBER_OF_CAP_SETS;
109 
110     /* fill external capability set */
111     memcpy(&result->magic, external_magic, CAP_EXT_MAGIC_SIZE);
112     result->length_of_capset = len_set;
113 
114     for (i=0; i<NUMBER_OF_CAP_SETS; ++i) {
115 	size_t j;
116 	for (j=0; j<len_set; ) {
117 	    __u32 val;
118 
119 	    val = cap_d->u[j/sizeof(__u32)].flat[i];
120 
121 	    result->bytes[j++][i] =      val        & 0xFF;
122 	    if (j < len_set) {
123 		result->bytes[j++][i] = (val >>= 8) & 0xFF;
124 	    }
125 	    if (j < len_set) {
126 		result->bytes[j++][i] = (val >>= 8) & 0xFF;
127 	    }
128 	    if (j < len_set) {
129 		result->bytes[j++][i] = (val >> 8)  & 0xFF;
130 	    }
131 	}
132     }
133 
134     /* All done: return length of external representation */
135     _cap_mu_unlock_return(&cap_d->mutex, csz);
136 }
137 
138 /*
139  * Import an external representation to produce an internal rep.
140  * the internal rep should be liberated with cap_free().
141  *
142  * Note, this function assumes that cap_ext has a valid length. That
143  * is, feeding garbage to this function will likely crash the program.
144  */
cap_copy_int(const void * cap_ext)145 cap_t cap_copy_int(const void *cap_ext)
146 {
147     const struct cap_ext_struct *export =
148 	(const struct cap_ext_struct *) cap_ext;
149     cap_t cap_d;
150     int set, blen;
151 
152     /* Does the external representation make sense? */
153     if ((export == NULL)
154 	|| memcmp(export->magic, external_magic, CAP_EXT_MAGIC_SIZE)) {
155 	errno = EINVAL;
156 	return NULL;
157     }
158 
159     /* Obtain a new internal capability set */
160     if (!(cap_d = cap_init()))
161        return NULL;
162 
163     blen = export->length_of_capset;
164     for (set=0; set<NUMBER_OF_CAP_SETS; ++set) {
165 	unsigned blk;
166 	int bno = 0;
167 	for (blk=0; blk<(CAP_SET_SIZE/sizeof(__u32)); ++blk) {
168 	    __u32 val = 0;
169 
170 	    if (bno != blen)
171 		val  = export->bytes[bno++][set];
172 	    if (bno != blen)
173 		val |= export->bytes[bno++][set] << 8;
174 	    if (bno != blen)
175 		val |= export->bytes[bno++][set] << 16;
176 	    if (bno != blen)
177 		val |= export->bytes[bno++][set] << 24;
178 
179 	    cap_d->u[blk].flat[set] = val;
180 	}
181     }
182 
183     /* all done */
184     return cap_d;
185 }
186 
187 /*
188  * This function is the same as cap_copy_int() although it requires an
189  * extra argument that is the length of the cap_ext data. Before
190  * running cap_copy_int() the function validates that length is
191  * consistent with the stated length. It returns NULL on error.
192  */
cap_copy_int_check(const void * cap_ext,ssize_t length)193 cap_t cap_copy_int_check(const void *cap_ext, ssize_t length)
194 {
195     const struct cap_ext_struct *export =
196 	(const struct cap_ext_struct *) cap_ext;
197 
198     if (length < 1+CAP_EXT_MAGIC_SIZE) {
199 	errno = EINVAL;
200 	return NULL;
201     }
202     if (length < 1+CAP_EXT_MAGIC_SIZE + export->length_of_capset * NUMBER_OF_CAP_SETS) {
203 	errno = EINVAL;
204 	return NULL;
205     }
206     return cap_copy_int(cap_ext);
207 }
208