Make use of single configuration point for SELinux

For now, FreeIPA supports SELinux things as they are in RedHat/Fedora.
But different distributions may have their own SELinux customizations.

This moves SELinux configuration out to platform constants:
- SELINUX_MCS_MAX
- SELINUX_MCS_REGEX
- SELINUX_MLS_MAX
- SELINUX_MLS_REGEX
- SELINUX_USER_REGEX
- SELINUX_USERMAP_DEFAULT
- SELINUX_USERMAP_ORDER

and applies corresponding changes to the test code.

Fixes: https://pagure.io/freeipa/issue/7996
Signed-off-by: Stanislav Levin <slev@altlinux.org>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
This commit is contained in:
Stanislav Levin
2019-06-27 11:52:40 +03:00
committed by Alexander Bokovoy
parent 215e8f768c
commit b2acd65013
11 changed files with 147 additions and 58 deletions

View File

@@ -426,8 +426,8 @@ ipaDefaultEmailDomain: $DOMAIN
ipaMigrationEnabled: FALSE
ipaConfigString: AllowNThash
ipaConfigString: KDC:Disable Last Success
ipaSELinuxUserMapOrder: guest_u:s0$$xguest_u:s0$$user_u:s0$$staff_u:s0-s0:c0.c1023$$sysadm_u:s0-s0:c0.c1023$$unconfined_u:s0-s0:c0.c1023
ipaSELinuxUserMapDefault: unconfined_u:s0-s0:c0.c1023
ipaSELinuxUserMapOrder: $SELINUX_USERMAP_ORDER
ipaSELinuxUserMapDefault: $SELINUX_USERMAP_DEFAULT
dn: cn=cosTemplates,cn=accounts,$SUFFIX
changetype: add

View File

@@ -1,7 +1,7 @@
dn: cn=ipaConfig,cn=etc,$SUFFIX
replace: ipaSELinuxUserMapOrder: ipaSELinuxUserMapOrder: guest_u:s0$$xguest_u:s0$$user_u:s0$$staff_u:s0-s0:c0.c1023$$unconfined_u:s0-s0:c0.c1023::guest_u:s0$$xguest_u:s0$$user_u:s0$$staff_u:s0-s0:c0.c1023$$unconfined_u:s0-s0:c0.c1023
replace: ipaSELinuxUserMapOrder: guest_u:s0$$xguest_u:s0$$user_u:s0-s0:c0.c1023$$staff_u:s0-s0:c0.c1023$$unconfined_u:s0-s0:c0.c1023::guest_u:s0$$xguest_u:s0$$user_u:s0$$staff_u:s0-s0:c0.c1023$$unconfined_u:s0-s0:c0.c1023
add:ipaSELinuxUserMapDefault: unconfined_u:s0-s0:c0.c1023
add:ipaSELinuxUserMapDefault: $SELINUX_USERMAP_DEFAULT
add:ipaUserObjectClasses: ipasshuser
remove:ipaConfigString:AllowLMhash
add:objectClass: ipaUserAuthTypeClass

View File

@@ -54,6 +54,20 @@ class BaseConstantsNamespace:
'samba_share_nfs': 'on',
},
}
SELINUX_MCS_MAX = 1023
SELINUX_MCS_REGEX = r"^c(\d+)([.,-]c(\d+))*$"
SELINUX_MLS_MAX = 15
SELINUX_MLS_REGEX = r"^s(\d+)(-s(\d+))?$"
SELINUX_USER_REGEX = r"^[a-zA-Z][a-zA-Z_\.]*$"
SELINUX_USERMAP_DEFAULT = "unconfined_u:s0-s0:c0.c1023"
SELINUX_USERMAP_ORDER = (
"guest_u:s0"
"$$xguest_u:s0"
"$$user_u:s0"
"$$staff_u:s0-s0:c0.c1023"
"$$sysadm_u:s0-s0:c0.c1023"
"$$unconfined_u:s0-s0:c0.c1023"
)
SSSD_USER = "sssd"
# WSGI module override, only used on Fedora
MOD_WSGI_PYTHON2 = None

View File

@@ -521,6 +521,8 @@ class DsInstance(service.Service):
' '.join(replication.TOTAL_EXCLUDES),
DEFAULT_SHELL=platformconstants.DEFAULT_SHELL,
DEFAULT_ADMIN_SHELL=platformconstants.DEFAULT_ADMIN_SHELL,
SELINUX_USERMAP_DEFAULT=platformconstants.SELINUX_USERMAP_DEFAULT,
SELINUX_USERMAP_ORDER=platformconstants.SELINUX_USERMAP_ORDER,
)
def __create_instance(self):

