Re: [PATCH] libsepol/cil: Add userattribute{set} functionality

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

On Thu, Sep 10, 2015 at 05:15:45PM +0000, Yuli Khodorkovskiy wrote:
> 
> 
> >-----Original Message-----
> >From: Dominick Grift [mailto:dac.override@xxxxxxxxx]
> >Sent: Thursday, September 10, 2015 1:03 PM
> >To: Yuli Khodorkovskiy
> >Cc: selinux@xxxxxxxxxxxxx
> >Subject: Re: [PATCH] libsepol/cil: Add userattribute{set} functionality
> >
> >-----BEGIN PGP SIGNED MESSAGE-----
> >Hash: SHA512
> >
> >On Thu, Sep 10, 2015 at 12:56:05PM -0400, Yuli Khodorkovskiy wrote:
> >> This adds a userattribute statement that may be used in userroles and
> >> constraints. The syntax is the same as typeattributset.
> >>
> >> Also, disallow roleattributes where roles are accepted in contexts.
> >
> >what does this mean?
> 
> There was a bug where we allowed roleattributes in contexts when we should have only accepted roles. We now check for this in cil_resolve.

Thanks for clarifying

> 
> >
> >>
> >> Specify a userattribute
> >>
> >>     (userattribute foo)
> >>
> >> Add users to the set foo
> >>
> >>     (userattributeset foo (u1 u2))
> >>
> >> Signed-off-by: Yuli Khodorkovskiy <ykhodorkovskiy@xxxxxxxxxx>
> >> ---
> >>  libsepol/cil/src/cil.c                    |  36 ++++++
> >>  libsepol/cil/src/cil_binary.c             |  99 +++++++++++-----
> >>  libsepol/cil/src/cil_binary.h             |   7 +-
> >>  libsepol/cil/src/cil_build_ast.c          | 129 ++++++++++++++++++++-
> >>  libsepol/cil/src/cil_build_ast.h          |   4 +
> >>  libsepol/cil/src/cil_copy_ast.c           |  41 +++++++
> >>  libsepol/cil/src/cil_copy_ast.h           |   2 +
> >>  libsepol/cil/src/cil_flavor.h             |   2 +
> >>  libsepol/cil/src/cil_internal.h           |  23 +++-
> >>  libsepol/cil/src/cil_policy.c             |   5 -
> >>  libsepol/cil/src/cil_post.c               | 182
> >++++++++++++++++++++++++++++++
> >>  libsepol/cil/src/cil_reset_ast.c          |  33 +++++-
> >>  libsepol/cil/src/cil_resolve_ast.c        | 114 +++++++++++++++++--
> >>  libsepol/cil/src/cil_resolve_ast.h        |   1 +
> >>  libsepol/cil/src/cil_tree.c               |  22 +++-
> >>  libsepol/cil/src/cil_verify.c             |  12 +-
> >>  secilc/docs/cil_constraint_statements.xml |   8 +-
> >>  secilc/docs/cil_user_statements.xml       | 110 +++++++++++++++++-
> >>  secilc/test/policy.cil                    |  22 +++-
> >>  19 files changed, 786 insertions(+), 66 deletions(-)
> >>
> >> diff --git a/libsepol/cil/src/cil.c b/libsepol/cil/src/cil.c
> >> index a89c585..8716deb 100644
> >> --- a/libsepol/cil/src/cil.c
> >> +++ b/libsepol/cil/src/cil.c
> >> @@ -122,6 +122,8 @@ static void cil_init_keys(void)
> >>  	CIL_KEY_TYPE = cil_strpool_add("type");
> >>  	CIL_KEY_ROLE = cil_strpool_add("role");
> >>  	CIL_KEY_USER = cil_strpool_add("user");
> >> +	CIL_KEY_USERATTRIBUTE = cil_strpool_add("userattribute");
> >> +	CIL_KEY_USERATTRIBUTESET = cil_strpool_add("userattributeset");
> >>  	CIL_KEY_SENSITIVITY = cil_strpool_add("sensitivity");
> >>  	CIL_KEY_CATEGORY = cil_strpool_add("category");
> >>  	CIL_KEY_CATSET = cil_strpool_add("categoryset");
> >> @@ -266,9 +268,11 @@ void cil_db_init(struct cil_db **db)
> >>  	(*db)->num_classes = 0;
> >>  	(*db)->num_types = 0;
> >>  	(*db)->num_roles = 0;
> >> +	(*db)->num_users = 0;
> >>  	(*db)->num_cats = 0;
> >>  	(*db)->val_to_type = NULL;
> >>  	(*db)->val_to_role = NULL;
> >> +	(*db)->val_to_user = NULL;
> >>
> >>  	(*db)->disable_dontaudit = CIL_FALSE;
> >>  	(*db)->disable_neverallow = CIL_FALSE;
> >> @@ -311,6 +315,7 @@ void cil_db_destroy(struct cil_db **db)
> >>  	cil_strpool_destroy();
> >>  	free((*db)->val_to_type);
> >>  	free((*db)->val_to_role);
> >> +	free((*db)->val_to_user);
> >>
> >>  	free(*db);
> >>  	*db = NULL;
> >> @@ -552,6 +557,12 @@ void cil_destroy_data(void **data, enum
> >cil_flavor flavor)
> >>  	case CIL_USER:
> >>  		cil_destroy_user(*data);
> >>  		break;
> >> +	case CIL_USERATTRIBUTE:
> >> +		cil_destroy_userattribute(*data);
> >> +		break;
> >> +	case CIL_USERATTRIBUTESET:
> >> +		cil_destroy_userattributeset(*data);
> >> +		break;
> >>  	case CIL_USERPREFIX:
> >>  		cil_destroy_userprefix(*data);
> >>  		break;
> >> @@ -794,6 +805,7 @@ int cil_flavor_to_symtab_index(enum cil_flavor
> >flavor, enum cil_sym_index *sym_i
> >>  		*sym_index = CIL_SYM_CLASSPERMSETS;
> >>  		break;
> >>  	case CIL_USER:
> >> +	case CIL_USERATTRIBUTE:
> >>  		*sym_index = CIL_SYM_USERS;
> >>  		break;
> >>  	case CIL_ROLE:
> >> @@ -924,6 +936,10 @@ const char * cil_node_to_string(struct
> >cil_tree_node *node)
> >>  		return CIL_KEY_CLASSPERMISSIONSET;
> >>  	case CIL_USER:
> >>  		return CIL_KEY_USER;
> >> +	case CIL_USERATTRIBUTE:
> >> +		return CIL_KEY_USERATTRIBUTE;
> >> +	case CIL_USERATTRIBUTESET:
> >> +		return CIL_KEY_USERATTRIBUTESET;
> >>  	case CIL_USERPREFIX:
> >>  		return CIL_KEY_USERPREFIX;
> >>  	case CIL_USERROLE:
> >> @@ -2379,6 +2395,26 @@ void cil_user_init(struct cil_user **user)
> >>  	(*user)->roles = NULL;
> >>  	(*user)->dftlevel = NULL;
> >>  	(*user)->range = NULL;
> >> +	(*user)->value = 0;
> >> +}
> >> +
> >> +void cil_userattribute_init(struct cil_userattribute **attr)
> >> +{
> >> +	*attr = cil_malloc(sizeof(**attr));
> >> +
> >> +	cil_symtab_datum_init(&(*attr)->datum);
> >> +
> >> +	(*attr)->expr_list = NULL;
> >> +	(*attr)->users = NULL;
> >> +}
> >> +
> >> +void cil_userattributeset_init(struct cil_userattributeset **attrset)
> >> +{
> >> +	*attrset = cil_malloc(sizeof(**attrset));
> >> +
> >> +	(*attrset)->attr_str = NULL;
> >> +	(*attrset)->str_expr = NULL;
> >> +	(*attrset)->datum_expr = NULL;
> >>  }
> >>
> >>  void cil_userlevel_init(struct cil_userlevel **usrlvl)
> >> diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c
> >> index 52ce067..32304e2 100644
> >> --- a/libsepol/cil/src/cil_binary.c
> >> +++ b/libsepol/cil/src/cil_binary.c
> >> @@ -144,6 +144,34 @@ static int
> >__cil_get_sepol_level_datum(policydb_t *pdb, struct cil_symtab_datum
> >>  	return SEPOL_OK;
> >>  }
> >>
> >> +static int __cil_expand_user(struct cil_symtab_datum *datum,
> >ebitmap_t *new)
> >> +{
> >> +	struct cil_tree_node *node = datum->nodes->head->data;
> >> +	struct cil_user *user = NULL;
> >> +	struct cil_userattribute *attr = NULL;
> >> +
> >> +	if (node->flavor == CIL_USERATTRIBUTE) {
> >> +		attr = (struct cil_userattribute *)datum;
> >> +		if (ebitmap_cpy(new, attr->users)) {
> >> +			cil_log(CIL_ERR, "Failed to copy user bits\n");
> >> +			goto exit;
> >> +		}
> >> +	} else {
> >> +		user = (struct cil_user *)datum;
> >> +		ebitmap_init(new);
> >> +		if (ebitmap_set_bit(new, user->value, 1)) {
> >> +			cil_log(CIL_ERR, "Failed to set user bit\n");
> >> +			ebitmap_destroy(new);
> >> +			goto exit;
> >> +		}
> >> +	}
> >> +
> >> +	return SEPOL_OK;
> >> +
> >> +exit:
> >> +	return SEPOL_ERR;
> >> +}
> >> +
> >>  static int __cil_expand_role(struct cil_symtab_datum *datum,
> >ebitmap_t *new)
> >>  {
> >>  	struct cil_tree_node *node = datum->nodes->head->data;
> >> @@ -746,43 +774,41 @@ exit:
> >>  	return SEPOL_ERR;
> >>  }
> >>
> >> -int cil_userrole_to_policydb(policydb_t *pdb, const struct cil_db *db,
> >struct cil_userrole *userrole)
> >> +int cil_userrole_to_policydb(policydb_t *pdb, const struct cil_db *db,
> >struct cil_user *user)
> >>  {
> >>  	int rc = SEPOL_ERR;
> >>  	user_datum_t *sepol_user = NULL;
> >>  	role_datum_t *sepol_role = NULL;
> >> -	ebitmap_t role_bitmap;
> >> -	ebitmap_node_t *rnode;
> >> +	ebitmap_node_t *rnode = NULL;
> >>  	unsigned int i;
> >>
> >> -	rc = __cil_get_sepol_user_datum(pdb, DATUM(userrole->user),
> >&sepol_user);
> >> -	if (rc != SEPOL_OK) goto exit;
> >> -
> >> -	rc = __cil_expand_role(userrole->role, &role_bitmap);
> >> -	if (rc != SEPOL_OK) goto exit;
> >> -
> >> -	ebitmap_for_each_bit(&role_bitmap, rnode, i) {
> >> -		if (!ebitmap_get_bit(&role_bitmap, i)) continue;
> >> +	if (user->roles) {
> >> +		rc = __cil_get_sepol_user_datum(pdb, DATUM(user),
> >&sepol_user);
> >> +		if (rc != SEPOL_OK) {
> >> +			goto exit;
> >> +		}
> >>
> >> -		rc = __cil_get_sepol_role_datum(pdb, DATUM(db-
> >>val_to_role[i]), &sepol_role);
> >> -		if (rc != SEPOL_OK) goto exit;
> >> +		ebitmap_for_each_bit(user->roles, rnode, i) {
> >> +			if (!ebitmap_get_bit(user->roles, i)) {
> >> +				continue;
> >> +			}
> >>
> >> -		if (sepol_role->s.value == 1) {
> >> -			// role is object_r, ignore it since it is implicitly
> >associated
> >> -			// with all users
> >> -			continue;
> >> -		}
> >> +			rc = __cil_get_sepol_role_datum(pdb, DATUM(db-
> >>val_to_role[i]), &sepol_role);
> >> +			if (rc != SEPOL_OK) {
> >> +				goto exit;
> >> +			}
> >>
> >> -		if (ebitmap_set_bit(&sepol_user->roles.roles, sepol_role-
> >>s.value - 1, 1)) {
> >> -			cil_log(CIL_INFO, "Failed to set role bit for user\n");
> >> -			goto exit;
> >> +			if (ebitmap_set_bit(&sepol_user->roles.roles,
> >sepol_role->s.value - 1, 1)) {
> >> +				cil_log(CIL_INFO, "Failed to set role bit for
> >user\n");
> >> +				rc = SEPOL_ERR;
> >> +				goto exit;
> >> +			}
> >>  		}
> >>  	}
> >>
> >>  	rc = SEPOL_OK;
> >>
> >>  exit:
> >> -	ebitmap_destroy(&role_bitmap);
> >>  	return rc;
> >>  }
> >>
> >> @@ -2183,12 +2209,30 @@ int
> >__cil_constrain_expr_datum_to_sepol_expr(policydb_t *pdb, const struct
> >cil_d
> >>
> >>  	if (expr_flavor == CIL_USER) {
> >>  		user_datum_t *sepol_user = NULL;
> >> -		rc = __cil_get_sepol_user_datum(pdb, item->data,
> >&sepol_user);
> >> +		ebitmap_t user_bitmap;
> >> +		ebitmap_node_t *unode;
> >> +		unsigned int i;
> >> +
> >> +		rc = __cil_expand_user(item->data, &user_bitmap);
> >>  		if (rc != SEPOL_OK) goto exit;
> >>
> >> -		if (ebitmap_set_bit(&expr->names, sepol_user->s.value - 1,
> >1)) {
> >> -			goto exit;
> >> +		ebitmap_for_each_bit(&user_bitmap, unode, i) {
> >> +			if (!ebitmap_get_bit(&user_bitmap, i)) {
> >> +				continue;
> >> +			}
> >> +
> >> +			rc = __cil_get_sepol_user_datum(pdb, DATUM(db-
> >>val_to_user[i]), &sepol_user);
> >> +			if (rc != SEPOL_OK) {
> >> +				ebitmap_destroy(&user_bitmap);
> >> +				goto exit;
> >> +			}
> >> +
> >> +			if (ebitmap_set_bit(&expr->names, sepol_user-
> >>s.value - 1, 1)) {
> >> +				ebitmap_destroy(&user_bitmap);
> >> +				goto exit;
> >> +			}
> >>  		}
> >> +		ebitmap_destroy(&user_bitmap);
> >>  	} else if (expr_flavor == CIL_ROLE) {
> >>  		role_datum_t *sepol_role = NULL;
> >>  		ebitmap_t role_bitmap;
> >> @@ -3374,9 +3418,10 @@ int __cil_node_to_policydb(struct
> >cil_tree_node *node, void *extra_args)
> >>  			if (rc != SEPOL_OK) goto exit;
> >>  			if (pdb->mls == CIL_TRUE) {
> >>  				rc =
> >cil_userlevel_userrange_to_policydb(pdb, node->data);
> >> +				if (rc != SEPOL_OK) {
> >> +					goto exit;
> >> +				}
> >>  			}
> >> -			break;
> >> -		case CIL_USERROLE:
> >>  			rc = cil_userrole_to_policydb(pdb, db, node-
> >>data);
> >>  			break;
> >>  		case CIL_TYPE_RULE:
> >> diff --git a/libsepol/cil/src/cil_binary.h b/libsepol/cil/src/cil_binary.h
> >> index 33b43f9..c59b1e3 100644
> >> --- a/libsepol/cil/src/cil_binary.h
> >> +++ b/libsepol/cil/src/cil_binary.h
> >> @@ -184,12 +184,13 @@ int cil_user_to_policydb(policydb_t *pdb,
> >struct cil_user *cil_user);
> >>  /**
> >>   * Insert cil userrole structure into sepol policydb.
> >>   *
> >> - * @param[in] pdb THe policy database to insert the userrole into.
> >> - * @param[in] datum The cil_userrole datum.
> >> + * @param[in] pdb The policy database to insert the userrole into.
> >> + * @param[in] db The cil database
> >> + * @param[in] datum The cil_user
> >>   *
> >>   * @return SEPOL_OK upon success or SEPOL_ERR otherwise.
> >>   */
> >> -int cil_userrole_to_policydb(policydb_t *pdb, const struct cil_db *db,
> >struct cil_userrole *userrole);
> >> +int cil_userrole_to_policydb(policydb_t *pdb, const struct cil_db *db,
> >struct cil_user *user);
> >>
> >>  /**
> >>   * Insert cil bool structure into sepol policydb.
> >> diff --git a/libsepol/cil/src/cil_build_ast.c b/libsepol/cil/src/cil_build_ast.c
> >> index 32ebee1..861b606 100644
> >> --- a/libsepol/cil/src/cil_build_ast.c
> >> +++ b/libsepol/cil/src/cil_build_ast.c
> >> @@ -1196,10 +1196,132 @@ void cil_destroy_user(struct cil_user *user)
> >>  	}
> >>
> >>  	cil_symtab_datum_destroy(&user->datum);
> >> -	cil_list_destroy(&user->roles, CIL_FALSE);
> >> +	ebitmap_destroy(user->roles);
> >> +	free(user->roles);
> >>  	free(user);
> >>  }
> >>
> >> +int cil_gen_userattribute(__attribute__((unused)) struct cil_db *db,
> >struct cil_tree_node *parse_current, struct cil_tree_node *ast_node)
> >> +{
> >> +	enum cil_syntax syntax[] = {
> >> +		CIL_SYN_STRING,
> >> +		CIL_SYN_STRING,
> >> +		CIL_SYN_END
> >> +	};
> >> +	int syntax_len = sizeof(syntax)/sizeof(*syntax);
> >> +	char *key = NULL;
> >> +	struct cil_userattribute *attr = NULL;
> >> +	int rc = SEPOL_ERR;
> >> +
> >> +	if (parse_current == NULL || ast_node == NULL) {
> >> +		goto exit;
> >> +	}
> >> +
> >> +	rc = __cil_verify_syntax(parse_current, syntax, syntax_len);
> >> +	if (rc != SEPOL_OK) {
> >> +		goto exit;
> >> +	}
> >> +
> >> +	cil_userattribute_init(&attr);
> >> +
> >> +	key = parse_current->next->data;
> >> +	rc = cil_gen_node(db, ast_node, (struct cil_symtab_datum*)attr,
> >(hashtab_key_t)key, CIL_SYM_USERS, CIL_USERATTRIBUTE);
> >> +	if (rc != SEPOL_OK) {
> >> +		goto exit;
> >> +	}
> >> +
> >> +	return SEPOL_OK;
> >> +exit:
> >> +	cil_log(CIL_ERR, "Bad userattribute declaration at line %d of %s\n",
> >> +		parse_current->line, parse_current->path);
> >> +	cil_destroy_userattribute(attr);
> >> +	cil_clear_node(ast_node);
> >> +	return rc;
> >> +}
> >> +
> >> +void cil_destroy_userattribute(struct cil_userattribute *attr)
> >> +{
> >> +	struct cil_list_item *expr = NULL;
> >> +	struct cil_list_item *next = NULL;
> >> +
> >> +	if (attr == NULL) {
> >> +		return;
> >> +	}
> >> +
> >> +	if (attr->expr_list != NULL) {
> >> +		/* we don't want to destroy the expression stacks (cil_list)
> >inside
> >> +		 * this list cil_list_destroy destroys sublists, so we need to
> >do it
> >> +		 * manually */
> >> +		expr = attr->expr_list->head;
> >> +		while (expr != NULL) {
> >> +			next = expr->next;
> >> +			cil_list_item_destroy(&expr, CIL_FALSE);
> >> +			expr = next;
> >> +		}
> >> +		free(attr->expr_list);
> >> +		attr->expr_list = NULL;
> >> +	}
> >> +
> >> +	cil_symtab_datum_destroy(&attr->datum);
> >> +	ebitmap_destroy(attr->users);
> >> +	free(attr->users);
> >> +	free(attr);
> >> +}
> >> +
> >> +int cil_gen_userattributeset(__attribute__((unused)) struct cil_db *db,
> >struct cil_tree_node *parse_current, struct cil_tree_node *ast_node)
> >> +{
> >> +	enum cil_syntax syntax[] = {
> >> +		CIL_SYN_STRING,
> >> +		CIL_SYN_STRING,
> >> +		CIL_SYN_STRING | CIL_SYN_LIST,
> >> +		CIL_SYN_END
> >> +	};
> >> +	int syntax_len = sizeof(syntax)/sizeof(*syntax);
> >> +	struct cil_userattributeset *attrset = NULL;
> >> +	int rc = SEPOL_ERR;
> >> +
> >> +	if (db == NULL || parse_current == NULL || ast_node == NULL) {
> >> +		goto exit;
> >> +	}
> >> +
> >> +	rc = __cil_verify_syntax(parse_current, syntax, syntax_len);
> >> +	if (rc != SEPOL_OK) {
> >> +		goto exit;
> >> +	}
> >> +
> >> +	cil_userattributeset_init(&attrset);
> >> +
> >> +	attrset->attr_str = parse_current->next->data;
> >> +
> >> +	rc = cil_gen_expr(parse_current->next->next, CIL_USER, &attrset-
> >>str_expr);
> >> +	if (rc != SEPOL_OK) {
> >> +		goto exit;
> >> +	}
> >> +	ast_node->data = attrset;
> >> +	ast_node->flavor = CIL_USERATTRIBUTESET;
> >> +
> >> +	return SEPOL_OK;
> >> +
> >> +exit:
> >> +	cil_log(CIL_ERR, "Bad userattributeset declaration at line %d of
> >%s\n",
> >> +		parse_current->line, parse_current->path);
> >> +	cil_destroy_userattributeset(attrset);
> >> +
> >> +	return rc;
> >> +}
> >> +
> >> +void cil_destroy_userattributeset(struct cil_userattributeset *attrset)
> >> +{
> >> +	if (attrset == NULL) {
> >> +		return;
> >> +	}
> >> +
> >> +	cil_list_destroy(&attrset->str_expr, CIL_TRUE);
> >> +	cil_list_destroy(&attrset->datum_expr, CIL_FALSE);
> >> +
> >> +	free(attrset);
> >> +}
> >> +
> >>  int cil_gen_userlevel(__attribute__((unused)) struct cil_db *db, struct
> >cil_tree_node *parse_current, struct cil_tree_node *ast_node)
> >>  {
> >>  	enum cil_syntax syntax[] = {
> >> @@ -5855,6 +5977,11 @@ int __cil_build_ast_node_helper(struct
> >cil_tree_node *parse_current, uint32_t *f
> >>  		*finished = CIL_TREE_SKIP_NEXT;
> >>  	} else if (parse_current->data == CIL_KEY_USER) {
> >>  		rc = cil_gen_user(db, parse_current, ast_node);
> >> +	} else if (parse_current->data == CIL_KEY_USERATTRIBUTE) {
> >> +		rc = cil_gen_userattribute(db, parse_current, ast_node);
> >> +	} else if (parse_current->data == CIL_KEY_USERATTRIBUTESET) {
> >> +		rc = cil_gen_userattributeset(db, parse_current, ast_node);
> >> +		*finished = CIL_TREE_SKIP_NEXT;
> >>  	} else if (parse_current->data == CIL_KEY_USERLEVEL) {
> >>  		rc = cil_gen_userlevel(db, parse_current, ast_node);
> >>  		*finished = CIL_TREE_SKIP_NEXT;
> >> diff --git a/libsepol/cil/src/cil_build_ast.h
> >b/libsepol/cil/src/cil_build_ast.h
> >> index 1b40ae5..11f51f5 100644
> >> --- a/libsepol/cil/src/cil_build_ast.h
> >> +++ b/libsepol/cil/src/cil_build_ast.h
> >> @@ -80,6 +80,10 @@ int cil_gen_sidorder(struct cil_db *db, struct
> >cil_tree_node *parse_current, str
> >>  void cil_destroy_sidorder(struct cil_sidorder *sidorder);
> >>  int cil_gen_user(struct cil_db *db, struct cil_tree_node *parse_current,
> >struct cil_tree_node *ast_node);
> >>  void cil_destroy_user(struct cil_user *user);
> >> +int cil_gen_userattribute(struct cil_db *db, struct cil_tree_node
> >*parse_current, struct cil_tree_node *ast_node);
> >> +void cil_destroy_userattribute(struct cil_userattribute *attr);
> >> +int cil_gen_userattributeset(struct cil_db *db, struct cil_tree_node
> >*parse_current, struct cil_tree_node *ast_node);
> >> +void cil_destroy_userattributeset(struct cil_userattributeset *attrset);
> >>  int cil_gen_userlevel(struct cil_db *db, struct cil_tree_node
> >*parse_current, struct cil_tree_node *ast_node);
> >>  void cil_destroy_userlevel(struct cil_userlevel *usrlvl);
> >>  int cil_gen_userrange(struct cil_db *db, struct cil_tree_node
> >*parse_current, struct cil_tree_node *ast_node);
> >> diff --git a/libsepol/cil/src/cil_copy_ast.c b/libsepol/cil/src/cil_copy_ast.c
> >> index d488870..8c50ff0 100644
> >> --- a/libsepol/cil/src/cil_copy_ast.c
> >> +++ b/libsepol/cil/src/cil_copy_ast.c
> >> @@ -392,6 +392,41 @@ int cil_copy_user(__attribute__((unused)) struct
> >cil_db *db, void *data, void **
> >>  	return SEPOL_OK;
> >>  }
> >>
> >> +int cil_copy_userattribute(__attribute__((unused)) struct cil_db *db,
> >void *data, void **copy, symtab_t *symtab)
> >> +{
> >> +	struct cil_userattribute *orig = data;
> >> +	struct cil_userattribute *new = NULL;
> >> +	char *key = orig->datum.name;
> >> +	struct cil_symtab_datum *datum = NULL;
> >> +
> >> +	cil_symtab_get_datum(symtab, key, &datum);
> >> +	if (datum == NULL) {
> >> +		cil_userattribute_init(&new);
> >> +		*copy = new;
> >> +	} else {
> >> +		*copy = datum;
> >> +	}
> >> +
> >> +	return SEPOL_OK;
> >> +}
> >> +
> >> +int cil_copy_userattributeset(struct cil_db *db, void *data, void **copy,
> >__attribute__((unused)) symtab_t *symtab)
> >> +{
> >> +	struct cil_userattributeset *orig = data;
> >> +	struct cil_userattributeset *new = NULL;
> >> +
> >> +	cil_userattributeset_init(&new);
> >> +
> >> +	new->attr_str = orig->attr_str;
> >> +
> >> +	cil_copy_expr(db, orig->str_expr, &new->str_expr);
> >> +	cil_copy_expr(db, orig->datum_expr, &new->datum_expr);
> >> +
> >> +	*copy = new;
> >> +
> >> +	return SEPOL_OK;
> >> +}
> >> +
> >>  int cil_copy_userrole(__attribute__((unused)) struct cil_db *db, void
> >*data, void **copy, __attribute__((unused)) symtab_t *symtab)
> >>  {
> >>  	struct cil_userrole *orig = data;
> >> @@ -1717,6 +1752,12 @@ int __cil_copy_node_helper(struct
> >cil_tree_node *orig, __attribute__((unused)) u
> >>  	case CIL_USER:
> >>  		copy_func = &cil_copy_user;
> >>  		break;
> >> +	case CIL_USERATTRIBUTE:
> >> +		copy_func = &cil_copy_userattribute;
> >> +		break;
> >> +	case CIL_USERATTRIBUTESET:
> >> +		copy_func = &cil_copy_userattributeset;
> >> +		break;
> >>  	case CIL_USERROLE:
> >>  		copy_func = &cil_copy_userrole;
> >>  		break;
> >> diff --git a/libsepol/cil/src/cil_copy_ast.h b/libsepol/cil/src/cil_copy_ast.h
> >> index bd3a231..78c34b8 100644
> >> --- a/libsepol/cil/src/cil_copy_ast.h
> >> +++ b/libsepol/cil/src/cil_copy_ast.h
> >> @@ -57,6 +57,8 @@ int cil_copy_sid(struct cil_db *db, void *data, void
> >**copy, symtab_t *symtab);
> >>  int cil_copy_sidcontext(struct cil_db *db, void *data, void **copy,
> >symtab_t *symtab);
> >>  int cil_copy_sidorder(struct cil_db *db, void *data, void **copy,
> >symtab_t *symtab);
> >>  int cil_copy_user(struct cil_db *db, void *data, void **copy, symtab_t
> >*symtab);
> >> +int cil_copy_userattribute(struct cil_db *db, void *data, void **copy,
> >symtab_t *symtab);
> >> +int cil_copy_userattributeset(struct cil_db *db, void *data, void **copy,
> >symtab_t *symtab);
> >>  int cil_copy_userrole(struct cil_db *db, void *data, void **copy,
> >symtab_t *symtab);
> >>  int cil_copy_userlevel(struct cil_db *db, void *data, void **copy,
> >symtab_t *symtab);
> >>  int cil_copy_userrange(struct cil_db *db, void *data, void **copy,
> >symtab_t *symtab);
> >> diff --git a/libsepol/cil/src/cil_flavor.h b/libsepol/cil/src/cil_flavor.h
> >> index 79483c7..9fb5083 100644
> >> --- a/libsepol/cil/src/cil_flavor.h
> >> +++ b/libsepol/cil/src/cil_flavor.h
> >> @@ -63,6 +63,7 @@ enum cil_flavor {
> >>  	CIL_CLASSPERMISSIONSET,
> >>  	CIL_USERPREFIX,
> >>  	CIL_USERROLE,
> >> +	CIL_USERATTRIBUTESET,
> >>  	CIL_USERLEVEL,
> >>  	CIL_USERRANGE,
> >>  	CIL_USERBOUNDS,
> >> @@ -164,6 +165,7 @@ enum cil_flavor {
> >>  	CIL_MAP_CLASS,
> >>  	CIL_CLASSPERMISSION,
> >>  	CIL_USER,
> >> +	CIL_USERATTRIBUTE,
> >>  	CIL_ROLE,
> >>  	CIL_ROLEATTRIBUTE,
> >>  	CIL_TYPE,
> >> diff --git a/libsepol/cil/src/cil_internal.h b/libsepol/cil/src/cil_internal.h
> >> index e596ab5..a736eff 100644
> >> --- a/libsepol/cil/src/cil_internal.h
> >> +++ b/libsepol/cil/src/cil_internal.h
> >> @@ -127,6 +127,8 @@ char *CIL_KEY_TRANS;
> >>  char *CIL_KEY_TYPE;
> >>  char *CIL_KEY_ROLE;
> >>  char *CIL_KEY_USER;
> >> +char *CIL_KEY_USERATTRIBUTE;
> >> +char *CIL_KEY_USERATTRIBUTESET;
> >>  char *CIL_KEY_SENSITIVITY;
> >>  char *CIL_KEY_CATEGORY;
> >>  char *CIL_KEY_CATSET;
> >> @@ -290,8 +292,10 @@ struct cil_db {
> >>  	int num_cats;
> >>  	int num_types;
> >>  	int num_roles;
> >> +	int num_users;
> >>  	struct cil_type **val_to_type;
> >>  	struct cil_role **val_to_role;
> >> +	struct cil_user **val_to_user;
> >>  	int disable_dontaudit;
> >>  	int disable_neverallow;
> >>  	int preserve_tunables;
> >> @@ -418,14 +422,27 @@ struct cil_sidorder {
> >>  struct cil_user {
> >>  	struct cil_symtab_datum datum;
> >>  	struct cil_user *bounds;
> >> -	struct cil_list *roles;
> >> +	ebitmap_t *roles;
> >>  	struct cil_level *dftlevel;
> >>  	struct cil_levelrange *range;
> >> +	int value;
> >> +};
> >> +
> >> +struct cil_userattribute {
> >> +	struct cil_symtab_datum datum;
> >> +	struct cil_list *expr_list;
> >> +	ebitmap_t *users;
> >> +};
> >> +
> >> +struct cil_userattributeset {
> >> +	char *attr_str;
> >> +	struct cil_list *str_expr;
> >> +	struct cil_list *datum_expr;
> >>  };
> >>
> >>  struct cil_userrole {
> >>  	char *user_str;
> >> -	struct cil_user *user;
> >> +	void *user;
> >>  	char *role_str;
> >>  	void *role;
> >>  };
> >> @@ -1002,5 +1019,7 @@ void cil_default_init(struct cil_default **def);
> >>  void cil_defaultrange_init(struct cil_defaultrange **def);
> >>  void cil_handleunknown_init(struct cil_handleunknown **unk);
> >>  void cil_mls_init(struct cil_mls **mls);
> >> +void cil_userattribute_init(struct cil_userattribute **attribute);
> >> +void cil_userattributeset_init(struct cil_userattributeset **attrset);
> >>
> >>  #endif
> >> diff --git a/libsepol/cil/src/cil_policy.c b/libsepol/cil/src/cil_policy.c
> >> index eefcbc1..a9e2426 100644
> >> --- a/libsepol/cil/src/cil_policy.c
> >> +++ b/libsepol/cil/src/cil_policy.c
> >> @@ -1155,11 +1155,6 @@ int __cil_gen_policy_node_helper(struct
> >cil_tree_node *node, uint32_t *finished,
> >>  		case CIL_USER:
> >>  			cil_multimap_insert(users, node->data, NULL,
> >CIL_USERROLE, CIL_NONE);
> >>  			break;
> >> -		case CIL_USERROLE: {
> >> -			struct cil_userrole *userrole = node->data;
> >> -			cil_multimap_insert(users, &userrole->user-
> >>datum, (struct cil_symtab_datum *)userrole->role, CIL_USERROLE,
> >CIL_ROLE);
> >> -		}
> >> -			break;
> >>  		case CIL_CATALIAS: {
> >>  			struct cil_alias *alias = node->data;
> >>  			struct cil_symtab_datum *datum = alias->actual;
> >> diff --git a/libsepol/cil/src/cil_post.c b/libsepol/cil/src/cil_post.c
> >> index c4ea66a..8050bbb 100644
> >> --- a/libsepol/cil/src/cil_post.c
> >> +++ b/libsepol/cil/src/cil_post.c
> >> @@ -375,6 +375,17 @@ static int __cil_post_db_count_helper(struct
> >cil_tree_node *node, uint32_t *fini
> >>  		}
> >>  		break;
> >>  	}
> >> +	case CIL_USER: {
> >> +		struct cil_user *user = node->data;
> >> +		if (user->datum.nodes->head->data == node) {
> >> +			// multiple AST nodes can point to the same
> >cil_user data (like if
> >> +			// copied from a macro). This check ensures we
> >only count the
> >> +			// duplicates once
> >> +			user->value = db->num_users;
> >> +			db->num_users++;
> >> +		}
> >> +		break;
> >> +	}
> >>  	case CIL_NETIFCON:
> >>  		db->netifcon->count++;
> >>  		break;
> >> @@ -446,6 +457,14 @@ static int __cil_post_db_array_helper(struct
> >cil_tree_node *node, __attribute__(
> >>  		db->val_to_role[role->value] = role;
> >>  		break;
> >>  	}
> >> +	case CIL_USER: {
> >> +		struct cil_user *user= node->data;
> >> +		if (db->val_to_user == NULL) {
> >> +			db->val_to_user = cil_malloc(sizeof(*db-
> >>val_to_user) * db->num_users);
> >> +		}
> >> +		db->val_to_user[user->value] = user;
> >> +		break;
> >> +	}
> >>  	case CIL_USERPREFIX: {
> >>  		cil_list_append(db->userprefixes, CIL_USERPREFIX, node-
> >>data);
> >>  		break;
> >> @@ -638,6 +657,54 @@ exit:
> >>  	return rc;
> >>  }
> >>
> >> +static int __evaluate_user_expression(struct cil_userattribute *attr,
> >struct cil_db *db)
> >> +{
> >> +	int rc;
> >> +
> >> +	attr->users = cil_malloc(sizeof(*attr->users));
> >> +	rc = __cil_expr_list_to_bitmap(attr->expr_list, attr->users, db-
> >>num_users, db);
> >> +	if (rc != SEPOL_OK) {
> >> +		cil_log(CIL_ERR, "Failed to expand user attribute to
> >bitmap\n");
> >> +		ebitmap_destroy(attr->users);
> >> +		free(attr->users);
> >> +		attr->users = NULL;
> >> +	}
> >> +	return rc;
> >> +}
> >> +
> >> +static int __cil_user_to_bitmap(struct cil_symtab_datum *datum,
> >ebitmap_t *bitmap, struct cil_db *db)
> >> +{
> >> +	int rc = SEPOL_ERR;
> >> +	struct cil_tree_node *node = datum->nodes->head->data;
> >> +	struct cil_userattribute *attr = NULL;
> >> +	struct cil_user *user = NULL;
> >> +
> >> +	ebitmap_init(bitmap);
> >> +
> >> +	if (node->flavor == CIL_USERATTRIBUTE) {
> >> +		attr = (struct cil_userattribute *)datum;
> >> +		if (attr->users == NULL) {
> >> +			rc = __evaluate_user_expression(attr, db);
> >> +			if (rc != SEPOL_OK) {
> >> +				goto exit;
> >> +			}
> >> +		}
> >> +		ebitmap_union(bitmap, attr->users);
> >> +	} else {
> >> +		user = (struct cil_user *)datum;
> >> +		if (ebitmap_set_bit(bitmap, user->value, 1)) {
> >> +			cil_log(CIL_ERR, "Failed to set user bit\n");
> >> +			ebitmap_destroy(bitmap);
> >> +			goto exit;
> >> +		}
> >> +	}
> >> +
> >> +	return SEPOL_OK;
> >> +
> >> +exit:
> >> +	return rc;
> >> +}
> >> +
> >>  static int __evaluate_role_expression(struct cil_roleattribute *attr,
> >struct cil_db *db)
> >>  {
> >>  	int rc;
> >> @@ -941,6 +1008,9 @@ static int __cil_expr_to_bitmap_helper(struct
> >cil_list_item *curr, enum cil_flav
> >>  		case CIL_ROLE:
> >>  			rc = __cil_role_to_bitmap(curr->data, bitmap, db);
> >>  			break;
> >> +		case CIL_USER:
> >> +			rc = __cil_user_to_bitmap(curr->data, bitmap, db);
> >> +			break;
> >>  		case CIL_PERM:
> >>  			rc = __cil_perm_to_bitmap(curr->data, bitmap,
> >db);
> >>  			break;
> >> @@ -1163,6 +1233,16 @@ static int __cil_post_db_attr_helper(struct
> >cil_tree_node *node, __attribute__((
> >>  		if (rc != SEPOL_OK) goto exit;
> >>  		break;
> >>  	}
> >> +	case CIL_USERATTRIBUTE: {
> >> +		struct cil_userattribute *attr = node->data;
> >> +		if (attr->users == NULL) {
> >> +			rc = __evaluate_user_expression(attr, db);
> >> +			if (rc != SEPOL_OK) {
> >> +				goto exit;
> >> +			}
> >> +		}
> >> +		break;
> >> +	}
> >>  	default:
> >>  		break;
> >>  	}
> >> @@ -1268,6 +1348,102 @@ exit:
> >>  	return rc;
> >>  }
> >>
> >> +static int __cil_user_assign_roles(struct cil_user *user, struct
> >cil_symtab_datum *datum)
> >> +{
> >> +	struct cil_tree_node *node = datum->nodes->head->data;
> >> +	struct cil_role *role = NULL;
> >> +	struct cil_roleattribute *attr = NULL;
> >> +
> >> +	if (user->roles == NULL) {
> >> +		user->roles = cil_malloc(sizeof(*user->roles));
> >> +		ebitmap_init(user->roles);
> >> +	}
> >> +
> >> +	if (node->flavor == CIL_ROLE) {
> >> +		role = (struct cil_role *)datum;
> >> +		if (ebitmap_set_bit(user->roles, role->value, 1)) {
> >> +			cil_log(CIL_INFO, "Failed to set bit in user roles
> >bitmap\n");
> >> +			goto exit;
> >> +		}
> >> +	} else if (node->flavor == CIL_ROLEATTRIBUTE) {
> >> +		attr = (struct cil_roleattribute *)datum;
> >> +		ebitmap_union(user->roles, attr->roles);
> >> +	}
> >> +
> >> +	return SEPOL_OK;
> >> +
> >> +exit:
> >> +	return SEPOL_ERR;
> >> +}
> >> +
> >> +static int __cil_post_db_userrole_helper(struct cil_tree_node *node,
> >__attribute__((unused)) uint32_t *finished, void *extra_args)
> >> +{
> >> +	int rc = SEPOL_ERR;
> >> +	struct cil_db *db = extra_args;
> >> +	struct cil_block *blk = NULL;
> >> +	struct cil_userrole *userrole = NULL;
> >> +	struct cil_symtab_datum *user_datum = NULL;
> >> +	struct cil_symtab_datum *role_datum = NULL;
> >> +	struct cil_tree_node *user_node = NULL;
> >> +	struct cil_userattribute *u_attr = NULL;
> >> +	unsigned int i;
> >> +	struct cil_user *user = NULL;
> >> +	ebitmap_node_t *unode = NULL;
> >> +
> >> +	switch (node->flavor) {
> >> +	case CIL_BLOCK: {
> >> +		blk = node->data;
> >> +		if (blk->is_abstract == CIL_TRUE) {
> >> +			*finished = CIL_TREE_SKIP_HEAD;
> >> +		}
> >> +		break;
> >> +	}
> >> +	case CIL_MACRO: {
> >> +		*finished = CIL_TREE_SKIP_HEAD;
> >> +		break;
> >> +	}
> >> +	case CIL_USERROLE: {
> >> +		userrole = node->data;
> >> +		user_datum = userrole->user;
> >> +		role_datum = userrole->role;
> >> +		user_node = user_datum->nodes->head->data;
> >> +
> >> +		if (user_node->flavor == CIL_USERATTRIBUTE) {
> >> +			u_attr = userrole->user;
> >> +
> >> +			ebitmap_for_each_bit(u_attr->users, unode, i) {
> >> +				if (!ebitmap_get_bit(u_attr->users, i)) {
> >> +					continue;
> >> +				}
> >> +
> >> +				user = db->val_to_user[i];
> >> +
> >> +				rc = __cil_user_assign_roles(user,
> >role_datum);
> >> +				if (rc != SEPOL_OK) {
> >> +					goto exit;
> >> +				}
> >> +			}
> >> +		} else {
> >> +			user = userrole->user;
> >> +
> >> +			rc = __cil_user_assign_roles(user, role_datum);
> >> +			if (rc != SEPOL_OK) {
> >> +				goto exit;
> >> +			}
> >> +		}
> >> +
> >> +		break;
> >> +	}
> >> +	default:
> >> +		break;
> >> +	}
> >> +
> >> +	return SEPOL_OK;
> >> +exit:
> >> +	cil_log(CIL_INFO, "cil_post_db_userrole_helper failed\n");
> >> +	return rc;
> >> +}
> >> +
> >>  static int __evaluate_level_expression(struct cil_level *level, struct
> >cil_db *db)
> >>  {
> >>  	if (level->cats != NULL) {
> >> @@ -1739,6 +1915,12 @@ static int cil_post_db(struct cil_db *db)
> >>  		goto exit;
> >>  	}
> >>
> >> +	rc = cil_tree_walk(db->ast->root, __cil_post_db_userrole_helper,
> >NULL, NULL, db);
> >> +	if (rc != SEPOL_OK) {
> >> +		cil_log(CIL_INFO, "Failed during userrole association\n");
> >> +		goto exit;
> >> +	}
> >> +
> >>  	rc = cil_tree_walk(db->ast->root, __cil_post_db_classperms_helper,
> >NULL, NULL, db);
> >>  	if (rc != SEPOL_OK) {
> >>  		cil_log(CIL_INFO, "Failed to evaluate class mapping
> >permissions expressions\n");
> >> diff --git a/libsepol/cil/src/cil_reset_ast.c b/libsepol/cil/src/cil_reset_ast.c
> >> index 92f7720..09cff05 100644
> >> --- a/libsepol/cil/src/cil_reset_ast.c
> >> +++ b/libsepol/cil/src/cil_reset_ast.c
> >> @@ -99,7 +99,32 @@ static void cil_reset_user(struct cil_user *user)
> >>  	user->bounds = NULL;
> >>  	user->dftlevel = NULL;
> >>  	user->range = NULL;
> >> -	cil_list_destroy(&user->roles, CIL_FALSE);
> >> +}
> >> +
> >> +static void cil_reset_userattr(struct cil_userattribute *attr)
> >> +{
> >> +	struct cil_list_item *expr = NULL;
> >> +	struct cil_list_item *next = NULL;
> >> +
> >> +	/* during a re-resolve, we need to reset the lists of expression
> >stacks associated with this attribute from a userattribute statement */
> >> +	if (attr->expr_list != NULL) {
> >> +		/* we don't want to destroy the expression stacks (cil_list)
> >inside
> >> +		 * this list cil_list_destroy destroys sublists, so we need to
> >do it
> >> +		 * manually */
> >> +		expr = attr->expr_list->head;
> >> +		while (expr != NULL) {
> >> +			next = expr->next;
> >> +			cil_list_item_destroy(&expr, CIL_FALSE);
> >> +			expr = next;
> >> +		}
> >> +		free(attr->expr_list);
> >> +		attr->expr_list = NULL;
> >> +	}
> >> +}
> >> +
> >> +static void cil_reset_userattributeset(struct cil_userattributeset *uas)
> >> +{
> >> +	cil_list_destroy(&uas->datum_expr, CIL_FALSE);
> >>  }
> >>
> >>  static void cil_reset_selinuxuser(struct cil_selinuxuser *selinuxuser)
> >> @@ -403,6 +428,12 @@ int __cil_reset_node(struct cil_tree_node
> >*node,  __attribute__((unused)) uint32
> >>  	case CIL_USER:
> >>  		cil_reset_user(node->data);
> >>  		break;
> >> +	case CIL_USERATTRIBUTE:
> >> +		cil_reset_userattr(node->data);
> >> +		break;
> >> +	case CIL_USERATTRIBUTESET:
> >> +		cil_reset_userattributeset(node->data);
> >> +		break;
> >>  	case CIL_SELINUXUSERDEFAULT:
> >>  	case CIL_SELINUXUSER:
> >>  		cil_reset_selinuxuser(node->data);
> >> diff --git a/libsepol/cil/src/cil_resolve_ast.c
> >b/libsepol/cil/src/cil_resolve_ast.c
> >> index 5ff4534..0df5c63 100644
> >> --- a/libsepol/cil/src/cil_resolve_ast.c
> >> +++ b/libsepol/cil/src/cil_resolve_ast.c
> >> @@ -820,12 +820,6 @@ int cil_resolve_userrole(struct cil_tree_node
> >*current, void *extra_args)
> >>  	}
> >>  	userrole->role = role_datum;
> >>
> >> -	if (userrole->user->roles == NULL) {
> >> -		cil_list_init(&userrole->user->roles, CIL_LIST_ITEM);
> >> -	}
> >> -
> >> -	cil_list_append(userrole->user->roles, CIL_ROLE, userrole->role);
> >> -
> >>  	return SEPOL_OK;
> >>
> >>  exit:
> >> @@ -838,12 +832,22 @@ int cil_resolve_userlevel(struct cil_tree_node
> >*current, void *extra_args)
> >>  	struct cil_symtab_datum *user_datum = NULL;
> >>  	struct cil_symtab_datum *lvl_datum = NULL;
> >>  	struct cil_user *user = NULL;
> >> +	struct cil_tree_node *user_node = NULL;
> >>  	int rc = SEPOL_ERR;
> >>
> >>  	rc = cil_resolve_name(current, usrlvl->user_str, CIL_SYM_USERS,
> >extra_args, &user_datum);
> >>  	if (rc != SEPOL_OK) {
> >>  		goto exit;
> >>  	}
> >> +
> >> +	user_node = user_datum->nodes->head->data;
> >> +
> >> +	if (user_node->flavor != CIL_USER) {
> >> +		cil_log(CIL_ERR, "Userlevel must be a user\n");
> >> +		rc = SEPOL_ERR;
> >> +		goto exit;
> >> +	}
> >> +
> >>  	user = (struct cil_user*)user_datum;
> >>
> >>  	if (usrlvl->level_str != NULL) {
> >> @@ -881,12 +885,22 @@ int cil_resolve_userrange(struct cil_tree_node
> >*current, void *extra_args)
> >>  	struct cil_symtab_datum *user_datum = NULL;
> >>  	struct cil_symtab_datum *range_datum = NULL;
> >>  	struct cil_user *user = NULL;
> >> +	struct cil_tree_node *user_node = NULL;
> >>  	int rc = SEPOL_ERR;
> >>
> >>  	rc = cil_resolve_name(current, userrange->user_str,
> >CIL_SYM_USERS, extra_args, &user_datum);
> >>  	if (rc != SEPOL_OK) {
> >>  		goto exit;
> >>  	}
> >> +
> >> +	user_node = user_datum->nodes->head->data;
> >> +
> >> +	if (user_node->flavor != CIL_USER) {
> >> +		cil_log(CIL_ERR, "Userrange must be a user: %s\n",
> >user_datum->fqn);
> >> +		rc = SEPOL_ERR;
> >> +		goto exit;
> >> +	}
> >> +
> >>  	user = (struct cil_user*)user_datum;
> >>
> >>  	if (userrange->range_str != NULL) {
> >> @@ -922,12 +936,22 @@ int cil_resolve_userprefix(struct cil_tree_node
> >*current, void *extra_args)
> >>  {
> >>  	struct cil_userprefix *userprefix = current->data;
> >>  	struct cil_symtab_datum *user_datum = NULL;
> >> +	struct cil_tree_node *user_node = NULL;
> >>  	int rc = SEPOL_ERR;
> >>
> >>  	rc = cil_resolve_name(current, userprefix->user_str,
> >CIL_SYM_USERS, extra_args, &user_datum);
> >>  	if (rc != SEPOL_OK) {
> >>  		goto exit;
> >>  	}
> >> +
> >> +	user_node = user_datum->nodes->head->data;
> >> +
> >> +	if (user_node->flavor != CIL_USER) {
> >> +		cil_log(CIL_ERR, "Userprefix must be a user: %s\n",
> >user_datum->fqn);
> >> +		rc = SEPOL_ERR;
> >> +		goto exit;
> >> +	}
> >> +
> >>  	userprefix->user = (struct cil_user*)user_datum;
> >>
> >>  exit:
> >> @@ -939,12 +963,22 @@ int cil_resolve_selinuxuser(struct cil_tree_node
> >*current, void *extra_args)
> >>  	struct cil_selinuxuser *selinuxuser = current->data;
> >>  	struct cil_symtab_datum *user_datum = NULL;
> >>  	struct cil_symtab_datum *lvlrange_datum = NULL;
> >> +	struct cil_tree_node *user_node = NULL;
> >>  	int rc = SEPOL_ERR;
> >>
> >>  	rc = cil_resolve_name(current, selinuxuser->user_str,
> >CIL_SYM_USERS, extra_args, &user_datum);
> >>  	if (rc != SEPOL_OK) {
> >>  		goto exit;
> >>  	}
> >> +
> >> +	user_node = user_datum->nodes->head->data;
> >> +
> >> +	if (user_node->flavor != CIL_USER) {
> >> +		cil_log(CIL_ERR, "Selinuxuser must be a user: %s\n",
> >user_datum->fqn);
> >> +		rc = SEPOL_ERR;
> >> +		goto exit;
> >> +	}
> >> +
> >>  	selinuxuser->user = (struct cil_user*)user_datum;
> >>
> >>  	if (selinuxuser->range_str != NULL) {
> >> @@ -1715,7 +1749,7 @@ int cil_resolve_context(struct cil_tree_node
> >*current, struct cil_context *conte
> >>  	struct cil_symtab_datum *user_datum = NULL;
> >>  	struct cil_symtab_datum *role_datum = NULL;
> >>  	struct cil_symtab_datum *type_datum = NULL;
> >> -	struct cil_tree_node *type_node = NULL;
> >> +	struct cil_tree_node *node = NULL;
> >>  	struct cil_symtab_datum *lvlrange_datum = NULL;
> >>
> >>  	int rc = SEPOL_ERR;
> >> @@ -1724,12 +1758,29 @@ int cil_resolve_context(struct cil_tree_node
> >*current, struct cil_context *conte
> >>  	if (rc != SEPOL_OK) {
> >>  		goto exit;
> >>  	}
> >> +
> >> +	node = user_datum->nodes->head->data;
> >> +
> >> +	if (node->flavor != CIL_USER) {
> >> +		cil_log(CIL_ERR, "Context user must be a user: %s\n",
> >user_datum->fqn);
> >> +		rc = SEPOL_ERR;
> >> +		goto exit;
> >> +	}
> >> +
> >>  	context->user = (struct cil_user*)user_datum;
> >>
> >>  	rc = cil_resolve_name(current, context->role_str, CIL_SYM_ROLES,
> >extra_args, &role_datum);
> >>  	if (rc != SEPOL_OK) {
> >>  		goto exit;
> >>  	}
> >> +
> >> +	node = role_datum->nodes->head->data;
> >> +	if (node->flavor != CIL_ROLE) {
> >> +		rc = SEPOL_ERR;
> >> +		cil_log(CIL_ERR, "Context role not a role: %s\n",
> >role_datum->fqn);
> >> +		goto exit;
> >> +	}
> >> +
> >>  	context->role = (struct cil_role*)role_datum;
> >>
> >>  	rc = cil_resolve_name(current, context->type_str, CIL_SYM_TYPES,
> >extra_args, &type_datum);
> >> @@ -1737,9 +1788,9 @@ int cil_resolve_context(struct cil_tree_node
> >*current, struct cil_context *conte
> >>  		goto exit;
> >>  	}
> >>
> >> -	type_node = type_datum->nodes->head->data;
> >> +	node = type_datum->nodes->head->data;
> >>
> >> -	if (type_node->flavor != CIL_TYPE && type_node->flavor !=
> >CIL_TYPEALIAS) {
> >> +	if (node->flavor != CIL_TYPE && node->flavor != CIL_TYPEALIAS) {
> >>  		rc = SEPOL_ERR;
> >>  		cil_log(CIL_ERR, "Type not a type or type alias\n");
> >>  		goto exit;
> >> @@ -3036,6 +3087,48 @@ exit:
> >>  	return rc;
> >>  }
> >>
> >> +int cil_resolve_userattributeset(struct cil_tree_node *current, void
> >*extra_args)
> >> +{
> >> +	int rc = SEPOL_ERR;
> >> +	struct cil_userattributeset *attrusers = current->data;
> >> +	struct cil_symtab_datum *attr_datum = NULL;
> >> +	struct cil_tree_node *attr_node = NULL;
> >> +	struct cil_userattribute *attr = NULL;
> >> +
> >> +	rc = cil_resolve_name(current, attrusers->attr_str, CIL_SYM_USERS,
> >extra_args, &attr_datum);
> >> +	if (rc != SEPOL_OK) {
> >> +		goto exit;
> >> +	}
> >> +	attr_node = attr_datum->nodes->head->data;
> >> +
> >> +	if (attr_node->flavor != CIL_USERATTRIBUTE) {
> >> +		rc = SEPOL_ERR;
> >> +		cil_log(CIL_ERR, "Attribute user not an attribute\n");
> >> +		goto exit;
> >> +	}
> >> +	attr = (struct cil_userattribute*)attr_datum;
> >> +
> >> +	rc = cil_resolve_expr(CIL_USERATTRIBUTESET, attrusers->str_expr,
> >&attrusers->datum_expr, current, extra_args);
> >> +	if (rc != SEPOL_OK) {
> >> +		goto exit;
> >> +	}
> >> +
> >> +	rc = cil_verify_no_self_reference(attr_datum, attrusers-
> >>datum_expr);
> >> +	if (rc != SEPOL_OK) {
> >> +		goto exit;
> >> +	}
> >> +
> >> +	if (attr->expr_list == NULL) {
> >> +		cil_list_init(&attr->expr_list, CIL_USERATTRIBUTE);
> >> +	}
> >> +
> >> +	cil_list_append(attr->expr_list, CIL_LIST, attrusers->datum_expr);
> >> +
> >> +	return SEPOL_OK;
> >> +
> >> +exit:
> >> +	return rc;
> >> +}
> >>
> >>  int __cil_resolve_ast_node(struct cil_tree_node *node, void *extra_args)
> >>  {
> >> @@ -3296,6 +3389,9 @@ int __cil_resolve_ast_node(struct
> >cil_tree_node *node, void *extra_args)
> >>  		case CIL_DEFAULTRANGE:
> >>  			rc = cil_resolve_defaultrange(node, args);
> >>  			break;
> >> +		case CIL_USERATTRIBUTESET:
> >> +			rc = cil_resolve_userattributeset(node, args);
> >> +			break;
> >>  		default:
> >>  			break;
> >>  		}
> >> diff --git a/libsepol/cil/src/cil_resolve_ast.h
> >b/libsepol/cil/src/cil_resolve_ast.h
> >> index e99f0a4..1175f97 100644
> >> --- a/libsepol/cil/src/cil_resolve_ast.h
> >> +++ b/libsepol/cil/src/cil_resolve_ast.h
> >> @@ -54,6 +54,7 @@ int cil_resolve_userlevel(struct cil_tree_node
> >*current, void *extra_args);
> >>  int cil_resolve_userrange(struct cil_tree_node *current, void
> >*extra_args);
> >>  int cil_resolve_userbounds(struct cil_tree_node *current, void
> >*extra_args);
> >>  int cil_resolve_userprefix(struct cil_tree_node *current, void
> >*extra_args);
> >> +int cil_resolve_userattributeset(struct cil_tree_node *current, void
> >*extra_args);
> >>  int cil_resolve_selinuxuser(struct cil_tree_node *current, void
> >*extra_args);
> >>  int cil_resolve_roletype(struct cil_tree_node *current, void *extra_args);
> >>  int cil_resolve_roletransition(struct cil_tree_node *current, void
> >*extra_args);
> >> diff --git a/libsepol/cil/src/cil_tree.c b/libsepol/cil/src/cil_tree.c
> >> index 6a731f2..f641baa 100644
> >> --- a/libsepol/cil/src/cil_tree.c
> >> +++ b/libsepol/cil/src/cil_tree.c
> >> @@ -640,15 +640,18 @@ void cil_tree_print_node(struct cil_tree_node
> >*node)
> >>  		case CIL_USERROLE: {
> >>  			struct cil_userrole *userrole = node->data;
> >>  			cil_log(CIL_INFO, "USERROLE:");
> >> +			struct cil_symtab_datum *datum = NULL;
> >>
> >>  			if (userrole->user != NULL) {
> >> -				cil_log(CIL_INFO, " %s", userrole->user-
> >>datum.name);
> >> +				datum = userrole->user;
> >> +				cil_log(CIL_INFO, " %s", datum->name);
> >>  			} else if (userrole->user_str != NULL) {
> >>  				cil_log(CIL_INFO, " %s", userrole->user_str);
> >>  			}
> >>
> >>  			if (userrole->role != NULL) {
> >> -				cil_log(CIL_INFO, " %s", ((struct
> >cil_symtab_datum *)userrole->role)->name);
> >> +				datum = userrole->role;
> >> +				cil_log(CIL_INFO, " %s", datum->name);
> >>  			} else if (userrole->role_str != NULL) {
> >>  				cil_log(CIL_INFO, " %s", userrole->role_str);
> >>  			}
> >> @@ -785,6 +788,21 @@ void cil_tree_print_node(struct cil_tree_node
> >*node)
> >>  			cil_log(CIL_INFO, "ROLEATTRIBUTE: %s\n", attr-
> >>datum.name);
> >>  			return;
> >>  		}
> >> +		case CIL_USERATTRIBUTESET: {
> >> +			struct cil_userattributeset *attr = node->data;
> >> +
> >> +			cil_log(CIL_INFO, "(USERATTRIBUTESET %s ", attr-
> >>attr_str);
> >> +
> >> +			cil_tree_print_expr(attr->datum_expr, attr-
> >>str_expr);
> >> +
> >> +			cil_log(CIL_INFO, "\n");
> >> +			return;
> >> +		}
> >> +		case CIL_USERATTRIBUTE: {
> >> +			struct cil_userattribute *attr = node->data;
> >> +			cil_log(CIL_INFO, "USERATTRIBUTE: %s\n", attr-
> >>datum.name);
> >> +			return;
> >> +		}
> >>  		case CIL_ROLEBOUNDS: {
> >>  			struct cil_bounds *bnds = node->data;
> >>  			cil_log(CIL_INFO, "ROLEBOUNDS: role: %s, bounds:
> >%s\n", bnds->parent_str, bnds->child_str);
> >> diff --git a/libsepol/cil/src/cil_verify.c b/libsepol/cil/src/cil_verify.c
> >> index 065de88..9ebfa81 100644
> >> --- a/libsepol/cil/src/cil_verify.c
> >> +++ b/libsepol/cil/src/cil_verify.c
> >> @@ -737,16 +737,8 @@ int __cil_verify_context(struct cil_db *db, struct
> >cil_context *ctx)
> >>  	int found = CIL_FALSE;
> >>
> >>  	if (user->roles != NULL) {
> >> -		cil_list_for_each(curr, user->roles) {
> >> -			struct cil_role *userrole = curr->data;
> >> -			if (userrole == role) {
> >> -				break;
> >> -			}
> >> -		}
> >> -
> >> -		if (curr == NULL) {
> >> -			cil_log(CIL_ERR, "Role %s is invalid for user %s\n",
> >> -					ctx->role_str, ctx->user_str);
> >> +		if (!ebitmap_get_bit(user->roles, role->value)) {
> >> +			cil_log(CIL_ERR, "Role %s is invalid for user %s\n",
> >ctx->role_str, ctx->user_str);
> >>  			rc = SEPOL_ERR;
> >>  			goto exit;
> >>  		}
> >> diff --git a/secilc/docs/cil_constraint_statements.xml
> >b/secilc/docs/cil_constraint_statements.xml
> >> index 6f5d9c6..8ef1642 100644
> >> --- a/secilc/docs/cil_constraint_statements.xml
> >> +++ b/secilc/docs/cil_constraint_statements.xml
> >> @@ -51,7 +51,7 @@
> >>                       <simpara>and:</simpara>
> >>                          <simpara><literal>  op      : eq neq</literal></simpara>
> >>                          <simpara><literal>  role_op : eq neq dom domby
> >incomp</literal></simpara>
> >> -                        <simpara><literal>  user_id : A single <link
> >linkend="user">user</link> identifier.</literal></simpara>
> >> +                        <simpara><literal>  user_id : A single <link
> >linkend="user">user</link> or <link
> >linkend="userattribute">userattribute</link>
> >identifier.</literal></simpara>
> >>                          <simpara><literal>  role_id : A single <link
> >linkend="role">role</link> or <link
> >linkend="roleattribute">roleattribute</link>
> >identifier.</literal></simpara>
> >>                          <simpara><literal>  type_id : A single <link
> >linkend="type">type</link>, <link linkend="typealias">typealias</link> or
> ><link linkend="typeattribute">typeattribute</link>
> >identifier.</literal></simpara>
> >>                    </entry>
> >> @@ -154,7 +154,7 @@
> >>                       <simpara>and:</simpara>
> >>                          <simpara><literal>  op      : eq neq</literal></simpara>
> >>                          <simpara><literal>  role_op : eq neq dom domby
> >incomp</literal></simpara>
> >> -                        <simpara><literal>  user_id : A single <link
> >linkend="user">user</link> identifier.</literal></simpara>
> >> +                        <simpara><literal>  user_id : A single <link
> >linkend="user">user</link> or <link
> >linkend="userattribute">userattribute</link>
> >identifier.</literal></simpara>
> >>                          <simpara><literal>  role_id : A single <link
> >linkend="role">role</link> or <link
> >linkend="roleattribute">roleattribute</link>
> >identifier.</literal></simpara>
> >>                          <simpara><literal>  type_id : A single <link
> >linkend="type">type</link>, <link linkend="typealias">typealias</link> or
> ><link linkend="typeattribute">typeattribute</link>
> >identifier.</literal></simpara>
> >>                    </entry>
> >> @@ -236,7 +236,7 @@
> >>                       <simpara>and:</simpara>
> >>                          <simpara><literal>  op          : eq neq</literal></simpara>
> >>                          <simpara><literal>  mls_role_op : eq neq dom domby
> >incomp</literal></simpara>
> >> -                        <simpara><literal>  user_id     : A single <link
> >linkend="user">user</link> identifier.</literal></simpara>
> >> +                        <simpara><literal>  user_id     : A single <link
> >linkend="user">user</link> or <link
> >linkend="userattribute">userattribute</link>
> >identifier.</literal></simpara>
> >>                          <simpara><literal>  role_id     : A single <link
> >linkend="role">role</link> or <link
> >linkend="roleattribute">roleattribute</link>
> >identifier.</literal></simpara>
> >>                          <simpara><literal>  type_id     : A single <link
> >linkend="type">type</link>, <link linkend="typealias">typealias</link> or
> ><link linkend="typeattribute">typeattribute</link>
> >identifier.</literal></simpara>
> >>                    </entry>
> >> @@ -332,7 +332,7 @@
> >>                       <simpara>and:</simpara>
> >>                          <simpara><literal>  op          : eq neq</literal></simpara>
> >>                          <simpara><literal>  mls_role_op : eq neq dom domby
> >incomp</literal></simpara>
> >> -                        <simpara><literal>  user_id     : A single <link
> >linkend="user">user</link> identifier.</literal></simpara>
> >> +                        <simpara><literal>  user_id     : A single <link
> >linkend="user">user</link> or <link
> >linkend="userattribute">userattribute</link>
> >identifier.</literal></simpara>
> >>                          <simpara><literal>  role_id     : A single <link
> >linkend="role">role</link> or <link
> >linkend="roleattribute">roleattribute</link>
> >identifier.</literal></simpara>
> >>                          <simpara><literal>  type_id     : A single <link
> >linkend="type">type</link>, <link linkend="typealias">typealias</link> or
> ><link linkend="typeattribute">typeattribute</link>
> >identifier.</literal></simpara>
> >>                    </entry>
> >> diff --git a/secilc/docs/cil_user_statements.xml
> >b/secilc/docs/cil_user_statements.xml
> >> index 9fa1a51..38a7d6e 100644
> >> --- a/secilc/docs/cil_user_statements.xml
> >> +++ b/secilc/docs/cil_user_statements.xml
> >> @@ -66,7 +66,7 @@
> >>                       <para><literal>user_id</literal></para>
> >>                    </entry>
> >>                    <entry>
> >> -                     <para>A previously declared SELinux <literal><link
> >linkend="user">user</link></literal> identifier.</para>
> >> +                     <para>A previously declared SELinux <literal><link
> >linkend="user">user</link></literal> or <literal><link
> >linkend="userattribute">userattribute</link></literal> identifier.</para>
> >>                    </entry>
> >>                 </row>
> >>                 <row>
> >> @@ -91,6 +91,114 @@
> >>           </programlisting>
> >>        </sect2>
> >>
> >> +      <sect2 id="userattribute">
> >> +         <title>userattribute</title>
> >> +         <para>Declares a user attribute identifier in the current
> >namespace. The identifier may have zero or more <literal><link
> >linkend="user">user</link></literal> and <literal><link
> >linkend="userattribute">userattribute</link></literal> identifiers
> >associated to it via the <literal><link
> >linkend="userattributeset">userattributeset</link></literal>
> >statement.</para>
> >> +         <para><emphasis role="bold">Statement
> >definition:</emphasis></para>
> >> +         <programlisting><![CDATA[(userattribute
> >userattribute_id)]]></programlisting>
> >> +         <para><emphasis role="bold">Where:</emphasis></para>
> >> +         <informaltable frame="all">
> >> +            <tgroup cols="2">
> >> +            <colspec colwidth="2 *"/>
> >> +            <colspec colwidth="6 *"/>
> >> +               <tbody>
> >> +               <row>
> >> +                  <entry>
> >> +                     <para><literal>userattribute</literal></para>
> >> +                  </entry>
> >> +                  <entry>
> >> +                     <para>The <literal>userattribute</literal> keyword.</para>
> >> +                  </entry>
> >> +               </row>
> >> +               <row>
> >> +                  <entry>
> >> +                     <para><literal>userattribute_id</literal></para>
> >> +                  </entry>
> >> +                  <entry>
> >> +                     <para>The <literal>userattribute</literal>
> >identifier.</para>
> >> +                  </entry>
> >> +               </row>
> >> +            </tbody></tgroup>
> >> +         </informaltable>
> >> +
> >> +         <para><emphasis role="bold">Example:</emphasis></para>
> >> +         <para>This example will declare a user attribute
> ><literal>users.user_holder</literal> that will have an empty set:</para>
> >> +         <programlisting><![CDATA[
> >> +(block users
> >> +    (userattribute user_holder)
> >> +)]]>
> >> +         </programlisting>
> >> +      </sect2>
> >> +
> >> +      <sect2 id="userattributeset">
> >> +         <title>userattributeset</title>
> >> +         <para>Allows the association of one or more previously declared
> ><literal><link linkend="user">user</link></literal> or <literal><link
> >linkend="userattribute">userattribute</link></literal> identifiers to a
> ><literal><link linkend="userattribute">userattribute</link></literal>
> >identifier. Expressions may be used to refine the associations as shown in
> >the examples.</para>
> >> +         <para><emphasis role="bold">Statement
> >definition:</emphasis></para>
> >> +         <programlisting><![CDATA[(userattributeset userattribute_id
> >(user_id ... | expr ...))]]></programlisting>
> >> +         <para><emphasis role="bold">Where:</emphasis></para>
> >> +         <informaltable frame="all">
> >> +            <tgroup cols="2">
> >> +            <colspec colwidth="2 *"/>
> >> +            <colspec colwidth="6 *"/>
> >> +               <tbody>
> >> +               <row>
> >> +                  <entry>
> >> +                     <para><literal>userattributeset</literal></para>
> >> +                  </entry>
> >> +                  <entry>
> >> +                     <para>The <literal>userattributeset</literal>
> >keyword.</para>
> >> +                  </entry>
> >> +               </row>
> >> +               <row>
> >> +                  <entry>
> >> +                     <para><literal>userattribute_id</literal></para>
> >> +                  </entry>
> >> +                  <entry>
> >> +                     <para>A single previously declared <literal><link
> >linkend="roleattribute">userattribute</link></literal> identifier.</para>
> >> +                  </entry>
> >> +               </row>
> >> +               <row>
> >> +                  <entry>
> >> +                     <para><literal>user_id</literal></para>
> >> +                  </entry>
> >> +                  <entry>
> >> +                     <para>Zero or more previously declared <literal><link
> >linkend="role">user</link></literal> or <literal><link
> >linkend="userattribute">userattribute</link></literal> identifiers.</para>
> >> +                     <para>Note that there must be at least one
> ><literal>user_id</literal> or <literal>expr</literal> parameter
> >declared.</para>
> >> +                  </entry>
> >> +               </row>
> >> +               <row>
> >> +                  <entry>
> >> +                     <para><literal>expr</literal></para>
> >> +                  </entry>
> >> +                  <entry>
> >> +                     <para>Zero or more <literal>expr</literal>'s, the valid
> >operators and syntax are:</para>
> >> +                     <simpara><literal>    (and (user_id ...) (user_id
> >...))</literal></simpara>
> >> +                     <simpara><literal>    (or  (user_id ...) (user_id
> >...))</literal></simpara>
> >> +                     <simpara><literal>    (xor (user_id ...) (user_id
> >...))</literal></simpara>
> >> +                     <simpara><literal>    (not (user_id ...))</literal></simpara>
> >> +                     <simpara><literal>    (all)</literal></simpara>
> >> +                  </entry>
> >> +               </row>
> >> +            </tbody></tgroup>
> >> +         </informaltable>
> >> +
> >> +         <para><emphasis role="bold">Example:</emphasis></para>
> >> +         <para>This example will declare three users and two user
> >attributes, then associate all the users to them as shown:</para>
> >> +         <programlisting><![CDATA[
> >> +(block users
> >> +    (user user_1)
> >> +    (user user_2)
> >> +    (user user_3)
> >> +
> >> +    (userattribute user_holder)
> >> +    (userattributeset user_holder (user_1 user_2 user_3))
> >> +
> >> +    (userattribute user_holder_all)
> >> +    (userattributeset user_holder_all (all))
> >> +)]]>
> >> +         </programlisting>
> >> +      </sect2>
> >> +
> >>        <sect2 id="userlevel">
> >>           <title>userlevel</title>
> >>           <para>Associates a previously declared <literal><link
> >linkend="user">user</link></literal> identifier with a previously declared
> ><literal><link linkend="level">level</link></literal> identifier. The
> ><literal><link linkend="level">level</link></literal> may be named or
> >anonymous.</para>
> >> diff --git a/secilc/test/policy.cil b/secilc/test/policy.cil
> >> index 0b532a9..69103d1 100644
> >> --- a/secilc/test/policy.cil
> >> +++ b/secilc/test/policy.cil
> >> @@ -124,7 +124,9 @@
> >>  	(roleattribute foo_role)
> >>  	(roleattribute bar_role)
> >>  	(roleattribute baz_role)
> >> +	(roleattribute foo_role_a)
> >>  	(roleattributeset exec_role (or user_r system_r))
> >> +	(roleattributeset foo_role_a (baz_r user_r system_r))
> >>  	(roleattributeset foo_role (and exec_role system_r))
> >>  	(roleattributeset bar_role (xor exec_role foo_role))
> >>  	(roleattributeset baz_role (not user_r))
> >> @@ -194,6 +196,7 @@
> >>
> >>  	(role system_r)
> >>  	(role user_r)
> >> +	(role baz_r)
> >>
> >>  	(roletype system_r bin_t)
> >>  	(roletype system_r kernel_t)
> >> @@ -207,6 +210,23 @@
> >>
> >>  	(userrole foo_u foo_role)
> >>  	(userlevel foo_u low)
> >> +
> >> +	(userattribute ua1)
> >> +	(userattribute ua2)
> >> +	(userattribute ua3)
> >> +	(userattribute ua4)
> >> +	(userattributeset ua1 (user_u system_u))
> >> +	(userattributeset ua2 (foo_u system_u))
> >> +	(userattributeset ua3 (and ua1 ua2))
> >> +	(user u5)
> >> +	(user u6)
> >> +	(userlevel u5 low)
> >> +	(userlevel u6 low)
> >> +	(userrange u5 low_high)
> >> +	(userrange u6 low_high)
> >> +	(userattributeset ua4 (u5 u6))
> >> +	(userrole ua4 foo_role_a)
> >> +
> >>  	(userrange foo_u low_high)
> >>
> >>  	(userrole system_u system_r)
> >> @@ -253,7 +273,7 @@
> >>  	(constrain (files (read)) (not (or (and (eq t1 exec_t) (eq t2 bin_t))
> >(eq r1 r2))))
> >>  	(constrain char_w (not (or (and (eq t1 exec_t) (eq t2 bin_t)) (eq r1
> >r2))))
> >>
> >> -	(constrain (file (read)) (or (and (eq t1 exec_t) (neq t2 bin_t) ) (eq u1
> >u2) ) )
> >> +	(constrain (file (read)) (or (and (eq t1 exec_t) (neq t2 bin_t) ) (eq u1
> >ua4) ) )
> >>  	(constrain (file (open)) (dom r1 r2))
> >>  	(constrain (file (open)) (domby r1 r2))
> >>  	(constrain (file (open)) (incomp r1 r2))
> >> --
> >> 1.9.3
> >>
> >> _______________________________________________
> >> Selinux mailing list
> >> Selinux@xxxxxxxxxxxxx
> >> To unsubscribe, send email to Selinux-leave@xxxxxxxxxxxxx.
> >> To get help, send an email containing "help" to Selinux-
> >request@xxxxxxxxxxxxx.
> >
> >- --
> >02DFF788
> >4D30 903A 1CF3 B756 FB48  1514 3148 83A2 02DF F788
> >https://sks-
> >keyservers.net/pks/lookup?op=get&search=0x314883A202DFF788
> >Dominick Grift
> >-----BEGIN PGP SIGNATURE-----
> >Version: GnuPG v2
> >
> >iQGcBAEBCgAGBQJV8beiAAoJENAR6kfG5xmcVKAMAK5tAZRQnq2gIOzRv1fnc
> >iOc
> >k91ABlOqm5j+ongchsRSJJj+mleKnzCVqPA/dtKbV3Nw5fWsrdgz4rLQpI9w+rCi
> >qiSewA+xDSgsxuBgASprIycRB3X1Hxg4T6e6qSyM95hT6LFsYpPhG078Td85q6
> >w9
> >lJ5zhvAKS8BIYOy3RZkR+ejEox9w/AxA6fCP66+vvQhqCwhmLTqH8X8l9+KHIM
> >m+
> >0o81Ooa4vE/6BIU+dTctvqUJ69rkwVoau7IT0/qYDzcEepQoF9Ynb3I+JOamut
> >Mh
> >79DPB7bjuDf9p0GC7kusimsZtAmvvJSS5qB3oVAFhHaB/DNJpTYoJyhWIY/iErP
> >x
> >OTjinbzq2gr1ya9eXsqhcR2+AxagvKC7NEkivO9h6JESIjlPzmJfe0uAUeqtirXt
> >nvlU/5yckmF1TtxvI/HQGfH7SRy1S1zODMx5VkNhlycDo2EbPUyq9aqHKkK9wE
> >XP
> >rvo73IJBIyAt0iaCvKJTM+pGjBjMsJOu7/Liq3JnaQ==
> >=ZuJf
> >-----END PGP SIGNATURE-----

- -- 
02DFF788
4D30 903A 1CF3 B756 FB48  1514 3148 83A2 02DF F788
https://sks-keyservers.net/pks/lookup?op=get&search=0x314883A202DFF788
Dominick Grift
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQGcBAEBCgAGBQJV8bsiAAoJENAR6kfG5xmcthcMAL+zQGPPXuwRXcSTnZYJD/Z3
5+FBhQRsc8JLBMZKk25NTd9raqaZgLSEX1PgIScBHD0GV3xmuD+zeLDpdhNG1WLV
+P5xxeGmj0ggxu5ABCNcJLYlYN/aelzk9da1q5b/dSgt9SuPDW+oyB6x7oSibF5t
lej+jvw++NJhgxOxoXPvoggnHqNbD0NEoIL5fu8gSjdiIbS6K1Nm8niDCX0zFAst
CB2c+uW0qentTkiHHelmzmMIIAF4HdHTkexghg+94ukXkbtoqLPTN7meehWqGNhQ
rinXkJ++CcLS4+vwWZSuPheE7sQlo/mSFPTF1Fp23jWfSW3UW889l7E9QA1SNA0S
O62oYw0nd6JIzNn42LyrrFk2uyM1yP7hyAtgT61d7DPvlumWjuym8X1PWR3utGUw
RB/3Ha9X95VVmiqcc3s9uCFKODNdg59FQDMZjMp5cTuLxSY5TTMTvhAUTn3f1t5i
O/eeWlTezJsLDNhtrQdamb8kRtQEsuTMGSzjg/FZAA==
=PAKc
-----END PGP SIGNATURE-----
_______________________________________________
Selinux mailing list
Selinux@xxxxxxxxxxxxx
To unsubscribe, send email to Selinux-leave@xxxxxxxxxxxxx.
To get help, send an email containing "help" to Selinux-request@xxxxxxxxxxxxx.




[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux