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#include <dso.h>
31
32struct val_to_name {
33	unsigned int val;
34	char *name;
35};
36
37/* Add an unsigned integer to a dynamically reallocated array.  *cnt
38 * is a reference pointer to the number of values already within array
39 * *a; it will be incremented upon successfully appending i.  If *a is
40 * NULL then this function will create a new array (*cnt is reset to
41 * 0).  Return 0 on success, -1 on out of memory. */
42int add_i_to_a(uint32_t i, uint32_t * cnt, uint32_t ** a)
43{
44	if (cnt == NULL || a == NULL)
45		return -1;
46
47	/* FIX ME: This is not very elegant! We use an array that we
48	 * grow as new uint32_t are added to an array.  But rather
49	 * than be smart about it, for now we realloc() the array each
50	 * time a new uint32_t is added! */
51	if (*a != NULL)
52		*a = (uint32_t *) realloc(*a, (*cnt + 1) * sizeof(uint32_t));
53	else {			/* empty list */
54
55		*cnt = 0;
56		*a = (uint32_t *) malloc(sizeof(uint32_t));
57	}
58	if (*a == NULL) {
59		return -1;
60	}
61	(*a)[*cnt] = i;
62	(*cnt)++;
63	return 0;
64}
65
66static int perm_name(hashtab_key_t key, hashtab_datum_t datum, void *data)
67{
68	struct val_to_name *v = data;
69	perm_datum_t *perdatum;
70
71	perdatum = (perm_datum_t *) datum;
72
73	if (v->val == perdatum->s.value) {
74		v->name = key;
75		return 1;
76	}
77
78	return 0;
79}
80
81char *sepol_av_to_string(policydb_t * policydbp, uint32_t tclass,
82			 sepol_access_vector_t av)
83{
84	struct val_to_name v;
85	static char avbuf[1024];
86	class_datum_t *cladatum;
87	char *perm = NULL, *p;
88	unsigned int i;
89	int rc;
90	int avlen = 0, len;
91
92	memset(avbuf, 0, sizeof avbuf);
93	cladatum = policydbp->class_val_to_struct[tclass - 1];
94	p = avbuf;
95	for (i = 0; i < cladatum->permissions.nprim; i++) {
96		if (av & (1 << i)) {
97			v.val = i + 1;
98			rc = hashtab_map(cladatum->permissions.table,
99					 perm_name, &v);
100			if (!rc && cladatum->comdatum) {
101				rc = hashtab_map(cladatum->comdatum->
102						 permissions.table, perm_name,
103						 &v);
104			}
105			if (rc)
106				perm = v.name;
107			if (perm) {
108				len =
109				    snprintf(p, sizeof(avbuf) - avlen, " %s",
110					     perm);
111				if (len < 0
112				    || (size_t) len >= (sizeof(avbuf) - avlen))
113					return NULL;
114				p += len;
115				avlen += len;
116			}
117		}
118	}
119
120	return avbuf;
121}
122
123#define next_bit_in_range(i, p) ((i + 1 < sizeof(p)*8) && xperm_test((i + 1), p))
124
125char *sepol_extended_perms_to_string(avtab_extended_perms_t *xperms)
126{
127	uint16_t value;
128	uint16_t low_bit;
129	uint16_t low_value;
130	unsigned int bit;
131	unsigned int in_range = 0;
132	static char xpermsbuf[2048];
133	xpermsbuf[0] = '\0';
134	char *p;
135	int len, xpermslen = 0;
136	p = xpermsbuf;
137
138	if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION)
139		&& (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER))
140		return NULL;
141
142	len = snprintf(p, sizeof(xpermsbuf) - xpermslen, "ioctl { ");
143	p += len;
144	xpermslen += len;
145
146	for (bit = 0; bit < sizeof(xperms->perms)*8; bit++) {
147		if (!xperm_test(bit, xperms->perms))
148			continue;
149
150		if (in_range && next_bit_in_range(bit, xperms->perms)) {
151			/* continue until high value found */
152			continue;
153		} else if (next_bit_in_range(bit, xperms->perms)) {
154			/* low value */
155			low_bit = bit;
156			in_range = 1;
157			continue;
158		}
159
160		if (xperms->specified & AVTAB_XPERMS_IOCTLFUNCTION) {
161			value = xperms->driver<<8 | bit;
162			low_value = xperms->driver<<8 | low_bit;
163			if (in_range) {
164				len = snprintf(p, sizeof(xpermsbuf) - xpermslen, "0x%hx-0x%hx ", low_value, value);
165			} else {
166				len = snprintf(p, sizeof(xpermsbuf) - xpermslen, "0x%hx ", value);
167			}
168		} else if (xperms->specified & AVTAB_XPERMS_IOCTLDRIVER) {
169			value = bit << 8;
170			low_value = low_bit << 8;
171			if (in_range) {
172				len = snprintf(p, sizeof(xpermsbuf) - xpermslen, "0x%hx-0x%hx ", low_value, (uint16_t) (value|0xff));
173			} else {
174				len = snprintf(p, sizeof(xpermsbuf) - xpermslen, "0x%hx-0x%hx ", value, (uint16_t) (value|0xff));
175			}
176
177		}
178
179		if (len < 0 || (size_t) len >= (sizeof(xpermsbuf) - xpermslen))
180			return NULL;
181
182		p += len;
183		xpermslen += len;
184		if (in_range)
185			in_range = 0;
186	}
187
188	len = snprintf(p, sizeof(xpermsbuf) - xpermslen, "}");
189	if (len < 0 || (size_t) len >= (sizeof(xpermsbuf) - xpermslen))
190		return NULL;
191
192	return xpermsbuf;
193}
194
195/*
196 * The tokenize and tokenize_str functions may be used to
197 * replace sscanf to read tokens from buffers.
198 */
199
200/* Read a token from a buffer */
201static inline int tokenize_str(char delim, char **str, char **ptr, size_t *len)
202{
203	char *tmp_buf = *ptr;
204	*str = NULL;
205
206	while (**ptr != '\0') {
207		if (isspace(delim) && isspace(**ptr)) {
208			(*ptr)++;
209			break;
210		} else if (!isspace(delim) && **ptr == delim) {
211			(*ptr)++;
212			break;
213		}
214
215		(*ptr)++;
216	}
217
218	*len = *ptr - tmp_buf;
219	/* If the end of the string has not been reached, this will ensure the
220	 * delimiter is not included when returning the token.
221	 */
222	if (**ptr != '\0') {
223		(*len)--;
224	}
225
226	*str = strndup(tmp_buf, *len);
227	if (!*str) {
228		return -1;
229	}
230
231	/* Squash spaces if the delimiter is a whitespace character */
232	while (**ptr != '\0' && isspace(delim) && isspace(**ptr)) {
233		(*ptr)++;
234	}
235
236	return 0;
237}
238
239/*
240 * line_buf - Buffer containing string to tokenize.
241 * delim - The delimiter used to tokenize line_buf. A whitespace delimiter will
242 *	    be tokenized using isspace().
243 * num_args - The number of parameter entries to process.
244 * ...      - A 'char **' for each parameter.
245 * returns  - The number of items processed.
246 *
247 * This function calls tokenize_str() to do the actual string processing. The
248 * caller is responsible for calling free() on each additional argument. The
249 * function will not tokenize more than num_args and the last argument will
250 * contain the remaining content of line_buf. If the delimiter is any whitespace
251 * character, then all whitespace will be squashed.
252 */
253int hidden tokenize(char *line_buf, char delim, int num_args, ...)
254{
255	char **arg, *buf_p;
256	int rc, items;
257	size_t arg_len = 0;
258	va_list ap;
259
260	buf_p = line_buf;
261
262	/* Process the arguments */
263	va_start(ap, num_args);
264
265	for (items = 0; items < num_args && *buf_p != '\0'; items++) {
266		arg = va_arg(ap, char **);
267
268		/* Save the remainder of the string in arg */
269		if (items == num_args - 1) {
270			*arg = strdup(buf_p);
271			if (*arg == NULL) {
272				goto exit;
273			}
274
275			continue;
276		}
277
278		rc = tokenize_str(delim, arg, &buf_p, &arg_len);
279		if (rc < 0) {
280			goto exit;
281		}
282	}
283
284exit:
285	va_end(ap);
286	return items;
287}
288