Coverage Report

Created: 2026-03-21 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}