Home | History | Annotate | Download | only in src
      1 /* Author : Stephen Smalley, <sds (at) epoch.ncsc.mil> */
      2 /*
      3  * Updated: Trusted Computer Solutions, Inc. <dgoeddel (at) 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 
     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 
     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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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 
    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  */
    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 
    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 
    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 
    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 
    709 void mls_semantic_cat_init(mls_semantic_cat_t * c)
    710 {
    711 	memset(c, 0, sizeof(mls_semantic_cat_t));
    712 }
    713 
    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 
    720 void mls_semantic_level_init(mls_semantic_level_t * l)
    721 {
    722 	memset(l, 0, sizeof(mls_semantic_level_t));
    723 }
    724 
    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 
    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 
    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 
    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 
    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