Coverage Report

Created: 2026-10-17 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/selinux/libsepol/src/context.c
Line
Count
Source
1
#include <stdlib.h>
2
#include <string.h>
3
#include <errno.h>
4
5
#include <sepol/policydb/policydb.h>
6
#include <sepol/policydb/services.h>
7
#include "context_internal.h"
8
9
#include "debug.h"
10
#include "context.h"
11
#include "handle.h"
12
#include "mls.h"
13
#include "private.h"
14
15
/* ----- Compatibility ---- */
16
int policydb_context_isvalid(const policydb_t *p, const context_struct_t *c)
17
7.05k
{
18
7.05k
  return context_is_valid(p, c);
19
7.05k
}
20
21
int sepol_check_context(const char *context)
22
0
{
23
0
  return sepol_context_to_sid(context, strlen(context) + 1, NULL);
24
0
}
25
26
/* ---- End compatibility --- */
27
28
/*
29
 * Return 1 if the fields in the security context
30
 * structure `c' are valid.  Return 0 otherwise.
31
 */
32
int context_is_valid(const policydb_t *p, const context_struct_t *c)
33
7.05k
{
34
7.05k
  role_datum_t *role;
35
7.05k
  user_datum_t *usrdatum;
36
7.05k
  ebitmap_t types, roles;
37
38
7.05k
  ebitmap_init(&types);
39
7.05k
  ebitmap_init(&roles);
40
7.05k
  if (!c->role || c->role > p->p_roles.nprim)
41
59
    return 0;
42
43
6.99k
  if (!c->user || c->user > p->p_users.nprim)
44
73
    return 0;
45
46
6.91k
  if (!c->type || c->type > p->p_types.nprim)
47
67
    return 0;
48
49
6.85k
  if (c->role != OBJECT_R_VAL) {
50
    /*
51
     * Role must be authorized for the type.
52
     */
53
1.34k
    role = p->role_val_to_struct[c->role - 1];
54
1.34k
    if (!role || !ebitmap_get_bit(&role->cache, c->type - 1))
55
      /* role may not be associated with type */
56
2
      return 0;
57
58
    /*
59
     * User must be authorized for the role.
60
     */
61
1.34k
    usrdatum = p->user_val_to_struct[c->user - 1];
62
1.34k
    if (!usrdatum)
63
1
      return 0;
64
65
1.34k
    if (!ebitmap_get_bit(&usrdatum->cache, c->role - 1))
66
      /* user may not be associated with role */
67
1
      return 0;
68
1.34k
  }
69
70
6.84k
  if (!mls_context_isvalid(p, c))
71
250
    return 0;
72
73
6.59k
  return 1;
74
6.84k
}
75
76
/*
77
 * Write the security context string representation of
78
 * the context structure `context' into a dynamically
79
 * allocated string of the correct size.  Set `*scontext'
80
 * to point to this string and set `*scontext_len' to
81
 * the length of the string.
82
 */
83
int context_to_string(sepol_handle_t *handle, const policydb_t *policydb,
84
          const context_struct_t *context, char **result,
85
          size_t *result_len)
86
0
{
87
0
  char *scontext = NULL;
88
0
  size_t scontext_len = 0;
89
0
  char *ptr;
90
91
  /* Compute the size of the context. */
92
0
  scontext_len +=
93
0
    strlen(policydb->p_user_val_to_name[context->user - 1]) + 1;
94
0
  scontext_len +=
95
0
    strlen(policydb->p_role_val_to_name[context->role - 1]) + 1;
96
0
  scontext_len += strlen(policydb->p_type_val_to_name[context->type - 1]);
97
0
  scontext_len += mls_compute_context_len(policydb, context);
98
99
  /* We must null terminate the string */
100
0
  scontext_len += 1;
101
102
  /* Allocate space for the context; caller must free this space. */
103
0
  scontext = malloc(scontext_len);
104
0
  if (!scontext)
105
0
    goto omem;
106
0
  scontext[scontext_len - 1] = '\0';
107
108
  /*
109
   * Copy the user name, role name and type name into the context.
110
   */
111
0
  ptr = scontext;
112
0
  sprintf(ptr, "%s:%s:%s",
113
0
    policydb->p_user_val_to_name[context->user - 1],
114
0
    policydb->p_role_val_to_name[context->role - 1],
115
0
    policydb->p_type_val_to_name[context->type - 1]);
116
117
0
  ptr += strlen(policydb->p_user_val_to_name[context->user - 1]) + 1 +
118
0
         strlen(policydb->p_role_val_to_name[context->role - 1]) + 1 +
119
0
         strlen(policydb->p_type_val_to_name[context->type - 1]);
120
121
0
  mls_sid_to_context(policydb, context, &ptr);
122
123
0
  *result = scontext;
124
0
  *result_len = scontext_len;
125
0
  return STATUS_SUCCESS;
126
127
0
omem:
128
0
  ERR(handle, "out of memory, could not convert "
129
0
        "context to string");
130
0
  free(scontext);
131
0
  return STATUS_ERR;
132
0
}
133
134
/*
135
 * Create a context structure from the given record
136
 */
