1 /* Author : Stephen Smalley, <sds@tycho.nsa.gov> */
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 hashtab_key_t key;
266
267 if (!p->mls)
268 return 1;
269
270 /*
271 * MLS range validity checks: high must dominate low, low level must
272 * be valid (category set <-> sensitivity check), and high level must
273 * be valid (category set <-> sensitivity check)
274 */
275 if (!mls_level_dom(&c->range.level[1], &c->range.level[0]))
276 /* High does not dominate low. */
277 return 0;
278
279 for (l = 0; l < 2; l++) {
280 if (!c->range.level[l].sens
281 || c->range.level[l].sens > p->p_levels.nprim)
282 return 0;
283
284 key = p->p_sens_val_to_name[c->range.level[l].sens - 1];
285 if (!key)
286 return 0;
287
288 levdatum = (level_datum_t *) hashtab_search(p->p_levels.table, key);
289 if (!levdatum)
290 return 0;
291
292 ebitmap_for_each_bit(&c->range.level[l].cat, cnode, i) {
293 if (ebitmap_node_get_bit(cnode, i)) {
294 if (i > p->p_cats.nprim)
295 return 0;
296 if (!ebitmap_get_bit(&levdatum->level->cat, i))
297 /*
298 * Category may not be associated with
299 * sensitivity in low level.
300 */
301 return 0;
302 }
303 }
304 }
305
306 if (c->role == OBJECT_R_VAL)
307 return 1;
308
309 /*
310 * User must be authorized for the MLS range.
311 */
312 if (!c->user || c->user > p->p_users.nprim)
313 return 0;
314 usrdatum = p->user_val_to_struct[c->user - 1];
315 if (!usrdatum || !mls_range_contains(usrdatum->exp_range, c->range))
316 return 0; /* user may not be associated with range */
317
318 return 1;
319 }
320
321 /*
322 * Set the MLS fields in the security context structure
323 * `context' based on the string representation in
324 * the string `*scontext'. Update `*scontext' to
325 * point to the end of the string representation of
326 * the MLS fields.
327 *
328 * This function modifies the string in place, inserting
329 * NULL characters to terminate the MLS fields.
330 */
mls_context_to_sid(const policydb_t * policydb,char oldc,char ** scontext,context_struct_t * context)331 int mls_context_to_sid(const policydb_t * policydb,
332 char oldc, char **scontext, context_struct_t * context)
333 {
334
335 char delim;
336 char *scontextp, *p, *rngptr;
337 level_datum_t *levdatum;
338 cat_datum_t *catdatum, *rngdatum;
339 unsigned int l;
340
341 if (!policydb->mls)
342 return 0;
343
344 /* No MLS component to the security context */
345 if (!oldc)
346 goto err;
347
348 /* Extract low sensitivity. */
349 scontextp = p = *scontext;
350 while (*p && *p != ':' && *p != '-')
351 p++;
352
353 delim = *p;
354 if (delim != 0)
355 *p++ = 0;
356
357 for (l = 0; l < 2; l++) {
358 levdatum =
359 (level_datum_t *) hashtab_search(policydb->p_levels.table,
360 (hashtab_key_t) scontextp);
361
362 if (!levdatum)
363 goto err;
364
365 context->range.level[l].sens = levdatum->level->sens;
366
367 if (delim == ':') {
368 /* Extract category set. */
369 while (1) {
370 scontextp = p;
371 while (*p && *p != ',' && *p != '-')
372 p++;
373 delim = *p;
374 if (delim != 0)
375 *p++ = 0;
376
377 /* Separate into range if exists */
378 if ((rngptr = strchr(scontextp, '.')) != NULL) {
379 /* Remove '.' */
380 *rngptr++ = 0;
381 }
382
383 catdatum =
384 (cat_datum_t *) hashtab_search(policydb->
385 p_cats.table,
386 (hashtab_key_t)
387 scontextp);
388 if (!catdatum)
389 goto err;
390
391 if (ebitmap_set_bit
392 (&context->range.level[l].cat,
393 catdatum->s.value - 1, 1))
394 goto err;
395
396 /* If range, set all categories in range */
397 if (rngptr) {
398 unsigned int i;
399
400 rngdatum = (cat_datum_t *)
401 hashtab_search(policydb->p_cats.
402 table,
403 (hashtab_key_t)
404 rngptr);
405 if (!rngdatum)
406 goto err;
407
408 if (catdatum->s.value >=
409 rngdatum->s.value)
410 goto err;
411
412 for (i = catdatum->s.value;
413 i < rngdatum->s.value; i++) {
414 if (ebitmap_set_bit
415 (&context->range.level[l].
416 cat, i, 1))
417 goto err;
418 }
419 }
420
421 if (delim != ',')
422 break;
423 }
424 }
425 if (delim == '-') {
426 /* Extract high sensitivity. */
427 scontextp = p;
428 while (*p && *p != ':')
429 p++;
430
431 delim = *p;
432 if (delim != 0)
433 *p++ = 0;
434 } else
435 break;
436 }
437
438 /* High level is missing, copy low level */
439 if (l == 0) {
440 if (mls_level_cpy(&context->range.level[1],
441 &context->range.level[0]) < 0)
442 goto err;
443 }
444 *scontext = ++p;
445
446 return STATUS_SUCCESS;
447
448 err:
449 return STATUS_ERR;
450 }
451
452 /*
453 * Copies the MLS range from `src' into `dst'.
454 */
mls_copy_context(context_struct_t * dst,context_struct_t * src)455 static inline int mls_copy_context(context_struct_t * dst,
456 context_struct_t * src)
457 {
458 int l, rc = 0;
459
460 /* Copy the MLS range from the source context */
461 for (l = 0; l < 2; l++) {
462 dst->range.level[l].sens = src->range.level[l].sens;
463 rc = ebitmap_cpy(&dst->range.level[l].cat,
464 &src->range.level[l].cat);
465 if (rc)
466 break;
467 }
468
469 return rc;
470 }
471
472 /*
473 * Copies the effective MLS range from `src' into `dst'.
474 */
mls_scopy_context(context_struct_t * dst,context_struct_t * src)475 static inline int mls_scopy_context(context_struct_t * dst,
476 context_struct_t * src)
477 {
478 int l, rc = 0;
479
480 /* Copy the MLS range from the source context */
481 for (l = 0; l < 2; l++) {
482 dst->range.level[l].sens = src->range.level[0].sens;
483 rc = ebitmap_cpy(&dst->range.level[l].cat,
484 &src->range.level[0].cat);
485 if (rc)
486 break;
487 }
488
489 return rc;
490 }
491
492 /*
493 * Copies the MLS range `range' into `context'.
494 */
mls_range_set(context_struct_t * context,mls_range_t * range)495 static inline int mls_range_set(context_struct_t * context, mls_range_t * range)
496 {
497 int l, rc = 0;
498
499 /* Copy the MLS range into the context */
500 for (l = 0; l < 2; l++) {
501 context->range.level[l].sens = range->level[l].sens;
502 rc = ebitmap_cpy(&context->range.level[l].cat,
503 &range->level[l].cat);
504 if (rc)
505 break;
506 }
507
508 return rc;
509 }
510
mls_setup_user_range(context_struct_t * fromcon,user_datum_t * user,context_struct_t * usercon,int mls)511 int mls_setup_user_range(context_struct_t * fromcon, user_datum_t * user,
512 context_struct_t * usercon, int mls)
513 {
514 if (mls) {
515 mls_level_t *fromcon_sen = &(fromcon->range.level[0]);
516 mls_level_t *fromcon_clr = &(fromcon->range.level[1]);
517 mls_level_t *user_low = &(user->exp_range.level[0]);
518 mls_level_t *user_clr = &(user->exp_range.level[1]);
519 mls_level_t *user_def = &(user->exp_dfltlevel);
520 mls_level_t *usercon_sen = &(usercon->range.level[0]);
521 mls_level_t *usercon_clr = &(usercon->range.level[1]);
522
523 /* Honor the user's default level if we can */
524 if (mls_level_between(user_def, fromcon_sen, fromcon_clr)) {
525 *usercon_sen = *user_def;
526 } else if (mls_level_between(fromcon_sen, user_def, user_clr)) {
527 *usercon_sen = *fromcon_sen;
528 } else if (mls_level_between(fromcon_clr, user_low, user_def)) {
529 *usercon_sen = *user_low;
530 } else
531 return -EINVAL;
532
533 /* Lower the clearance of available contexts
534 if the clearance of "fromcon" is lower than
535 that of the user's default clearance (but
536 only if the "fromcon" clearance dominates
537 the user's computed sensitivity level) */
538 if (mls_level_dom(user_clr, fromcon_clr)) {
539 *usercon_clr = *fromcon_clr;
540 } else if (mls_level_dom(fromcon_clr, user_clr)) {
541 *usercon_clr = *user_clr;
542 } else
543 return -EINVAL;
544 }
545
546 return 0;
547 }
548
549 /*
550 * Convert the MLS fields in the security context
551 * structure `c' from the values specified in the
552 * policy `oldp' to the values specified in the policy `newp'.
553 */
mls_convert_context(policydb_t * oldp,policydb_t * newp,context_struct_t * c)554 int mls_convert_context(policydb_t * oldp,
555 policydb_t * newp, context_struct_t * c)
556 {
557 level_datum_t *levdatum;
558 cat_datum_t *catdatum;
559 ebitmap_t bitmap;
560 unsigned int l, i;
561 ebitmap_node_t *cnode;
562
563 if (!oldp->mls)
564 return 0;
565
566 for (l = 0; l < 2; l++) {
567 levdatum =
568 (level_datum_t *) hashtab_search(newp->p_levels.table,
569 oldp->
570 p_sens_val_to_name[c->
571 range.
572 level
573 [l].
574 sens -
575 1]);
576
577 if (!levdatum)
578 return -EINVAL;
579 c->range.level[l].sens = levdatum->level->sens;
580
581 ebitmap_init(&bitmap);
582 ebitmap_for_each_bit(&c->range.level[l].cat, cnode, i) {
583 if (ebitmap_node_get_bit(cnode, i)) {
584 int rc;
585
586 catdatum =
587 (cat_datum_t *) hashtab_search(newp->p_cats.
588 table,
589 oldp->
590 p_cat_val_to_name
591 [i]);
592 if (!catdatum)
593 return -EINVAL;
594 rc = ebitmap_set_bit(&bitmap,
595 catdatum->s.value - 1, 1);
596 if (rc)
597 return rc;
598 }
599 }
600 ebitmap_destroy(&c->range.level[l].cat);
601 c->range.level[l].cat = bitmap;
602 }
603
604 return 0;
605 }
606
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)607 int mls_compute_sid(policydb_t * policydb,
608 context_struct_t * scontext,
609 context_struct_t * tcontext,
610 sepol_security_class_t tclass,
611 uint32_t specified, context_struct_t * newcontext)
612 {
613 range_trans_t rtr;
614 struct mls_range *r;
615 struct class_datum *cladatum;
616 int default_range = 0;
617
618 if (!policydb->mls)
619 return 0;
620
621 switch (specified) {
622 case AVTAB_TRANSITION:
623 /* Look for a range transition rule. */
624 rtr.source_type = scontext->type;
625 rtr.target_type = tcontext->type;
626 rtr.target_class = tclass;
627 r = hashtab_search(policydb->range_tr, (hashtab_key_t) &rtr);
628 if (r)
629 return mls_range_set(newcontext, r);
630
631 if (tclass && tclass <= policydb->p_classes.nprim) {
632 cladatum = policydb->class_val_to_struct[tclass - 1];
633 if (cladatum)
634 default_range = cladatum->default_range;
635 }
636
637 switch (default_range) {
638 case DEFAULT_SOURCE_LOW:
639 return mls_context_cpy_low(newcontext, scontext);
640 case DEFAULT_SOURCE_HIGH:
641 return mls_context_cpy_high(newcontext, scontext);
642 case DEFAULT_SOURCE_LOW_HIGH:
643 return mls_context_cpy(newcontext, scontext);
644 case DEFAULT_TARGET_LOW:
645 return mls_context_cpy_low(newcontext, tcontext);
646 case DEFAULT_TARGET_HIGH:
647 return mls_context_cpy_high(newcontext, tcontext);
648 case DEFAULT_TARGET_LOW_HIGH:
649 return mls_context_cpy(newcontext, tcontext);
650 }
651
652 /* Fallthrough */
653 case AVTAB_CHANGE:
654 if (tclass == SECCLASS_PROCESS)
655 /* Use the process MLS attributes. */
656 return mls_copy_context(newcontext, scontext);
657 else
658 /* Use the process effective MLS attributes. */
659 return mls_scopy_context(newcontext, scontext);
660 case AVTAB_MEMBER:
661 /* Use the process effective MLS attributes. */
662 return mls_context_cpy_low(newcontext, scontext);
663 default:
664 return -EINVAL;
665 }
666 return -EINVAL;
667 }
668
sepol_mls_contains(sepol_handle_t * handle,sepol_policydb_t * policydb,const char * mls1,const char * mls2,int * response)669 int sepol_mls_contains(sepol_handle_t * handle,
670 sepol_policydb_t * policydb,
671 const char *mls1, const char *mls2, int *response)
672 {
673
674 context_struct_t *ctx1 = NULL, *ctx2 = NULL;
675 ctx1 = malloc(sizeof(context_struct_t));
676 ctx2 = malloc(sizeof(context_struct_t));
677 if (ctx1 == NULL || ctx2 == NULL)
678 goto omem;
679 context_init(ctx1);
680 context_init(ctx2);
681
682 if (mls_from_string(handle, &policydb->p, mls1, ctx1) < 0)
683 goto err;
684
685 if (mls_from_string(handle, &policydb->p, mls2, ctx2) < 0)
686 goto err;
687
688 *response = mls_range_contains(ctx1->range, ctx2->range);
689 context_destroy(ctx1);
690 context_destroy(ctx2);
691 free(ctx1);
692 free(ctx2);
693 return STATUS_SUCCESS;
694
695 omem:
696 ERR(handle, "out of memory");
697
698 err:
699 ERR(handle, "could not check if mls context %s contains %s",
700 mls1, mls2);
701 context_destroy(ctx1);
702 context_destroy(ctx2);
703 free(ctx1);
704 free(ctx2);
705 return STATUS_ERR;
706 }
707
sepol_mls_check(sepol_handle_t * handle,sepol_policydb_t * policydb,const char * mls)708 int sepol_mls_check(sepol_handle_t * handle,
709 sepol_policydb_t * policydb, const char *mls)
710 {
711
712 int ret;
713 context_struct_t *con = malloc(sizeof(context_struct_t));
714 if (!con) {
715 ERR(handle, "out of memory, could not check if "
716 "mls context %s is valid", mls);
717 return STATUS_ERR;
718 }
719 context_init(con);
720
721 ret = mls_from_string(handle, &policydb->p, mls, con);
722 context_destroy(con);
723 free(con);
724 return ret;
725 }
726
mls_semantic_cat_init(mls_semantic_cat_t * c)727 void mls_semantic_cat_init(mls_semantic_cat_t * c)
728 {
729 memset(c, 0, sizeof(mls_semantic_cat_t));
730 }
731
mls_semantic_cat_destroy(mls_semantic_cat_t * c)732 void mls_semantic_cat_destroy(mls_semantic_cat_t * c __attribute__ ((unused)))
733 {
734 /* it's currently a simple struct - really nothing to destroy */
735 return;
736 }
737
mls_semantic_level_init(mls_semantic_level_t * l)738 void mls_semantic_level_init(mls_semantic_level_t * l)
739 {
740 memset(l, 0, sizeof(mls_semantic_level_t));
741 }
742
mls_semantic_level_destroy(mls_semantic_level_t * l)743 void mls_semantic_level_destroy(mls_semantic_level_t * l)
744 {
745 mls_semantic_cat_t *cur, *next;
746
747 if (l == NULL)
748 return;
749
750 next = l->cat;
751 while (next) {
752 cur = next;
753 next = cur->next;
754 mls_semantic_cat_destroy(cur);
755 free(cur);
756 }
757 }
758
mls_semantic_level_cpy(mls_semantic_level_t * dst,mls_semantic_level_t * src)759 int mls_semantic_level_cpy(mls_semantic_level_t * dst,
760 mls_semantic_level_t * src)
761 {
762 mls_semantic_cat_t *cat, *newcat, *lnewcat = NULL;
763
764 mls_semantic_level_init(dst);
765 dst->sens = src->sens;
766 cat = src->cat;
767 while (cat) {
768 newcat =
769 (mls_semantic_cat_t *) malloc(sizeof(mls_semantic_cat_t));
770 if (!newcat)
771 goto err;
772
773 mls_semantic_cat_init(newcat);
774 if (lnewcat)
775 lnewcat->next = newcat;
776 else
777 dst->cat = newcat;
778
779 newcat->low = cat->low;
780 newcat->high = cat->high;
781
782 lnewcat = newcat;
783 cat = cat->next;
784 }
785 return 0;
786
787 err:
788 mls_semantic_level_destroy(dst);
789 return -1;
790 }
791
mls_semantic_range_init(mls_semantic_range_t * r)792 void mls_semantic_range_init(mls_semantic_range_t * r)
793 {
794 mls_semantic_level_init(&r->level[0]);
795 mls_semantic_level_init(&r->level[1]);
796 }
797
mls_semantic_range_destroy(mls_semantic_range_t * r)798 void mls_semantic_range_destroy(mls_semantic_range_t * r)
799 {
800 mls_semantic_level_destroy(&r->level[0]);
801 mls_semantic_level_destroy(&r->level[1]);
802 }
803
mls_semantic_range_cpy(mls_semantic_range_t * dst,mls_semantic_range_t * src)804 int mls_semantic_range_cpy(mls_semantic_range_t * dst,
805 mls_semantic_range_t * src)
806 {
807 if (mls_semantic_level_cpy(&dst->level[0], &src->level[0]) < 0)
808 return -1;
809
810 if (mls_semantic_level_cpy(&dst->level[1], &src->level[1]) < 0) {
811 mls_semantic_level_destroy(&dst->level[0]);
812 return -1;
813 }
814
815 return 0;
816 }
817