summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ipalib/plugins/hbactest.py141
-rw-r--r--ipaserver/dcerpc.py56
2 files changed, 187 insertions, 10 deletions
diff --git a/ipalib/plugins/hbactest.py b/ipalib/plugins/hbactest.py
index 78fac0241..3e2c69eb0 100644
--- a/ipalib/plugins/hbactest.py
+++ b/ipalib/plugins/hbactest.py
@@ -17,11 +17,19 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from ipalib import api, errors, output
+from ipalib import api, errors, output, util
from ipalib import Command, Str, Flag, Int
from types import NoneType
from ipalib.cli import to_cli
from ipalib import _, ngettext
+from ipapython.dn import DN
+if api.env.in_server and api.env.context in ['lite', 'server']:
+ try:
+ import ipaserver.dcerpc
+ _dcerpc_bindings_installed = True
+ except ImportError:
+ _dcerpc_bindings_installed = False
+
import pyhbac
__doc__ = _("""
@@ -129,6 +137,74 @@ EXAMPLES:
notmatched: new-rule
matched: allow_all
+
+HBACTEST AND TRUSTED DOMAINS
+
+When an external trusted domain is configured in IPA, HBAC rules are also applied
+on users accessing IPA resources from the trusted domain. Trusted domain users and
+groups (and their SIDs) can be then assigned to external groups which can be
+members of POSIX groups in IPA which can be used in HBAC rules and thus allowing
+access to resources protected by the HBAC system.
+
+hbactest plugin is capable of testing access for both local IPA users and users
+from the trusted domains, either by a fully qualified user name or by user SID.
+Such user names need to have a trusted domain specified as a short name
+(DOMAIN\Administrator) or with a user principal name (UPN), Administrator@ad.test.
+
+Please note that hbactest executed with a trusted domain user as --user parameter
+can be only run by members of "trust admins" group.
+
+EXAMPLES:
+
+ 1. Test if a user from a trusted domain specified by its shortname matches any
+ rule:
+
+ $ ipa hbactest --user 'DOMAIN\Administrator' --host `hostname` --service sshd
+ --------------------
+ Access granted: True
+ --------------------
+ Matched rules: allow_all
+ Matched rules: can_login
+
+ 2. Test if a user from a trusted domain specified by its domain name matches
+ any rule:
+
+ $ ipa hbactest --user 'Administrator@domain.com' --host `hostname` --service sshd
+ --------------------
+ Access granted: True
+ --------------------
+ Matched rules: allow_all
+ Matched rules: can_login
+
+ 3. Test if a user from a trusted domain specified by its SID matches any rule:
+
+ $ ipa hbactest --user S-1-5-21-3035198329-144811719-1378114514-500 \\
+ --host `hostname` --service sshd
+ --------------------
+ Access granted: True
+ --------------------
+ Matched rules: allow_all
+ Matched rules: can_login
+
+ 4. Test if other user from a trusted domain specified by its SID matches any rule:
+
+ $ ipa hbactest --user S-1-5-21-3035198329-144811719-1378114514-500 \\
+ --host `hostname` --service sshd
+ --------------------
+ Access granted: True
+ --------------------
+ Matched rules: allow_all
+ Matched rules: can_login
+
+ 5. Test if other user from a trusted domain specified by its shortname matches
+ any rule:
+
+ $ ipa hbactest --user 'DOMAIN\Otheruser' --host `hostname` --service sshd
+ --------------------
+ Access granted: True
+ --------------------
+ Matched rules: allow_all
+ Not matched rules: can_login
""")
def convert_to_ipa_rule(rule):
@@ -298,15 +374,60 @@ class hbactest(Command):
request = pyhbac.HbacRequest()
if options['user'] != u'all':
- try:
- request.user.name = options['user']
- search_result = self.api.Command.user_show(request.user.name)['result']
- groups = search_result['memberof_group']
- if 'memberofindirect_group' in search_result:
- groups += search_result['memberofindirect_group']
- request.user.groups = sorted(set(groups))
- except:
- pass
+ # check first if this is not a trusted domain user
+ if _dcerpc_bindings_installed:
+ is_valid_sid = ipaserver.dcerpc.is_sid_valid(options['user'])
+ else:
+ is_valid_sid = False
+ components = util.normalize_name(options['user'])
+ if is_valid_sid or 'domain' in components or 'flatname' in components:
+ # this is a trusted domain user
+ if not _dcerpc_bindings_installed:
+ raise errors.NotFound(reason=_(
+ 'Cannot perform external member validation without '
+ 'Samba 4 support installed. Make sure you have installed '
+ 'server-trust-ad sub-package of IPA on the server'))
+ domain_validator = ipaserver.dcerpc.DomainValidator(self.api)
+ if not domain_validator.is_configured():
+ raise errors.NotFound(reason=_(
+ 'Cannot search in trusted domains without own domain configured. '
+ 'Make sure you have run ipa-adtrust-install on the IPA server first'))
+ user_sid, group_sids = domain_validator.get_trusted_domain_user_and_groups(options['user'])
+ request.user.name = user_sid
+
+ # Now search for all external groups that have this user or
+ # any of its groups in its external members. Found entires
+ # memberOf links will be then used to gather all groups where
+ # this group is assigned, including the nested ones
+ filter_sids = "(&(objectclass=ipaexternalgroup)(|(ipaExternalMember=%s)))" \
+ % ")(ipaExternalMember=".join(group_sids + [user_sid])
+
+ ldap = self.api.Backend.ldap2
+ group_container = DN(api.env.container_group, api.env.basedn)
+ try:
+ entries, truncated = ldap.find_entries(filter_sids, ['cn', 'memberOf'], group_container)
+ except errors.NotFound:
+ request.user.groups = []
+ else:
+ groups = []
+ for dn, entry in entries:
+ memberof_dns = entry.get('memberof', [])
+ for memberof_dn in memberof_dns:
+ if memberof_dn.endswith(group_container):
+ # this is a group object
+ groups.append(memberof_dn[0][0].value)
+ request.user.groups = sorted(set(groups))
+ else:
+ # try searching for a local user
+ try:
+ request.user.name = options['user']
+ search_result = self.api.Command.user_show(request.user.name)['result']
+ groups = search_result['memberof_group']
+ if 'memberofindirect_group' in search_result:
+ groups += search_result['memberofindirect_group']
+ request.user.groups = sorted(set(groups))
+ except:
+ pass
if options['service'] != u'all':
try:
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index 56d8b0319..6243ebbb9 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -275,6 +275,62 @@ class DomainValidator(object):
raise errors.ValidationError(name=_('trusted domain object'),
error= _('Trusted domain did not return a valid SID for the object'))
+ def get_trusted_domain_user_and_groups(self, object_name):
+ """
+ Returns a tuple with user SID and a list of SIDs of all groups he is
+ a member of.
+
+ LIMITATIONS:
+ - only Trusted Admins group members can use this function as it
+ uses secret for IPA-Trusted domain link
+ - List of group SIDs does not contain group memberships outside
+ of the trusted domain
+ """
+ components = normalize_name(object_name)
+ domain = components.get('domain')
+ flatname = components.get('flatname')
+ name = components.get('name')
+
+ is_valid_sid = is_sid_valid(object_name)
+ if is_valid_sid:
+ # Find a trusted domain for the SID
+ domain = self.get_domain_by_sid(object_name)
+ # Now search a trusted domain for a user with this SID
+ attrs = ['cn']
+ filter = '(&(objectClass=user)(objectSid=%(sid)s))' \
+ % dict(sid=object_name)
+ try:
+ entries = self.get_trusted_domain_objects(domain=domain, filter=filter,
+ attrs=attrs, scope=_ldap.SCOPE_SUBTREE)
+ except errors.NotFound:
+ raise errors.NotFound(reason=_('trusted domain user not found'))
+ user_dn = entries[0][0]
+ elif domain or flatname:
+ attrs = ['cn']
+ filter = '(&(sAMAccountName=%(name)s)(objectClass=user))' \
+ % dict(name=name)
+ try:
+ entries = self.get_trusted_domain_objects(domain,
+ flatname, filter, attrs, _ldap.SCOPE_SUBTREE)
+ except errors.NotFound:
+ raise errors.NotFound(reason=_('trusted domain user not found'))
+ user_dn = entries[0][0]
+ else:
+ # No domain or realm specified, ambiguous search
+ raise errors.ValidationError(name=_('trusted domain object'),
+ error= _('Ambiguous search, user domain was not specified'))
+
+ # Get SIDs of user object and it's groups
+ # tokenGroups attribute must be read with a scope BASE for a known user
+ # distinguished name to avoid search error
+ attrs = ['objectSID', 'tokenGroups']
+ filter = "(objectClass=user)"
+ entries = self.get_trusted_domain_objects(domain,
+ flatname, filter, attrs, _ldap.SCOPE_BASE, user_dn)
+ object_sid = self.__sid_to_str(entries[0][1]['objectSid'][0])
+ group_sids = [self.__sid_to_str(sid) for sid in entries[0][1]['tokenGroups']]
+ return (object_sid, group_sids)
+
def __sid_to_str(self, sid):
"""
Converts binary SID to string representation