137
int context_from_record(sepol_handle_t *handle, const policydb_t *policydb,
138
      context_struct_t **cptr, const sepol_context_t *record)
139
0
{
140
0
  context_struct_t *scontext = NULL;
141
0
  user_datum_t *usrdatum;
142
0
  role_datum_t *roldatum;
143
0
  type_datum_t *typdatum;
144
145
  /* Hashtab keys are not constant - suppress warnings */
146
0
  char *user = strdup(sepol_context_get_user(record));
147
0
  char *role = strdup(sepol_context_get_role(record));
148
0
  char *type = strdup(sepol_context_get_type(record));
149
0
  const char *mls = sepol_context_get_mls(record);
150
151
0
  scontext = (context_struct_t *)malloc(sizeof(context_struct_t));
152
0
  if (!user || !role || !type || !scontext) {
153
0
    ERR(handle, "out of memory");
154
0
    goto err;
155
0
  }
156
0
  context_init(scontext);
157
158
  /* User */
159
0
  usrdatum = (user_datum_t *)hashtab_search(policydb->p_users.table,
160
0
              (hashtab_key_t)user);
161
0
  if (!usrdatum) {
162
0
    ERR(handle, "user %s is not defined", user);
163
0
    goto err_destroy;
164
0
  }
165
0
  scontext->user = usrdatum->s.value;
166
167
  /* Role */
168
0
  roldatum = (role_datum_t *)hashtab_search(policydb->p_roles.table,
169
0
              (hashtab_key_t)role);
170
0
  if (!roldatum) {
171
0
    ERR(handle, "role %s is not defined", role);
172
0
    goto err_destroy;
173
0
  }
174
0
  scontext->role = roldatum->s.value;
175
176
  /* Type */
177
0
  typdatum = (type_datum_t *)hashtab_search(policydb->p_types.table,
178
0
              (hashtab_key_t)type);
179
0
  if (!typdatum || typdatum->flavor == TYPE_ATTRIB) {
180
0
    ERR(handle, "type %s is not defined", type);
181
0
    goto err_destroy;
182
0
  }
183
0
  scontext->type = typdatum->s.value;
184
185
  /* MLS */
186
0
  if (mls && !policydb->mls) {
187
0
    ERR(handle, "MLS is disabled, but MLS context \"%s\" found",
188
0
        mls);
189
0
    goto err_destroy;
190
0
  } else if (!mls && policydb->mls) {
191
0
    ERR(handle, "MLS is enabled, but no MLS context found");
192
0
    goto err_destroy;
193
0
  }
194
0
  if (mls && (mls_from_string(handle, policydb, mls, scontext) < 0))
195
0
    goto err_destroy;
196
197
  /* Validity check */
198
0
  if (!context_is_valid(policydb, scontext)) {
199
0
    if (mls) {
200
0
      ERR(handle, "invalid security context: \"%s:%s:%s:%s\"",
201
0
          user, role, type, mls);
202
0
    } else {
203
0
      ERR(handle, "invalid security context: \"%s:%s:%s\"",
204
0
          user, role, type);
205
0
    }
206
0
    goto err_destroy;
207
0
  }
208
209
0
  *cptr = scontext;
210
0
  free(user);
211
0
  free(type);
212
0
  free(role);
213
0
  return STATUS_SUCCESS;
214
215
0
err_destroy:
216
0
  errno = EINVAL;
217
0
  context_destroy(scontext);
218
219
0
err:
220
0
  free(scontext);
221
0
  free(user);
222
0
  free(type);
223
0
  free(role);
224
0
  ERR(handle, "could not create context structure");
225
0
  return STATUS_ERR;
226
0
}
227
228
/*
229
 * Create a record from the given context structure
230
 */
