Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / security / selinux / ss / mls.c
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-2006 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 "sidtab.h"
19 #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  */
27 int mls_compute_context_len(struct context * context)
28 {
29         int i, l, len, range;
30         struct ebitmap_node *node;
31
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
40                 ebitmap_for_each_bit(&context->range.level[l].cat, node, i) {
41                         if (ebitmap_node_get_bit(node, i)) {
42                                 if (range) {
43                                         range++;
44                                         continue;
45                                 }
46
47                                 len += strlen(policydb.p_cat_val_to_name[i]) + 1;
48                                 range++;
49                         } else {
50                                 if (range > 1)
51                                         len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
52                                 range = 0;
53                         }
54                 }
55                 /* Handle case where last category is the end of range */
56                 if (range > 1)
57                         len += strlen(policydb.p_cat_val_to_name[i - 1]) + 1;
58
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  */
76 void mls_sid_to_context(struct context *context,
77                         char **scontext)
78 {
79         char *scontextp;
80         int i, l, range, wrote_sep;
81         struct ebitmap_node *node;
82
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 */
99                 ebitmap_for_each_bit(&context->range.level[l].cat, node, i) {
100                         if (ebitmap_node_get_bit(node, i)) {
101                                 if (range) {
102                                         range++;
103                                         continue;
104                                 }
105
106                                 if (!wrote_sep) {
107                                         *scontextp++ = ':';
108                                         wrote_sep = 1;
109                                 } else
110                                         *scontextp++ = ',';
111                                 strcpy(scontextp, policydb.p_cat_val_to_name[i]);
112                                 scontextp += strlen(policydb.p_cat_val_to_name[i]);
113                                 range++;
114                         } else {
115                                 if (range > 1) {
116                                         if (range > 2)
117                                                 *scontextp++ = '.';
118                                         else
119                                                 *scontextp++ = ',';
120
121                                         strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
122                                         scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
123                                 }
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
135                         strcpy(scontextp, policydb.p_cat_val_to_name[i - 1]);
136                         scontextp += strlen(policydb.p_cat_val_to_name[i - 1]);
137                 }
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  */
158 int mls_context_isvalid(struct policydb *p, struct context *c)
159 {
160         struct level_datum *levdatum;
161         struct user_datum *usrdatum;
162         struct ebitmap_node *node;
163         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
185                 ebitmap_for_each_bit(&c->range.level[l].cat, node, i) {
186                         if (ebitmap_node_get_bit(node, i)) {
187                                 if (i > p->p_cats.nprim)
188                                         return 0;
189                                 if (!ebitmap_get_bit(&levdatum->level->cat, i))
190                                         /*
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 /*
215  * Copies the MLS range from `src' into `dst'.
216  */
217 static 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 /*
235  * 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.
243  *
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  *
251  */
252 int mls_context_to_sid(char oldc,
253                        char **scontext,
254                        struct context *context,
255                        struct sidtab *s,
256                        u32 def_sid)
257 {
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
265         if (!selinux_mls_enabled) {
266                 if (def_sid != SECSID_NULL && oldc)
267                         *scontext += strlen(*scontext)+1;
268                 return 0;
269         }
270
271         /*
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);
286                 goto out;
287         }
288
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;
383 out:
384         return rc;
385 }
386
387 /*
388  * 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  */
393 int 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 /*
416  * Copies the effective MLS range from `src' into `dst'.
417  */
418 static 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  */
438 static 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
455 int 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  */
498 int 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;
505         struct ebitmap_node *node;
506         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);
520                 ebitmap_for_each_bit(&c->range.level[l].cat, node, i) {
521                         if (ebitmap_node_get_bit(node, i)) {
522                                 int rc;
523
524                                 catdatum = hashtab_search(newp->p_cats.table,
525                                                 oldp->p_cat_val_to_name[i]);
526                                 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
540 int mls_compute_sid(struct context *scontext,
541                     struct context *tcontext,
542                     u16 tclass,
543                     u32 specified,
544                     struct context *newcontext)
545 {
546         struct range_trans *rtr;
547
548         if (!selinux_mls_enabled)
549                 return 0;
550
551         switch (specified) {
552         case AVTAB_TRANSITION:
553                 /* Look for a range transition rule. */
554                 for (rtr = policydb.range_tr; rtr; rtr = rtr->next) {
555                         if (rtr->source_type == scontext->type &&
556                             rtr->target_type == tcontext->type &&
557                             rtr->target_class == tclass) {
558                                 /* Set the range from the rule */
559                                 return mls_range_set(newcontext,
560                                                      &rtr->target_range);
561                         }
562                 }
563                 /* Fallthrough */
564         case AVTAB_CHANGE:
565                 if (tclass == SECCLASS_PROCESS)
566                         /* Use the process MLS attributes. */
567                         return mls_copy_context(newcontext, scontext);
568                 else
569                         /* Use the process effective MLS attributes. */
570                         return mls_scopy_context(newcontext, scontext);
571         case AVTAB_MEMBER:
572                 /* Only polyinstantiate the MLS attributes if
573                    the type is being polyinstantiated */
574                 if (newcontext->type != tcontext->type) {
575                         /* Use the process effective MLS attributes. */
576                         return mls_scopy_context(newcontext, scontext);
577                 } else {
578                         /* Use the related object MLS attributes. */
579                         return mls_copy_context(newcontext, tcontext);
580                 }
581         default:
582                 return -EINVAL;
583         }
584         return -EINVAL;
585 }
586