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/context.h>
31 #include <sepol/policydb/policydb.h>
32 #include <sepol/policydb/services.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_positive_bit(&c->range.level[l].cat, cnode, i) {
293 if (i > p->p_cats.nprim)
294 return 0;
295 if (!ebitmap_get_bit(&levdatum->level->cat, i))
296 /*
297 * Category may not be associated with
298 * sensitivity in low level.
299 */
300 return 0;
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 (!usrdatum || !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,const context_struct_t * src)453 static inline int mls_copy_context(context_struct_t * dst,
454 const 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,const context_struct_t * src)473 static inline int mls_scopy_context(context_struct_t * dst,
474 const 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,const mls_range_t * range)493 static inline int mls_range_set(context_struct_t * context, const 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_positive_bit(&c->range.level[l].cat, cnode, i) {
581 int rc;
582
583 catdatum =
584 (cat_datum_t *) hashtab_search(newp->p_cats.
585 table,
586 oldp->
587 p_cat_val_to_name
588 [i]);
589 if (!catdatum)
590 return -EINVAL;
591 rc = ebitmap_set_bit(&bitmap,
592 catdatum->s.value - 1, 1);
593 if (rc)
594 return rc;
595 }
596 ebitmap_destroy(&c->range.level[l].cat);
597 c->range.level[l].cat = bitmap;
598 }
599
600 return 0;
601 }
602
mls_compute_sid(policydb_t * policydb,const context_struct_t * scontext,const context_struct_t * tcontext,sepol_security_class_t tclass,uint32_t specified,context_struct_t * newcontext)603 int mls_compute_sid(policydb_t * policydb,
604 const context_struct_t * scontext,
605 const context_struct_t * tcontext,
606 sepol_security_class_t tclass,
607 uint32_t specified, context_struct_t * newcontext)
608 {
609 range_trans_t rtr;
610 struct mls_range *r;
611 struct class_datum *cladatum;
612 int default_range = 0;
613
614 if (!policydb->mls)
615 return 0;
616
617 switch (specified) {
618 case AVTAB_TRANSITION:
619 /* Look for a range transition rule. */
620 rtr.source_type = scontext->type;
621 rtr.target_type = tcontext->type;
622 rtr.target_class = tclass;
623 r = hashtab_search(policydb->range_tr, (hashtab_key_t) &rtr);
624 if (r)
625 return mls_range_set(newcontext, r);
626
627 if (tclass && tclass <= policydb->p_classes.nprim) {
628 cladatum = policydb->class_val_to_struct[tclass - 1];
629 if (cladatum)
630 default_range = cladatum->default_range;
631 }
632
633 switch (default_range) {
634 case DEFAULT_SOURCE_LOW:
635 return mls_context_cpy_low(newcontext, scontext);
636 case DEFAULT_SOURCE_HIGH:
637 return mls_context_cpy_high(newcontext, scontext);
638 case DEFAULT_SOURCE_LOW_HIGH:
639 return mls_context_cpy(newcontext, scontext);
640 case DEFAULT_TARGET_LOW:
641 return mls_context_cpy_low(newcontext, tcontext);
642 case DEFAULT_TARGET_HIGH:
643 return mls_context_cpy_high(newcontext, tcontext);
644 case DEFAULT_TARGET_LOW_HIGH:
645 return mls_context_cpy(newcontext, tcontext);
646 case DEFAULT_GLBLUB:
647 return mls_context_glblub(newcontext, scontext, tcontext);
648 }
649
650 /* Fallthrough */
651 case AVTAB_CHANGE:
652 if (tclass == policydb->process_class)
653 /* Use the process MLS attributes. */
654 return mls_copy_context(newcontext, scontext);
655 else
656 /* Use the process effective MLS attributes. */
657 return mls_scopy_context(newcontext, scontext);
658 case AVTAB_MEMBER:
659 /* Use the process effective MLS attributes. */
660 return mls_context_cpy_low(newcontext, scontext);
661 default:
662 return -EINVAL;
663 }
664 return -EINVAL;
665 }
666
sepol_mls_contains(sepol_handle_t * handle,const sepol_policydb_t * policydb,const char * mls1,const char * mls2,int * response)667 int sepol_mls_contains(sepol_handle_t * handle,
668 const sepol_policydb_t * policydb,
669 const char *mls1, const char *mls2, int *response)
670 {
671
672 context_struct_t *ctx1 = NULL, *ctx2 = NULL;
673 ctx1 = malloc(sizeof(context_struct_t));
674 ctx2 = malloc(sizeof(context_struct_t));
675 if (ctx1 == NULL || ctx2 == NULL)
676 goto omem;
677 context_init(ctx1);
678 context_init(ctx2);
679
680 if (mls_from_string(handle, &policydb->p, mls1, ctx1) < 0)
681 goto err;
682
683 if (mls_from_string(handle, &policydb->p, mls2, ctx2) < 0)
684 goto err;
685
686 *response = mls_range_contains(ctx1->range, ctx2->range);
687 context_destroy(ctx1);
688 context_destroy(ctx2);
689 free(ctx1);
690 free(ctx2);
691 return STATUS_SUCCESS;
692
693 omem:
694 ERR(handle, "out of memory");
695
696 err:
697 ERR(handle, "could not check if mls context %s contains %s",
698 mls1, mls2);
699 context_destroy(ctx1);
700 context_destroy(ctx2);
701 free(ctx1);
702 free(ctx2);
703 return STATUS_ERR;
704 }
705
sepol_mls_check(sepol_handle_t * handle,const sepol_policydb_t * policydb,const char * mls)706 int sepol_mls_check(sepol_handle_t * handle,
707 const sepol_policydb_t * policydb, const char *mls)
708 {
709
710 int ret;
711 context_struct_t *con = malloc(sizeof(context_struct_t));
712 if (!con) {
713 ERR(handle, "out of memory, could not check if "
714 "mls context %s is valid", mls);
715 return STATUS_ERR;
716 }
717 context_init(con);
718
719 ret = mls_from_string(handle, &policydb->p, mls, con);
720 context_destroy(con);
721 free(con);
722 return ret;
723 }
724
mls_semantic_cat_init(mls_semantic_cat_t * c)725 void mls_semantic_cat_init(mls_semantic_cat_t * c)
726 {
727 memset(c, 0, sizeof(mls_semantic_cat_t));
728 }
729
mls_semantic_cat_destroy(mls_semantic_cat_t * c)730 void mls_semantic_cat_destroy(mls_semantic_cat_t * c __attribute__ ((unused)))
731 {
732 /* it's currently a simple struct - really nothing to destroy */
733 return;
734 }
735
mls_semantic_level_init(mls_semantic_level_t * l)736 void mls_semantic_level_init(mls_semantic_level_t * l)
737 {
738 memset(l, 0, sizeof(mls_semantic_level_t));
739 }
740
mls_semantic_level_destroy(mls_semantic_level_t * l)741 void mls_semantic_level_destroy(mls_semantic_level_t * l)
742 {
743 mls_semantic_cat_t *cur, *next;
744
745 if (l == NULL)
746 return;
747
748 next = l->cat;
749 while (next) {
750 cur = next;
751 next = cur->next;
752 mls_semantic_cat_destroy(cur);
753 free(cur);
754 }
755 }
756
mls_semantic_level_cpy(mls_semantic_level_t * dst,const mls_semantic_level_t * src)757 int mls_semantic_level_cpy(mls_semantic_level_t * dst,
758 const mls_semantic_level_t * src)
759 {
760 const mls_semantic_cat_t *cat;
761 mls_semantic_cat_t *newcat, *lnewcat = NULL;
762
763 mls_semantic_level_init(dst);
764 dst->sens = src->sens;
765 cat = src->cat;
766 while (cat) {
767 newcat =
768 (mls_semantic_cat_t *) malloc(sizeof(mls_semantic_cat_t));
769 if (!newcat)
770 goto err;
771
772 mls_semantic_cat_init(newcat);
773 if (lnewcat)
774 lnewcat->next = newcat;
775 else
776 dst->cat = newcat;
777
778 newcat->low = cat->low;
779 newcat->high = cat->high;
780
781 lnewcat = newcat;
782 cat = cat->next;
783 }
784 return 0;
785
786 err:
787 mls_semantic_level_destroy(dst);
788 return -1;
789 }
790
mls_semantic_range_init(mls_semantic_range_t * r)791 void mls_semantic_range_init(mls_semantic_range_t * r)
792 {
793 mls_semantic_level_init(&r->level[0]);
794 mls_semantic_level_init(&r->level[1]);
795 }
796
mls_semantic_range_destroy(mls_semantic_range_t * r)797 void mls_semantic_range_destroy(mls_semantic_range_t * r)
798 {
799 mls_semantic_level_destroy(&r->level[0]);
800 mls_semantic_level_destroy(&r->level[1]);
801 }
802
mls_semantic_range_cpy(mls_semantic_range_t * dst,const mls_semantic_range_t * src)803 int mls_semantic_range_cpy(mls_semantic_range_t * dst,
804 const mls_semantic_range_t * src)
805 {
806 if (mls_semantic_level_cpy(&dst->level[0], &src->level[0]) < 0)
807 return -1;
808
809 if (mls_semantic_level_cpy(&dst->level[1], &src->level[1]) < 0) {
810 mls_semantic_level_destroy(&dst->level[0]);
811 return -1;
812 }
813
814 return 0;
815 }
816