/src/selinux/libsepol/src/util.c
Line | Count | Source |
1 | | /* Authors: Joshua Brindle <jbrindle@tresys.com> |
2 | | * Jason Tang <jtang@tresys.com> |
3 | | * |
4 | | * Copyright (C) 2005-2006 Tresys Technology, LLC |
5 | | * |
6 | | * This library is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * This library is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public |
17 | | * License along with this library; if not, write to the Free Software |
18 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
19 | | */ |
20 | | |
21 | | #include <assert.h> |
22 | | #include <ctype.h> |
23 | | #include <stdarg.h> |
24 | | #include <stdio.h> |
25 | | #include <stdlib.h> |
26 | | |
27 | | #include <sepol/policydb/flask_types.h> |
28 | | #include <sepol/policydb/policydb.h> |
29 | | #include <sepol/policydb/util.h> |
30 | | |
31 | | #include "private.h" |
32 | | |
33 | | struct val_to_name { |
34 | | unsigned int val; |
35 | | const char *name; |
36 | | }; |
37 | | |
38 | | /* Add an unsigned integer to a dynamically reallocated array. *cnt |
39 | | * is a reference pointer to the number of values already within array |
40 | | * *a; it will be incremented upon successfully appending i. If *a is |
41 | | * NULL then this function will create a new array (*cnt is reset to |
42 | | * 0). Return 0 on success, -1 on out of memory. */ |
43 | | int add_i_to_a(uint32_t i, uint32_t * cnt, uint32_t ** a) |
44 | 12.0k | { |
45 | 12.0k | uint32_t *new; |
46 | | |
47 | 12.0k | if (cnt == NULL || *cnt == UINT32_MAX || a == NULL) |
48 | 0 | return -1; |
49 | | |
50 | | /* FIX ME: This is not very elegant! We use an array that we |
51 | | * grow as new uint32_t are added to an array. But rather |
52 | | * than be smart about it, for now we realloc() the array each |
53 | | * time a new uint32_t is added! */ |
54 | 12.0k | if (*a != NULL) |
55 | 0 | new = (uint32_t *) reallocarray(*a, *cnt + 1, sizeof(uint32_t)); |
56 | 12.0k | else { /* empty list */ |
57 | | |
58 | 12.0k | *cnt = 0; |
59 | 12.0k | new = (uint32_t *) malloc(sizeof(uint32_t)); |
60 | 12.0k | } |
61 | 12.0k | if (new == NULL) { |
62 | 0 | return -1; |
63 | 0 | } |
64 | 12.0k | new[*cnt] = i; |
65 | 12.0k | (*cnt)++; |
66 | 12.0k | *a = new; |
67 | 12.0k | return 0; |
68 | 12.0k | } |
69 | | |
70 | | static int perm_name(hashtab_key_t key, hashtab_datum_t datum, void *data) |
71 | 1.15k | { |
72 | 1.15k | struct val_to_name *v = data; |
73 | 1.15k | perm_datum_t *perdatum; |
74 | | |
75 | 1.15k | perdatum = (perm_datum_t *) datum; |
76 | | |
77 | 1.15k | if (v->val == perdatum->s.value) { |
78 | 780 | v->name = key; |
79 | 780 | return 1; |
80 | 780 | } |
81 | | |
82 | 378 | return 0; |
83 | 1.15k | } |
84 | | |
85 | | char *sepol_av_to_string(const policydb_t *policydbp, sepol_security_class_t tclass, |
86 | | sepol_access_vector_t av) |
87 | 554 | { |
88 | 554 | struct val_to_name v; |
89 | 554 | const class_datum_t *cladatum = policydbp->class_val_to_struct[tclass - 1]; |
90 | 554 | uint32_t i; |
91 | 554 | int rc; |
92 | 554 | char *buffer = NULL, *p; |
93 | 554 | int len; |
94 | 554 | size_t remaining, size = 64; |
95 | | |
96 | 558 | retry: |
97 | 558 | if (__builtin_mul_overflow(size, 2, &size)) |
98 | 0 | goto err; |
99 | 558 | p = realloc(buffer, size); |
100 | 558 | if (!p) |
101 | 0 | goto err; |
102 | 558 | *p = '\0'; /* Just in case there are no permissions */ |
103 | 558 | buffer = p; |
104 | 558 | remaining = size; |
105 | | |
106 | 1.65k | for (i = 0; i < cladatum->permissions.nprim; i++) { |
107 | 1.09k | if (av & (UINT32_C(1) << i)) { |
108 | 780 | v.val = i + 1; |
109 | 780 | rc = hashtab_map(cladatum->permissions.table, |
110 | 780 | perm_name, &v); |
111 | 780 | if (!rc && cladatum->comdatum) { |
112 | 22 | rc = hashtab_map(cladatum->comdatum-> |
113 | 22 | permissions.table, perm_name, |
114 | 22 | &v); |
115 | 22 | } |
116 | 780 | if (rc == 1) { |
117 | 780 | len = snprintf(p, remaining, " %s", v.name); |
118 | 780 | if (len < 0) |
119 | 0 | goto err; |
120 | 780 | if ((size_t) len >= remaining) |
121 | 4 | goto retry; |
122 | 776 | p += len; |
123 | 776 | remaining -= len; |
124 | 776 | } |
125 | 780 | } |
126 | 1.09k | } |
127 | | |
128 | 554 | return buffer; |
129 | | |
130 | 0 | err: |
131 | 0 | free(buffer); |
132 | 0 | return NULL; |
133 | 558 | } |
134 | | |
135 | 573k | #define next_bit_in_range(i, p) (((i) + 1 < sizeof(p)*8) && xperm_test(((i) + 1), p)) |
136 | | |
137 | | char *sepol_extended_perms_to_string(const avtab_extended_perms_t *xperms) |
138 | 3.19k | { |
139 | 3.19k | uint16_t value; |
140 | 3.19k | uint16_t low_bit; |
141 | 3.19k | uint16_t low_value; |
142 | 3.19k | unsigned int bit; |
143 | 3.19k | unsigned int in_range; |
144 | 3.19k | char *buffer = NULL, *p; |
145 | 3.19k | int len; |
146 | 3.19k | size_t remaining, size = 128; |
147 | | |
148 | 3.19k | if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION) |
149 | 2.13k | && (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER) |
150 | 792 | && (xperms->specified != AVTAB_XPERMS_NLMSG)) |
151 | 0 | return NULL; |
152 | | |
153 | 6.30k | retry: |
154 | 6.30k | if (__builtin_mul_overflow(size, 2, &size)) |
155 | 0 | goto err; |
156 | 6.30k | p = realloc(buffer, size); |
157 | 6.30k | if (!p) |
158 | 0 | goto err; |
159 | 6.30k | buffer = p; |
160 | 6.30k | remaining = size; |
161 | | |
162 | 6.30k | if ((xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION) |
163 | 4.86k | || (xperms->specified == AVTAB_XPERMS_IOCTLDRIVER)) { |
164 | 4.86k | len = snprintf(p, remaining, "ioctl { "); |
165 | 4.86k | } else { |
166 | 1.43k | len = snprintf(p, remaining, "nlmsg { "); |
167 | 1.43k | } |
168 | 6.30k | if (len < 0 || (size_t)len >= remaining) |
169 | 0 | goto err; |
170 | 6.30k | p += len; |
171 | 6.30k | remaining -= len; |
172 | | |
173 | 6.30k | in_range = 0; |
174 | 1.33M | for (bit = 0; bit < sizeof(xperms->perms)*8; bit++) { |
175 | 1.33M | if (!xperm_test(bit, xperms->perms)) |
176 | 849k | continue; |
177 | | |
178 | 484k | if (in_range && next_bit_in_range(bit, xperms->perms)) { |
179 | | /* continue until high value found */ |
180 | 209k | continue; |
181 | 275k | } else if (next_bit_in_range(bit, xperms->perms)) { |
182 | | /* low value */ |
183 | 88.6k | low_bit = bit; |
184 | 88.6k | in_range = 1; |
185 | 88.6k | continue; |
186 | 88.6k | } |
187 | | |
188 | 186k | if (xperms->specified == AVTAB_XPERMS_IOCTLFUNCTION || xperms->specified == AVTAB_XPERMS_NLMSG) { |
189 | 103k | value = xperms->driver<<8 | bit; |
190 | 103k | if (in_range) { |
191 | 50.7k | low_value = xperms->driver<<8 | low_bit; |
192 | 50.7k | len = snprintf(p, remaining, "0x%hx-0x%hx ", low_value, value); |
193 | 52.6k | } else { |
194 | 52.6k | len = snprintf(p, remaining, "0x%hx ", value); |
195 | 52.6k | } |
196 | 103k | } else if (xperms->specified == AVTAB_XPERMS_IOCTLDRIVER) { |
197 | 83.3k | value = bit << 8; |
198 | 83.3k | if (in_range) { |
199 | 37.9k | low_value = low_bit << 8; |
200 | 37.9k | len = snprintf(p, remaining, "0x%hx-0x%hx ", low_value, (uint16_t) (value|0xff)); |
201 | 45.3k | } else { |
202 | 45.3k | len = snprintf(p, remaining, "0x%hx-0x%hx ", value, (uint16_t) (value|0xff)); |
203 | 45.3k | } |
204 | | |
205 | 83.3k | } |
206 | | |
207 | 186k | if (len < 0) |
208 | 0 | goto err; |
209 | 186k | if ((size_t) len >= remaining) |
210 | 3.07k | goto retry; |
211 | | |
212 | 183k | p += len; |
213 | 183k | remaining -= len; |
214 | 183k | if (in_range) |
215 | 86.9k | in_range = 0; |
216 | 183k | } |
217 | | |
218 | 3.23k | len = snprintf(p, remaining, "}"); |
219 | 3.23k | if (len < 0) |
220 | 0 | goto err; |
221 | 3.23k | if ((size_t) len >= remaining) |
222 | 32 | goto retry; |
223 | | |
224 | 3.19k | return buffer; |
225 | | |
226 | 0 | err: |
227 | 0 | free(buffer); |
228 | 0 | return NULL; |
229 | 3.23k | } |
230 | | |
231 | | /* |
232 | | * The tokenize and tokenize_str functions may be used to |
233 | | * replace sscanf to read tokens from buffers. |
234 | | */ |
235 | | |
236 | | /* Read a token from a buffer */ |
237 | | static inline int tokenize_str(char delim, char **str, const char **ptr, size_t *len) |
238 | 0 | { |
239 | 0 | const char *tmp_buf = *ptr; |
240 | 0 | *str = NULL; |
241 | |
|
242 | 0 | while (**ptr != '\0') { |
243 | 0 | if (isspace(delim) && isspace(**ptr)) { |
244 | 0 | (*ptr)++; |
245 | 0 | break; |
246 | 0 | } else if (!isspace(delim) && **ptr == delim) { |
247 | 0 | (*ptr)++; |
248 | 0 | break; |
249 | 0 | } |
250 | | |
251 | 0 | (*ptr)++; |
252 | 0 | } |
253 | |
|
254 | 0 | *len = *ptr - tmp_buf; |
255 | | /* If the end of the string has not been reached, this will ensure the |
256 | | * delimiter is not included when returning the token. |
257 | | */ |
258 | 0 | if (**ptr != '\0') { |
259 | 0 | (*len)--; |
260 | 0 | } |
261 | |
|
262 | 0 | *str = strndup(tmp_buf, *len); |
263 | 0 | if (!*str) { |
264 | 0 | return -1; |
265 | 0 | } |
266 | | |
267 | | /* Squash spaces if the delimiter is a whitespace character */ |
268 | 0 | while (**ptr != '\0' && isspace(delim) && isspace(**ptr)) { |
269 | 0 | (*ptr)++; |
270 | 0 | } |
271 | |
|
272 | 0 | return 0; |
273 | 0 | } |
274 | | |
275 | | /* |
276 | | * line_buf - Buffer containing string to tokenize. |
277 | | * delim - The delimiter used to tokenize line_buf. A whitespace delimiter will |
278 | | * be tokenized using isspace(). |
279 | | * num_args - The number of parameter entries to process. |
280 | | * ... - A 'char **' for each parameter. |
281 | | * returns - The number of items processed. |
282 | | * |
283 | | * This function calls tokenize_str() to do the actual string processing. The |
284 | | * caller is responsible for calling free() on each additional argument. The |
285 | | * function will not tokenize more than num_args and the last argument will |
286 | | * contain the remaining content of line_buf. If the delimiter is any whitespace |
287 | | * character, then all whitespace will be squashed. |
288 | | */ |
289 | | int tokenize(const char *line_buf, char delim, int num_args, ...) |
290 | 0 | { |
291 | 0 | char **arg; |
292 | 0 | const char *buf_p; |
293 | 0 | int rc, items; |
294 | 0 | size_t arg_len = 0; |
295 | 0 | va_list ap; |
296 | |
|
297 | 0 | buf_p = line_buf; |
298 | | |
299 | | /* Process the arguments */ |
300 | 0 | va_start(ap, num_args); |
301 | |
|
302 | 0 | for (items = 0; items < num_args && *buf_p != '\0'; items++) { |
303 | 0 | arg = va_arg(ap, char **); |
304 | | |
305 | | /* Save the remainder of the string in arg */ |
306 | 0 | if (items == num_args - 1) { |
307 | 0 | *arg = strdup(buf_p); |
308 | 0 | if (*arg == NULL) { |
309 | 0 | goto exit; |
310 | 0 | } |
311 | | |
312 | 0 | continue; |
313 | 0 | } |
314 | | |
315 | 0 | rc = tokenize_str(delim, arg, &buf_p, &arg_len); |
316 | 0 | if (rc < 0) { |
317 | 0 | goto exit; |
318 | 0 | } |
319 | 0 | } |
320 | | |
321 | 0 | exit: |
322 | | va_end(ap); |
323 | 0 | return items; |
324 | 0 | } |