Merge branch 'upstream-linus' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[linux-2.6] / 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                 if (def_sid != SECSID_NULL && oldc)
267                         *scontext += strlen(*scontext);
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  * Copies the effective MLS range from `src' into `dst'.
389  */
390 static inline int mls_scopy_context(struct context *dst,
391                                     struct context *src)
392 {
393         int l, rc = 0;
394
395         /* Copy the MLS range from the source context */
396         for (l = 0; l < 2; l++) {
397                 dst->range.level[l].sens = src->range.level[0].sens;
398                 rc = ebitmap_cpy(&dst->range.level[l].cat,
399                                  &src->range.level[0].cat);
400                 if (rc)
401                         break;
402         }
403
404         return rc;
405 }
406
407 /*
408  * Copies the MLS range `range' into `context'.
409  */
410 static inline int mls_range_set(struct context *context,
411                                 struct mls_range *range)
412 {
413         int l, rc = 0;
414
415         /* Copy the MLS range into the  context */
416         for (l = 0; l < 2; l++) {
417                 context->range.level[l].sens = range->level[l].sens;
418                 rc = ebitmap_cpy(&context->range.level[l].cat,
419                                  &range->level[l].cat);
420                 if (rc)
421                         break;
422         }
423
424         return rc;
425 }
426
427 int mls_setup_user_range(struct context *fromcon, struct user_datum *user,
428                          struct context *usercon)
429 {
430         if (selinux_mls_enabled) {
431                 struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
432                 struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
433                 struct mls_level *user_low = &(user->range.level[0]);
434                 struct mls_level *user_clr = &(user->range.level[1]);
435                 struct mls_level *user_def = &(user->dfltlevel);
436                 struct mls_level *usercon_sen = &(usercon->range.level[0]);
437                 struct mls_level *usercon_clr = &(usercon->range.level[1]);
438
439                 /* Honor the user's default level if we can */
440                 if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) {
441                         *usercon_sen = *user_def;
442                 } else if (mls_level_between(fromcon_sen, user_def, user_clr)) {
443                         *usercon_sen = *fromcon_sen;
444                 } else if (mls_level_between(fromcon_clr, user_low, user_def)) {
445                         *usercon_sen = *user_low;
446                 } else
447                         return -EINVAL;
448
449                 /* Lower the clearance of available contexts
450                    if the clearance of "fromcon" is lower than
451                    that of the user's default clearance (but
452                    only if the "fromcon" clearance dominates
453                    the user's computed sensitivity level) */
454                 if (mls_level_dom(user_clr, fromcon_clr)) {
455                         *usercon_clr = *fromcon_clr;
456                 } else if (mls_level_dom(fromcon_clr, user_clr)) {
457                         *usercon_clr = *user_clr;
458                 } else
459                         return -EINVAL;
460         }
461
462         return 0;
463 }
464
465 /*
466  * Convert the MLS fields in the security context
467  * structure `c' from the values specified in the
468  * policy `oldp' to the values specified in the policy `newp'.
469  */
470 int mls_convert_context(struct policydb *oldp,
471                         struct policydb *newp,
472                         struct context *c)
473 {
474         struct level_datum *levdatum;
475         struct cat_datum *catdatum;
476         struct ebitmap bitmap;
477         struct ebitmap_node *node;
478         int l, i;
479
480         if (!selinux_mls_enabled)
481                 return 0;
482
483         for (l = 0; l < 2; l++) {
484                 levdatum = hashtab_search(newp->p_levels.table,
485                         oldp->p_sens_val_to_name[c->range.level[l].sens - 1]);
486
487                 if (!levdatum)
488                         return -EINVAL;
489                 c->range.level[l].sens = levdatum->level->sens;
490
491                 ebitmap_init(&bitmap);
492                 ebitmap_for_each_bit(&c->range.level[l].cat, node, i) {
493                         if (ebitmap_node_get_bit(node, i)) {
494                                 int rc;
495
496                                 catdatum = hashtab_search(newp->p_cats.table,
497                                                 oldp->p_cat_val_to_name[i]);
498                                 if (!catdatum)
499                                         return -EINVAL;
500                                 rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
501                                 if (rc)
502                                         return rc;
503                         }
504                 }
505                 ebitmap_destroy(&c->range.level[l].cat);
506                 c->range.level[l].cat = bitmap;
507         }
508
509         return 0;
510 }
511
512 int mls_compute_sid(struct context *scontext,
513                     struct context *tcontext,
514                     u16 tclass,
515                     u32 specified,
516                     struct context *newcontext)
517 {
518         if (!selinux_mls_enabled)
519                 return 0;
520
521         switch (specified) {
522         case AVTAB_TRANSITION:
523                 if (tclass == SECCLASS_PROCESS) {
524                         struct range_trans *rangetr;
525                         /* Look for a range transition rule. */
526                         for (rangetr = policydb.range_tr; rangetr;
527                              rangetr = rangetr->next) {
528                                 if (rangetr->dom == scontext->type &&
529                                     rangetr->type == tcontext->type) {
530                                         /* Set the range from the rule */
531                                         return mls_range_set(newcontext,
532                                                              &rangetr->range);
533                                 }
534                         }
535                 }
536                 /* Fallthrough */
537         case AVTAB_CHANGE:
538                 if (tclass == SECCLASS_PROCESS)
539                         /* Use the process MLS attributes. */
540                         return mls_copy_context(newcontext, scontext);
541                 else
542                         /* Use the process effective MLS attributes. */
543                         return mls_scopy_context(newcontext, scontext);
544         case AVTAB_MEMBER:
545                 /* Only polyinstantiate the MLS attributes if
546                    the type is being polyinstantiated */
547                 if (newcontext->type != tcontext->type) {
548                         /* Use the process effective MLS attributes. */
549                         return mls_scopy_context(newcontext, scontext);
550                 } else {
551                         /* Use the related object MLS attributes. */
552                         return mls_copy_context(newcontext, tcontext);
553                 }
554         default:
555                 return -EINVAL;
556         }
557         return -EINVAL;
558 }
559