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