View File

@@ -40,6 +40,7 @@ from ipapython import ipautil, ipaldap
from ipalib import errors
from ipalib import api, create_api
from ipalib import constants
from ipaplatform.constants import constants as platformconstants
from ipaplatform.paths import paths
from ipapython.dn import DN
@@ -318,6 +319,9 @@ class LDAPUpdate:
if not self.sub_dict.get("TOTAL_EXCLUDES"):
self.sub_dict["TOTAL_EXCLUDES"] = "(objectclass=*) $ EXCLUDE " + \
" ".join(constants.REPL_AGMT_TOTAL_EXCLUDES)
if not self.sub_dict.get("SELINUX_USERMAP_DEFAULT"):
self.sub_dict["SELINUX_USERMAP_DEFAULT"] = \
platformconstants.SELINUX_USERMAP_DEFAULT
self.api = create_api(mode=None)
self.api.bootstrap(in_server=True,
context='updates',

View File

@@ -37,6 +37,7 @@ from ipalib import _, ngettext
from ipalib import output
from .hbacrule import is_all
from ipapython.dn import DN
from ipaplatform.constants import constants as platformconstants
__doc__ = _("""
SELinux User Mapping
@@ -92,37 +93,62 @@ def validate_selinuxuser(ugettext, user):
"""
An SELinux user has 3 components: user:MLS:MCS. user and MLS are required.
user traditionally ends with _u but this is not mandatory.
The regex is ^[a-zA-Z][a-zA-Z_\.]*
The regex is {name}
The MLS part can only be:
Level: s[0-15](-s[0-15])
Level: {mls}
MaxLevel: {mls_max}
Then MCS could be c[0-1023].c[0-1023] and/or c[0-1023]-c[0-c0123]
Meaning
s0 s0-s1 s0-s15:c0.c1023 s0-s1:c0,c2,c15.c26 s0-s0:c0.c1023
Then MCS could be {mcs}
MaxCat: {mcs_max}
Returns a message on invalid, returns nothing on valid.
"""
regex_name = re.compile(r'^[a-zA-Z][a-zA-Z_\.]*$')
regex_mls = re.compile(r'^s[0-9][1-5]{0,1}(-s[0-9][1-5]{0,1}){0,1}$')
regex_mcs = re.compile(r'^c(\d+)([.,-]c(\d+))*?$')
""".format(
name=platformconstants.SELINUX_USER_REGEX,
mls=platformconstants.SELINUX_MLS_REGEX,
mls_max=platformconstants.SELINUX_MLS_MAX,
mcs=platformconstants.SELINUX_MCS_REGEX,
mcs_max=platformconstants.SELINUX_MCS_MAX,
)
SELINUX_MCS_MAX = platformconstants.SELINUX_MCS_MAX
SELINUX_MCS_REGEX = platformconstants.SELINUX_MCS_REGEX
SELINUX_MLS_MAX = platformconstants.SELINUX_MLS_MAX
SELINUX_MLS_REGEX = platformconstants.SELINUX_MLS_REGEX
SELINUX_USER_REGEX = platformconstants.SELINUX_USER_REGEX
regex_name = re.compile(SELINUX_USER_REGEX)
regex_mls = re.compile(SELINUX_MLS_REGEX)
regex_mcs = re.compile(SELINUX_MCS_REGEX)
# If we add in ::: we don't have to check to see if some values are
# empty
(name, mls, mcs, _ignore) = (user + ':::').split(':', 3)
if not regex_name.match(name):
return _('Invalid SELinux user name, only a-Z, _ and . are allowed')
if not mls or not regex_mls.match(mls):
return _('Invalid MLS value, must match s[0-15](-s[0-15])')
m = regex_mcs.match(mcs)
if mcs and (not m or (m.group(3) and (int(m.group(3)) > 1023))):
return _('Invalid MCS value, must match c[0-1023].c[0-1023] '
'and/or c[0-1023]-c[0-c0123]')
return _('Invalid SELinux user name, must match {}').format(
SELINUX_USER_REGEX)
def _validate_level(level, level_regex, upper_limit):
if not level_regex.match(level):
return False
for m in re.finditer(r'\d+', level):
if int(m.group()) > upper_limit:
return False
return True
if not mls or not _validate_level(mls, regex_mls, SELINUX_MLS_MAX):
return _(
'Invalid MLS value, must match {mls}, where max level '
'{mls_max}').format(mls=SELINUX_MLS_REGEX, mls_max=SELINUX_MLS_MAX)
if mcs and not _validate_level(mcs, regex_mcs, SELINUX_MCS_MAX):
return _(
'Invalid MCS value, must match {mcs}, where max category '
'{mcs_max}').format(mcs=SELINUX_MCS_REGEX, mcs_max=SELINUX_MCS_MAX)
return None
def validate_selinuxuser_inlist(ldap, user):
"""
Ensure the user is in the list of allowed SELinux users.

View File

@@ -59,6 +59,7 @@ class TestWinsyncMigrate(IntegrationTest):
ipa_group = 'ipa_group'
ad_user = 'testuser'
default_shell = platformconstants.DEFAULT_SHELL
selinuxuser = platformconstants.SELINUX_USERMAP_ORDER.split("$$")[0]
test_role = 'test_role'
test_hbac_rule = 'test_hbac_rule'
test_selinux_map = 'test_selinux_map'
@@ -102,7 +103,7 @@ class TestWinsyncMigrate(IntegrationTest):
cls.master.run_command(['ipa', 'hbacrule-add', cls.test_hbac_rule])
cls.master.run_command([
'ipa', 'selinuxusermap-add', cls.test_selinux_map,
'--selinuxuser', 'guest_u:s0'])
'--selinuxuser', cls.selinuxuser])
@classmethod
def setup_user_memberships(cls, user):

View File

@@ -2,6 +2,19 @@
# Copyright (C) 2018 FreeIPA Contributors see COPYING for license
#
from ipaplatform.constants import constants as platformconstants
# for example, user_u:s0
selinuxuser1 = platformconstants.SELINUX_USERMAP_ORDER.split("$$")[0]
selinuxuser2 = platformconstants.SELINUX_USERMAP_ORDER.split("$$")[1]
selinux_mcs_max = platformconstants.SELINUX_MCS_MAX
selinux_mls_max = platformconstants.SELINUX_MLS_MAX
second_mls_level = 's{}'.format(list(range(0, selinux_mls_max + 1))[0])
second_mcs_level = 'c{}'.format(list(range(0, selinux_mcs_max + 1))[0])
mcs_range = '{0}.{0}'.format(second_mcs_level)
ENTITY = 'selinuxusermap'
PKEY = 'itest-selinuxusermap'
@@ -9,7 +22,7 @@ DATA = {
'pkey': PKEY,
'add': [
('textbox', 'cn', PKEY),
('textbox', 'ipaselinuxuser', 'user_u:s0'),
('textbox', 'ipaselinuxuser', selinuxuser1),
],
'mod': [
('textarea', 'description', 'itest-selinuxusermap desc'),
@@ -21,7 +34,7 @@ DATA2 = {
'pkey': PKEY2,
'add': [
('textbox', 'cn', PKEY2),
('textbox', 'ipaselinuxuser', 'unconfined_u:s0-s0:c0.c1023'),
('textbox', 'ipaselinuxuser', selinuxuser2),
],
'mod': [
('textarea', 'description', 'itest-selinuxusermap desc2'),
@@ -33,7 +46,7 @@ DATA_MLS_RANGE = {
'pkey': PKEY_MLS_RANGE,
'add': [
('textbox', 'cn', PKEY_MLS_RANGE),
('textbox', 'ipaselinuxuser', 'user_u:s0-s1'),
('textbox', 'ipaselinuxuser', 'foo:s0-{}'.format(second_mls_level)),
],
}
@@ -42,7 +55,9 @@ DATA_MCS_RANGE = {
'pkey': PKEY_MCS_RANGE,
'add': [
('textbox', 'cn', PKEY_MCS_RANGE),
('textbox', 'ipaselinuxuser', 'user_u:s0-s15:c0.c1023'),
('textbox', 'ipaselinuxuser',
'foo:s0-s{}:c0.c{}'.format(selinux_mls_max, selinux_mcs_max)
),
],
}
@@ -51,7 +66,10 @@ DATA_MCS_COMMAS = {
'pkey': PKEY_MCS_COMMAS,
'add': [
('textbox', 'cn', PKEY_MCS_COMMAS),
('textbox', 'ipaselinuxuser', 'user_u:s0-s1:c0,c2,c15.c26'),
('textbox', 'ipaselinuxuser',
'foo:s0-{}:c0,{},{}'.format(
second_mls_level, second_mcs_level, mcs_range),
),
],
}
@@ -60,7 +78,9 @@ DATA_MLS_SINGLE_VAL = {
'pkey': PKEY_MLS_SINGLE_VAL,
'add': [
('textbox', 'cn', PKEY_MLS_SINGLE_VAL),
('textbox', 'ipaselinuxuser', 'user_u:s0-s0:c0.c1023'),
('textbox', 'ipaselinuxuser',
'foo:s0-s0:c0.c{}'.format(selinux_mcs_max)
),
],
}
@@ -69,7 +89,7 @@ DATA_NON_EXIST_SEUSER = {
'pkey': PKEY_NON_EXIST_SEUSER,
'add': [
('textbox', 'cn', PKEY_NON_EXIST_SEUSER),
('textbox', 'ipaselinuxuser', 'abc:s0'),
('textbox', 'ipaselinuxuser', 'foo:s0'),
],
}
@@ -78,7 +98,7 @@ DATA_INVALID_MCS = {
'pkey': PKEY_INVALID_MCS,
'add': [
('textbox', 'cn', PKEY_INVALID_MCS),
('textbox', 'ipaselinuxuser', 'user:s0:c'),
('textbox', 'ipaselinuxuser', 'foo:s0:c'),
],
}
@@ -87,7 +107,7 @@ DATA_INVALID_MLS = {
'pkey': PKEY_INVALID_MLS,
'add': [
('textbox', 'cn', PKEY_INVALID_MLS),
('textbox', 'ipaselinuxuser', 'user'),
('textbox', 'ipaselinuxuser', 'foo'),
],
}

View File

@@ -21,6 +21,7 @@
SELinux user map tests
"""
from ipaplatform.constants import constants as platformconstants
from ipatests.test_webui.ui_driver import UI_driver
from ipatests.test_webui.ui_driver import screenshot
import ipatests.test_webui.data_user as user
@@ -41,10 +42,14 @@ RULE_ALR_EXIST = 'SELinux User Map rule with name "{}" already exists'
RULE_UPDATED = 'SELinux User Map {} updated'
RULE_ADDED = 'SELinux User Map successfully added'
INVALID_SEUSER = 'SELinux user {} not found in ordering list (in config)'
INVALID_MCS = ("invalid 'selinuxuser': Invalid MCS value, must match c[0-1023]"
".c[0-1023] and/or c[0-1023]-c[0-c0123]")
INVALID_MLS = ("invalid 'selinuxuser': Invalid MLS value, must match "
"s[0-15](-s[0-15])")
INVALID_MCS = ("invalid 'selinuxuser': Invalid MCS value, must match {}, "
"where max category {}").format(
platformconstants.SELINUX_MCS_REGEX,
platformconstants.SELINUX_MCS_MAX)
INVALID_MLS = ("invalid 'selinuxuser': Invalid MLS value, must match {}, "
"where max level {}").format(
platformconstants.SELINUX_MLS_REGEX,
platformconstants.SELINUX_MLS_MAX)
HBAC_DEL_ERR = ('{} cannot be deleted because SELinux User Map {} requires '
'it')
HBAC_MEMBER_ERR = 'HBAC rule and local members cannot both be set'

View File

@@ -23,6 +23,7 @@ Test the `ipaserver/plugins/config.py` module.
"""
from ipalib import api, errors
from ipaplatform.constants import constants as platformconstants
from ipatests.test_xmlrpc.xmlrpc_test import Declarative
import pytest
@@ -109,8 +110,11 @@ class test_config(Declarative):
desc='Try to set invalid ipaselinuxusermapdefault',
command=('config_mod', [],
dict(ipaselinuxusermapdefault=u'foo')),
expected=errors.ValidationError(name='ipaselinuxusermapdefault',
error='Invalid MLS value, must match s[0-15](-s[0-15])'),
expected=errors.ValidationError(
name='ipaselinuxusermapdefault',
error='Invalid MLS value, must match {}, where max level '
'{}'.format(platformconstants.SELINUX_MLS_REGEX,
platformconstants.SELINUX_MLS_MAX)),
),
dict(
@@ -140,10 +144,13 @@ class test_config(Declarative):
dict(
desc='Try to set invalid selinux user in ipaselinuxusermaporder',
command=('config_mod', [],
dict(ipaselinuxusermaporder=u'unconfined_u:s0-s0:c0.c1023$baduser$guest_u:s0')),
expected=errors.ValidationError(name='ipaselinuxusermaporder',
dict(ipaselinuxusermaporder=u'baduser')),
expected=errors.ValidationError(
name='ipaselinuxusermaporder',
error='SELinux user \'baduser\' is not valid: Invalid MLS '
'value, must match s[0-15](-s[0-15])'),
'value, must match {}, where max level {}'.format(
platformconstants.SELINUX_MLS_REGEX,
platformconstants.SELINUX_MLS_MAX)),
),
dict(
@@ -151,9 +158,7 @@ class test_config(Declarative):
command=(
'config_mod', [],
dict(
ipaselinuxusermaporder=u'xguest_u:s0$guest_u:s0'
u'$user_u:s0-s0:c0.c1023$staff_u:s0-s0:c0.c1023'
u'$sysadm_u:s0-s0:c0.c1023$unconfined_u:s0-s0:c0.c1023',
ipaselinuxusermaporder=u'foo:s0',
ipaselinuxusermapdefault=u'unknown_u:s0')),
expected=errors.ValidationError(name='ipaselinuxusermapdefault',
error='SELinux user map default user not in order list'),

View File

@@ -21,6 +21,8 @@ Test the `ipaserver/plugins/selinuxusermap.py` module.
"""
from ipalib import api, errors
from ipaplatform.constants import constants as platformconstants
from ipatests.test_xmlrpc import objectclasses
from ipatests.test_xmlrpc.xmlrpc_test import (Declarative, fuzzy_digits,
fuzzy_uuid)
@@ -30,8 +32,16 @@ from ipatests.test_xmlrpc.test_user_plugin import get_user_result
import pytest
rule1 = u'selinuxrule1'
selinuxuser1 = u'guest_u:s0'
selinuxuser2 = u'xguest_u:s0'
selinuxuser1 = platformconstants.SELINUX_USERMAP_ORDER.split("$$")[0]
selinuxuser2 = platformconstants.SELINUX_USERMAP_ORDER.split("$$")[1]
INVALID_MCS = "Invalid MCS value, must match {}, where max category {}".format(
platformconstants.SELINUX_MCS_REGEX,
platformconstants.SELINUX_MCS_MAX)
INVALID_MLS = "Invalid MLS value, must match {}, where max level {}".format(
platformconstants.SELINUX_MLS_REGEX,
platformconstants.SELINUX_MLS_MAX)
user1 = u'tuser1'
group1 = u'testgroup1'
@@ -623,44 +633,46 @@ class test_selinuxusermap(Declarative):
),
expected=errors.ValidationError(
name='selinuxuser',
error=u'Invalid SELinux user name, only a-Z, _ '
'and . are allowed'
error=u'Invalid SELinux user name, must match {}'.format(
platformconstants.SELINUX_USER_REGEX)
),
),
dict(
desc='Create rule with invalid MCS xguest_u:s999',
desc='Create rule with invalid MLS foo:s{}'.format(
platformconstants.SELINUX_MLS_MAX + 1),
command=(
'selinuxusermap_add', [rule1],
dict(ipaselinuxuser=u'xguest_u:s999')
dict(ipaselinuxuser=u'foo:s{}'.format(
platformconstants.SELINUX_MLS_MAX + 1))
),
expected=errors.ValidationError(name='selinuxuser',
error=u'Invalid MLS value, must match s[0-15](-s[0-15])'),
error=INVALID_MLS),
),
dict(
desc='Create rule with invalid MLS xguest_u:s0:p88',
desc='Create rule with invalid MCS foo:s0:p88',
command=(
'selinuxusermap_add', [rule1],
dict(ipaselinuxuser=u'xguest_u:s0:p88')
dict(ipaselinuxuser=u'foo:s0:p88')
),
expected=errors.ValidationError(name='selinuxuser',
error=u'Invalid MCS value, must match c[0-1023].c[0-1023] ' +
u'and/or c[0-1023]-c[0-c0123]'),
error=INVALID_MCS),
),
dict(
desc='Create rule with invalid MLS xguest_u:s0:c0.c1028',
desc='Create rule with invalid MCS foo:s0:c0.c{}'.format(
platformconstants.SELINUX_MCS_MAX + 1),
command=(
'selinuxusermap_add', [rule1],
dict(ipaselinuxuser=u'xguest_u:s0-s0:c0.c1028')
dict(ipaselinuxuser=u'foo:s0-s0:c0.c{}'.format(
platformconstants.SELINUX_MCS_MAX + 1))
),
expected=errors.ValidationError(name='selinuxuser',
error=u'Invalid MCS value, must match c[0-1023].c[0-1023] ' +
u'and/or c[0-1023]-c[0-c0123]'),
error=INVALID_MCS),
),
@@ -671,7 +683,7 @@ class test_selinuxusermap(Declarative):
dict(setattr=u'ipaselinuxuser=deny')
),
expected=errors.ValidationError(name='ipaselinuxuser',
error=u'Invalid MLS value, must match s[0-15](-s[0-15])'),
error=INVALID_MLS),
),
dict(