There is no point in initializing a variable which gets
almost-immediately assigned an other value.
Signed-off-by: Nicolas Iooss <nicolas.iooss@m4x.org>
Even though "hashtab_key_t" is an alias for "char *", "const
hashtab_key_t" is not an alias for "(const char) *" but means "(char *)
const".
Introduce const_hashtab_key_t to map "(const char) *" and use it in
hashtab_search() and hashtab key comparison functions.
Signed-off-by: Nicolas Iooss <nicolas.iooss@m4x.org>
Adds a check for avrules with type attributes that have a bitmap cardinality
of 0 (i.e., no types in their set) before adding them to the libsepol policy in
__cil_avrule_to_avtab(). Also adds an exception for neverallow rules to
prevent breaking anything from AOSP mentioned in
f9927d9370.
Signed-off-by: Gary Tierney <gary.tierney@gmx.com>
The ability to create a policy.conf file from the CIL AST has been
a desire from the beginning to assist in debugging and for general
flexibility. Some work towards this end was started early in CIL's
history, but cil_policy.c has not been remotely functional in a long
time. Until now.
The function cil_write_policy_conf() will write a policy.conf file
from a CIL AST after cil_build_ast(), cil_resolve_ast(),
cil_fqn_qualify(), and cil_post_process() have been called.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
range transition and name-based type transition rules were originally
simple unordered lists. They were converted to hashtabs in the kernel
by commit 2f3e82d694d3d7a2db019db1bb63385fbc1066f3 ("selinux: convert range
transition list to a hashtab") and by commit
2463c26d50adc282d19317013ba0ff473823ca47 ("SELinux: put name based
create rules in a hashtable"), but left unchanged in libsepol and
checkpolicy. Convert libsepol and checkpolicy to use the same hashtabs
as the kernel for the range transitions and name-based type transitions.
With this change and the preceding one, it is possible to directly compare
a policy file generated by libsepol/checkpolicy and the kernel-generated
/sys/fs/selinux/policy pseudo file after normalizing them both through
checkpolicy. To do so, you can run the following sequence of commands:
checkpolicy -M -b /etc/selinux/targeted/policy/policy.30 -o policy.1
checkpolicy -M -b /sys/fs/selinux/policy -o policy.2
cmp policy.1 policy.2
Normalizing the two files via checkpolicy is still necessary to ensure
consistent ordering of the avtab entries. There may still be potential
for other areas of difference, e.g. xperms entries may lack a well-defined
order.
Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
Nicholas Iooss discovered that using an unknown permission with a
map class will cause a segfault.
CIL will only give a warning when it fails to resolve an unknown
permission to support the use of policy module packages that use
permissions that don't exit on the current system. When resolving
the unknown map class permission an empty list is used to represent
the unknown permission. When it is evaluated later the list is
assumed to be a permission and a segfault occurs.
There is no reason to allow unknown class map permissions because
the class maps and permissions are defined by the policy.
Exit with an error when failing to resolve a class map permission.
Reported-by: Nicolas Iooss <nicolas.iooss@m4x.org>
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
The blank default symver fails to compile with ld.gold. This updates the
symver from blank to LIBSEPOL_1.0. The dynamic linker will first look
for the symbol with the explicit version specified. If there is none, it
will pick the first listed symbol so there is no breakage.
This also matches how symvers are defined in libsemanage.
Signed-off-by: Jason Zaman <jason@perfinion.com>
cil_strpool currently provides an interface to a statically stored
global data structure. This interface does not accomodate multiple
consumers, however, as two calls to cil_strpool_init() will lead to a
memory leak and a call to cil_strpool_destroy() by one consumer will
remove data from use by others, and subsequently lead to a segfault on
the next cil_strpool_destroy() invocation.
Add a reference counter so that the strpool is only initialized once and
protect the exported interface with a mutex.
Tested by calling cil_db_init() on two cil_dbs and then calling
cil_db_destroy() on each.
Signed-off-by: Daniel Cashman <dcashman@android.com>
Nicolas Iooss found while fuzzing secilc with AFL that using an attribute
as a child in a typebounds statement will cause a segfault.
This happens because the child datum is assumed to be part of a cil_type
struct when it is really part of a cil_typeattribute struct. The check to
verify that it is a type and not an attribute comes after it is used.
This bug effects user and role bounds as well because they do not check
whether a datum refers to an attribute or not.
Add checks to verify that neither the child nor the parent datum refer
to an attribute before using them in user, role, and type bounds.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
Nicolas Iooss found while fuzzing secilc with AFL that the statement
"(sensitivityaliasactual SENS SENS)" will cause a segfault.
The segfault occurs because when the aliasactual is resolved the first
identifier is assumed to refer to an alias structure, but it is not.
Add a check to verify that the datum retrieved is actually an alias
and exit with an error if it is not.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
Nicolas Iooss found while fuzzing secilc with AFL that the statement
"(class C (()))" will cause a segfault.
CIL expects a list of permissions in the class declaration and "(())"
is a valid list. Each item of the list is expected to be an identifier
and as the list is processed each item is checked to see if it is a
list. An error is given if it is a list, otherwise the item is assumed
to be an identifier. Unfortunately, the check only works if the list
is not empty. In this case, the item passes the check and is assumed
to be an identifier and a NULL is passed as the string for name
verification. If name verification assumes that a non-NULL value will
be passed in, a segfault will occur.
Add a check for an empty list when processing a permission list and
improve the error handling for permissions when building the AST.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
Nicolas Iooss found while fuzzing secilc with AFL that the statement
"(class C (()))" will cause a segfault.
When CIL checks the syntax of the class statement it sees "(())" as a
valid permission list, but since "()" is not an identifier a NULL is
passed as the string for name verification. A segfault occurs because
name verification assumes that the string being checked is non-NULL.
Check if identifier is NULL when verifying name.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
Nicolas Iooss found while fuzzing secilc with AFL that the statement
"(classpermissionset CPERM (CLASS (and unknow PERM)))" will cause a
segfault.
In order to support a policy module package using a permission that
does not exist on the system it is loaded on, CIL will only give a
warning when it fails to resolve an unknown permission. CIL itself will
just ignore the unknown permission. This means that an expression like
"(and UNKNOWN p1)" will look like "(and p1)" to CIL, but, since syntax
checking has already been done, CIL won't know that the expression is not
well-formed. When the expression is evaluated a segfault will occur
because all expressions are assumed to be well-formed at evaluation time.
Use an empty list to represent an unknown permission so that expressions
will continue to be well-formed and expression evaluation will work but
the unknown permission will still be ignored.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
Nicolas Iooss found while fuzzing secilc with AFL that the following
policy will cause a segfault.
(category c0)
(category c1)
(categoryorder (c0 c1))
(sensitivity s0)
(sensitivitycategory s0 (not (all)))
The expression "(not (all))" is evaluated as containing no categories.
There is a check for the resulting empty list and the category datum
expression is set to NULL. The segfault occurs because the datum
expression is assumed to be non-NULL after evaluation.
Assign the list to the datum expression even if it is empty.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
Nicolas Iooss found while fuzzing secilc with AFL that the following
policy will cause a segfault.
(category c0)
(category c1)
(categoryorder (c0 c1))
(sensitivity s0)
(sensitivitycategory s0 (range c1 c0))
The category range "(range c1 c0)" is invalid because c1 comes after c0
in order.
The invalid range is evaluated as containing no categories. There is a
check for the resulting empty list and the category datum expression is
set to NULL. The segfault occurs because the datum expression is assumed
to be non-NULL after evaluation.
Add a check for an invalid range when evaluating category ranges.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
__cil_fill_expr() initializes 'cil_list *sub_expr' but does not destroy
it when __cil_fill_expr_helper() fails. This list is therefore leaked
when __cil_fill_expr() returns.
This occurs when secilc compiles the following policy:
(class CLASS (PERM))
(classorder (CLASS))
(sid SID)
(sidorder (SID))
(user USER)
(role ROLE)
(type TYPE)
(category CAT)
(categoryorder (CAT))
(sensitivity SENS)
(sensitivityorder (SENS))
(sensitivitycategory SENS (CAT))
(allow TYPE self (CLASS (PERM)))
(roletype ROLE TYPE)
(userrole USER ROLE)
(userlevel USER (SENS))
(userrange USER ((SENS)(SENS (CAT))))
(sidcontext SID (USER ROLE TYPE ((SENS)(SENS))))
(categoryset cats (not (range unknown)))
This bug has been found using gcc address sanitizer.
Signed-off-by: Nicolas Iooss <nicolas.iooss@m4x.org>
When cil_fill_cats() fails to parse an expression and destroys a
category set, it fails to reset *cats to NULL. This makes this object be
destroyed again in cil_destroy_catset().
This bug can be triggered by the following policy:
(class CLASS (PERM))
(classorder (CLASS))
(sid SID)
(sidorder (SID))
(user USER)
(role ROLE)
(type TYPE)
(category CAT)
(categoryorder (CAT))
(sensitivity SENS)
(sensitivityorder (SENS))
(sensitivitycategory SENS (CAT))
(allow TYPE self (CLASS (PERM)))
(roletype ROLE TYPE)
(userrole USER ROLE)
(userlevel USER (SENS))
(userrange USER ((SENS)(SENS (CAT))))
(sidcontext SID (USER ROLE TYPE ((SENS)(SENS))))
(categoryset cats (range unknown))
This bug has been found by fuzzing secilc with american fuzzy lop.
Signed-off-by: Nicolas Iooss <nicolas.iooss@m4x.org>
This CIL policy makes secilc crash with a NULL pointer dereference:
(class CLASS (PERM))
(classorder (CLASS))
(sid SID)
(sidorder (SID))
(user USER)
(role ROLE)
(type TYPE)
(category CAT)
(categoryorder (CAT))
(sensitivity SENS)
(sensitivityorder (SENS))
(sensitivitycategory SENS (CAT))
(allow TYPE self (CLASS (PERM)))
(roletype ROLE TYPE)
(userrole USER ROLE)
(userlevel USER (SENS))
(userrange USER ((SENS)(SENS (CAT))))
(sidcontext SID (USER ROLE TYPE ((SENS)(SENS))))
(allow . self (CLASS (PERM)))
Using "." in the allow statement makes strtok_r() return NULL in
cil_resolve_name() and this result is then used in a call to
cil_symtab_get_datum(), which is thus invalid.
Instead of crashing, make secilc fail with an error message.
This bug has been found by fuzzing secilc with american fuzzy lop.
Signed-off-by: Nicolas Iooss <nicolas.iooss@m4x.org>
Pre-expands the role and user caches used in context validation when
conerting a cildb to a binary policydb. This is currently only done
when loading a binary policy and prevents context validation from
working correctly with a newly built policy (i.e., when semanage builds
a new policy and then runs genhomedircon).
Also adds declarations for the hashtable mapping functions used:
policydb_role_cache and policydb_user_cache().
Signed-off-by: Gary Tierney <gary.tierney@gmx.com>
Fixes bug found by Nicolas Iooss as described below in the way suggested by Steve Lawrence.
Nicolass reported:
When compiling a CIL policy with more than 32 items in a class (e.g. in
(class capability (chown ...)) with many items),
cil_classorder_to_policydb() overflows perm_value_to_cil[class_index]
array. As this array is allocated on the heap through
calloc(PERMS_PER_CLASS+1, sizeof(...)), this makes secilc crash with the
following message:
*** Error in `/usr/bin/secilc': double free or corruption (!prev): 0x000000000062be80 ***
======= Backtrace: =========
/usr/lib/libc.so.6(+0x70c4b)[0x7ffff76a7c4b]
/usr/lib/libc.so.6(+0x76fe6)[0x7ffff76adfe6]
/usr/lib/libc.so.6(+0x777de)[0x7ffff76ae7de]
/lib/libsepol.so.1(+0x14fbda)[0x7ffff7b24bda]
/lib/libsepol.so.1(+0x152db8)[0x7ffff7b27db8]
/lib/libsepol.so.1(cil_build_policydb+0x63)[0x7ffff7af8723]
/usr/bin/secilc[0x40273b]
/usr/lib/libc.so.6(__libc_start_main+0xf1)[0x7ffff7657291]
/usr/bin/secilc[0x402f7a]
This bug has been found by fuzzing secilc with american fuzzy lop.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
The removal of attributes that are only used in neverallow rules is
hindering AOSP adoption of the CIL compiler. This is because AOSP
extracts neverallow rules from its policy.conf for use in the Android
compatibility test suite. These neverallow rules are applied against
the binary policy being tested to check for a violation. Any neverallow
rules with an attribute that has been removed cannot be checked.
Now attributes are kept unless they are not used in any allow rule and
they are auto-generated or named "cil_gen_require" or do not have any
types associated with them.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
If a policy module package has been created with a policy that contains
a permission and then is used on a system without that permission CIL
will fail with an error when it cannot resolve the permission.
This will prevent the installation on policy and the user will not
know that the policy has not been installed.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
Commit 77779d2ca, which added support for userattributes in CIL,
accidentally removed code that ignored object_r when adding userrole
mappings to the policydb. This meant that running commands like
`semanage user -l` would incorrectly show object_r. This patch adds that
code back in. Note that CIL requires that these mappings exist to
properly validate file contexts, so pp2cil's behavior of creating these
mappings is not modified.
Signed-off-by: Steve Lawrence <slawrence@tresys.com>
Add missing <stdarg.h> include
This is needed to fix the build on uClibc, due to the usage of
va_list.
Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Remove path field from cil_tree_node struct and all references
to it in CIL. This will reduce memory usage by 5%.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
Replace all calls to cil_log() that print path information with a
call to cil_tree_log() which will also print information about any
high-level sources.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
Provide more detailed log messages containing all relevant CIL and
high-level language source file information through cil_tree_log().
cil_tree_log() uses two new functions: cil_tree_get_next_path() and
cil_tree_get_cil_path().
cil_tree_get_next_path() traverses up the parse tree or AST until
it finds the next CIL or high-level language source information nodes.
It will return the path and whether or not the path is for a CIL file.
cil_tree_get_cil_path() uses cil_tree_get_next_path() to return
the CIL path.
Example cil_tree_log() message:
Problem at policy.cil:21 from foo.hll:11 from bar.hll:2
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
Use some of the functionality recently added to support high-level
language line marking to track the CIL filename.
The goal is to eventually remove the path field from the tree node
struct and offset the addtion of the hll_line field.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
Adds support for tracking original file and line numbers for
better error reporting when a high-level language is translated
into CIL.
This adds a field called "hll_line" to struct cil_tree_node which
increases memory usage by 5%.
Syntax:
;;* lm(s|x) LINENO FILENAME
(CIL STATEMENTS)
;;* lme
lms is used when each of the following CIL statements corresponds
to a line in the original file.
lmx is used when the following CIL statements are all expanded
from a single high-level language line.
lme ends a line mark block.
Example:
;;* lms 1 foo.hll
(CIL-1)
(CIL-2)
;;* lme
;;* lmx 10 bar.hll
(CIL-3)
(CIL-4)
;;* lms 100 baz.hll
(CIL-5)
(CIL-6)
;;* lme
(CIL-7)
;;* lme
CIL-1 is from line 1 of foo.hll
CIL-2 is from line 2 of foo.hll
CIL-3 is from line 10 of bar.hll
CIL-4 is from line 10 of bar.hll
CIL-5 is from line 100 of baz.hll
CIL-6 is from line 101 of baz.hll
CIL-7 is from line 10 of bar.hll
Based on work originally done by Yuli Khodorkovskiy of Tresys.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
The attribute to type map is used to get all of the types that are
asociated with an attribute. To make neverallow and bounds checking
easier it was convienent to map a type to itself. However, CIL was
wrongly mapping an attribute to itself in addition to the types
associated with it. This caused type bounds checking to fail if the
parent was granted a permission through one attribute while the child
was granted the permission through another attribute.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
Commit 3895fbbe0c ("selinux: Add support
for portcon dccp protocol") added support for the (portcon dccp ..)
statement. This fix will allow policy to be built on platforms
(see [1]) that do not have DCCP support by defining the IANA
assigned IP Protocol Number 33 to IPPROTO_DCCP.
[1] https://android-review.googlesource.com/#/c/219568/
Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
For both neverallow and bounds checking keep neverallow and bounds
failures separate from program faults.
Have secilc exit with an error (and fail to build a binary policy)
when bounds checks fail.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
There are three improvements.
When calling cil_find_matching_avrule_in_ast(), one parameter specifies
whether to return a match of the exact same (not a duplicate) rule.
Since the target passed in is created and not actually in the tree
by making this parameter true an extra comparison can be avoided.
Currently, when printing a bounds violation trace, every match except
for the last one has only the parents of the rule printed. Only the
last rule has both its parents and the actual rule printed. Now the
parents and rule are printed for each match. This has the additional
benefit that if a match is not found (there should always be a match)
a seg fault will not occur.
To reduce the amount of error reporting, only print a trace of a
matching rule if it is different from the previous one.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
An attribute that has no types associated with it should still match
with itself, but ebitmap_match_any() will return false if there are
no bits set in either bitmap. The solution is to check to see if the
two datums passed into cil_type_match_any() are the same. This has
the additional advantage of providing a quick match anytime the
attributes are the same.
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
This adds CIL and checkpolicy support for the (portcon dccp ...)
statement. The kernel already handles name_bind and name_connect
permissions for the dccp_socket class.
Signed-off-by: Richard Haines <richard_c_haines@btinternet.com>
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
During resolution of classcommon statements (cil_resolve_classcommon),
we add the number of class common permissions to the values of the class
permissions. This way, the internal CIL values of the common permission
go from 0 to N, and the values of class permissions start at N+1 (where
N is the number of common permissions). When we reset a class due to
reresolve (cil_reset_class), we must then reverse this process by
subtracting the number of common permissions from the class permission
values.
However, there is a bug when resetting classes in which we subtract the
number of common permissions from the common permissions value rather
than the class permissions value. This means that class permissions
could be too high (since they are not reduced on reset) and common
permissions underflowed (since they are reduced, but should not be).
In most cases, this didn't actually matter since these permission values
aren't used when creating the binary. Additionally, we always access the
permissions via a hash table lookup or map, and then use whatever value
they have to set bits in bitmaps. As long as the bits in the bitmap
match the values, things work as expected. However, the one case where
these values do matter is if you use 'all' in a class permission set. In
this case, we enable bits 0 through number of permissions in a bitmap.
But because our permission values are all mixed up, these enabled bits
do not correspond to the permission values. This results in making it
look like no permissions were set in a class permission set, and the
rule is essentially ignored.
This patch fixes the bug so that the values of class permissions are
properly reset, allowing one to use 'all' in class permission sets in a
policy that reresolves.
Signed-off-by: Steve Lawrence <slawrence@tresys.com>
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
clang warns about variables which are used in a function body even
though they were marked __attribute__((unused)). For example:
interfaces.c:129:2: error: 'handle' was marked unused but was used
[-Werror,-Wused-but-marked-unused]
handle = NULL;
^
interfaces.c:233:2: error: 'handle' was marked unused but was used
[-Werror,-Wused-but-marked-unused]
handle = NULL;
^
Remove these warnings either by removing meaningless assigments or by
removing the attribute.
Signed-off-by: Nicolas Iooss <nicolas.iooss@m4x.org>
clang warns that __cil_permx_to_sepol_class_perms() return value, rc,
may be unitialized:
../cil/src/cil_binary.c:4188:9: error: variable 'rc' may be
uninitialized when used here [-Werror,-Wconditional-uninitialized]
return rc;
^~
../cil/src/cil_binary.c:4148:8: note: initialize the variable 'rc'
to silence this warning
int rc;
^
= 0
This theoretically happens when cil_expand_class(permx->obj) returns an
empty list.
Signed-off-by: Nicolas Iooss <nicolas.iooss@m4x.org>
Classes used in extended avrules and permissionxs must have an "ioctl"
permission. Add validation to ensure that is the case, or print an error
message otherwise.
Signed-off-by: Steve Lawrence <slawrence@tresys.com>
Add a new statement, neverallowx, which has the same syntax as allowx:
(neverallowx foo bar (ioctl file (range 0x2000 0x20FF)))
(allowx foo bar (ioctl file (0x20A0))) ; this fails
Much of the changes just move functions around or split functions up to
ease the sharing of avrule and avrulex comparisons with neverallows.
This refactoring also modifies the avrule struct to include a union of
either class permission information for standard avrules or extended
permission information for extended avrules, also done to support
sharing code.
This also changes assertion.c and avtab.c to allow
check_assertion_avtab_match to work with extended avrules.
Signed-off-by: Steve Lawrence <slawrence@tresys.com>
Resolves https://github.com/SELinuxProject/cil/issues/3
An 'unordered' keyword provides the ability to append classes to the current
list of ordered classes. This allows users to not need knowledge of existing
classes when creating a class and fixes dependencies on classes when removing a
module. This enables userspace object managers with custom objects to be
modularized.
If a class is declared in both an unordered and ordered statement, then the
ordered statement will supercede the unordered declaration.
Example usage:
; Appends new_class to the existing list of classes
(class new_class ())
(classorder (unordered new_class))
Signed-off-by: Yuli Khodorkovskiy <ykhodorkovskiy@tresys.com>
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
Add support for detecting recursive blockinherits, and print a trace of
the detected loop. Output will look something like this upon detection:
Recursive blockinherit found:
test.cil:42: block a
test.cil:43: blockinherit b
test.cil:36: block b
test.cil:37: blockinherit c
test.cil:39: block c
test.cil:40: blockinherit a
Additionally, improve support for detecting recursive macros/calls. Due
to the way calls are copied, the existing code only detected recursion
with call depth of three or more. Smaller depths, like
(macro m ()
(call m))
were not detected and caused a segfault. The callstack that was used for
this was not sufficient, so that is removed and replaced with a method
similar to the block recursion detection. A similar trace is also
displayed for recursive macros/calls.
Also, cleanup sidorder, classorder, catorder, sensorder, and in lists at
the end of resolve, fixing a potential memory leak if errors occur
during resolve.
Signed-off-by: Steve Lawrence <slawrence@tresys.com>
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
On older versions of gcc, an error is incorrectly given about
uninitialized variables. This will initialize the culprits.
Signed-off-by: Yuli Khodorkovskiy <ykhodorkovskiy@tresys.com>
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
Fixes https://github.com/SELinuxProject/cil/issues/7.
This fixes a bug where cil_verify_classperms was executed on NULL
classperms lists. A check is now performed when verifying
classpermissions and classmap to ensure the classperms lists are not
empty.
Signed-off-by: Yuli Khodorkovskiy <ykhodorkovskiy@tresys.com>
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
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.
Specify a userattribute
(userattribute foo)
Add users to the set foo
(userattributeset foo (u1 u2))
Signed-off-by: Yuli Khodorkovskiy <ykhodorkovskiy@tresys.com>
Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>