import ipa-4.8.7-14.module+el8.3.0+9419+8502777d
This commit is contained in:
parent
615535485a
commit
6e41b73a3b
@ -0,0 +1,236 @@
|
||||
From 1441b999d3fe9b4e59fe942294d13480ecee7d94 Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Date: Wed, 28 Oct 2020 17:46:56 +0200
|
||||
Subject: [PATCH] rpcserver: fallback to non-armored kinit in case of trusted
|
||||
domains
|
||||
|
||||
MIT Kerberos implements FAST negotiation as specified in RFC 6806
|
||||
section 11. The implementation relies on the caller to provide a hint
|
||||
whether FAST armoring must be used.
|
||||
|
||||
FAST armor can only be used when both client and KDC have a shared
|
||||
secret. When KDC is from a trusted domain, there is no way to have a
|
||||
shared secret between a generic Kerberos client and that KDC.
|
||||
|
||||
[MS-KILE] section 3.2.5.4 'Using FAST When the Realm Supports FAST'
|
||||
allows KILE clients (Kerberos clients) to have local settings that
|
||||
direct it to enforce use of FAST. This is equal to the current
|
||||
implementation of 'kinit' utility in MIT Kerberos requiring to use FAST
|
||||
if armor cache (option '-T') is provided.
|
||||
|
||||
[MS-KILE] section 3.3.5.7.4 defines a way for a computer from a
|
||||
different realm to use compound identity TGS-REQ to create FAST TGS-REQ
|
||||
explicitly armored with the computer's TGT. However, this method is not
|
||||
available to IPA framework as we don't have access to the IPA server's
|
||||
host key. In addition, 'kinit' utility does not support this method.
|
||||
|
||||
Active Directory has a policy to force use of FAST when client
|
||||
advertizes its use. Since we cannot know in advance whether a principal
|
||||
to obtain initial credentials for belongs to our realm or to a trusted
|
||||
one due to enterprise principal canonicalization, we have to try to
|
||||
kinit. Right now we fail unconditionally if FAST couldn't be used and
|
||||
libkrb5 communication with a KDC from the user realm (e.g. from a
|
||||
trusted forest) causes enforcement of a FAST.
|
||||
|
||||
In the latter case, as we cannot use FAST anyway, try to kinit again
|
||||
without advertizing FAST. This works even in the situations when FAST
|
||||
enforcement is enabled on Active Directory side: if client doesn't
|
||||
advertize FAST capability, it is not required. Additionally, FAST cannot
|
||||
be used for any practical need for a trusted domain's users yet.
|
||||
|
||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipalib/errors.py | 6 ++
|
||||
ipaserver/rpcserver.py | 94 ++++++++++++++++---------
|
||||
ipatests/test_integration/test_trust.py | 21 ++++++
|
||||
3 files changed, 86 insertions(+), 35 deletions(-)
|
||||
|
||||
diff --git a/ipalib/errors.py b/ipalib/errors.py
|
||||
index 1b17ca7ed..fa51e15c0 100644
|
||||
--- a/ipalib/errors.py
|
||||
+++ b/ipalib/errors.py
|
||||
@@ -245,6 +245,12 @@ class PluginModuleError(PrivateError):
|
||||
format = '%(name)s is not a valid plugin module'
|
||||
|
||||
|
||||
+class KrbPrincipalWrongFAST(PrivateError):
|
||||
+ """
|
||||
+ Raised when it is not possible to use our FAST armor for kinit
|
||||
+ """
|
||||
+ format = '%(principal)s cannot use Anonymous PKINIT as a FAST armor'
|
||||
+
|
||||
##############################################################################
|
||||
# Public errors:
|
||||
|
||||
diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
|
||||
index 181295471..ed775170e 100644
|
||||
--- a/ipaserver/rpcserver.py
|
||||
+++ b/ipaserver/rpcserver.py
|
||||
@@ -46,9 +46,11 @@ from ipalib.capabilities import VERSION_WITHOUT_CAPABILITIES
|
||||
from ipalib.frontend import Local
|
||||
from ipalib.install.kinit import kinit_armor, kinit_password
|
||||
from ipalib.backend import Executioner
|
||||
-from ipalib.errors import (PublicError, InternalError, JSONError,
|
||||
+from ipalib.errors import (
|
||||
+ PublicError, InternalError, JSONError,
|
||||
CCacheError, RefererError, InvalidSessionPassword, NotFound, ACIError,
|
||||
- ExecutionError, PasswordExpired, KrbPrincipalExpired, UserLocked)
|
||||
+ ExecutionError, PasswordExpired, KrbPrincipalExpired, KrbPrincipalWrongFAST,
|
||||
+ UserLocked)
|
||||
from ipalib.request import context, destroy_context
|
||||
from ipalib.rpc import (xml_dumps, xml_loads,
|
||||
json_encode_binary, json_decode_binary)
|
||||
@@ -957,6 +959,34 @@ class login_password(Backend, KerberosSession):
|
||||
self.api.Backend.wsgi_dispatch.mount(self, self.key)
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
+ def attempt_kinit(user_principal, password,
|
||||
+ ipa_ccache_name, use_armor=True):
|
||||
+ try:
|
||||
+ # try to remove in case an old file was there
|
||||
+ os.unlink(ipa_ccache_name)
|
||||
+ except OSError:
|
||||
+ pass
|
||||
+ try:
|
||||
+ self.kinit(user_principal, password,
|
||||
+ ipa_ccache_name, use_armor=use_armor)
|
||||
+ except PasswordExpired as e:
|
||||
+ return self.unauthorized(environ, start_response,
|
||||
+ str(e), 'password-expired')
|
||||
+ except InvalidSessionPassword as e:
|
||||
+ return self.unauthorized(environ, start_response,
|
||||
+ str(e), 'invalid-password')
|
||||
+ except KrbPrincipalExpired as e:
|
||||
+ return self.unauthorized(environ,
|
||||
+ start_response,
|
||||
+ str(e),
|
||||
+ 'krbprincipal-expired')
|
||||
+ except UserLocked as e:
|
||||
+ return self.unauthorized(environ,
|
||||
+ start_response,
|
||||
+ str(e),
|
||||
+ 'user-locked')
|
||||
+ return None
|
||||
+
|
||||
logger.debug('WSGI login_password.__call__:')
|
||||
|
||||
# Get the user and password parameters from the request
|
||||
@@ -1007,26 +1037,14 @@ class login_password(Backend, KerberosSession):
|
||||
ipa_ccache_name = os.path.join(paths.IPA_CCACHES,
|
||||
'kinit_{}'.format(os.getpid()))
|
||||
try:
|
||||
- # try to remove in case an old file was there
|
||||
- os.unlink(ipa_ccache_name)
|
||||
- except OSError:
|
||||
- pass
|
||||
- try:
|
||||
- self.kinit(user_principal, password, ipa_ccache_name)
|
||||
- except PasswordExpired as e:
|
||||
- return self.unauthorized(environ, start_response, str(e), 'password-expired')
|
||||
- except InvalidSessionPassword as e:
|
||||
- return self.unauthorized(environ, start_response, str(e), 'invalid-password')
|
||||
- except KrbPrincipalExpired as e:
|
||||
- return self.unauthorized(environ,
|
||||
- start_response,
|
||||
- str(e),
|
||||
- 'krbprincipal-expired')
|
||||
- except UserLocked as e:
|
||||
- return self.unauthorized(environ,
|
||||
- start_response,
|
||||
- str(e),
|
||||
- 'user-locked')
|
||||
+ result = attempt_kinit(user_principal, password,
|
||||
+ ipa_ccache_name, use_armor=True)
|
||||
+ except KrbPrincipalWrongFAST:
|
||||
+ result = attempt_kinit(user_principal, password,
|
||||
+ ipa_ccache_name, use_armor=False)
|
||||
+
|
||||
+ if result is not None:
|
||||
+ return result
|
||||
|
||||
result = self.finalize_kerberos_acquisition('login_password',
|
||||
ipa_ccache_name, environ,
|
||||
@@ -1038,21 +1056,24 @@ class login_password(Backend, KerberosSession):
|
||||
pass
|
||||
return result
|
||||
|
||||
- def kinit(self, principal, password, ccache_name):
|
||||
- # get anonymous ccache as an armor for FAST to enable OTP auth
|
||||
- armor_path = os.path.join(paths.IPA_CCACHES,
|
||||
- "armor_{}".format(os.getpid()))
|
||||
+ def kinit(self, principal, password, ccache_name, use_armor=True):
|
||||
+ if use_armor:
|
||||
+ # get anonymous ccache as an armor for FAST to enable OTP auth
|
||||
+ armor_path = os.path.join(paths.IPA_CCACHES,
|
||||
+ "armor_{}".format(os.getpid()))
|
||||
|
||||
- logger.debug('Obtaining armor in ccache %s', armor_path)
|
||||
+ logger.debug('Obtaining armor in ccache %s', armor_path)
|
||||
|
||||
- try:
|
||||
- kinit_armor(
|
||||
- armor_path,
|
||||
- pkinit_anchors=[paths.KDC_CERT, paths.KDC_CA_BUNDLE_PEM],
|
||||
- )
|
||||
- except RuntimeError as e:
|
||||
- logger.error("Failed to obtain armor cache")
|
||||
- # We try to continue w/o armor, 2FA will be impacted
|
||||
+ try:
|
||||
+ kinit_armor(
|
||||
+ armor_path,
|
||||
+ pkinit_anchors=[paths.KDC_CERT, paths.KDC_CA_BUNDLE_PEM],
|
||||
+ )
|
||||
+ except RuntimeError as e:
|
||||
+ logger.error("Failed to obtain armor cache")
|
||||
+ # We try to continue w/o armor, 2FA will be impacted
|
||||
+ armor_path = None
|
||||
+ else:
|
||||
armor_path = None
|
||||
|
||||
try:
|
||||
@@ -1080,6 +1101,9 @@ class login_password(Backend, KerberosSession):
|
||||
'while getting initial credentials') in str(e):
|
||||
raise UserLocked(principal=principal,
|
||||
message=unicode(e))
|
||||
+ elif ('kinit: Error constructing AP-REQ armor: '
|
||||
+ 'Matching credential not found') in str(e):
|
||||
+ raise KrbPrincipalWrongFAST(principal=principal)
|
||||
raise InvalidSessionPassword(principal=principal,
|
||||
message=unicode(e))
|
||||
|
||||
diff --git a/ipatests/test_integration/test_trust.py b/ipatests/test_integration/test_trust.py
|
||||
index a6a055c2a..bec918a31 100644
|
||||
--- a/ipatests/test_integration/test_trust.py
|
||||
+++ b/ipatests/test_integration/test_trust.py
|
||||
@@ -175,6 +175,27 @@ class TestTrust(BaseTestTrust):
|
||||
tasks.kdestroy_all(self.master)
|
||||
tasks.kinit_admin(self.master)
|
||||
|
||||
+ def test_password_login_as_aduser(self):
|
||||
+ """Test if AD user can login with password to Web UI"""
|
||||
+ ad_admin = 'Administrator@%s' % self.ad_domain
|
||||
+
|
||||
+ tasks.kdestroy_all(self.master)
|
||||
+ user_and_password = ('user=%s&password=%s' %
|
||||
+ (ad_admin, self.master.config.ad_admin_password))
|
||||
+ host = self.master.hostname
|
||||
+ cmd_args = [
|
||||
+ paths.BIN_CURL,
|
||||
+ '-v',
|
||||
+ '-H', 'referer:https://{}/ipa'.format(host),
|
||||
+ '-H', 'Content-Type:application/x-www-form-urlencoded',
|
||||
+ '-H', 'Accept:text/plain',
|
||||
+ '--cacert', paths.IPA_CA_CRT,
|
||||
+ '--data', user_and_password,
|
||||
+ 'https://{}/ipa/session/login_password'.format(host)]
|
||||
+ result = self.master.run_command(cmd_args)
|
||||
+ assert "Set-Cookie: ipa_session=MagBearerToken" in result.stdout_text
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+
|
||||
def test_ipauser_authentication_with_nonposix_trust(self):
|
||||
ipauser = u'tuser'
|
||||
original_passwd = 'Secret123'
|
||||
--
|
||||
2.29.2
|
||||
|
@ -0,0 +1,27 @@
|
||||
From 12de9ee69f12f7c0021ea98e9c1163db7d59e5d3 Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Date: Wed, 28 Oct 2020 19:37:11 +0200
|
||||
Subject: [PATCH] pylint: remove unused variable
|
||||
|
||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipaserver/rpcserver.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
|
||||
index 27850e867..181295471 100644
|
||||
--- a/ipaserver/rpcserver.py
|
||||
+++ b/ipaserver/rpcserver.py
|
||||
@@ -972,7 +972,7 @@ class login_password(Backend, KerberosSession):
|
||||
|
||||
try:
|
||||
query_dict = parse_qs(query_string)
|
||||
- except Exception as e:
|
||||
+ except Exception:
|
||||
return self.bad_request(environ, start_response, "cannot parse query data")
|
||||
|
||||
user = query_dict.get('user', None)
|
||||
--
|
||||
2.29.2
|
||||
|
@ -0,0 +1,121 @@
|
||||
From 29262465edf034d521c165e3854e28835d86b98d Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Date: Fri, 6 Nov 2020 09:53:35 +0200
|
||||
Subject: [PATCH] wgi/plugins.py: ignore empty plugin directories
|
||||
|
||||
Dynamic plugin registry returns as a plugin any folder within the
|
||||
plugins directory. Web UI then attempts to load for each plugin 'foo' a
|
||||
JavaScript file named 'foo/foo.js'. The problem is that if 'foo/foo.js'
|
||||
does not exist, Web UI breaks and it is impossible to recover until the
|
||||
empty folder is removed or 'foo/foo.js' (even empty) is created at the
|
||||
server side.
|
||||
|
||||
Check that 'foo/foo.js' actual exists when including a plugin into the
|
||||
registry.
|
||||
|
||||
Test the registry generator by creating fake plugins and removing them
|
||||
during the test.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/8567
|
||||
|
||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
---
|
||||
install/wsgi/plugins.py | 5 +-
|
||||
ipatests/test_ipaserver/test_jsplugins.py | 68 +++++++++++++++++++++++
|
||||
2 files changed, 72 insertions(+), 1 deletion(-)
|
||||
create mode 100644 ipatests/test_ipaserver/test_jsplugins.py
|
||||
|
||||
diff --git a/install/wsgi/plugins.py b/install/wsgi/plugins.py
|
||||
index f80cfb9fe..4c43e7f87 100644
|
||||
--- a/install/wsgi/plugins.py
|
||||
+++ b/install/wsgi/plugins.py
|
||||
@@ -36,7 +36,10 @@ def get_plugin_index():
|
||||
|
||||
dirs = os.listdir(paths.IPA_JS_PLUGINS_DIR)
|
||||
index = 'define([],function(){return['
|
||||
- index += ','.join("'"+x+"'" for x in dirs)
|
||||
+ for x in dirs:
|
||||
+ p = os.path.join(paths.IPA_JS_PLUGINS_DIR, x, x + '.js')
|
||||
+ if os.path.exists(p):
|
||||
+ index += "'" + x + "',"
|
||||
index += '];});'
|
||||
return index.encode('utf-8')
|
||||
|
||||
diff --git a/ipatests/test_ipaserver/test_jsplugins.py b/ipatests/test_ipaserver/test_jsplugins.py
|
||||
new file mode 100644
|
||||
index 000000000..354e6992c
|
||||
--- /dev/null
|
||||
+++ b/ipatests/test_ipaserver/test_jsplugins.py
|
||||
@@ -0,0 +1,68 @@
|
||||
+# Copyright (C) 2020 FreeIPA Contributors see COPYING for license
|
||||
+
|
||||
+import os
|
||||
+import pytest
|
||||
+
|
||||
+from ipatests.test_ipaserver.httptest import Unauthorized_HTTP_test
|
||||
+from ipatests.util import assert_equal, assert_not_equal
|
||||
+from ipaplatform.paths import paths
|
||||
+
|
||||
+
|
||||
+@pytest.mark.tier1
|
||||
+class test_jsplugins(Unauthorized_HTTP_test):
|
||||
+ app_uri = '/ipa/ui/js/freeipa/plugins.js'
|
||||
+ jsplugins = (('foo', 'foo.js'), ('bar', ''))
|
||||
+ content_type = 'application/javascript'
|
||||
+
|
||||
+ def test_jsplugins(self):
|
||||
+ empty_response = "define([],function(){return[];});"
|
||||
+
|
||||
+ # Step 1: make sure default response has no additional plugins
|
||||
+ response = self.send_request(method='GET')
|
||||
+ assert_equal(response.status, 200)
|
||||
+ response_data = response.read().decode(encoding='utf-8')
|
||||
+ assert_equal(response_data, empty_response)
|
||||
+
|
||||
+ # Step 2: add fake plugins
|
||||
+ try:
|
||||
+ for (d, f) in self.jsplugins:
|
||||
+ dir = os.path.join(paths.IPA_JS_PLUGINS_DIR, d)
|
||||
+ if not os.path.exists(dir):
|
||||
+ os.mkdir(dir, 0o755)
|
||||
+ if f:
|
||||
+ with open(os.path.join(dir, f), 'w') as js:
|
||||
+ js.write("/* test js plugin */")
|
||||
+
|
||||
+ except OSError as e:
|
||||
+ pytest.skip(
|
||||
+ 'Cannot set up test JS plugin: %s' % e
|
||||
+ )
|
||||
+
|
||||
+ # Step 3: query plugins to see if our plugins exist
|
||||
+ response = self.send_request(method='GET')
|
||||
+ assert_equal(response.status, 200)
|
||||
+ response_data = response.read().decode(encoding='utf-8')
|
||||
+ assert_not_equal(response_data, empty_response)
|
||||
+ for (d, f) in self.jsplugins:
|
||||
+ if f:
|
||||
+ assert "'" + d + "'" in response_data
|
||||
+ else:
|
||||
+ assert "'" + d + "'" not in response_data
|
||||
+
|
||||
+ # Step 4: remove fake plugins
|
||||
+ try:
|
||||
+ for (d, f) in self.jsplugins:
|
||||
+ dir = os.path.join(paths.IPA_JS_PLUGINS_DIR, d)
|
||||
+ file = os.path.join(dir, f)
|
||||
+ if f and os.path.exists(file):
|
||||
+ os.unlink(file)
|
||||
+ if os.path.exists(dir):
|
||||
+ os.rmdir(dir)
|
||||
+ except OSError:
|
||||
+ pass
|
||||
+
|
||||
+ # Step 5: make sure default response has no additional plugins
|
||||
+ response = self.send_request(method='GET')
|
||||
+ assert_equal(response.status, 200)
|
||||
+ response_data = response.read().decode(encoding='utf-8')
|
||||
+ assert_equal(response_data, empty_response)
|
||||
--
|
||||
2.29.2
|
||||
|
@ -0,0 +1,76 @@
|
||||
From d5cca835d5439331c05475d0ad2f993ac6f8b615 Mon Sep 17 00:00:00 2001
|
||||
From: Sudhir Menon <sumenon@redhat.com>
|
||||
Date: Wed, 11 Nov 2020 14:55:32 +0530
|
||||
Subject: [PATCH] ipatests: support subordinate upn suffixes
|
||||
|
||||
This test adds new UPN Suffix on the AD side
|
||||
within the ad.test subtree i.e new.ad.test and this
|
||||
UPN is then assigned to aduser and then try to
|
||||
kinit using aduser along with the UPN set, to ensure
|
||||
that the kinit succeeds
|
||||
|
||||
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_trust.py | 45 +++++++++++++++++++++++++
|
||||
1 file changed, 45 insertions(+)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_trust.py b/ipatests/test_integration/test_trust.py
|
||||
index 7e4dbcc6e..31349ced7 100644
|
||||
--- a/ipatests/test_integration/test_trust.py
|
||||
+++ b/ipatests/test_integration/test_trust.py
|
||||
@@ -245,6 +245,51 @@ class TestTrust(BaseTestTrust):
|
||||
self.master.run_command(['kinit', '-C', '-E', self.upn_principal],
|
||||
stdin_text=self.upn_password)
|
||||
|
||||
+ def test_subordinate_suffix(self):
|
||||
+ """Test subordinate UPN Suffixes"""
|
||||
+ tasks.configure_dns_for_trust(self.master, self.ad)
|
||||
+ tasks.establish_trust_with_ad(
|
||||
+ self.master, self.ad_domain,
|
||||
+ extra_args=['--range-type', 'ipa-ad-trust'])
|
||||
+ # Clear all UPN Suffixes
|
||||
+ ps_cmd = "Get-ADForest | Set-ADForest -UPNSuffixes $null"
|
||||
+ self.ad.run_command(["powershell", "-c", ps_cmd])
|
||||
+ result = self.master.run_command(["ipa", "trust-show", self.ad_domain])
|
||||
+ assert (
|
||||
+ "ipantadditionalsuffixes: {}".format(self.upn_suffix)
|
||||
+ not in result.stdout_text
|
||||
+ )
|
||||
+ # Run Get-ADForest
|
||||
+ ps_cmd1 = "Get-ADForest"
|
||||
+ self.ad.run_command(["powershell", "-c", ps_cmd1])
|
||||
+ # Add new UPN for AD
|
||||
+ ps_cmd2 = (
|
||||
+ 'Get-ADForest | Set-ADForest -UPNSuffixes '
|
||||
+ '@{add="new.ad.test", "upn.dom"}'
|
||||
+ )
|
||||
+ self.ad.run_command(["powershell", "-c", ps_cmd2])
|
||||
+ self.ad.run_command(["powershell", "-c", ps_cmd1])
|
||||
+ self.master.run_command(
|
||||
+ ["ipa", "trust-fetch-domains", self.ad_domain],
|
||||
+ raiseonerr=False)
|
||||
+ self.master.run_command(["ipa", "trust-show", self.ad_domain])
|
||||
+ # Set UPN for the aduser
|
||||
+ ps_cmd3 = (
|
||||
+ 'set-aduser -UserPrincipalName '
|
||||
+ 'Administrator@new.ad.test -Identity Administrator'
|
||||
+ )
|
||||
+ self.ad.run_command(["powershell", "-c", ps_cmd3])
|
||||
+ # kinit to IPA using AD user Administrator@new.ad.test
|
||||
+ result = self.master.run_command(
|
||||
+ ["getent", "passwd", "Administrator@new.ad.test"]
|
||||
+ )
|
||||
+ assert result.returncode == 0
|
||||
+ self.master.run_command(
|
||||
+ ["kinit", "-E", "Administrator@new.ad.test"],
|
||||
+ stdin_text="Secret123",
|
||||
+ )
|
||||
+ tasks.kdestroy_all(self.master)
|
||||
+
|
||||
def test_remove_nonposix_trust(self):
|
||||
self.remove_trust(self.ad)
|
||||
tasks.unconfigure_dns_for_trust(self.master, self.ad)
|
||||
--
|
||||
2.29.2
|
||||
|
@ -0,0 +1,114 @@
|
||||
From 1f0702bf9231a4898a2d58325fc51c71fea25047 Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Date: Fri, 23 Oct 2020 18:45:09 +0300
|
||||
Subject: [PATCH] ipa-kdb: support subordinate/superior UPN suffixes
|
||||
|
||||
[MS-ADTS] 6.1.6.9.3.2 requires msDS-TrustForestTrustInfo attribute of
|
||||
trusted domain information in Active Directory to conform certain rules.
|
||||
One side-effect of those rules is that list of UPN suffixes reported
|
||||
through the netr_DsRGetForestTrustInformation function is dynamically
|
||||
filtered to deduplicate subordinate suffixes.
|
||||
|
||||
It means that if list of UPN suffixes contains the following top level
|
||||
names (TLNs):
|
||||
|
||||
fabrikam.com
|
||||
sub.fabrikam.com
|
||||
|
||||
then netr_DsRGetForestTrustInformation would only return 'fabrikam.com'
|
||||
as the TLN, fully filtering 'sub.fabrikam.com'.
|
||||
|
||||
IPA KDB driver used exact comparison of the UPN suffixes so any
|
||||
subordinate had to be specified exactly.
|
||||
|
||||
Modify logic so that if exact check does not succeed, we validate a
|
||||
realm to test being a subordinate of the known UPN suffixes. The
|
||||
subordinate check is done by making sure UPN suffix is at the end of the
|
||||
test realm and is immediately preceded with a dot.
|
||||
|
||||
Because the function to check suffixes potentially called for every
|
||||
Kerberos principal, precalculate and cache length for each UPN suffix at
|
||||
the time we retrieve the list of them.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/8554
|
||||
|
||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Robbie Harwood <rharwood@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Robbie Harwood <rharwood@redhat.com>
|
||||
---
|
||||
daemons/ipa-kdb/ipa_kdb_mspac.c | 30 +++++++++++++++++++++++++
|
||||
daemons/ipa-kdb/ipa_kdb_mspac_private.h | 1 +
|
||||
2 files changed, 31 insertions(+)
|
||||
|
||||
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
||||
index 29dadc183..692f542c9 100644
|
||||
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
|
||||
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
||||
@@ -2393,6 +2393,7 @@ void ipadb_mspac_struct_free(struct ipadb_mspac **mspac)
|
||||
free((*mspac)->trusts[i].upn_suffixes[j]);
|
||||
}
|
||||
free((*mspac)->trusts[i].upn_suffixes);
|
||||
+ free((*mspac)->trusts[i].upn_suffixes_len);
|
||||
}
|
||||
}
|
||||
free((*mspac)->trusts);
|
||||
@@ -2603,6 +2604,24 @@ krb5_error_code ipadb_mspac_get_trusted_domains(struct ipadb_context *ipactx)
|
||||
}
|
||||
}
|
||||
|
||||
+ t[n].upn_suffixes_len = NULL;
|
||||
+ if (t[n].upn_suffixes != NULL) {
|
||||
+ size_t len = 0;
|
||||
+
|
||||
+ for (; t[n].upn_suffixes[len] != NULL; len++);
|
||||
+
|
||||
+ if (len != 0) {
|
||||
+ t[n].upn_suffixes_len = calloc(n, sizeof(size_t));
|
||||
+ if (t[n].upn_suffixes_len == NULL) {
|
||||
+ ret = ENOMEM;
|
||||
+ goto done;
|
||||
+ }
|
||||
+ for (i = 0; i < len; i++) {
|
||||
+ t[n].upn_suffixes_len[i] = strlen(t[n].upn_suffixes[i]);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
ret = ipadb_ldap_attr_to_strlist(lc, le, "ipaNTSIDBlacklistIncoming",
|
||||
&sid_blacklist_incoming);
|
||||
|
||||
@@ -2972,6 +2991,17 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
||||
result = strncasecmp(test_realm,
|
||||
ipactx->mspac->trusts[i].upn_suffixes[j],
|
||||
size) == 0;
|
||||
+ if (!result) {
|
||||
+ /* if UPN suffix did not match exactly, find if it is
|
||||
+ * superior to the test_realm, e.g. if test_realm ends
|
||||
+ * with the UPN suffix prefixed with dot*/
|
||||
+ size_t len = ipactx->mspac->trusts[i].upn_suffixes_len[j];
|
||||
+ if ((size > len) && (test_realm[size - len - 1] == '.')) {
|
||||
+ result = strncasecmp(test_realm + (size - len),
|
||||
+ ipactx->mspac->trusts[i].upn_suffixes[j],
|
||||
+ len) == 0;
|
||||
+ }
|
||||
+ }
|
||||
if (result)
|
||||
break;
|
||||
}
|
||||
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac_private.h b/daemons/ipa-kdb/ipa_kdb_mspac_private.h
|
||||
index 30382d2ee..b21aa163f 100644
|
||||
--- a/daemons/ipa-kdb/ipa_kdb_mspac_private.h
|
||||
+++ b/daemons/ipa-kdb/ipa_kdb_mspac_private.h
|
||||
@@ -48,6 +48,7 @@ struct ipadb_adtrusts {
|
||||
struct ipadb_adtrusts *parent;
|
||||
char *parent_name;
|
||||
char **upn_suffixes;
|
||||
+ size_t *upn_suffixes_len;
|
||||
};
|
||||
|
||||
int string_to_sid(const char *str, struct dom_sid *sid);
|
||||
--
|
||||
2.29.2
|
||||
|
@ -0,0 +1,57 @@
|
||||
From 6b224e57672e3f73f93bb9eddd9031e945529a1e Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Date: Tue, 24 Nov 2020 16:03:36 +0200
|
||||
Subject: [PATCH] ad trust: accept subordinate domains of the forest trust root
|
||||
|
||||
Commit 8b6d1ab854387840f7526d6d59ddc7102231957f added support for
|
||||
subordinate UPN suffixes but missed the case where subordinate UPN is a
|
||||
subdomain of the forest root domain and not mentioned in the UPN
|
||||
suffixes list.
|
||||
|
||||
Correct this situation by applying the same check to the trusted domain
|
||||
name as well.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/8554
|
||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
daemons/ipa-kdb/ipa_kdb_mspac.c | 12 +++++++++++-
|
||||
1 file changed, 11 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
||||
index f2bd60e11..c6ac593ca 100644
|
||||
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
|
||||
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
||||
@@ -2976,10 +2976,20 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
||||
|
||||
/* Iterate through list of trusts and check if input realm belongs to any of the trust */
|
||||
for(i = 0 ; i < ipactx->mspac->num_trusts ; i++) {
|
||||
+ size_t len = 0;
|
||||
result = strncasecmp(test_realm,
|
||||
ipactx->mspac->trusts[i].domain_name,
|
||||
size) == 0;
|
||||
|
||||
+ if (!result) {
|
||||
+ len = strlen(ipactx->mspac->trusts[i].domain_name);
|
||||
+ if ((size > len) && (test_realm[size - len - 1] == '.')) {
|
||||
+ result = strncasecmp(test_realm + (size - len),
|
||||
+ ipactx->mspac->trusts[i].domain_name,
|
||||
+ len) == 0;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
if (!result && (ipactx->mspac->trusts[i].flat_name != NULL)) {
|
||||
result = strncasecmp(test_realm,
|
||||
ipactx->mspac->trusts[i].flat_name,
|
||||
@@ -2995,7 +3005,7 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
||||
/* if UPN suffix did not match exactly, find if it is
|
||||
* superior to the test_realm, e.g. if test_realm ends
|
||||
* with the UPN suffix prefixed with dot*/
|
||||
- size_t len = ipactx->mspac->trusts[i].upn_suffixes_len[j];
|
||||
+ len = ipactx->mspac->trusts[i].upn_suffixes_len[j];
|
||||
if ((size > len) && (test_realm[size - len - 1] == '.')) {
|
||||
result = strncasecmp(test_realm + (size - len),
|
||||
ipactx->mspac->trusts[i].upn_suffixes[j],
|
||||
--
|
||||
2.29.2
|
||||
|
@ -149,7 +149,7 @@
|
||||
|
||||
Name: %{package_name}
|
||||
Version: %{IPA_VERSION}
|
||||
Release: 13%{?dist}
|
||||
Release: 14%{?dist}
|
||||
Summary: The Identity, Policy and Audit system
|
||||
|
||||
License: GPLv3+
|
||||
@ -185,6 +185,12 @@ Patch0018: 0018-dogtaginstance.py-add-debug-to-pkispawn_rhbz#1879604.patch
|
||||
Patch0019: 0019-SELinux-add-dedicated-policy-for-ipa-pki-retrieve-key-ipatests-enhance-TestSubCAkeyReplication_rhbz#1870202.patch
|
||||
Patch0020: 0020-SELinux-do-not-double-define-node_t-and-pki_tomcat_c_rhbz#1870202.patch
|
||||
Patch0021: 0021-Fix-nsslapd-db-lock-tuning-of-BDB-backend_rhbz#1882472.patch
|
||||
Patch0022: 0022-rpcserver-fallback-to-non-armored-kinit-in-case-of-trusted-domains_rhbz#1914821.patch
|
||||
Patch0023: 0023-pylint-remove-unused-variable_rhbz#1914821.patch
|
||||
Patch0024: 0024-wgi-plugins.py-ignore-empty-plugin-directories_rhbz#1895910.patch
|
||||
Patch0025: 0025-ipatests-support-subordinate-upn-suffixes_rhbz#1914823.patch
|
||||
Patch0026: 0026-ipa-kdb-support-subordinate-superior-UPN-suffixes_rhbz#1914823.patch
|
||||
Patch0027: 0027-ad-trust-accept-subordinate-domains-of-the-forest-trust-root_rhbz#1914823.patch
|
||||
Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch
|
||||
Patch1002: 1002-4.8.0-Remove-csrgen.patch
|
||||
Patch1003: 1003-Revert-WebUI-use-python3-rjsmin-to-minify-JavaScript.patch
|
||||
@ -1535,6 +1541,20 @@ fi
|
||||
|
||||
|
||||
%changelog
|
||||
* Tue Jan 12 2021 Rafael Jeffman <rjeffman@redhat.com> - 4.8.7-14
|
||||
- wgi/plugins.py: ignore empty plugin directories
|
||||
Resolves: RHBZ#1895910
|
||||
- rpcserver: fallback to non-armored kinit in case of trusted domains
|
||||
Resolves: RHBZ#1914821
|
||||
- pylint: remove unused variable
|
||||
Resolves: RHBZ#1914821
|
||||
- ipa-kdb: support subordinate/superior UPN suffixes
|
||||
Resolves: RHBZ#1914823
|
||||
- ad trust: accept subordinate domains of the forest trust root
|
||||
Resolves: RHBZ#1914823
|
||||
- ipatests: support subordinate upn suffixes
|
||||
Resolves: RHBZ#1914823
|
||||
|
||||
* Thu Oct 08 2020 Thomas Woerner <twoerner@redhat.com> - 4.8.7-13
|
||||
- Fix nsslapd-db-lock tuning of BDB backend
|
||||
Resolves: RHBZ#1882472
|
||||
|
Loading…
Reference in New Issue
Block a user