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 	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  */
    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  */
    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  */
    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  */
    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 
    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  */
    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 
    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 
    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 
    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 
    727 void mls_semantic_cat_init(mls_semantic_cat_t * c)
    728 {
    729 	memset(c, 0, sizeof(mls_semantic_cat_t));
    730 }
    731 
    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 
    738 void mls_semantic_level_init(mls_semantic_level_t * l)
    739 {
    740 	memset(l, 0, sizeof(mls_semantic_level_t));
    741 }
    742 
    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 
    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 
    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 
    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 
    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