blob: 7bc5b6440f70ba1ab45a66d0a7b3152d394096af [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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 *
Darrel Goeddel376bd9c2006-02-24 15:44:05 -060011 * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
Linus Torvalds1da177e2005-04-16 15:20:36 -070012 */
13
14#include <linux/kernel.h>
15#include <linux/slab.h>
16#include <linux/string.h>
17#include <linux/errno.h>
James Morrisf5c1d5b2005-07-28 01:07:37 -070018#include "sidtab.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include "mls.h"
20#include "policydb.h"
21#include "services.h"
22
23/*
24 * Return the length in bytes for the MLS fields of the
25 * security context string representation of `context'.
26 */
27int mls_compute_context_len(struct context * context)
28{
29 int i, l, len, range;
Stephen Smalley782ebb92005-09-03 15:55:16 -070030 struct ebitmap_node *node;
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
32 if (!selinux_mls_enabled)
33 return 0;
34
35 len = 1; /* for the beginning ":" */
36 for (l = 0; l < 2; l++) {
37 range = 0;
38 len += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
39
Stephen Smalley782ebb92005-09-03 15:55:16 -070040 ebitmap_for_each_bit(&context->range.level[l].cat, node, i) {
41 if (ebitmap_node_get_bit(node, i)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 if (range) {
43 range++;
44 continue;
45 }
46
Stephen Smalley782ebb92005-09-03 15:55:16 -070047 len += strlen(policydb.p_cat_val_to_name[i]) + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070048 range++;
49 } else {
50 if (range > 1)
Stephen Smalley782ebb92005-09-03 15:55:16 -070051 len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070052 range = 0;
53 }
54 }
55 /* Handle case where last category is the end of range */
56 if (range > 1)
Stephen Smalley782ebb92005-09-03 15:55:16 -070057 len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
59 if (l == 0) {
60 if (mls_level_eq(&context->range.level[0],
61 &context->range.level[1]))
62 break;
63 else
64 len++;
65 }
66 }
67
68 return len;
69}
70
71/*
72 * Write the security context string representation of
73 * the MLS fields of `context' into the string `*scontext'.
74 * Update `*scontext' to point to the end of the MLS fields.
75 */
76void mls_sid_to_context(struct context *context,
77 char **scontext)
78{
79 char *scontextp;
80 int i, l, range, wrote_sep;
Stephen Smalley782ebb92005-09-03 15:55:16 -070081 struct ebitmap_node *node;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082
83 if (!selinux_mls_enabled)
84 return;
85
86 scontextp = *scontext;
87
88 *scontextp = ':';
89 scontextp++;
90
91 for (l = 0; l < 2; l++) {
92 range = 0;
93 wrote_sep = 0;
94 strcpy(scontextp,
95 policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
96 scontextp += strlen(policydb.p_sens_val_to_name[context->range.level[l].sens - 1]);
97
98 /* categories */
Stephen Smalley782ebb92005-09-03 15:55:16 -070099 ebitmap_for_each_bit(&context->range.level[l].cat, node, i) {
100 if (ebitmap_node_get_bit(node, i)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 if (range) {
102 range++;
103 continue;
104 }
105
106 if (!wrote_sep) {
107 *scontextp++ = ':';
108 wrote_sep = 1;
109 } else
110 *scontextp++ = ',';
Stephen Smalley782ebb92005-09-03 15:55:16 -0700111 strcpy(scontextp, policydb.p_cat_val_to_name[i]);
112 scontextp += strlen(policydb.p_cat_val_to_name[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 range++;
114 } else {
115 if (range > 1) {
116 if (range > 2)
117 *scontextp++ = '.';
118 else
119 *scontextp++ = ',';
120
Stephen Smalley782ebb92005-09-03 15:55:16 -0700121 strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
122 scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 }
124 range = 0;
125 }
126 }
127
128 /* Handle case where last category is the end of range */
129 if (range > 1) {
130 if (range > 2)
131 *scontextp++ = '.';
132 else
133 *scontextp++ = ',';
134
Stephen Smalley782ebb92005-09-03 15:55:16 -0700135 strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
136 scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 }
138
139 if (l == 0) {
140 if (mls_level_eq(&context->range.level[0],
141 &context->range.level[1]))
142 break;
143 else {
144 *scontextp = '-';
145 scontextp++;
146 }
147 }
148 }
149
150 *scontext = scontextp;
151 return;
152}
153
154/*
155 * Return 1 if the MLS fields in the security context
156 * structure `c' are valid. Return 0 otherwise.
157 */
158int mls_context_isvalid(struct policydb *p, struct context *c)
159{
160 struct level_datum *levdatum;
161 struct user_datum *usrdatum;
Stephen Smalley782ebb92005-09-03 15:55:16 -0700162 struct ebitmap_node *node;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 int i, l;
164
165 if (!selinux_mls_enabled)
166 return 1;
167
168 /*
169 * MLS range validity checks: high must dominate low, low level must
170 * be valid (category set <-> sensitivity check), and high level must
171 * be valid (category set <-> sensitivity check)
172 */
173 if (!mls_level_dom(&c->range.level[1], &c->range.level[0]))
174 /* High does not dominate low. */
175 return 0;
176
177 for (l = 0; l < 2; l++) {
178 if (!c->range.level[l].sens || c->range.level[l].sens > p->p_levels.nprim)
179 return 0;
180 levdatum = hashtab_search(p->p_levels.table,
181 p->p_sens_val_to_name[c->range.level[l].sens - 1]);
182 if (!levdatum)
183 return 0;
184
Stephen Smalley782ebb92005-09-03 15:55:16 -0700185 ebitmap_for_each_bit(&c->range.level[l].cat, node, i) {
186 if (ebitmap_node_get_bit(node, i)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 if (i > p->p_cats.nprim)
188 return 0;
Stephen Smalley782ebb92005-09-03 15:55:16 -0700189 if (!ebitmap_get_bit(&levdatum->level->cat, i))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 /*
191 * Category may not be associated with
192 * sensitivity in low level.
193 */
194 return 0;
195 }
196 }
197 }
198
199 if (c->role == OBJECT_R_VAL)
200 return 1;
201
202 /*
203 * User must be authorized for the MLS range.
204 */
205 if (!c->user || c->user > p->p_users.nprim)
206 return 0;
207 usrdatum = p->user_val_to_struct[c->user - 1];
208 if (!mls_range_contains(usrdatum->range, c->range))
209 return 0; /* user may not be associated with range */
210
211 return 1;
212}
213
214/*
James Morrisf5c1d5b2005-07-28 01:07:37 -0700215 * Copies the MLS range from `src' into `dst'.
216 */
217static inline int mls_copy_context(struct context *dst,
218 struct context *src)
219{
220 int l, rc = 0;
221
222 /* Copy the MLS range from the source context */
223 for (l = 0; l < 2; l++) {
224 dst->range.level[l].sens = src->range.level[l].sens;
225 rc = ebitmap_cpy(&dst->range.level[l].cat,
226 &src->range.level[l].cat);
227 if (rc)
228 break;
229 }
230
231 return rc;
232}
233
234/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 * Set the MLS fields in the security context structure
236 * `context' based on the string representation in
237 * the string `*scontext'. Update `*scontext' to
238 * point to the end of the string representation of
239 * the MLS fields.
240 *
241 * This function modifies the string in place, inserting
242 * NULL characters to terminate the MLS fields.
James Morrisf5c1d5b2005-07-28 01:07:37 -0700243 *
244 * If a def_sid is provided and no MLS field is present,
245 * copy the MLS field of the associated default context.
246 * Used for upgraded to MLS systems where objects may lack
247 * MLS fields.
248 *
249 * Policy read-lock must be held for sidtab lookup.
250 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 */
252int mls_context_to_sid(char oldc,
253 char **scontext,
James Morrisf5c1d5b2005-07-28 01:07:37 -0700254 struct context *context,
255 struct sidtab *s,
256 u32 def_sid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257{
258
259 char delim;
260 char *scontextp, *p, *rngptr;
261 struct level_datum *levdatum;
262 struct cat_datum *catdatum, *rngdatum;
263 int l, rc = -EINVAL;
264
Stephen Smalleye517a0c2005-11-08 21:34:32 -0800265 if (!selinux_mls_enabled) {
266 if (def_sid != SECSID_NULL && oldc)
Ron Yorstonab5703b2006-04-18 22:21:04 -0700267 *scontext += strlen(*scontext)+1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 return 0;
Stephen Smalleye517a0c2005-11-08 21:34:32 -0800269 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270
James Morrisf5c1d5b2005-07-28 01:07:37 -0700271 /*
272 * No MLS component to the security context, try and map to
273 * default if provided.
274 */
275 if (!oldc) {
276 struct context *defcon;
277
278 if (def_sid == SECSID_NULL)
279 goto out;
280
281 defcon = sidtab_search(s, def_sid);
282 if (!defcon)
283 goto out;
284
285 rc = mls_copy_context(context, defcon);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286 goto out;
James Morrisf5c1d5b2005-07-28 01:07:37 -0700287 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288
289 /* Extract low sensitivity. */
290 scontextp = p = *scontext;
291 while (*p && *p != ':' && *p != '-')
292 p++;
293
294 delim = *p;
295 if (delim != 0)
296 *p++ = 0;
297
298 for (l = 0; l < 2; l++) {
299 levdatum = hashtab_search(policydb.p_levels.table, scontextp);
300 if (!levdatum) {
301 rc = -EINVAL;
302 goto out;
303 }
304
305 context->range.level[l].sens = levdatum->level->sens;
306
307 if (delim == ':') {
308 /* Extract category set. */
309 while (1) {
310 scontextp = p;
311 while (*p && *p != ',' && *p != '-')
312 p++;
313 delim = *p;
314 if (delim != 0)
315 *p++ = 0;
316
317 /* Separate into range if exists */
318 if ((rngptr = strchr(scontextp, '.')) != NULL) {
319 /* Remove '.' */
320 *rngptr++ = 0;
321 }
322
323 catdatum = hashtab_search(policydb.p_cats.table,
324 scontextp);
325 if (!catdatum) {
326 rc = -EINVAL;
327 goto out;
328 }
329
330 rc = ebitmap_set_bit(&context->range.level[l].cat,
331 catdatum->value - 1, 1);
332 if (rc)
333 goto out;
334
335 /* If range, set all categories in range */
336 if (rngptr) {
337 int i;
338
339 rngdatum = hashtab_search(policydb.p_cats.table, rngptr);
340 if (!rngdatum) {
341 rc = -EINVAL;
342 goto out;
343 }
344
345 if (catdatum->value >= rngdatum->value) {
346 rc = -EINVAL;
347 goto out;
348 }
349
350 for (i = catdatum->value; i < rngdatum->value; i++) {
351 rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
352 if (rc)
353 goto out;
354 }
355 }
356
357 if (delim != ',')
358 break;
359 }
360 }
361 if (delim == '-') {
362 /* Extract high sensitivity. */
363 scontextp = p;
364 while (*p && *p != ':')
365 p++;
366
367 delim = *p;
368 if (delim != 0)
369 *p++ = 0;
370 } else
371 break;
372 }
373
374 if (l == 0) {
375 context->range.level[1].sens = context->range.level[0].sens;
376 rc = ebitmap_cpy(&context->range.level[1].cat,
377 &context->range.level[0].cat);
378 if (rc)
379 goto out;
380 }
381 *scontext = ++p;
382 rc = 0;
383out:
384 return rc;
385}
386
387/*
Darrel Goeddel376bd9c2006-02-24 15:44:05 -0600388 * Set the MLS fields in the security context structure
389 * `context' based on the string representation in
390 * the string `str'. This function will allocate temporary memory with the
391 * given constraints of gfp_mask.
392 */
393int mls_from_string(char *str, struct context *context, gfp_t gfp_mask)
394{
395 char *tmpstr, *freestr;
396 int rc;
397
398 if (!selinux_mls_enabled)
399 return -EINVAL;
400
401 /* we need freestr because mls_context_to_sid will change
402 the value of tmpstr */
403 tmpstr = freestr = kstrdup(str, gfp_mask);
404 if (!tmpstr) {
405 rc = -ENOMEM;
406 } else {
407 rc = mls_context_to_sid(':', &tmpstr, context,
408 NULL, SECSID_NULL);
409 kfree(freestr);
410 }
411
412 return rc;
413}
414
415/*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 * Copies the effective MLS range from `src' into `dst'.
417 */
418static inline int mls_scopy_context(struct context *dst,
419 struct context *src)
420{
421 int l, rc = 0;
422
423 /* Copy the MLS range from the source context */
424 for (l = 0; l < 2; l++) {
425 dst->range.level[l].sens = src->range.level[0].sens;
426 rc = ebitmap_cpy(&dst->range.level[l].cat,
427 &src->range.level[0].cat);
428 if (rc)
429 break;
430 }
431
432 return rc;
433}
434
435/*
436 * Copies the MLS range `range' into `context'.
437 */
438static inline int mls_range_set(struct context *context,
439 struct mls_range *range)
440{
441 int l, rc = 0;
442
443 /* Copy the MLS range into the context */
444 for (l = 0; l < 2; l++) {
445 context->range.level[l].sens = range->level[l].sens;
446 rc = ebitmap_cpy(&context->range.level[l].cat,
447 &range->level[l].cat);
448 if (rc)
449 break;
450 }
451
452 return rc;
453}
454
455int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
456 struct context *usercon)
457{
458 if (selinux_mls_enabled) {
459 struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
460 struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
461 struct mls_level *user_low = &(user->range.level[0]);
462 struct mls_level *user_clr = &(user->range.level[1]);
463 struct mls_level *user_def = &(user->dfltlevel);
464 struct mls_level *usercon_sen = &(usercon->range.level[0]);
465 struct mls_level *usercon_clr = &(usercon->range.level[1]);
466
467 /* Honor the user's default level if we can */
468 if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) {
469 *usercon_sen = *user_def;
470 } else if (mls_level_between(fromcon_sen, user_def, user_clr)) {
471 *usercon_sen = *fromcon_sen;
472 } else if (mls_level_between(fromcon_clr, user_low, user_def)) {
473 *usercon_sen = *user_low;
474 } else
475 return -EINVAL;
476
477 /* Lower the clearance of available contexts
478 if the clearance of "fromcon" is lower than
479 that of the user's default clearance (but
480 only if the "fromcon" clearance dominates
481 the user's computed sensitivity level) */
482 if (mls_level_dom(user_clr, fromcon_clr)) {
483 *usercon_clr = *fromcon_clr;
484 } else if (mls_level_dom(fromcon_clr, user_clr)) {
485 *usercon_clr = *user_clr;
486 } else
487 return -EINVAL;
488 }
489
490 return 0;
491}
492
493/*
494 * Convert the MLS fields in the security context
495 * structure `c' from the values specified in the
496 * policy `oldp' to the values specified in the policy `newp'.
497 */
498int mls_convert_context(struct policydb *oldp,
499 struct policydb *newp,
500 struct context *c)
501{
502 struct level_datum *levdatum;
503 struct cat_datum *catdatum;
504 struct ebitmap bitmap;
Stephen Smalley782ebb92005-09-03 15:55:16 -0700505 struct ebitmap_node *node;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 int l, i;
507
508 if (!selinux_mls_enabled)
509 return 0;
510
511 for (l = 0; l < 2; l++) {
512 levdatum = hashtab_search(newp->p_levels.table,
513 oldp->p_sens_val_to_name[c->range.level[l].sens - 1]);
514
515 if (!levdatum)
516 return -EINVAL;
517 c->range.level[l].sens = levdatum->level->sens;
518
519 ebitmap_init(&bitmap);
Stephen Smalley782ebb92005-09-03 15:55:16 -0700520 ebitmap_for_each_bit(&c->range.level[l].cat, node, i) {
521 if (ebitmap_node_get_bit(node, i)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 int rc;
523
524 catdatum = hashtab_search(newp->p_cats.table,
Stephen Smalley782ebb92005-09-03 15:55:16 -0700525 oldp->p_cat_val_to_name[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 if (!catdatum)
527 return -EINVAL;
528 rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
529 if (rc)
530 return rc;
531 }
532 }
533 ebitmap_destroy(&c->range.level[l].cat);
534 c->range.level[l].cat = bitmap;
535 }
536
537 return 0;
538}
539
540int mls_compute_sid(struct context *scontext,
541 struct context *tcontext,
542 u16 tclass,
543 u32 specified,
544 struct context *newcontext)
545{
546 if (!selinux_mls_enabled)
547 return 0;
548
549 switch (specified) {
550 case AVTAB_TRANSITION:
551 if (tclass == SECCLASS_PROCESS) {
552 struct range_trans *rangetr;
553 /* Look for a range transition rule. */
554 for (rangetr = policydb.range_tr; rangetr;
555 rangetr = rangetr->next) {
556 if (rangetr->dom == scontext->type &&
557 rangetr->type == tcontext->type) {
558 /* Set the range from the rule */
559 return mls_range_set(newcontext,
560 &rangetr->range);
561 }
562 }
563 }
564 /* Fallthrough */
565 case AVTAB_CHANGE:
566 if (tclass == SECCLASS_PROCESS)
567 /* Use the process MLS attributes. */
568 return mls_copy_context(newcontext, scontext);
569 else
570 /* Use the process effective MLS attributes. */
571 return mls_scopy_context(newcontext, scontext);
572 case AVTAB_MEMBER:
573 /* Only polyinstantiate the MLS attributes if
574 the type is being polyinstantiated */
575 if (newcontext->type != tcontext->type) {
576 /* Use the process effective MLS attributes. */
577 return mls_scopy_context(newcontext, scontext);
578 } else {
579 /* Use the related object MLS attributes. */
580 return mls_copy_context(newcontext, tcontext);
581 }
582 default:
583 return -EINVAL;
584 }
585 return -EINVAL;
586}
587