Coverage Report

Created: 2026-02-14 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/selinux/libselinux/src/label.c
Line
Count
Source
1
/*
2
 * Generalized labeling frontend for userspace object managers.
3
 *
4
 * Author : Eamon Walsh <ewalsh@epoch.ncsc.mil>
5
 */
6
7
#include <sys/types.h>
8
#include <ctype.h>
9
#include <errno.h>
10
#include <stdio.h>
11
#include <stdlib.h>
12
#include <string.h>
13
#include <sys/stat.h>
14
#include <selinux/selinux.h>
15
#include "callbacks.h"
16
#include "label_internal.h"
17
18
0
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
19
20
#ifdef NO_MEDIA_BACKEND
21
#define CONFIG_MEDIA_BACKEND(fnptr) NULL
22
#else
23
#define CONFIG_MEDIA_BACKEND(fnptr) &fnptr
24
#endif
25
26
#ifdef NO_X_BACKEND
27
#define CONFIG_X_BACKEND(fnptr) NULL
28
#else
29
#define CONFIG_X_BACKEND(fnptr) &fnptr
30
#endif
31
32
#ifdef NO_DB_BACKEND
33
#define CONFIG_DB_BACKEND(fnptr) NULL
34
#else
35
#define CONFIG_DB_BACKEND(fnptr) &fnptr
36
#endif
37
38
#ifdef NO_ANDROID_BACKEND
39
#define CONFIG_ANDROID_BACKEND(fnptr) NULL
40
#else
41
#define CONFIG_ANDROID_BACKEND(fnptr) (&(fnptr))
42
#endif
43
44
typedef int (*selabel_initfunc)(struct selabel_handle *rec,
45
        const struct selinux_opt *opts,
46
        unsigned nopts);
47
48
static const selabel_initfunc initfuncs[] = {
49
  &selabel_file_init,
50
  CONFIG_MEDIA_BACKEND(selabel_media_init),
51
  CONFIG_X_BACKEND(selabel_x_init),
52
  CONFIG_DB_BACKEND(selabel_db_init),
53
  CONFIG_ANDROID_BACKEND(selabel_property_init),
54
  CONFIG_ANDROID_BACKEND(selabel_service_init),
55
};
56
57
static inline struct selabel_digest *selabel_is_digest_set
58
            (const struct selinux_opt *opts,
59
            unsigned n)
60
0
{
61
0
  struct selabel_digest *digest = NULL;
62
63
0
  while (n) {
64
0
    n--;
65
0
    if (opts[n].type == SELABEL_OPT_DIGEST &&
66
0
              !!opts[n].value) {
67
0
      digest = calloc(1, sizeof(*digest));
68
0
      if (!digest)
69
0
        goto err;
70
71
0
      digest->digest = calloc(1, DIGEST_SPECFILE_SIZE + 1);
72
0
      if (!digest->digest)
73
0
        goto err;
74
75
0
      digest->specfile_list = calloc(DIGEST_FILES_MAX,
76
0
                  sizeof(char *));
77
0
      if (!digest->specfile_list)
78
0
        goto err;
79
80
0
      return digest;
81
0
    }
82
0
  }
83
0
  return NULL;
84
85
0
err:
86
0
  if (digest) {
87
0
    free(digest->digest);
88
0
    free(digest->specfile_list);
89
0
    free(digest);
90
0
  }
91
0
  return NULL;
92
0
}
93
94
static void selabel_digest_fini(struct selabel_digest *ptr)
95
0
{
96
0
  int i;
97
98
0
  free(ptr->digest);
99
0
  free(ptr->hashbuf);
100
101
0
  if (ptr->specfile_list) {
102
0
    for (i = 0; ptr->specfile_list[i]; i++)
103
0
      free(ptr->specfile_list[i]);
104
0
    free(ptr->specfile_list);
105
0
  }
106
0
  free(ptr);
107
0
}
108
109
/*
110
 * Validation functions
111
 */
112
113
static inline int selabel_is_validate_set(const struct selinux_opt *opts,
114
            unsigned n)
