GNU Linux-libre 5.4.241-gnu1
[releases.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'.
222  *
223  * This function modifies the string in place, inserting
224  * NULL characters to terminate the MLS fields.
225  *
226  * If a def_sid is provided and no MLS field is present,
227  * copy the MLS field of the associated default context.
228  * Used for upgraded to MLS systems where objects may lack
229  * MLS fields.
230  *
231  * Policy read-lock must be held for sidtab lookup.
232  *
233  */
234 int mls_context_to_sid(struct policydb *pol,
235                        char oldc,
236                        char *scontext,
237                        struct context *context,
238                        struct sidtab *s,
239                        u32 def_sid)
240 {
241         char *sensitivity, *cur_cat, *next_cat, *rngptr;
242         struct level_datum *levdatum;
243         struct cat_datum *catdatum, *rngdatum;
244         int l, rc, i;
245         char *rangep[2];
246
247         if (!pol->mls_enabled) {
248                 /*
249                  * With no MLS, only return -EINVAL if there is a MLS field
250                  * and it did not come from an xattr.
251                  */
252                 if (oldc && def_sid == SECSID_NULL)
253                         return -EINVAL;
254                 return 0;
255         }
256
257         /*
258          * No MLS component to the security context, try and map to
259          * default if provided.
260          */
261         if (!oldc) {
262                 struct context *defcon;
263
264                 if (def_sid == SECSID_NULL)
265                         return -EINVAL;
266
267                 defcon = sidtab_search(s, def_sid);
268                 if (!defcon)
269                         return -EINVAL;
270
271                 return mls_context_cpy(context, defcon);
272         }
273
274         /*
275          * If we're dealing with a range, figure out where the two parts
276          * of the range begin.
277          */
278         rangep[0] = scontext;
279         rangep[1] = strchr(scontext, '-');
280         if (rangep[1]) {
281                 rangep[1][0] = '\0';
282                 rangep[1]++;
283         }
284
285         /* For each part of the range: */
286         for (l = 0; l < 2; l++) {
287                 /* Split sensitivity and category set. */
288                 sensitivity = rangep[l];
289                 if (sensitivity == NULL)
290                         break;
291                 next_cat = strchr(sensitivity, ':');
292                 if (next_cat)
293                         *(next_cat++) = '\0';
294
295                 /* Parse sensitivity. */
296                 levdatum = hashtab_search(pol->p_levels.table, sensitivity);
297                 if (!levdatum)
298                         return -EINVAL;
299                 context->range.level[l].sens = levdatum->level->sens;
300
301                 /* Extract category set. */
302                 while (next_cat != NULL) {
303                         cur_cat = next_cat;
304                         next_cat = strchr(next_cat, ',');
305                         if (next_cat != NULL)
306                                 *(next_cat++) = '\0';
307
308                         /* Separate into range if exists */
309                         rngptr = strchr(cur_cat, '.');
310                         if (rngptr != NULL) {
311                                 /* Remove '.' */
312                                 *rngptr++ = '\0';
313                         }
314
315                         catdatum = hashtab_search(pol->p_cats.table, cur_cat);
316                         if (!catdatum)
317                                 return -EINVAL;
318
319                         rc = ebitmap_set_bit(&context->range.level[l].cat,
320                                              catdatum->value - 1, 1);
321                         if (rc)
322                                 return rc;
323
324                         /* If range, set all categories in range */
325                         if (rngptr == NULL)
326                                 continue;
327
328                         rngdatum = hashtab_search(pol->p_cats.table, rngptr);
329                         if (!rngdatum)
330                                 return -EINVAL;
331
332                         if (catdatum->value >= rngdatum->value)
333                                 return -EINVAL;
334
335                         for (i = catdatum->value; i < rngdatum->value; i++) {
336                                 rc = ebitmap_set_bit(&context->range.level[l].cat, i, 1);
337                                 if (rc)
338                                         return rc;
339                         }
340                 }
341         }
342
343         /* If we didn't see a '-', the range start is also the range end. */
344         if (rangep[1] == NULL) {
345                 context->range.level[1].sens = context->range.level[0].sens;
346                 rc = ebitmap_cpy(&context->range.level[1].cat,
347                                  &context->range.level[0].cat);
348                 if (rc)
349                         return rc;
350         }
351
352         return 0;
353 }
354
355 /*
356  * Set the MLS fields in the security context structure
357  * `context' based on the string representation in
358  * the string `str'.  This function will allocate temporary memory with the
359  * given constraints of gfp_mask.
360  */
361 int mls_from_string(struct policydb *p, char *str, struct context *context,
362                     gfp_t gfp_mask)
363 {
364         char *tmpstr;
365         int rc;
366
367         if (!p->mls_enabled)
368                 return -EINVAL;
369
370         tmpstr = kstrdup(str, gfp_mask);
371         if (!tmpstr) {
372                 rc = -ENOMEM;
373         } else {
374                 rc = mls_context_to_sid(p, ':', tmpstr, context,
375                                         NULL, SECSID_NULL);
376                 kfree(tmpstr);
377         }
378
379         return rc;
380 }
381
382 /*
383  * Copies the MLS range `range' into `context'.
384  */
385 int mls_range_set(struct context *context,
386                                 struct mls_range *range)
387 {
388         int l, rc = 0;
389
390         /* Copy the MLS range into the  context */
391         for (l = 0; l < 2; l++) {
392                 context->range.level[l].sens = range->level[l].sens;
393                 rc = ebitmap_cpy(&context->range.level[l].cat,
394                                  &range->level[l].cat);
395                 if (rc)
396                         break;
397         }
398
399         return rc;
400 }
401
402 int mls_setup_user_range(struct policydb *p,
403                          struct context *fromcon, struct user_datum *user,
404                          struct context *usercon)
405 {
406         if (p->mls_enabled) {
407                 struct mls_level *fromcon_sen = &(fromcon->range.level[0]);
408                 struct mls_level *fromcon_clr = &(fromcon->range.level[1]);
409                 struct mls_level *user_low = &(user->range.level[0]);
410                 struct mls_level *user_clr = &(user->range.level[1]);
411                 struct mls_level *user_def = &(user->dfltlevel);
412                 struct mls_level *usercon_sen = &(usercon->range.level[0]);
413                 struct mls_level *usercon_clr = &(usercon->range.level[1]);
414
415                 /* Honor the user's default level if we can */
416                 if (mls_level_between(user_def, fromcon_sen, fromcon_clr))
417                         *usercon_sen = *user_def;
418                 else if (mls_level_between(fromcon_sen, user_def, user_clr))
419                         *usercon_sen = *fromcon_sen;
420                 else if (mls_level_between(fromcon_clr, user_low, user_def))
421                         *usercon_sen = *user_low;
422                 else
423                         return -EINVAL;
424
425                 /* Lower the clearance of available contexts
426                    if the clearance of "fromcon" is lower than
427                    that of the user's default clearance (but
428                    only if the "fromcon" clearance dominates
429                    the user's computed sensitivity level) */
430                 if (mls_level_dom(user_clr, fromcon_clr))
431                         *usercon_clr = *fromcon_clr;
432                 else if (mls_level_dom(fromcon_clr, user_clr))
433                         *usercon_clr = *user_clr;
434                 else
435                         return -EINVAL;
436         }
437
438         return 0;
439 }
440
441 /*
442  * Convert the MLS fields in the security context
443  * structure `oldc' from the values specified in the
444  * policy `oldp' to the values specified in the policy `newp',
445  * storing the resulting context in `newc'.
446  */
447 int mls_convert_context(struct policydb *oldp,
448                         struct policydb *newp,
449                         struct context *oldc,
450                         struct context *newc)
451 {
452         struct level_datum *levdatum;
453         struct cat_datum *catdatum;
454         struct ebitmap_node *node;
455         int l, i;
456
457         if (!oldp->mls_enabled || !newp->mls_enabled)
458                 return 0;
459
460         for (l = 0; l < 2; l++) {
461                 levdatum = hashtab_search(newp->p_levels.table,
462                                           sym_name(oldp, SYM_LEVELS,
463                                                    oldc->range.level[l].sens - 1));
464
465                 if (!levdatum)
466                         return -EINVAL;
467                 newc->range.level[l].sens = levdatum->level->sens;
468
469                 ebitmap_for_each_positive_bit(&oldc->range.level[l].cat,
470                                               node, i) {
471                         int rc;
472
473                         catdatum = hashtab_search(newp->p_cats.table,
474                                                   sym_name(oldp, SYM_CATS, i));
475                         if (!catdatum)
476                                 return -EINVAL;
477                         rc = ebitmap_set_bit(&newc->range.level[l].cat,
478                                              catdatum->value - 1, 1);
479                         if (rc)
480                                 return rc;
481                 }
482         }
483
484         return 0;
485 }
486
487 int mls_compute_sid(struct policydb *p,
488                     struct context *scontext,
489                     struct context *tcontext,
490                     u16 tclass,
491                     u32 specified,
492                     struct context *newcontext,
493                     bool sock)
494 {
495         struct range_trans rtr;
496         struct mls_range *r;
497         struct class_datum *cladatum;
498         int default_range = 0;
499
500         if (!p->mls_enabled)
501                 return 0;
502
503         switch (specified) {
504         case AVTAB_TRANSITION:
505                 /* Look for a range transition rule. */
506                 rtr.source_type = scontext->type;
507                 rtr.target_type = tcontext->type;
508                 rtr.target_class = tclass;
509                 r = hashtab_search(p->range_tr, &rtr);
510                 if (r)
511                         return mls_range_set(newcontext, r);
512
513                 if (tclass && tclass <= p->p_classes.nprim) {
514                         cladatum = p->class_val_to_struct[tclass - 1];
515                         if (cladatum)
516                                 default_range = cladatum->default_range;
517                 }
518
519                 switch (default_range) {
520                 case DEFAULT_SOURCE_LOW:
521                         return mls_context_cpy_low(newcontext, scontext);
522                 case DEFAULT_SOURCE_HIGH:
523                         return mls_context_cpy_high(newcontext, scontext);
524                 case DEFAULT_SOURCE_LOW_HIGH:
525                         return mls_context_cpy(newcontext, scontext);
526                 case DEFAULT_TARGET_LOW:
527                         return mls_context_cpy_low(newcontext, tcontext);
528                 case DEFAULT_TARGET_HIGH:
529                         return mls_context_cpy_high(newcontext, tcontext);
530                 case DEFAULT_TARGET_LOW_HIGH:
531                         return mls_context_cpy(newcontext, tcontext);
532                 }
533
534                 /* Fallthrough */
535         case AVTAB_CHANGE:
536                 if ((tclass == p->process_class) || (sock == true))
537                         /* Use the process MLS attributes. */
538                         return mls_context_cpy(newcontext, scontext);
539                 else
540                         /* Use the process effective MLS attributes. */
541                         return mls_context_cpy_low(newcontext, scontext);
542         case AVTAB_MEMBER:
543                 /* Use the process effective MLS attributes. */
544                 return mls_context_cpy_low(newcontext, scontext);
545
546         /* fall through */
547         }
548         return -EINVAL;
549 }
550
551 #ifdef CONFIG_NETLABEL
552 /**
553  * mls_export_netlbl_lvl - Export the MLS sensitivity levels to NetLabel
554  * @context: the security context
555  * @secattr: the NetLabel security attributes
556  *
557  * Description:
558  * Given the security context copy the low MLS sensitivity level into the
559  * NetLabel MLS sensitivity level field.
560  *
561  */
562 void mls_export_netlbl_lvl(struct policydb *p,
563                            struct context *context,
564                            struct netlbl_lsm_secattr *secattr)
565 {
566         if (!p->mls_enabled)
567                 return;
568
569         secattr->attr.mls.lvl = context->range.level[0].sens - 1;
570         secattr->flags |= NETLBL_SECATTR_MLS_LVL;
571 }
572
573 /**
574  * mls_import_netlbl_lvl - Import the NetLabel MLS sensitivity levels
575  * @context: the security context
576  * @secattr: the NetLabel security attributes
577  *
578  * Description:
579  * Given the security context and the NetLabel security attributes, copy the
580  * NetLabel MLS sensitivity level into the context.
581  *
582  */
583 void mls_import_netlbl_lvl(struct policydb *p,
584                            struct context *context,
585                            struct netlbl_lsm_secattr *secattr)
586 {
587         if (!p->mls_enabled)
588                 return;
589
590         context->range.level[0].sens = secattr->attr.mls.lvl + 1;
591         context->range.level[1].sens = context->range.level[0].sens;
592 }
593
594 /**
595  * mls_export_netlbl_cat - Export the MLS categories to NetLabel
596  * @context: the security context
597  * @secattr: the NetLabel security attributes
598  *
599  * Description:
600  * Given the security context copy the low MLS categories into the NetLabel
601  * MLS category field.  Returns zero on success, negative values on failure.
602  *
603  */
604 int mls_export_netlbl_cat(struct policydb *p,
605                           struct context *context,
606                           struct netlbl_lsm_secattr *secattr)
607 {
608         int rc;
609
610         if (!p->mls_enabled)
611                 return 0;
612
613         rc = ebitmap_netlbl_export(&context->range.level[0].cat,
614                                    &secattr->attr.mls.cat);
615         if (rc == 0 && secattr->attr.mls.cat != NULL)
616                 secattr->flags |= NETLBL_SECATTR_MLS_CAT;
617
618         return rc;
619 }
620
621 /**
622  * mls_import_netlbl_cat - Import the MLS categories from NetLabel
623  * @context: the security context
624  * @secattr: the NetLabel security attributes
625  *
626  * Description:
627  * Copy the NetLabel security attributes into the SELinux context; since the
628  * NetLabel security attribute only contains a single MLS category use it for
629  * both the low and high categories of the context.  Returns zero on success,
630  * negative values on failure.
631  *
632  */
633 int mls_import_netlbl_cat(struct policydb *p,
634                           struct context *context,
635                           struct netlbl_lsm_secattr *secattr)
636 {
637         int rc;
638
639         if (!p->mls_enabled)
640                 return 0;
641
642         rc = ebitmap_netlbl_import(&context->range.level[0].cat,
643                                    secattr->attr.mls.cat);
644         if (rc)
645                 goto import_netlbl_cat_failure;
646         memcpy(&context->range.level[1].cat, &context->range.level[0].cat,
647                sizeof(context->range.level[0].cat));
648
649         return 0;
650
651 import_netlbl_cat_failure:
652         ebitmap_destroy(&context->range.level[0].cat);
653         return rc;
654 }
655 #endif /* CONFIG_NETLABEL */