Linux-libre 4.17.3-gnu
[librecmc/linux-libre.git] / security / selinux / ss / mls.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Implementation of the multi-level security (MLS) policy.
4  *
5  * Author : Stephen Smalley, <sds@tycho.nsa.gov>
6  */
7 /*
8  * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
9  *
10  *      Support for enhanced MLS infrastructure.
11  *
12  * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
13  */
14 /*
15  * Updated: Hewlett-Packard <paul@paul-moore.com>
16  *
17  *      Added support to import/export the MLS label from NetLabel
18  *
19  * (c) Copyright Hewlett-Packard Development Company, L.P., 2006
20  */
21
22 #include <linux/kernel.h>
23 #include <linux/slab.h>
24 #include <linux/string.h>
25 #include <linux/errno.h>
26 #include <net/netlabel.h>
27 #include "sidtab.h"
28 #include "mls.h"
29 #include "policydb.h"
30 #include "services.h"
31
32 /*
33  * Return the length in bytes for the MLS fields of the
34  * security context string representation of `context'.
35  */
36 int mls_compute_context_len(struct policydb *p, struct context *context)
37 {
38         int i, l, len, head, prev;
39         char *nm;
40         struct ebitmap *e;
41         struct ebitmap_node *node;
42
43         if (!p->mls_enabled)
44                 return 0;
45
46         len = 1; /* for the beginning ":" */
47         for (l = 0; l < 2; l++) {
48                 int index_sens = context->range.level[l].sens;
49                 len += strlen(sym_name(p, SYM_LEVELS, index_sens - 1));
50
51                 /* categories */
52                 head = -2;
53                 prev = -2;
54                 e = &context->range.level[l].cat;
55                 ebitmap_for_each_positive_bit(e, node, i) {
56                         if (i - prev > 1) {
57                                 /* one or more negative bits are skipped */
58                                 if (head != prev) {
59                                         nm = sym_name(p, SYM_CATS, prev);
60                                         len += strlen(nm) + 1;
61                                 }
62                                 nm = sym_name(p, SYM_CATS, i);
63                                 len += strlen(nm) + 1;
64                                 head = i;
65                         }
66                         prev = i;
67                 }
68                 if (prev != head) {
69                         nm = sym_name(p, SYM_CATS, prev);
70                         len += strlen(nm) + 1;
71                 }
72                 if (l == 0) {
73                         if (mls_level_eq(&context->range.level[0],
74                                          &context->range.level[1]))
75                                 break;
76                         else
77                                 len++;
78                 }
79         }
80
81         return len;
82 }
83
84 /*
85  * Write the security context string representation of
86  * the MLS fields of `context' into the string `*scontext'.
87  * Update `*scontext' to point to the end of the MLS fields.
88  */
89 void mls_sid_to_context(struct policydb *p,
90                         struct context *context,
91                         char **scontext)
92 {
93         char *scontextp, *nm;
94         int i, l, head, prev;
95         struct ebitmap *e;
96         struct ebitmap_node *node;
97
98         if (!p->mls_enabled)
99                 return;
100
101         scontextp = *scontext;
102
103         *scontextp = ':';
104         scontextp++;
105
106         for (l = 0; l < 2; l++) {
107                 strcpy(scontextp, sym_name(p, SYM_LEVELS,
108                                            context->range.level[l].sens - 1));
109                 scontextp += strlen(scontextp);
110
111                 /* categories */
112                 head = -2;
113                 prev = -2;
114                 e = &context->range.level[l].cat;
115                 ebitmap_for_each_positive_bit(e, node, i) {
116                         if (i - prev > 1) {
117                                 /* one or more negative bits are skipped */
118                                 if (prev != head) {
119                                         if (prev - head > 1)
120                                                 *scontextp++ = '.';
121                                         else
122                                                 *scontextp++ = ',';
123                                         nm = sym_name(p, SYM_CATS, prev);
124                                         strcpy(scontextp, nm);
125                                         scontextp += strlen(nm);
126                                 }
127                                 if (prev < 0)
128                                         *scontextp++ = ':';
129                                 else
130                                         *scontextp++ = ',';
131                                 nm = sym_name(p, SYM_CATS, i);
132                                 strcpy(scontextp, nm);
133                                 scontextp += strlen(nm);
134                                 head = i;
135                         }
136                         prev = i;
137                 }
138
139                 if (prev != head) {
140                         if (prev - head > 1)
141                                 *scontextp++ = '.';
142                         else
143                                 *scontextp++ = ',';
144                         nm = sym_name(p, SYM_CATS, prev);
145                         strcpy(scontextp, nm);
146                         scontextp += strlen(nm);
147                 }
148
149                 if (l == 0) {
150                         if (mls_level_eq(&context->range.level[0],
151                                          &context->range.level[1]))
152                                 break;
153                         else
154                                 *scontextp++ = '-';
155                 }
156         }
157
158         *scontext = scontextp;
159         return;
160 }
161
162 int mls_level_isvalid(struct policydb *p, struct mls_level *l)
163 {
164         struct level_datum *levdatum;
165
166         if (!l->sens || l->sens > p->p_levels.nprim)
167                 return 0;
168         levdatum = hashtab_search(p->p_levels.table,
169                                   sym_name(p, SYM_LEVELS, l->sens - 1));
170         if (!levdatum)
171                 return 0;
172
173         /*
174          * Return 1 iff all the bits set in l->cat are also be set in
175          * levdatum->level->cat and no bit in l->cat is larger than
176          * p->p_cats.nprim.
177          */
178         return ebitmap_contains(&levdatum->level->cat, &l->cat,
179                                 p->p_cats.nprim);
180 }
181
182 int mls_range_isvalid(struct policydb *p, struct mls_range *r)
183 {
184         return (mls_level_isvalid(p, &r->level[0]) &&
185                 mls_level_isvalid(p, &r->level[1]) &&
186                 mls_level_dom(&r->level[1], &r->level[0]));
187 }
188
189 /*
190  * Return 1 if the MLS fields in the security context
191  * structure `c' are valid.  Return 0 otherwise.
192  */
193 int mls_context_isvalid(struct policydb *p, struct context *c)
194 {
195         struct user_datum *usrdatum;
196
197         if (!p->mls_enabled)
198                 return 1;
199
200         if (!mls_range_isvalid(p, &c->range))
201                 return 0;
202
203         if (c->role == OBJECT_R_VAL)
204                 return 1;
205
206         /*
207          * User must be authorized for the MLS range.
208          */
209         if (!c->user || c->user > p->p_users.nprim)
210                 return 0;
211         usrdatum = p->user_val_to_struct[c->user - 1];
212         if (!mls_range_contains(usrdatum->range, c->range))
213                 return 0; /* user may not be associated with range */
214
215         return 1;
216 }
217
218 /*
219  * Set the MLS fields in the security context structure
220  * `context' based on the string representation in
221  * the string `*scontext'.  Update `*scontext' to
222  * point to the end of the string representation of
223  * the MLS fields.
224  *
225  * This function modifies the string in place, inserting
226  * NULL characters to terminate the MLS fields.
227  *
228  * If a def_sid is provided and no MLS field is present,
229  * copy the MLS field of the associated default context.
230  * Used for upgraded to MLS systems where objects may lack
231  * MLS fields.
232  *
233  * Policy read-lock must be held for sidtab lookup.
234  *
235  */
236 int mls_context_to_sid(struct policydb *pol,
237                        char oldc,
238                        char **scontext,
239                        struct context *context,
240                        struct sidtab *s,
241                        u32 def_sid)
242 {
243
244         char delim;
245         char *scontextp, *p, *rngptr;
246         struct level_datum *levdatum;
247         struct cat_datum *catdatum, *rngdatum;
248         int l, rc = -EINVAL;
249
250         if (!pol->mls_enabled) {
251                 if (def_sid != SECSID_NULL && oldc)
252                         *scontext += strlen(*scontext) + 1;
253                 return 0;
254         }
255
256         /*
257          * No MLS component to the security context, try and map to
258          * default if provided.
259          */
260         if (!oldc) {
261                 struct context *defcon;
262
263                 if (def_sid == SECSID_NULL)
264                         goto out;
265
266                 defcon = sidtab_search(s, def_sid);
267                 if (!defcon)
268                         goto out;
269
270                 rc = mls_context_cpy(context, defcon);
271                 goto out;
272         }
273
274         /* Extract low sensitivity. */
275         scontextp = p = *scontext;
276         while (*p && *p != ':' && *p != '-')
277                 p++;
278
279         delim = *p;
280         if (delim != '\0')
281                 *p++ = '\0';
282
283         for (l = 0; l < 2; l++) {
284                 levdatum = hashtab_search(pol->p_levels.table, scontextp);
285                 if (!levdatum) {
286                         rc = -EINVAL;
287                         goto out;
288                 }
289
290                 context->range.level[l].sens = levdatum->level->sens;
291
292                 if (delim == ':') {
293                         /* Extract category set. */
294                         while (1) {
295                                 scontextp = p;
296                                 while (*p && *p != ',' && *p != '-')
297                                         p++;
298                                 delim = *p;
299                                 if (delim != '\0')
300                                         *p++ = '\0';
301
302                                 /* Separate into range if exists */
303                                 rngptr = strchr(scontextp, '.');
304                                 if (rngptr != NULL) {
305                                         /* Remove '.' */
306                                         *rngptr++ = '\0';
307                                 }
308
309                                 catdatum = hashtab_search(pol->p_cats.table,
310                                                           scontextp);
311                                 if (!catdatum) {
312                                         rc = -EINVAL;
313                                         goto out;
314                                 }
315
316                                 rc = ebitmap_set_bit(&context->range.level[l].cat,
317                                                      catdatum->value - 1, 1);
318                                 if (rc)
319                                         goto out;
320
321                                 /* If range, set all categories in range */
322                                 if (rngptr) {
323                                         int i;
324
325                                         rngdatum = hashtab_search(pol->p_cats.table, rngptr);
326                                         if (!rngdatum) {
327                                                 rc = -EINVAL;
328                                                 goto out;
329                                         }
330
331                                         if (catdatum->value >= rngdatum->value) {
332                                                 rc = -EINVAL;
333                                                 goto out;
334                                         }
335
336                                         for (i = catdatum->value; i < rngdatum->value; i++) {
337                                                 rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
338                                                 if (rc)
339                                                         goto out;
340                                         }
341                                 }
342
343                                 if (delim != ',')
344                                         break;
345                         }
346                 }
347                 if (delim == '-') {
348                         /* Extract high sensitivity. */
349                         scontextp = p;
350                         while (*p && *p != ':')
351                                 p++;
352
353                         delim = *p;
354                         if (delim != '\0')
355                                 *p++ = '\0';
356                 } else
357                         break;
358         }
359
360         if (l == 0) {
361                 context->range.level[1].sens = context->range.level[0].sens;
362                 rc = ebitmap_cpy(&context->range.level[1].cat,
363                                  &context->range.level[0].cat);
364                 if (rc)
365                         goto out;
366         }
367         *scontext = ++p;
368         rc = 0;
369 out:
370         return rc;
371 }
372
373 /*
374  * Set the MLS fields in the security context structure
375  * `context' based on the string representation in
376  * the string `str'.  This function will allocate temporary memory with the
377  * given constraints of gfp_mask.
378  */
379 int mls_from_string(struct policydb *p, char *str, struct context *context,
380                     gfp_t gfp_mask)
381 {
382         char *tmpstr, *freestr;
383         int rc;
384
385         if (!p->mls_enabled)
386                 return -EINVAL;
387
388         /* we need freestr because mls_context_to_sid will change
389            the value of tmpstr */
390         tmpstr = freestr = kstrdup(str, gfp_mask);
391         if (!tmpstr) {
392                 rc = -ENOMEM;
393         } else {
394                 rc = mls_context_to_sid(p, ':', &tmpstr, context,
395                                         NULL, SECSID_NULL);
396                 kfree(freestr);
397         }
398
399         return rc;
400 }
401
402 /*
403  * Copies the MLS range `range' into `context'.
404  */
405 int mls_range_set(struct context *context,
406                                 struct mls_range *range)
407 {
408         int l, rc = 0;
409
410         /* Copy the MLS range into the  context */
411         for (l = 0; l < 2; l++) {
412                 context->range.level[l].sens = range->level[l].sens;
413                 rc = ebitmap_cpy(&context->range.level[l].cat,
414                                  &range->level[l].cat);
415                 if (rc)
416                         break;
417         }
418
419         return rc;
420 }
421
422 int mls_setup_user_range(struct policydb *p,
423                          struct context *fromcon, struct user_datum *user,
424                          struct context *usercon)
425 {
426         if (p->mls_enabled) {
427                 struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
428                 struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
429                 struct mls_level *user_low = &(user->range.level[0]);
430                 struct mls_level *user_clr = &(user->range.level[1]);
431                 struct mls_level *user_def = &(user->dfltlevel);
432                 struct mls_level *usercon_sen = &(usercon->range.level[0]);
433                 struct mls_level *usercon_clr = &(usercon->range.level[1]);
434
435                 /* Honor the user's default level if we can */
436                 if (mls_level_between(user_def, fromcon_sen, fromcon_clr))
437                         *usercon_sen = *user_def;
438                 else if (mls_level_between(fromcon_sen, user_def, user_clr))
439                         *usercon_sen = *fromcon_sen;
440                 else if (mls_level_between(fromcon_clr, user_low, user_def))
441                         *usercon_sen = *user_low;
442                 else
443                         return -EINVAL;
444
445                 /* Lower the clearance of available contexts
446                    if the clearance of "fromcon" is lower than
447                    that of the user's default clearance (but
448                    only if the "fromcon" clearance dominates
449                    the user's computed sensitivity level) */
450                 if (mls_level_dom(user_clr, fromcon_clr))
451                         *usercon_clr = *fromcon_clr;
452                 else if (mls_level_dom(fromcon_clr, user_clr))
453                         *usercon_clr = *user_clr;
454                 else
455                         return -EINVAL;
456         }
457
458         return 0;
459 }
460
461 /*
462  * Convert the MLS fields in the security context
463  * structure `c' from the values specified in the
464  * policy `oldp' to the values specified in the policy `newp'.
465  */
466 int mls_convert_context(struct policydb *oldp,
467                         struct policydb *newp,
468                         struct context *c)
469 {
470         struct level_datum *levdatum;
471         struct cat_datum *catdatum;
472         struct ebitmap bitmap;
473         struct ebitmap_node *node;
474         int l, i;
475
476         if (!oldp->mls_enabled || !newp->mls_enabled)
477                 return 0;
478
479         for (l = 0; l < 2; l++) {
480                 levdatum = hashtab_search(newp->p_levels.table,
481                                           sym_name(oldp, SYM_LEVELS,
482                                                    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_positive_bit(&c->range.level[l].cat, node, i) {
490                         int rc;
491
492                         catdatum = hashtab_search(newp->p_cats.table,
493                                                   sym_name(oldp, SYM_CATS, i));
494                         if (!catdatum)
495                                 return -EINVAL;
496                         rc = ebitmap_set_bit(&bitmap, catdatum->value - 1, 1);
497                         if (rc)
498                                 return rc;
499
500                         cond_resched();
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 policydb *p,
510                     struct context *scontext,
511                     struct context *tcontext,
512                     u16 tclass,
513                     u32 specified,
514                     struct context *newcontext,
515                     bool sock)
516 {
517         struct range_trans rtr;
518         struct mls_range *r;
519         struct class_datum *cladatum;
520         int default_range = 0;
521
522         if (!p->mls_enabled)
523                 return 0;
524
525         switch (specified) {
526         case AVTAB_TRANSITION:
527                 /* Look for a range transition rule. */
528                 rtr.source_type = scontext->type;
529                 rtr.target_type = tcontext->type;
530                 rtr.target_class = tclass;
531                 r = hashtab_search(p->range_tr, &rtr);
532                 if (r)
533                         return mls_range_set(newcontext, r);
534
535                 if (tclass && tclass <= p->p_classes.nprim) {
536                         cladatum = p->class_val_to_struct[tclass - 1];
537                         if (cladatum)
538                                 default_range = cladatum->default_range;
539                 }
540
541                 switch (default_range) {
542                 case DEFAULT_SOURCE_LOW:
543                         return mls_context_cpy_low(newcontext, scontext);
544                 case DEFAULT_SOURCE_HIGH:
545                         return mls_context_cpy_high(newcontext, scontext);
546                 case DEFAULT_SOURCE_LOW_HIGH:
547                         return mls_context_cpy(newcontext, scontext);
548                 case DEFAULT_TARGET_LOW:
549                         return mls_context_cpy_low(newcontext, tcontext);
550                 case DEFAULT_TARGET_HIGH:
551                         return mls_context_cpy_high(newcontext, tcontext);
552                 case DEFAULT_TARGET_LOW_HIGH:
553                         return mls_context_cpy(newcontext, tcontext);
554                 }
555
556                 /* Fallthrough */
557         case AVTAB_CHANGE:
558                 if ((tclass == p->process_class) || (sock == true))
559                         /* Use the process MLS attributes. */
560                         return mls_context_cpy(newcontext, scontext);
561                 else
562                         /* Use the process effective MLS attributes. */
563                         return mls_context_cpy_low(newcontext, scontext);
564         case AVTAB_MEMBER:
565                 /* Use the process effective MLS attributes. */
566                 return mls_context_cpy_low(newcontext, scontext);
567
568         /* fall through */
569         }
570         return -EINVAL;
571 }
572
573 #ifdef CONFIG_NETLABEL
574 /**
575  * mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel
576  * @context: the security context
577  * @secattr: the NetLabel security attributes
578  *
579  * Description:
580  * Given the security context copy the low MLS sensitivity level into the
581  * NetLabel MLS sensitivity level field.
582  *
583  */
584 void mls_export_netlbl_lvl(struct policydb *p,
585                            struct context *context,
586                            struct netlbl_lsm_secattr *secattr)
587 {
588         if (!p->mls_enabled)
589                 return;
590
591         secattr->attr.mls.lvl = context->range.level[0].sens - 1;
592         secattr->flags |= NETLBL_SECATTR_MLS_LVL;
593 }
594
595 /**
596  * mls_import_netlbl_lvl - Import the NetLabel MLS sensitivity levels
597  * @context: the security context
598  * @secattr: the NetLabel security attributes
599  *
600  * Description:
601  * Given the security context and the NetLabel security attributes, copy the
602  * NetLabel MLS sensitivity level into the context.
603  *
604  */
605 void mls_import_netlbl_lvl(struct policydb *p,
606                            struct context *context,
607                            struct netlbl_lsm_secattr *secattr)
608 {
609         if (!p->mls_enabled)
610                 return;
611
612         context->range.level[0].sens = secattr->attr.mls.lvl + 1;
613         context->range.level[1].sens = context->range.level[0].sens;
614 }
615
616 /**
617  * mls_export_netlbl_cat - Export the MLS categories to NetLabel
618  * @context: the security context
619  * @secattr: the NetLabel security attributes
620  *
621  * Description:
622  * Given the security context copy the low MLS categories into the NetLabel
623  * MLS category field.  Returns zero on success, negative values on failure.
624  *
625  */
626 int mls_export_netlbl_cat(struct policydb *p,
627                           struct context *context,
628                           struct netlbl_lsm_secattr *secattr)
629 {
630         int rc;
631
632         if (!p->mls_enabled)
633                 return 0;
634
635         rc = ebitmap_netlbl_export(&context->range.level[0].cat,
636                                    &secattr->attr.mls.cat);
637         if (rc == 0 && secattr->attr.mls.cat != NULL)
638                 secattr->flags |= NETLBL_SECATTR_MLS_CAT;
639
640         return rc;
641 }
642
643 /**
644  * mls_import_netlbl_cat - Import the MLS categories from NetLabel
645  * @context: the security context
646  * @secattr: the NetLabel security attributes
647  *
648  * Description:
649  * Copy the NetLabel security attributes into the SELinux context; since the
650  * NetLabel security attribute only contains a single MLS category use it for
651  * both the low and high categories of the context.  Returns zero on success,
652  * negative values on failure.
653  *
654  */
655 int mls_import_netlbl_cat(struct policydb *p,
656                           struct context *context,
657                           struct netlbl_lsm_secattr *secattr)
658 {
659         int rc;
660
661         if (!p->mls_enabled)
662                 return 0;
663
664         rc = ebitmap_netlbl_import(&context->range.level[0].cat,
665                                    secattr->attr.mls.cat);
666         if (rc)
667                 goto import_netlbl_cat_failure;
668         memcpy(&context->range.level[1].cat, &context->range.level[0].cat,
669                sizeof(context->range.level[0].cat));
670
671         return 0;
672
673 import_netlbl_cat_failure:
674         ebitmap_destroy(&context->range.level[0].cat);
675         return rc;
676 }
677 #endif /* CONFIG_NETLABEL */