Merge master.kernel.org:/pub/scm/linux/kernel/git/chrisw/lsm-2.6
[powerpc.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-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 "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                 return 0;
267
268         /*
269          * No MLS component to the security context, try and map to
270          * default if provided.
271          */
272         if (!oldc) {
273                 struct context *defcon;
274
275                 if (def_sid == SECSID_NULL)
276                         goto out;
277
278                 defcon = sidtab_search(s, def_sid);
279                 if (!defcon)
280                         goto out;
281
282                 rc = mls_copy_context(context, defcon);
283                 goto out;
284         }
285
286         /* Extract low sensitivity. */
287         scontextp = p = *scontext;
288         while (*p && *p != ':' && *p != '-')
289                 p++;
290
291         delim = *p;
292         if (delim != 0)
293                 *p++ = 0;
294
295         for (l = 0; l < 2; l++) {
296                 levdatum = hashtab_search(policydb.p_levels.table, scontextp);
297                 if (!levdatum) {
298                         rc = -EINVAL;
299                         goto out;
300                 }
301
302                 context->range.level[l].sens = levdatum->level->sens;
303
304                 if (delim == ':') {
305                         /* Extract category set. */
306                         while (1) {
307                                 scontextp = p;
308                                 while (*p && *p != ',' && *p != '-')
309                                         p++;
310                                 delim = *p;
311                                 if (delim != 0)
312                                         *p++ = 0;
313
314                                 /* Separate into range if exists */
315                                 if ((rngptr = strchr(scontextp, '.')) != NULL) {
316                                         /* Remove '.' */
317                                         *rngptr++ = 0;
318                                 }
319
320                                 catdatum = hashtab_search(policydb.p_cats.table,
321                                                           scontextp);
322                                 if (!catdatum) {
323                                         rc = -EINVAL;
324                                         goto out;
325                                 }
326
327                                 rc = ebitmap_set_bit(&context->range.level[l].cat,
328                                                      catdatum->value - 1, 1);
329                                 if (rc)
330                                         goto out;
331
332                                 /* If range, set all categories in range */
333                                 if (rngptr) {
334                                         int i;
335
336                                         rngdatum = hashtab_search(policydb.p_cats.table, rngptr);
337                                         if (!rngdatum) {
338                                                 rc = -EINVAL;
339                                                 goto out;
340                                         }
341
342                                         if (catdatum->value >= rngdatum->value) {
343                                                 rc = -EINVAL;
344                                                 goto out;
345                                         }
346
347                                         for (i = catdatum->value; i < rngdatum->value; i++) {
348                                                 rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
349                                                 if (rc)
350                                                         goto out;
351                                         }
352                                 }
353
354                                 if (delim != ',')
355                                         break;
356                         }
357                 }
358                 if (delim == '-') {
359                         /* Extract high sensitivity. */
360                         scontextp = p;
361                         while (*p && *p != ':')
362                                 p++;
363
364                         delim = *p;
365                         if (delim != 0)
366                                 *p++ = 0;
367                 } else
368                         break;
369         }
370
371         if (l == 0) {
372                 context->range.level[1].sens = context->range.level[0].sens;
373                 rc = ebitmap_cpy(&context->range.level[1].cat,
374                                  &context->range.level[0].cat);
375                 if (rc)
376                         goto out;
377         }
378         *scontext = ++p;
379         rc = 0;
380 out:
381         return rc;
382 }
383
384 /*
385  * Copies the effective MLS range from `src' into `dst'.
386  */
387 static inline int mls_scopy_context(struct context *dst,
388                                     struct context *src)
389 {
390         int l, rc = 0;
391
392         /* Copy the MLS range from the source context */
393         for (l = 0; l < 2; l++) {
394                 dst->range.level[l].sens = src->range.level[0].sens;
395                 rc = ebitmap_cpy(&dst->range.level[l].cat,
396                                  &src->range.level[0].cat);
397                 if (rc)
398                         break;
399         }
400
401         return rc;
402 }
403
404 /*
405  * Copies the MLS range `range' into `context'.
406  */
407 static inline int mls_range_set(struct context *context,
408                                 struct mls_range *range)
409 {
410         int l, rc = 0;
411
412         /* Copy the MLS range into the  context */
413         for (l = 0; l < 2; l++) {
414                 context->range.level[l].sens = range->level[l].sens;
415                 rc = ebitmap_cpy(&context->range.level[l].cat,
416                                  &range->level[l].cat);
417                 if (rc)
418                         break;
419         }
420
421         return rc;
422 }
423
424 int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
425                          struct context *usercon)
426 {
427         if (selinux_mls_enabled) {
428                 struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
429                 struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
430                 struct mls_level *user_low = &(user->range.level[0]);
431                 struct mls_level *user_clr = &(user->range.level[1]);
432                 struct mls_level *user_def = &(user->dfltlevel);
433                 struct mls_level *usercon_sen = &(usercon->range.level[0]);
434                 struct mls_level *usercon_clr = &(usercon->range.level[1]);
435
436                 /* Honor the user's default level if we can */
437                 if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) {
438                         *usercon_sen = *user_def;
439                 } else if (mls_level_between(fromcon_sen, user_def, user_clr)) {
440                         *usercon_sen = *fromcon_sen;
441                 } else if (mls_level_between(fromcon_clr, user_low, user_def)) {
442                         *usercon_sen = *user_low;
443                 } else
444                         return -EINVAL;
445
446                 /* Lower the clearance of available contexts
447                    if the clearance of "fromcon" is lower than
448                    that of the user's default clearance (but
449                    only if the "fromcon" clearance dominates
450                    the user's computed sensitivity level) */
451                 if (mls_level_dom(user_clr, fromcon_clr)) {
452                         *usercon_clr = *fromcon_clr;
453                 } else if (mls_level_dom(fromcon_clr, user_clr)) {
454                         *usercon_clr = *user_clr;
455                 } else
456                         return -EINVAL;
457         }
458
459         return 0;
460 }
461
462 /*
463  * Convert the MLS fields in the security context
464  * structure `c' from the values specified in the
465  * policy `oldp' to the values specified in the policy `newp'.
466  */
467 int mls_convert_context(struct policydb *oldp,
468                         struct policydb *newp,
469                         struct context *c)
470 {
471         struct level_datum *levdatum;
472         struct cat_datum *catdatum;
473         struct ebitmap bitmap;
474         struct ebitmap_node *node;
475         int l, i;
476
477         if (!selinux_mls_enabled)
478                 return 0;
479
480         for (l = 0; l < 2; l++) {
481                 levdatum = hashtab_search(newp->p_levels.table,
482                         oldp->p_sens_val_to_name[c->range.level[l].sens - 1]);
483
484                 if (!levdatum)
485                         return -EINVAL;
486                 c->range.level[l].sens = levdatum->level->sens;
487
488                 ebitmap_init(&bitmap);
489                 ebitmap_for_each_bit(&c->range.level[l].cat, node, i) {
490                         if (ebitmap_node_get_bit(node, i)) {
491                                 int rc;
492
493                                 catdatum = hashtab_search(newp->p_cats.table,
494                                                 oldp->p_cat_val_to_name[i]);
495                                 if (!catdatum)
496                                         return -EINVAL;
497                                 rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
498                                 if (rc)
499                                         return rc;
500                         }
501                 }
502                 ebitmap_destroy(&c->range.level[l].cat);
503                 c->range.level[l].cat = bitmap;
504         }
505
506         return 0;
507 }
508
509 int mls_compute_sid(struct context *scontext,
510                     struct context *tcontext,
511                     u16 tclass,
512                     u32 specified,
513                     struct context *newcontext)
514 {
515         if (!selinux_mls_enabled)
516                 return 0;
517
518         switch (specified) {
519         case AVTAB_TRANSITION:
520                 if (tclass == SECCLASS_PROCESS) {
521                         struct range_trans *rangetr;
522                         /* Look for a range transition rule. */
523                         for (rangetr = policydb.range_tr; rangetr;
524                              rangetr = rangetr->next) {
525                                 if (rangetr->dom == scontext->type &&
526                                     rangetr->type == tcontext->type) {
527                                         /* Set the range from the rule */
528                                         return mls_range_set(newcontext,
529                                                              &rangetr->range);
530                                 }
531                         }
532                 }
533                 /* Fallthrough */
534         case AVTAB_CHANGE:
535                 if (tclass == SECCLASS_PROCESS)
536                         /* Use the process MLS attributes. */
537                         return mls_copy_context(newcontext, scontext);
538                 else
539                         /* Use the process effective MLS attributes. */
540                         return mls_scopy_context(newcontext, scontext);
541         case AVTAB_MEMBER:
542                 /* Only polyinstantiate the MLS attributes if
543                    the type is being polyinstantiated */
544                 if (newcontext->type != tcontext->type) {
545                         /* Use the process effective MLS attributes. */
546                         return mls_scopy_context(newcontext, scontext);
547                 } else {
548                         /* Use the related object MLS attributes. */
549                         return mls_copy_context(newcontext, tcontext);
550                 }
551         default:
552                 return -EINVAL;
553         }
554         return -EINVAL;
555 }
556