aboutsummaryrefslogtreecommitdiffstats
path: root/security/selinux/ss/mls.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/selinux/ss/mls.c')
-rw-r--r--security/selinux/ss/mls.c527
1 files changed, 527 insertions, 0 deletions
diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c
new file mode 100644
index 000000000000..756036bcc243
--- /dev/null
+++ b/security/selinux/ss/mls.c
@@ -0,0 +1,527 @@
1/*
2 * Implementation of the multi-level security (MLS) policy.
3 *
4 * Author : Stephen Smalley, <sds@epoch.ncsc.mil>
5 */
6/*
7 * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
8 *
9 * Support for enhanced MLS infrastructure.
10 *
11 * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
12 */
13
14#include <linux/kernel.h>
15#include <linux/slab.h>
16#include <linux/string.h>
17#include <linux/errno.h>
18#include "mls.h"
19#include "policydb.h"
20#include "services.h"
21
22/*
23 * Return the length in bytes for the MLS fields of the
24 * security context string representation of `context'.
25 */
26int mls_compute_context_len(struct context * context)
27{
28 int i, l, len, range;
29
30 if (!selinux_mls_enabled)
31 return 0;
32
33 len = 1; /* for the beginning ":" */
34 for (l = 0; l < 2; l++) {
35 range = 0;
36 len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
37
38 for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++) {
39 if (ebitmap_get_bit(&context->range.level[l].cat, i - 1)) {
40 if (range) {
41 range++;
42 continue;
43 }
44
45 len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
46 range++;
47 } else {
48 if (range > 1)
49 len += strlen(policydb.p_cat_val_to_name[i - 2]) + 1;
50 range = 0;
51 }
52 }
53 /* Handle case where last category is the end of range */
54 if (range > 1)
55 len += strlen(policydb.p_cat_val_to_name[i - 2]) + 1;
56
57 if (l == 0) {
58 if (mls_level_eq(&context->range.level[0],
59 &context->range.level[1]))
60 break;
61 else
62 len++;
63 }
64 }
65
66 return len;
67}
68
69/*
70 * Write the security context string representation of
71 * the MLS fields of `context' into the string `*scontext'.
72 * Update `*scontext' to point to the end of the MLS fields.
73 */
74void mls_sid_to_context(struct context *context,
75 char **scontext)
76{
77 char *scontextp;
78 int i, l, range, wrote_sep;
79
80 if (!selinux_mls_enabled)
81 return;
82
83 scontextp = *scontext;
84
85 *scontextp = ':';
86 scontextp++;
87
88 for (l = 0; l < 2; l++) {
89 range = 0;
90 wrote_sep = 0;
91 strcpy(scontextp,
92 policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
93 scontextp += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
94
95 /* categories */
96 for (i = 1; i <= ebitmap_length(&context->range.level[l].cat); i++) {
97 if (ebitmap_get_bit(&context->range.level[l].cat, i - 1)) {
98 if (range) {
99 range++;
100 continue;
101 }
102
103 if (!wrote_sep) {
104 *scontextp++ = ':';
105 wrote_sep = 1;
106 } else
107 *scontextp++ = ',';
108 strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
109 scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
110 range++;
111 } else {
112 if (range > 1) {
113 if (range > 2)
114 *scontextp++ = '.';
115 else
116 *scontextp++ = ',';
117
118 strcpy(scontextp, policydb.p_cat_val_to_name[i - 2]);
119 scontextp += strlen(policydb.p_cat_val_to_name[i - 2]);
120 }
121 range = 0;
122 }
123 }
124
125 /* Handle case where last category is the end of range */
126 if (range > 1) {
127 if (range > 2)
128 *scontextp++ = '.';
129 else
130 *scontextp++ = ',';
131
132 strcpy(scontextp, policydb.p_cat_val_to_name[i - 2]);
133 scontextp += strlen(policydb.p_cat_val_to_name[i - 2]);
134 }
135
136 if (l == 0) {
137 if (mls_level_eq(&context->range.level[0],
138 &context->range.level[1]))
139 break;
140 else {
141 *scontextp = '-';
142 scontextp++;
143 }
144 }
145 }
146
147 *scontext = scontextp;
148 return;
149}
150
151/*
152 * Return 1 if the MLS fields in the security context
153 * structure `c' are valid. Return 0 otherwise.
154 */
155int mls_context_isvalid(struct policydb *p, struct context *c)
156{
157 struct level_datum *levdatum;
158 struct user_datum *usrdatum;
159 int i, l;
160
161 if (!selinux_mls_enabled)
162 return 1;
163
164 /*
165 * MLS range validity checks: high must dominate low, low level must
166 * be valid (category set <-> sensitivity check), and high level must
167 * be valid (category set <-> sensitivity check)
168 */
169 if (!mls_level_dom(&c->range.level[1], &c->range.level[0]))
170 /* High does not dominate low. */
171 return 0;
172
173 for (l = 0; l < 2; l++) {
174 if (!c->range.level[l].sens || c->range.level[l].sens > p->p_levels.nprim)
175 return 0;
176 levdatum = hashtab_search(p->p_levels.table,
177 p->p_sens_val_to_name[c->range.level[l].sens - 1]);
178 if (!levdatum)
179 return 0;
180
181 for (i = 1; i <= ebitmap_length(&c->range.level[l].cat); i++) {
182 if (ebitmap_get_bit(&c->range.level[l].cat, i - 1)) {
183 if (i > p->p_cats.nprim)
184 return 0;
185 if (!ebitmap_get_bit(&levdatum->level->cat, i - 1))
186 /*
187 * Category may not be associated with
188 * sensitivity in low level.
189 */
190 return 0;
191 }
192 }
193 }
194
195 if (c->role == OBJECT_R_VAL)
196 return 1;
197
198 /*
199 * User must be authorized for the MLS range.
200 */
201 if (!c->user || c->user > p->p_users.nprim)
202 return 0;
203 usrdatum = p->user_val_to_struct[c->user - 1];
204 if (!mls_range_contains(usrdatum->range, c->range))
205 return 0; /* user may not be associated with range */
206
207 return 1;
208}
209
210/*
211 * Set the MLS fields in the security context structure
212 * `context' based on the string representation in
213 * the string `*scontext'. Update `*scontext' to
214 * point to the end of the string representation of
215 * the MLS fields.
216 *
217 * This function modifies the string in place, inserting
218 * NULL characters to terminate the MLS fields.
219 */
220int mls_context_to_sid(char oldc,
221 char **scontext,
222 struct context *context)
223{
224
225 char delim;
226 char *scontextp, *p, *rngptr;
227 struct level_datum *levdatum;
228 struct cat_datum *catdatum, *rngdatum;
229 int l, rc = -EINVAL;
230
231 if (!selinux_mls_enabled)
232 return 0;
233
234 /* No MLS component to the security context. */
235 if (!oldc)
236 goto out;
237
238 /* Extract low sensitivity. */
239 scontextp = p = *scontext;
240 while (*p && *p != ':' && *p != '-')
241 p++;
242
243 delim = *p;
244 if (delim != 0)
245 *p++ = 0;
246
247 for (l = 0; l < 2; l++) {
248 levdatum = hashtab_search(policydb.p_levels.table, scontextp);
249 if (!levdatum) {
250 rc = -EINVAL;
251 goto out;
252 }
253
254 context->range.level[l].sens = levdatum->level->sens;
255
256 if (delim == ':') {
257 /* Extract category set. */
258 while (1) {
259 scontextp = p;
260 while (*p && *p != ',' && *p != '-')
261 p++;
262 delim = *p;
263 if (delim != 0)
264 *p++ = 0;
265
266 /* Separate into range if exists */
267 if ((rngptr = strchr(scontextp, '.')) != NULL) {
268 /* Remove '.' */
269 *rngptr++ = 0;
270 }
271
272 catdatum = hashtab_search(policydb.p_cats.table,
273 scontextp);
274 if (!catdatum) {
275 rc = -EINVAL;
276 goto out;
277 }
278
279 rc = ebitmap_set_bit(&context->range.level[l].cat,
280 catdatum->value - 1, 1);
281 if (rc)
282 goto out;
283
284 /* If range, set all categories in range */
285 if (rngptr) {
286 int i;
287
288 rngdatum = hashtab_search(policydb.p_cats.table, rngptr);
289 if (!rngdatum) {
290 rc = -EINVAL;
291 goto out;
292 }
293
294 if (catdatum->value >= rngdatum->value) {
295 rc = -EINVAL;
296 goto out;
297 }
298
299 for (i = catdatum->value; i < rngdatum->value; i++) {
300 rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
301 if (rc)
302 goto out;
303 }
304 }
305
306 if (delim != ',')
307 break;
308 }
309 }
310 if (delim == '-') {
311 /* Extract high sensitivity. */
312 scontextp = p;
313 while (*p && *p != ':')
314 p++;
315
316 delim = *p;
317 if (delim != 0)
318 *p++ = 0;
319 } else
320 break;
321 }
322
323 if (l == 0) {
324 context->range.level[1].sens = context->range.level[0].sens;
325 rc = ebitmap_cpy(&context->range.level[1].cat,
326 &context->range.level[0].cat);
327 if (rc)
328 goto out;
329 }
330 *scontext = ++p;
331 rc = 0;
332out:
333 return rc;
334}
335
336/*
337 * Copies the MLS range from `src' into `dst'.
338 */
339static inline int mls_copy_context(struct context *dst,
340 struct context *src)
341{
342 int l, rc = 0;
343
344 /* Copy the MLS range from the source context */
345 for (l = 0; l < 2; l++) {
346 dst->range.level[l].sens = src->range.level[l].sens;
347 rc = ebitmap_cpy(&dst->range.level[l].cat,
348 &src->range.level[l].cat);
349 if (rc)
350 break;
351 }
352
353 return rc;
354}
355
356/*
357 * Copies the effective MLS range from `src' into `dst'.
358 */
359static inline int mls_scopy_context(struct context *dst,
360 struct context *src)
361{
362 int l, rc = 0;
363
364 /* Copy the MLS range from the source context */
365 for (l = 0; l < 2; l++) {
366 dst->range.level[l].sens = src->range.level[0].sens;
367 rc = ebitmap_cpy(&dst->range.level[l].cat,
368 &src->range.level[0].cat);
369 if (rc)
370 break;
371 }
372
373 return rc;
374}
375
376/*
377 * Copies the MLS range `range' into `context'.
378 */
379static inline int mls_range_set(struct context *context,
380 struct mls_range *range)
381{
382 int l, rc = 0;
383
384 /* Copy the MLS range into the context */
385 for (l = 0; l < 2; l++) {
386 context->range.level[l].sens = range->level[l].sens;
387 rc = ebitmap_cpy(&context->range.level[l].cat,
388 &range->level[l].cat);
389 if (rc)
390 break;
391 }
392
393 return rc;
394}
395
396int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
397 struct context *usercon)
398{
399 if (selinux_mls_enabled) {
400 struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
401 struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
402 struct mls_level *user_low = &(user->range.level[0]);
403 struct mls_level *user_clr = &(user->range.level[1]);
404 struct mls_level *user_def = &(user->dfltlevel);
405 struct mls_level *usercon_sen = &(usercon->range.level[0]);
406 struct mls_level *usercon_clr = &(usercon->range.level[1]);
407
408 /* Honor the user's default level if we can */
409 if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) {
410 *usercon_sen = *user_def;
411 } else if (mls_level_between(fromcon_sen, user_def, user_clr)) {
412 *usercon_sen = *fromcon_sen;
413 } else if (mls_level_between(fromcon_clr, user_low, user_def)) {
414 *usercon_sen = *user_low;
415 } else
416 return -EINVAL;
417
418 /* Lower the clearance of available contexts
419 if the clearance of "fromcon" is lower than
420 that of the user's default clearance (but
421 only if the "fromcon" clearance dominates
422 the user's computed sensitivity level) */
423 if (mls_level_dom(user_clr, fromcon_clr)) {
424 *usercon_clr = *fromcon_clr;
425 } else if (mls_level_dom(fromcon_clr, user_clr)) {
426 *usercon_clr = *user_clr;
427 } else
428 return -EINVAL;
429 }
430
431 return 0;
432}
433
434/*
435 * Convert the MLS fields in the security context
436 * structure `c' from the values specified in the
437 * policy `oldp' to the values specified in the policy `newp'.
438 */
439int mls_convert_context(struct policydb *oldp,
440 struct policydb *newp,
441 struct context *c)
442{
443 struct level_datum *levdatum;
444 struct cat_datum *catdatum;
445 struct ebitmap bitmap;
446 int l, i;
447
448 if (!selinux_mls_enabled)
449 return 0;
450
451 for (l = 0; l < 2; l++) {
452 levdatum = hashtab_search(newp->p_levels.table,
453 oldp->p_sens_val_to_name[c->range.level[l].sens - 1]);
454
455 if (!levdatum)
456 return -EINVAL;
457 c->range.level[l].sens = levdatum->level->sens;
458
459 ebitmap_init(&bitmap);
460 for (i = 1; i <= ebitmap_length(&c->range.level[l].cat); i++) {
461 if (ebitmap_get_bit(&c->range.level[l].cat, i - 1)) {
462 int rc;
463
464 catdatum = hashtab_search(newp->p_cats.table,
465 oldp->p_cat_val_to_name[i - 1]);
466 if (!catdatum)
467 return -EINVAL;
468 rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
469 if (rc)
470 return rc;
471 }
472 }
473 ebitmap_destroy(&c->range.level[l].cat);
474 c->range.level[l].cat = bitmap;
475 }
476
477 return 0;
478}
479
480int mls_compute_sid(struct context *scontext,
481 struct context *tcontext,
482 u16 tclass,
483 u32 specified,
484 struct context *newcontext)
485{
486 if (!selinux_mls_enabled)
487 return 0;
488
489 switch (specified) {
490 case AVTAB_TRANSITION:
491 if (tclass == SECCLASS_PROCESS) {
492 struct range_trans *rangetr;
493 /* Look for a range transition rule. */
494 for (rangetr = policydb.range_tr; rangetr;
495 rangetr = rangetr->next) {
496 if (rangetr->dom == scontext->type &&
497 rangetr->type == tcontext->type) {
498 /* Set the range from the rule */
499 return mls_range_set(newcontext,
500 &rangetr->range);
501 }
502 }
503 }
504 /* Fallthrough */
505 case AVTAB_CHANGE:
506 if (tclass == SECCLASS_PROCESS)
507 /* Use the process MLS attributes. */
508 return mls_copy_context(newcontext, scontext);
509 else
510 /* Use the process effective MLS attributes. */
511 return mls_scopy_context(newcontext, scontext);
512 case AVTAB_MEMBER:
513 /* Only polyinstantiate the MLS attributes if
514 the type is being polyinstantiated */
515 if (newcontext->type != tcontext->type) {
516 /* Use the process effective MLS attributes. */
517 return mls_scopy_context(newcontext, scontext);
518 } else {
519 /* Use the related object MLS attributes. */
520 return mls_copy_context(newcontext, tcontext);
521 }
522 default:
523 return -EINVAL;
524 }
525 return -EINVAL;
526}
527