• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 1997-8,2021 Andrew G. Morgan <morgan@kernel.org>
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 
39 /*
40  * return size of external capability set
41  */
cap_size(cap_t cap_d)42 ssize_t cap_size(cap_t cap_d)
43 {
44     if (good_cap_t(cap_d)) {
45 	size_t j, used;
46 	for (j=used=0; j<CAP_SET_SIZE; j+=sizeof(__u32)) {
47 	    int i;
48 	    __u32 val = 0;
49 	    for (i=0; i<NUMBER_OF_CAP_SETS; ++i) {
50 		val |= cap_d->u[j/sizeof(__u32)].flat[i];
51 	    }
52 	    if (val == 0) {
53 		continue;
54 	    }
55 	    if (val > 0x0000ffff) {
56 		if (val > 0x00ffffff) {
57 		    used = j+4;
58 		} else {
59 		    used = j+3;
60 		}
61 	    } else if (val > 0x000000ff) {
62 		used = j+2;
63 	    } else {
64 		used = j+1;
65 	    }
66 	}
67 	if (used < _libcap_min_ext_flag_size) {
68 	    used = _libcap_min_ext_flag_size;
69 	}
70 	return (ssize_t)(CAP_EXT_MAGIC_SIZE + 1+ NUMBER_OF_CAP_SETS * used);
71     }
72     return ssizeof(struct cap_ext_struct);
73 }
74 
75 /*
76  * Copy the internal (cap_d) capability set into an external
77  * representation.  The external representation is portable to other
78  * Linux architectures.
79  */
80 
cap_copy_ext(void * cap_ext,cap_t cap_d,ssize_t length)81 ssize_t cap_copy_ext(void *cap_ext, cap_t cap_d, ssize_t length)
82 {
83     struct cap_ext_struct *result = (struct cap_ext_struct *) cap_ext;
84     ssize_t csz, len_set;
85     int i;
86 
87     /* valid arguments? */
88     if (!good_cap_t(cap_d) || cap_ext == NULL) {
89 	errno = EINVAL;
90 	return -1;
91     }
92 
93     csz = cap_size(cap_d);
94     if (csz > length) {
95 	errno = EINVAL;
96 	return -1;
97     }
98     len_set = (csz - (CAP_EXT_MAGIC_SIZE+1))/NUMBER_OF_CAP_SETS;
99 
100     /* fill external capability set */
101     memcpy(&result->magic, external_magic, CAP_EXT_MAGIC_SIZE);
102     result->length_of_capset = len_set;
103 
104     for (i=0; i<NUMBER_OF_CAP_SETS; ++i) {
105 	size_t j;
106 	for (j=0; j<len_set; ) {
107 	    __u32 val;
108 
109 	    val = cap_d->u[j/sizeof(__u32)].flat[i];
110 
111 	    result->bytes[j++][i] =      val        & 0xFF;
112 	    if (j < len_set) {
113 		result->bytes[j++][i] = (val >>= 8) & 0xFF;
114 	    }
115 	    if (j < len_set) {
116 		result->bytes[j++][i] = (val >>= 8) & 0xFF;
117 	    }
118 	    if (j < len_set) {
119 		result->bytes[j++][i] = (val >> 8)  & 0xFF;
120 	    }
121 	}
122     }
123 
124     /* All done: return length of external representation */
125     return csz;
126 }
127 
128 /*
129  * Import an external representation to produce an internal rep.
130  * the internal rep should be liberated with cap_free().
131  *
132  * Note, this function assumes that cap_ext has a valid length. That
133  * is, feeding garbage to this function will likely crash the program.
134  */
cap_copy_int(const void * cap_ext)135 cap_t cap_copy_int(const void *cap_ext)
136 {
137     const struct cap_ext_struct *export =
138 	(const struct cap_ext_struct *) cap_ext;
139     cap_t cap_d;
140     int set, blen;
141 
142     /* Does the external representation make sense? */
143     if ((export == NULL)
144 	|| memcmp(export->magic, external_magic, CAP_EXT_MAGIC_SIZE)) {
145 	errno = EINVAL;
146 	return NULL;
147     }
148 
149     /* Obtain a new internal capability set */
150     if (!(cap_d = cap_init()))
151        return NULL;
152 
153     blen = export->length_of_capset;
154     for (set=0; set<NUMBER_OF_CAP_SETS; ++set) {
155 	unsigned blk;
156 	int bno = 0;
157 	for (blk=0; blk<(CAP_SET_SIZE/sizeof(__u32)); ++blk) {
158 	    __u32 val = 0;
159 
160 	    if (bno != blen)
161 		val  = export->bytes[bno++][set];
162 	    if (bno != blen)
163 		val |= export->bytes[bno++][set] << 8;
164 	    if (bno != blen)
165 		val |= export->bytes[bno++][set] << 16;
166 	    if (bno != blen)
167 		val |= export->bytes[bno++][set] << 24;
168 
169 	    cap_d->u[blk].flat[set] = val;
170 	}
171     }
172 
173     /* all done */
174     return cap_d;
175 }
176 
177 /*
178  * This function is the same as cap_copy_int() although it requires an
179  * extra argument that is the length of the cap_ext data. Before
180  * running cap_copy_int() the function validates that length is
181  * consistent with the stated length. It returns NULL on error.
182  */
cap_copy_int_check(const void * cap_ext,ssize_t length)183 cap_t cap_copy_int_check(const void *cap_ext, ssize_t length)
184 {
185     const struct cap_ext_struct *export =
186 	(const struct cap_ext_struct *) cap_ext;
187 
188     if (length < 1+CAP_EXT_MAGIC_SIZE) {
189 	errno = EINVAL;
190 	return NULL;
191     }
192     if (length < 1+CAP_EXT_MAGIC_SIZE + export->length_of_capset * NUMBER_OF_CAP_SETS) {
193 	errno = EINVAL;
194 	return NULL;
195     }
196     return cap_copy_int(cap_ext);
197 }
198