1 /* Author : Stephen Smalley, <sds@epoch.ncsc.mil> */
2 /*
3 * Updated: Trusted Computer Solutions, Inc. <dgoeddel@trustedcs.com>
4 *
5 * Support for enhanced MLS infrastructure.
6 *
7 * Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 /* FLASK */
25
26 /*
27 * Implementation of the multi-level security (MLS) policy.
28 */
29
30 #include <sepol/policydb/policydb.h>
31 #include <sepol/policydb/services.h>
32 #include <sepol/policydb/flask.h>
33 #include <sepol/policydb/context.h>
34
35 #include <stdlib.h>
36
37 #include "handle.h"
38 #include "debug.h"
39 #include "private.h"
40 #include "mls.h"
41
mls_to_string(sepol_handle_t * handle,const policydb_t * policydb,const context_struct_t * mls,char ** str)42 int mls_to_string(sepol_handle_t * handle,
43 const policydb_t * policydb,
44 const context_struct_t * mls, char **str)
45 {
46
47 char *ptr = NULL, *ptr2 = NULL;
48
49 /* Temporary buffer - length + NULL terminator */
50 int len = mls_compute_context_len(policydb, mls) + 1;
51
52 ptr = (char *)malloc(len);
53 if (ptr == NULL)
54 goto omem;
55
56 /* Final string w/ ':' cut off */
57 ptr2 = (char *)malloc(len - 1);
58 if (ptr2 == NULL)
59 goto omem;
60
61 mls_sid_to_context(policydb, mls, &ptr);
62 ptr -= len - 1;
63 strcpy(ptr2, ptr + 1);
64
65 free(ptr);
66 *str = ptr2;
67 return STATUS_SUCCESS;
68
69 omem:
70 ERR(handle, "out of memory, could not convert mls context to string");
71
72 free(ptr);
73 free(ptr2);
74 return STATUS_ERR;
75
76 }
77
mls_from_string(sepol_handle_t * handle,const policydb_t * policydb,const char * str,context_struct_t * mls)78 int mls_from_string(sepol_handle_t * handle,
79 const policydb_t * policydb,
80 const char *str, context_struct_t * mls)
81 {
82
83 char *tmp = strdup(str);
84 char *tmp_cp = tmp;
85 if (!tmp)
86 goto omem;
87
88 if (mls_context_to_sid(policydb, '$', &tmp_cp, mls) < 0) {
89 ERR(handle, "invalid MLS context %s", str);
90 free(tmp);
91 goto err;
92 }
93
94 free(tmp);
95 return STATUS_SUCCESS;
96
97 omem:
98 ERR(handle, "out of memory");
99
100 err:
101 ERR(handle, "could not construct mls context structure");
102 return STATUS_ERR;
103 }
104
105 /*
106 * Return the length in bytes for the MLS fields of the
107 * security context string representation of `context'.
108 */
mls_compute_context_len(const policydb_t * policydb,const context_struct_t * context)109 int mls_compute_context_len(const policydb_t * policydb,
110 const context_struct_t * context)
111 {
112
113 unsigned int i, l, len, range;
114 ebitmap_node_t *cnode;
115
116 if (!policydb->mls)
117 return 0;
118
119 len = 1; /* for the beginning ":" */
120 for (l = 0; l < 2; l++) {
121 range = 0;
122 len +=
123 strlen(policydb->
124 p_sens_val_to_name[context->range.level[l].sens -
125 1]);
126
127 ebitmap_for_each_bit(&context->range.level[l].cat, cnode, i) {
128 if (ebitmap_node_get_bit(cnode, i)) {
129 if (range) {
130 range++;
131 continue;
132 }
133
134 len +=
135 strlen(policydb->p_cat_val_to_name[i]) + 1;
136 range++;
137 } else {
138 if (range > 1)
139 len +=
140 strlen(policydb->
141 p_cat_val_to_name[i - 1]) +
142 1;
143 range = 0;
144 }
145 }
146 /* Handle case where last category is the end of range */
147 if (range > 1)
148 len += strlen(policydb->p_cat_val_to_name[i - 1]) + 1;
149
150 if (l == 0) {
151 if (mls_level_eq(&context->range.level[0],
152 &context->range.level[1]))
153 break;
154 else
155 len++;
156 }
157 }
158
159 return len;
160 }
161
162 /*
163 * Write the security context string representation of
164 * the MLS fields of `context' into the string `*scontext'.
165 * Update `*scontext' to point to the end of the MLS fields.
166 */
mls_sid_to_context(const policydb_t * policydb,const context_struct_t * context,char ** scontext)167 void mls_sid_to_context(const policydb_t * policydb,
168 const context_struct_t * context, char **scontext)
169 {
170
171 char *scontextp;
172 unsigned int i, l, range, wrote_sep;
173 ebitmap_node_t *cnode;
174
175 if (!policydb->mls)
176 return;
177
178 scontextp = *scontext;
179
180 *scontextp = ':';
181 scontextp++;
182
183 for (l = 0; l < 2; l++) {
184 range = 0;
185 wrote_sep = 0;
186 strcpy(scontextp,
187 policydb->p_sens_val_to_name[context->range.level[l].
188 sens - 1]);
189 scontextp +=
190 strlen(policydb->
191 p_sens_val_to_name[context->range.level[l].sens -
192 1]);
193 /* categories */
194 ebitmap_for_each_bit(&context->range.level[l].cat, cnode, i) {
195 if (ebitmap_node_get_bit(cnode, i)) {
196 if (range) {
197 range++;
198 continue;
199 }
200
201 if (!wrote_sep) {
202 *scontextp++ = ':';
203 wrote_sep = 1;
204 } else
205 *scontextp++ = ',';
206 strcpy(scontextp,
207 policydb->p_cat_val_to_name[i]);
208 scontextp +=
209 strlen(policydb->p_cat_val_to_name[i]);
210 range++;
211 } else {
212 if (range > 1) {
213 if (range > 2)
214 *scontextp++ = '.';
215 else
216 *scontextp++ = ',';
217
218 strcpy(scontextp,
219 policydb->p_cat_val_to_name[i -
220 1]);
221 scontextp +=
222 strlen(policydb->
223 p_cat_val_to_name[i - 1]);
224 }
225 range = 0;
226 }
227 }
228 /* Handle case where last category is the end of range */
229 if (range > 1) {
230 if (range > 2)
231 *scontextp++ = '.';
232 else
233 *scontextp++ = ',';
234
235 strcpy(scontextp, policydb->p_cat_val_to_name[i - 1]);
236 scontextp += strlen(policydb->p_cat_val_to_name[i - 1]);
237 }
238
239 if (l == 0) {
240 if (mls_level_eq(&context->range.level[0],
241 &context->range.level[1]))
242 break;
243 else {
244 *scontextp = '-';
245 scontextp++;
246 }
247 }
248 }
249
250 *scontext = scontextp;
251 return;
252 }
253
254 /*
255 * Return 1 if the MLS fields in the security context
256 * structure `c' are valid. Return 0 otherwise.
257 */
mls_context_isvalid(const policydb_t * p,const context_struct_t * c)258 int mls_context_isvalid(const policydb_t * p, const context_struct_t * c)
259 {
260
261 level_datum_t *levdatum;
262 user_datum_t *usrdatum;
263 unsigned int i, l;
264 ebitmap_node_t *cnode;
265
266 if (!p->mls)
267 return 1;
268
269 /*
270 * MLS range validity checks: high must dominate low, low level must
271 * be valid (category set <-> sensitivity check), and high level must
272 * be valid (category set <-> sensitivity check)
273 */
274 if (!mls_level_dom(&c->range.level[1], &c->range.level[0]))
275 /* High does not dominate low. */
276 return 0;
277
278 for (l = 0; l < 2; l++) {
279 if (!c->range.level[l].sens
280 || c->range.level[l].sens > p->p_levels.nprim)
281 return 0;
282 levdatum = (level_datum_t *) hashtab_search(p->p_levels.table,
283 p->
284 p_sens_val_to_name
285 [c->range.level[l].
286 sens - 1]);
287 if (!levdatum)
288 return 0;
289
290 ebitmap_for_each_bit(&c->range.level[l].cat, cnode, i) {
291 if (ebitmap_node_get_bit(cnode, i)) {
292 if (i > p->p_cats.nprim)
293 return 0;
294 if (!ebitmap_get_bit(&levdatum->level->cat, i))
295 /*
296 * Category may not be associated with
297 * sensitivity in low level.
298 */
299 return 0;
300 }
301 }
302 }
303
304 if (c->role == OBJECT_R_VAL)
305 return 1;
306
307 /*
308 * User must be authorized for the MLS range.
309 */
310 if (!c->user || c->user > p->p_users.nprim)
311 return 0;
312 usrdatum = p->user_val_to_struct[c->user - 1];
313 if (!mls_range_contains(usrdatum->exp_range, c->range))
314 return 0; /* user may not be associated with range */
315
316 return 1;
317 }
318
319 /*
320 * Set the MLS fields in the security context structure
321 * `context' based on the string representation in
322 * the string `*scontext'. Update `*scontext' to
323 * point to the end of the string representation of
324 * the MLS fields.
325 *
326 * This function modifies the string in place, inserting
327 * NULL characters to terminate the MLS fields.
328 */
mls_context_to_sid(const policydb_t * policydb,char oldc,char ** scontext,context_struct_t * context)329 int mls_context_to_sid(const policydb_t * policydb,
330 char oldc, char **scontext, context_struct_t * context)
331 {
332
333 char delim;
334 char *scontextp, *p, *rngptr;
335 level_datum_t *levdatum;
336 cat_datum_t *catdatum, *rngdatum;
337 unsigned int l;
338
339 if (!policydb->mls)
340 return 0;
341
342 /* No MLS component to the security context */
343 if (!oldc)
344 goto err;
345
346 /* Extract low sensitivity. */
347 scontextp = p = *scontext;
348 while (*p && *p != ':' && *p != '-')
349 p++;
350
351 delim = *p;
352 if (delim != 0)
353 *p++ = 0;
354
355 for (l = 0; l < 2; l++) {
356 levdatum =
357 (level_datum_t *) hashtab_search(policydb->p_levels.table,
358 (hashtab_key_t) scontextp);
359
360 if (!levdatum)
361 goto err;
362
363 context->range.level[l].sens = levdatum->level->sens;
364
365 if (delim == ':') {
366 /* Extract category set. */
367 while (1) {
368 scontextp = p;
369 while (*p && *p != ',' && *p != '-')
370 p++;
371 delim = *p;
372 if (delim != 0)
373 *p++ = 0;
374
375 /* Separate into range if exists */
376 if ((rngptr = strchr(scontextp, '.')) != NULL) {
377 /* Remove '.' */
378 *rngptr++ = 0;
379 }
380
381 catdatum =
382 (cat_datum_t *) hashtab_search(policydb->
383 p_cats.table,
384 (hashtab_key_t)
385 scontextp);
386 if (!catdatum)
387 goto err;
388
389 if (ebitmap_set_bit
390 (&context->range.level[l].cat,
391 catdatum->s.value - 1, 1))
392 goto err;
393
394 /* If range, set all categories in range */
395 if (rngptr) {
396 unsigned int i;
397
398 rngdatum = (cat_datum_t *)
399 hashtab_search(policydb->p_cats.
400 table,
401 (hashtab_key_t)
402 rngptr);
403 if (!rngdatum)
404 goto err;
405
406 if (catdatum->s.value >=
407 rngdatum->s.value)
408 goto err;
409
410 for (i = catdatum->s.value;
411 i < rngdatum->s.value; i++) {
412 if (ebitmap_set_bit
413 (&context->range.level[l].
414 cat, i, 1))
415 goto err;
416 }
417 }
418
419 if (delim != ',')
420 break;
421 }
422 }
423 if (delim == '-') {
424 /* Extract high sensitivity. */
425 scontextp = p;
426 while (*p && *p != ':')
427 p++;
428
429 delim = *p;
430 if (delim != 0)
431 *p++ = 0;
432 } else
433 break;
434 }
435
436 /* High level is missing, copy low level */
437 if (l == 0) {
438 if (mls_level_cpy(&context->range.level[1],
439 &context->range.level[0]) < 0)
440 goto err;
441 }
442 *scontext = ++p;
443
444 return STATUS_SUCCESS;
445
446 err:
447 return STATUS_ERR;
448 }
449
450 /*
451 * Copies the MLS range from `src' into `dst'.
452 */
mls_copy_context(context_struct_t * dst,context_struct_t * src)453 static inline int mls_copy_context(context_struct_t * dst,
454 context_struct_t * src)
455 {
456 int l, rc = 0;
457
458 /* Copy the MLS range from the source context */
459 for (l = 0; l < 2; l++) {
460 dst->range.level[l].sens = src->range.level[l].sens;
461 rc = ebitmap_cpy(&dst->range.level[l].cat,
462 &src->range.level[l].cat);
463 if (rc)
464 break;
465 }
466
467 return rc;
468 }
469
470 /*
471 * Copies the effective MLS range from `src' into `dst'.
472 */
mls_scopy_context(context_struct_t * dst,context_struct_t * src)473 static inline int mls_scopy_context(context_struct_t * dst,
474 context_struct_t * src)
475 {
476 int l, rc = 0;
477
478 /* Copy the MLS range from the source context */
479 for (l = 0; l < 2; l++) {
480 dst->range.level[l].sens = src->range.level[0].sens;
481 rc = ebitmap_cpy(&dst->range.level[l].cat,
482 &src->range.level[0].cat);
483 if (rc)
484 break;
485 }
486
487 return rc;
488 }
489
490 /*
491 * Copies the MLS range `range' into `context'.
492 */
mls_range_set(context_struct_t * context,mls_range_t * range)493 static inline int mls_range_set(context_struct_t * context, mls_range_t * range)
494 {
495 int l, rc = 0;
496
497 /* Copy the MLS range into the context */
498 for (l = 0; l < 2; l++) {
499 context->range.level[l].sens = range->level[l].sens;
500 rc = ebitmap_cpy(&context->range.level[l].cat,
501 &range->level[l].cat);
502 if (rc)
503 break;
504 }
505
506 return rc;
507 }
508
mls_setup_user_range(context_struct_t * fromcon,user_datum_t * user,context_struct_t * usercon,int mls)509 int mls_setup_user_range(context_struct_t * fromcon, user_datum_t * user,
510 context_struct_t * usercon, int mls)
511 {
512 if (mls) {
513 mls_level_t *fromcon_sen = &(fromcon->range.level[0]);
514 mls_level_t *fromcon_clr = &(fromcon->range.level[1]);
515 mls_level_t *user_low = &(user->exp_range.level[0]);
516 mls_level_t *user_clr = &(user->exp_range.level[1]);
517 mls_level_t *user_def = &(user->exp_dfltlevel);
518 mls_level_t *usercon_sen = &(usercon->range.level[0]);
519 mls_level_t *usercon_clr = &(usercon->range.level[1]);
520
521 /* Honor the user's default level if we can */
522 if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) {
523 *usercon_sen = *user_def;
524 } else if (mls_level_between(fromcon_sen, user_def, user_clr)) {
525 *usercon_sen = *fromcon_sen;
526 } else if (mls_level_between(fromcon_clr, user_low, user_def)) {
527 *usercon_sen = *user_low;
528 } else
529 return -EINVAL;
530
531 /* Lower the clearance of available contexts
532 if the clearance of "fromcon" is lower than
533 that of the user's default clearance (but
534 only if the "fromcon" clearance dominates
535 the user's computed sensitivity level) */
536 if (mls_level_dom(user_clr, fromcon_clr)) {
537 *usercon_clr = *fromcon_clr;
538 } else if (mls_level_dom(fromcon_clr, user_clr)) {
539 *usercon_clr = *user_clr;
540 } else
541 return -EINVAL;
542 }
543
544 return 0;
545 }
546
547 /*
548 * Convert the MLS fields in the security context
549 * structure `c' from the values specified in the
550 * policy `oldp' to the values specified in the policy `newp'.
551 */
mls_convert_context(policydb_t * oldp,policydb_t * newp,context_struct_t * c)552 int mls_convert_context(policydb_t * oldp,
553 policydb_t * newp, context_struct_t * c)
554 {
555 level_datum_t *levdatum;
556 cat_datum_t *catdatum;
557 ebitmap_t bitmap;
558 unsigned int l, i;
559 ebitmap_node_t *cnode;
560
561 if (!oldp->mls)
562 return 0;
563
564 for (l = 0; l < 2; l++) {
565 levdatum =
566 (level_datum_t *) hashtab_search(newp->p_levels.table,
567 oldp->
568 p_sens_val_to_name[c->
569 range.
570 level
571 [l].
572 sens -
573 1]);
574
575 if (!levdatum)
576 return -EINVAL;
577 c->range.level[l].sens = levdatum->level->sens;
578
579 ebitmap_init(&bitmap);
580 ebitmap_for_each_bit(&c->range.level[l].cat, cnode, i) {
581 if (ebitmap_node_get_bit(cnode, i)) {
582 int rc;
583
584 catdatum =
585 (cat_datum_t *) hashtab_search(newp->p_cats.
586 table,
587 oldp->
588 p_cat_val_to_name
589 [i]);
590 if (!catdatum)
591 return -EINVAL;
592 rc = ebitmap_set_bit(&bitmap,
593 catdatum->s.value - 1, 1);
594 if (rc)
595 return rc;
596 }
597 }
598 ebitmap_destroy(&c->range.level[l].cat);
599 c->range.level[l].cat = bitmap;
600 }
601
602 return 0;
603 }
604
mls_compute_sid(policydb_t * policydb,context_struct_t * scontext,context_struct_t * tcontext,sepol_security_class_t tclass,uint32_t specified,context_struct_t * newcontext)605 int mls_compute_sid(policydb_t * policydb,
606 context_struct_t * scontext,
607 context_struct_t * tcontext,
608 sepol_security_class_t tclass,
609 uint32_t specified, context_struct_t * newcontext)
610 {
611 range_trans_t *rtr;
612 if (!policydb->mls)
613 return 0;
614
615 switch (specified) {
616 case AVTAB_TRANSITION:
617 /* Look for a range transition rule. */
618 for (rtr = policydb->range_tr; rtr; rtr = rtr->next) {
619 if (rtr->source_type == scontext->type &&
620 rtr->target_type == tcontext->type &&
621 rtr->target_class == tclass) {
622 /* Set the range from the rule */
623 return mls_range_set(newcontext,
624 &rtr->target_range);
625 }
626 }
627 /* Fallthrough */
628 case AVTAB_CHANGE:
629 if (tclass == SECCLASS_PROCESS)
630 /* Use the process MLS attributes. */
631 return mls_copy_context(newcontext, scontext);
632 else
633 /* Use the process effective MLS attributes. */
634 return mls_scopy_context(newcontext, scontext);
635 case AVTAB_MEMBER:
636 /* Only polyinstantiate the MLS attributes if
637 the type is being polyinstantiated */
638 if (newcontext->type != tcontext->type) {
639 /* Use the process effective MLS attributes. */
640 return mls_scopy_context(newcontext, scontext);
641 } else {
642 /* Use the related object MLS attributes. */
643 return mls_copy_context(newcontext, tcontext);
644 }
645 default:
646 return -EINVAL;
647 }
648 return -EINVAL;
649 }
650
sepol_mls_contains(sepol_handle_t * handle,sepol_policydb_t * policydb,const char * mls1,const char * mls2,int * response)651 int sepol_mls_contains(sepol_handle_t * handle,
652 sepol_policydb_t * policydb,
653 const char *mls1, const char *mls2, int *response)
654 {
655
656 context_struct_t *ctx1 = NULL, *ctx2 = NULL;
657 ctx1 = malloc(sizeof(context_struct_t));
658 ctx2 = malloc(sizeof(context_struct_t));
659 if (ctx1 == NULL || ctx2 == NULL)
660 goto omem;
661 context_init(ctx1);
662 context_init(ctx2);
663
664 if (mls_from_string(handle, &policydb->p, mls1, ctx1) < 0)
665 goto err;
666
667 if (mls_from_string(handle, &policydb->p, mls2, ctx2) < 0)
668 goto err;
669
670 *response = mls_range_contains(ctx1->range, ctx2->range);
671 context_destroy(ctx1);
672 context_destroy(ctx2);
673 free(ctx1);
674 free(ctx2);
675 return STATUS_SUCCESS;
676
677 omem:
678 ERR(handle, "out of memory");
679
680 err:
681 ERR(handle, "could not check if mls context %s contains %s",
682 mls1, mls2);
683 context_destroy(ctx1);
684 context_destroy(ctx2);
685 free(ctx1);
686 free(ctx2);
687 return STATUS_ERR;
688 }
689
sepol_mls_check(sepol_handle_t * handle,sepol_policydb_t * policydb,const char * mls)690 int sepol_mls_check(sepol_handle_t * handle,
691 sepol_policydb_t * policydb, const char *mls)
692 {
693
694 int ret;
695 context_struct_t *con = malloc(sizeof(context_struct_t));
696 if (!con) {
697 ERR(handle, "out of memory, could not check if "
698 "mls context %s is valid", mls);
699 return STATUS_ERR;
700 }
701 context_init(con);
702
703 ret = mls_from_string(handle, &policydb->p, mls, con);
704 context_destroy(con);
705 free(con);
706 return ret;
707 }
708
mls_semantic_cat_init(mls_semantic_cat_t * c)709 void mls_semantic_cat_init(mls_semantic_cat_t * c)
710 {
711 memset(c, 0, sizeof(mls_semantic_cat_t));
712 }
713
mls_semantic_cat_destroy(mls_semantic_cat_t * c)714 void mls_semantic_cat_destroy(mls_semantic_cat_t * c __attribute__ ((unused)))
715 {
716 /* it's currently a simple struct - really nothing to destroy */
717 return;
718 }
719
mls_semantic_level_init(mls_semantic_level_t * l)720 void mls_semantic_level_init(mls_semantic_level_t * l)
721 {
722 memset(l, 0, sizeof(mls_semantic_level_t));
723 }
724
mls_semantic_level_destroy(mls_semantic_level_t * l)725 void mls_semantic_level_destroy(mls_semantic_level_t * l)
726 {
727 mls_semantic_cat_t *cur, *next;
728
729 if (l == NULL)
730 return;
731
732 next = l->cat;
733 while (next) {
734 cur = next;
735 next = cur->next;
736 mls_semantic_cat_destroy(cur);
737 free(cur);
738 }
739 }
740
mls_semantic_level_cpy(mls_semantic_level_t * dst,mls_semantic_level_t * src)741 int mls_semantic_level_cpy(mls_semantic_level_t * dst,
742 mls_semantic_level_t * src)
743 {
744 mls_semantic_cat_t *cat, *newcat, *lnewcat = NULL;
745
746 mls_semantic_level_init(dst);
747 dst->sens = src->sens;
748 cat = src->cat;
749 while (cat) {
750 newcat =
751 (mls_semantic_cat_t *) malloc(sizeof(mls_semantic_cat_t));
752 if (!newcat)
753 goto err;
754
755 mls_semantic_cat_init(newcat);
756 if (lnewcat)
757 lnewcat->next = newcat;
758 else
759 dst->cat = newcat;
760
761 newcat->low = cat->low;
762 newcat->high = cat->high;
763
764 lnewcat = newcat;
765 cat = cat->next;
766 }
767 return 0;
768
769 err:
770 mls_semantic_level_destroy(dst);
771 return -1;
772 }
773
mls_semantic_range_init(mls_semantic_range_t * r)774 void mls_semantic_range_init(mls_semantic_range_t * r)
775 {
776 mls_semantic_level_init(&r->level[0]);
777 mls_semantic_level_init(&r->level[1]);
778 }
779
mls_semantic_range_destroy(mls_semantic_range_t * r)780 void mls_semantic_range_destroy(mls_semantic_range_t * r)
781 {
782 mls_semantic_level_destroy(&r->level[0]);
783 mls_semantic_level_destroy(&r->level[1]);
784 }
785
mls_semantic_range_cpy(mls_semantic_range_t * dst,mls_semantic_range_t * src)786 int mls_semantic_range_cpy(mls_semantic_range_t * dst,
787 mls_semantic_range_t * src)
788 {
789 if (mls_semantic_level_cpy(&dst->level[0], &src->level[0]) < 0)
790 return -1;
791
792 if (mls_semantic_level_cpy(&dst->level[1], &src->level[1]) < 0) {
793 mls_semantic_level_destroy(&dst->level[0]);
794 return -1;
795 }
796
797 return 0;
798 }
799