115
0
{
116
0
  while (n) {
117
0
    n--;
118
0
    if (opts[n].type == SELABEL_OPT_VALIDATE)
119
0
      return !!opts[n].value;
120
0
  }
121
122
0
  return 0;
123
0
}
124
125
int selabel_validate(struct selabel_lookup_rec *contexts)
126
3.69M
{
127
3.69M
  bool validated;
128
3.69M
  int rc;
129
130
3.69M
  validated = __atomic_load_n(&contexts->validated, __ATOMIC_ACQUIRE);
131
3.69M
  if (validated)
132
0
    return 0;
133
134
3.69M
  __pthread_mutex_lock(&contexts->lock);
135
136
  /* Check if another thread validated the context while we waited on the mutex */
137
3.69M
  validated = __atomic_load_n(&contexts->validated, __ATOMIC_ACQUIRE);
138
3.69M
  if (validated) {
139
0
    __pthread_mutex_unlock(&contexts->lock);
140
0
    return 0;
141
0
  }
142
143
3.69M
  rc = selinux_validate(&contexts->ctx_raw);
144
3.69M
  if (rc == 0)
145
3.69M
    __atomic_store_n(&contexts->validated, true, __ATOMIC_RELEASE);
146
147
3.69M
  __pthread_mutex_unlock(&contexts->lock);
148
149
3.69M
  if (rc < 0)
150
0
    return -1;
151
152
3.69M
  return 0;
153
3.69M
}
154
155
/* Public API helpers */
156
static int selabel_fini(const struct selabel_handle *rec,
157
          struct selabel_lookup_rec *lr,
158
          bool translating)
159
0
{
160
0
  char *ctx_trans;
161
0
  int rc;
162
163
0
  if (compat_validate(rec, lr, rec->spec_file, lr->lineno))
164
0
    return -1;
165
166
0
  if (!translating)
167
0
    return 0;
168
169
0
  ctx_trans = __atomic_load_n(&lr->ctx_trans, __ATOMIC_ACQUIRE);
170
0
  if (ctx_trans)
171
0
    return 0;
172
173
0
  __pthread_mutex_lock(&lr->lock);
174
175
  /* Check if another thread translated the context while we waited on the mutex */
176
0
  ctx_trans = __atomic_load_n(&lr->ctx_trans, __ATOMIC_ACQUIRE);
177
0
  if (ctx_trans) {
178
0
    __pthread_mutex_unlock(&lr->lock);
179
0
    return 0;
180
0
  }
181
182
0
  rc = selinux_raw_to_trans_context(lr->ctx_raw, &ctx_trans);
183
0
  if (rc == 0)
184
0
    __atomic_store_n(&lr->ctx_trans, ctx_trans, __ATOMIC_RELEASE);
185
186
0
  __pthread_mutex_unlock(&lr->lock);
187
188
0
  if (rc)
189
0
    return -1;
190
191
0
  return 0;
192
0
}
193
194
static struct selabel_lookup_rec *
195
selabel_lookup_common(struct selabel_handle *rec, bool translating,
196
          const char *key, int type)
197
0
{
198
0
  struct selabel_lookup_rec *lr;
199
200
0
  if (key == NULL) {
201
0
    errno = EINVAL;
202
0
    return NULL;
203
0
  }
204
205
0
  lr = rec->func_lookup(rec, key, type);
206
0
  if (!lr)
207
0
    return NULL;
208
209
0
  if (selabel_fini(rec, lr, translating))
210
0
    return NULL;
211
212
0
  return lr;
213
0
}
214
215
static struct selabel_lookup_rec *
216
selabel_lookup_bm_common(struct selabel_handle *rec, bool translating,
217
          const char *key, int type, const char **aliases)
218
0
{
219
0
  struct selabel_lookup_rec *lr;
220
221
0
  if (key == NULL) {
222
0
    errno = EINVAL;
223
0
    return NULL;
224
0
  }
225
226
0
  lr = rec->func_lookup_best_match(rec, key, aliases, type);
227
0
  if (!lr)
228
0
    return NULL;
229
230
0
  if (selabel_fini(rec, lr, translating))
231
0
    return NULL;
232
233
0
  return lr;
234
0
}
235
236
/*
237
 * Public API
238
 */
239
240
struct selabel_handle *selabel_open(unsigned int backend,
241
            const struct selinux_opt *opts,
242
            unsigned nopts)
243
0
{
244
0
  struct selabel_handle *rec = NULL;
245
246
0
  if (backend >= ARRAY_SIZE(initfuncs)) {
247
0
    errno = EINVAL;
248
0
    goto out;
249
0
  }
250
251
0
  if (!initfuncs[backend]) {
252
0
    errno = ENOTSUP;
253
0
    goto out;
254
0
  }
255
256
0
  rec = (struct selabel_handle *)calloc(1, sizeof(*rec));
257
0
  if (!rec)
258
0
    goto out;
259
260
0
  rec->backend = backend;
261
0
  rec->validating = selabel_is_validate_set(opts, nopts);
262
263
0
  rec->digest = selabel_is_digest_set(opts, nopts);
264
265
0
  if ((*initfuncs[backend])(rec, opts, nopts)) {
266
0
    selabel_close(rec);
267
0
    rec = NULL;
268
0
  }
269
270
0
out:
271
0
  return rec;
272
0
}
273
274
int selabel_lookup(struct selabel_handle *rec, char **con,
275
       const char *key, int type)