231
int context_to_record(sepol_handle_t *handle, const policydb_t *policydb,
232
          const context_struct_t *context, sepol_context_t **record)
233
0
{
234
0
  sepol_context_t *tmp_record = NULL;
235
0
  char *mls = NULL;
236
237
0
  if (sepol_context_create(handle, &tmp_record) < 0)
238
0
    goto err;
239
240
0
  if (sepol_context_set_user(
241
0
        handle, tmp_record,
242
0
        policydb->p_user_val_to_name[context->user - 1]) < 0)
243
0
    goto err;
244
245
0
  if (sepol_context_set_role(
246
0
        handle, tmp_record,
247
0
        policydb->p_role_val_to_name[context->role - 1]) < 0)
248
0
    goto err;
249
250
0
  if (sepol_context_set_type(
251
0
        handle, tmp_record,
252
0
        policydb->p_type_val_to_name[context->type - 1]) < 0)
253
0
    goto err;
254
255
0
  if (policydb->mls) {
256
0
    if (mls_to_string(handle, policydb, context, &mls) < 0)
257
0
      goto err;
258
259
0
    if (sepol_context_set_mls(handle, tmp_record, mls) < 0)
260
0
      goto err;
261
0
  }
262
263
0
  free(mls);
264
0
  *record = tmp_record;
265
0
  return STATUS_SUCCESS;
266
267
0
err:
268
0
  ERR(handle, "could not create context record");
269
0
  sepol_context_free(tmp_record);
270
0
  free(mls);
271
0
  return STATUS_ERR;
272
0
}
273
274
/*
275
 * Create a context structure from the provided string.
276
 */
277
int context_from_string(sepol_handle_t *handle, const policydb_t *policydb,
278
      context_struct_t **cptr, const char *con_str,
279
      size_t con_str_len)
280
0
{
281
0
  char *con_cpy = NULL;
282
0
  sepol_context_t *ctx_record = NULL;
283
284
0
  if (zero_or_saturated(con_str_len)) {
285
0
    ERR(handle, "Invalid context length");
286
0
    goto err;
287
0
  }
288
289
  /* sepol_context_from_string expects a NULL-terminated string */
290
0
  con_cpy = malloc(con_str_len + 1);
291
0
  if (!con_cpy) {
292
0
    ERR(handle, "out of memory");
293
0
    goto err;
294
0
  }
295
296
0
  memcpy(con_cpy, con_str, con_str_len);
297
0
  con_cpy[con_str_len] = '\0';
298
299
0
  if (sepol_context_from_string(handle, con_cpy, &ctx_record) < 0)
300
0
    goto err;
301
302
  /* Now create from the data structure */
303
0
  if (context_from_record(handle, policydb, cptr, ctx_record) < 0)
304
0
    goto err;
305
306
0
  free(con_cpy);
307
0
  sepol_context_free(ctx_record);
308
0
  return STATUS_SUCCESS;
309
310
0
err:
311
0
  ERR(handle, "could not create context structure");
312
0
  free(con_cpy);
313
0
  sepol_context_free(ctx_record);
314
0
  return STATUS_ERR;
315
0
}
316
317
int sepol_context_check(sepol_handle_t *handle,
318
      const sepol_policydb_t *policydb,
319
      const sepol_context_t *context)
320
0
{
321
  context_struct_t *con = NULL;
322
0
  int ret = context_from_record(handle, &policydb->p, &con, context);
323
0
  context_destroy(con);
324
0
  free(con);
325
0
  return ret;
326
0
}