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