276
0
{
277
0
  struct selabel_lookup_rec *lr;
278
279
0
  lr = selabel_lookup_common(rec, true, key, type);
280
0
  if (!lr)
281
0
    return -1;
282
283
0
  *con = strdup(lr->ctx_trans);
284
0
  return *con ? 0 : -1;
285
0
}
286
287
int selabel_lookup_raw(struct selabel_handle *rec, char **con,
288
           const char *key, int type)
289
0
{
290
0
  struct selabel_lookup_rec *lr;
291
292
0
  lr = selabel_lookup_common(rec, false, key, type);
293
0
  if (!lr)
294
0
    return -1;
295
296
0
  *con = strdup(lr->ctx_raw);
297
0
  return *con ? 0 : -1;
298
0
}
299
300
bool selabel_partial_match(struct selabel_handle *rec, const char *key)
301
0
{
302
0
  if (!rec->func_partial_match) {
303
    /*
304
     * If the label backend does not support partial matching,
305
     * then assume a match is possible.
306
     */
307
0
    return true;
308
0
  }
309
310
0
  return rec->func_partial_match(rec, key);
311
0
}
312
313
bool selabel_get_digests_all_partial_matches(struct selabel_handle *rec,
314
               const char *key,
315
               uint8_t **calculated_digest,
316
               uint8_t **xattr_digest,
317
               size_t *digest_len)
318
0
{
319
0
  if (!rec->func_get_digests_all_partial_matches)
320
0
    return false;
321
322
0
  return rec->func_get_digests_all_partial_matches(rec, key,
323
0
               calculated_digest,
324
0
               xattr_digest,
325
0
               digest_len);
326
0
}
327
328
bool selabel_hash_all_partial_matches(struct selabel_handle *rec,
329
0
                                      const char *key, uint8_t *digest) {
330
0
  if (!rec->func_hash_all_partial_matches) {
331
0
    return false;
332
0
  }
333
334
0
  return rec->func_hash_all_partial_matches(rec, key, digest);
335
0
}
336
337
int selabel_lookup_best_match(struct selabel_handle *rec, char **con,
338
            const char *key, const char **aliases, int type)
339
0
{
340
0
  struct selabel_lookup_rec *lr;
341
342
0
  if (!rec->func_lookup_best_match) {
343
0
    errno = ENOTSUP;
344
0
    return -1;
345
0
  }
346
347
0
  lr = selabel_lookup_bm_common(rec, true, key, type, aliases);
348
0
  if (!lr)
349
0
    return -1;
350
351
0
  *con = strdup(lr->ctx_trans);
352
0
  return *con ? 0 : -1;
353
0
}
354
355
int selabel_lookup_best_match_raw(struct selabel_handle *rec, char **con,
356
            const char *key, const char **aliases, int type)
357
0
{
358
0
  struct selabel_lookup_rec *lr;
359
360
0
  if (!rec->func_lookup_best_match) {
361
0
    errno = ENOTSUP;
362
0
    return -1;
363
0
  }
364
365
0
  lr = selabel_lookup_bm_common(rec, false, key, type, aliases);
366
0
  if (!lr)
367
0
    return -1;
368
369
0
  *con = strdup(lr->ctx_raw);
370
0
  return *con ? 0 : -1;
371
0
}
372
373
enum selabel_cmp_result selabel_cmp(const struct selabel_handle *h1,
374
            const struct selabel_handle *h2)
375
0
{
376
0
  if (!h1->func_cmp || h1->func_cmp != h2->func_cmp)
377
0
    return SELABEL_INCOMPARABLE;
378
379
0
  return h1->func_cmp(h1, h2);
380
0
}
381
382
int selabel_digest(struct selabel_handle *rec,
383
            unsigned char **digest, size_t *digest_len,
384
            char ***specfiles, size_t *num_specfiles)
385
0
{
386
0
  if (!rec->digest) {
387
0
    errno = EINVAL;
388
0
    return -1;
389
0
  }
390
391
0
  *digest = rec->digest->digest;
392
0
  *digest_len = DIGEST_SPECFILE_SIZE;
393
0
  *specfiles = rec->digest->specfile_list;
394
0
  *num_specfiles = rec->digest->specfile_cnt;
395
0
  return 0;
396
0
}
397
398
void selabel_close(struct selabel_handle *rec)
399
0
{
400
0
  if (rec->digest)
401
0
    selabel_digest_fini(rec->digest);
402
0
  rec->func_close(rec);
403
0
  free(rec->spec_file);
404
0
  free(rec);
405
0
}
406
407
void selabel_stats(struct selabel_handle *rec)
408
0
{
409
0
  rec->func_stats(rec);
410
0
}